/* Voinly design system ---------------------------------------------------
   Master design spec §4 (tokens) and §5 (motion). Canvas #212831, primary blue #0084ff for actions,
   green #22c55e only for "on". UI font: Onest. Discord previews: gg sans / Noto Sans. */

:root {
  /* §4.1 surfaces */
  --clr-canvas:         #212831;
  --clr-surface-dark:   #1b2028;
  --clr-surface-darker: #181f25;
  --clr-dirtybar:       #141a1f;
  --clr-card-bg:        rgba(189, 189, 189, 0.05);
  --clr-card-hover:     rgba(255, 255, 255, 0.04);
  --clr-input-bg:       #333e4c;
  --clr-input-inset:    rgba(0, 0, 0, 0.25);
  --clr-backdrop:       rgba(0, 0, 0, 0.8);

  /* §4.2 borders */
  --border-subtle:  rgba(255, 255, 255, 0.07);
  --border-default: rgba(255, 255, 255, 0.10);
  --border-strong:  #2d3743;
  --border-focus:   #0084ff;
  /* The focus indicator of a native field (§4.2). --border-focus on the input background measures 2.97:1, under the
     3:1 a focus indicator owes, so a focused field draws the lighter blue --sky already carries: 2px, offset 2px from
     the control so it sits on the surface the field stands on, and the same colour on its border. 5.06:1 on
     --clr-input-bg #333e4c, 6.93:1 on --clr-canvas #212831, 7.62:1 on --clr-surface-dark #1b2028. --focus-ring-invalid
     is the ring of a focused invalid field, so it still reads invalid while it reads focused (5.46:1 and 7.48:1). */
  --focus-ring:         #5fb8ff;
  --focus-ring-soft:    rgba(95, 184, 255, 0.18);
  --focus-ring-invalid: #ff9d9d;
  --border-dashed:  1px dashed rgba(255, 255, 255, 0.18);

  /* §4.3 actions and status */
  --clr-primary:        #0084ff;
  --clr-primary-hover:  #1785cf;
  --clr-primary-active: #0074c2;
  --clr-primary-soft:   rgba(0, 132, 255, 0.15);
  --clr-success:        #22c55e;
  --clr-success-btn:    #30b530;
  --clr-success-btn-hover: #39cb39;
  --clr-warning:        #fa972e;
  --clr-danger:         #ce3636;
  --clr-danger-hover:   #e65b5b;
  --clr-danger-text:    #ff8a8a;  /* error text on dark surfaces (MAR-667 ui §1.2): 4.79:1 on the card */
  --clr-discord:        #5865f2;
  --clr-secondary-btn:  #2d3743;

  /* §4.4 type */
  --font-ui: "Onest", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  --dc-font: "gg sans", "Noto Sans", "Source Sans 3", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --dc-font-code: "gg mono", "Source Code Pro", Consolas, "Andale Mono WT", monospace;
  --font-discord: var(--dc-font);
  --font-code: ui-monospace, "SF Mono", "Cascadia Code", "Fira Code", Menlo, Consolas, monospace;
  --text-primary:   #ffffff;
  --text-secondary: #c2cbd6;
  --text-tertiary:  #9caabc;  /* 4.6:1 on inputs #333e4c, 5.7:1 on cards #292f38 (WCAG AA body text) */
  --text-disabled:  rgba(255, 255, 255, 0.28);

  /* §4.5 geometry. --radius-lg is only for cards, path bars and the dirty bar; modal chrome is a literal 12px. */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 10px;
  --radius-xl: 15px;
  --radius-full: 9999px;
  --topbar-height: 50px;
  --sidebar-width: 300px;
  --content-max-width: 1000px;

  /* §5.1 motion */
  --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out:    cubic-bezier(0.0, 0, 0.2, 1);
  --ease-in:     cubic-bezier(0.4, 0, 1, 1);
  --ease-exit:   cubic-bezier(0.4, 0, 1, 1);
  --dur-fast:  120ms;
  --dur-base:  200ms;
  --dur-enter: 250ms;

  /* Older names used across module templates, mapped onto the tokens above so every page picks them up. */
  --ink-0: var(--clr-surface-dark);
  --ink-1: #262d37;
  --ink-2: var(--clr-secondary-btn);
  --ink-3: var(--clr-input-bg);
  --ink-4: #3d4a5c;
  --line: var(--border-default);
  --line-soft: var(--border-subtle);
  --text: var(--text-primary);
  --text-2: var(--text-secondary);
  --muted: var(--text-tertiary);
  --iris: var(--clr-primary);
  --iris-btn: var(--clr-primary);
  --iris-btn-hover: var(--clr-primary-hover);
  --iris-soft: var(--clr-primary-soft);
  --mint: var(--clr-success);
  --mint-soft: rgba(34, 197, 94, 0.13);
  --amber: var(--clr-warning);
  --amber-soft: rgba(250, 151, 46, 0.12);
  --rose: var(--clr-danger-hover);
  --rose-btn: var(--clr-danger);
  --rose-soft: rgba(206, 54, 54, 0.14);
  --sky: #5fb8ff;
  --font-display: var(--font-ui);
  --r-sm: var(--radius-sm);
  --r-md: var(--radius-md);
  --r-lg: var(--radius-lg);
  --r-xl: var(--radius-xl);
  --sidebar-w: var(--sidebar-width);
  --content-w: var(--content-max-width);
  --ease: var(--ease-smooth);
  --shadow-pop: 0 18px 50px -12px rgba(0, 0, 0, .7), 0 0 0 1px var(--border-default);
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--clr-canvas);
  color: var(--text-primary);
  font: 400 15px/1.5 var(--font-ui);
  font-feature-settings: "lnum" 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img { max-width: 100%; display: block; }
a { color: var(--clr-primary); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
button, input, select, textarea { font: inherit; color: inherit; font-feature-settings: "lnum" 1; }
input[type=checkbox], input[type=radio] { accent-color: var(--clr-primary); }
[x-cloak] { display: none !important; }
:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; border-radius: 4px; }
::selection { background: rgba(0, 132, 255, .35); }
.icon { flex-shrink: 0; display: inline-block; vertical-align: middle; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* §4.4 type scale: H1 35/700, H2 22/600, card and path titles 20/500, body 15/400, caption 13 */
h1, h2, h3, h4 { font-family: var(--font-ui); font-weight: 700; margin: 0; }
h1 { font-size: 2.2rem; line-height: 1.2; letter-spacing: -0.015em; }
h2 { font-size: 1.4rem; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
h3 { font-size: 16.5px; font-weight: 600; line-height: 1.3; }
p { margin: 0; }
.muted { color: var(--text-tertiary); }
.text-2 { color: var(--text-secondary); }
.small { font-size: 13px; }
code, kbd { font-family: var(--font-code); font-size: .88em; background: var(--clr-surface-dark); border: 1px solid var(--border-default); padding: 1px 6px; border-radius: 5px; }

/* ---------------------------------------------------------------- buttons (radius 6px) */
.btn {
  --btn-bg: var(--clr-secondary-btn); --btn-fg: var(--text-primary); --btn-bd: transparent;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 16px; border-radius: var(--radius-sm);
  background: var(--btn-bg); color: var(--btn-fg); border: 1px solid var(--btn-bd);
  font-weight: 500; font-size: 15px; cursor: pointer; white-space: nowrap;
  transition: background-color 150ms ease, border-color 150ms ease, transform 80ms ease;
  text-decoration: none !important;
}
.btn:hover { --btn-bg: #3a4655; }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn.is-loading { opacity: .6; pointer-events: none; }
.btn-secondary { --btn-bg: var(--clr-secondary-btn); --btn-fg: var(--text-primary); }
.btn-secondary:hover { --btn-bg: #3a4655; }
.btn-primary { --btn-bg: var(--clr-primary); --btn-fg: #fff; }
.btn-primary:hover { --btn-bg: var(--clr-primary-hover); }
.btn-primary:active { --btn-bg: var(--clr-primary-active); }
.btn-success { --btn-bg: var(--clr-success-btn); --btn-fg: #fff; }
.btn-success:hover { --btn-bg: var(--clr-success-btn-hover); }
.btn-danger { --btn-bg: var(--clr-danger); --btn-fg: #fff; }
.btn-danger:hover { --btn-bg: var(--clr-danger-hover); }
.btn-ghost { --btn-bg: transparent; --btn-fg: var(--text-secondary); }
.btn-ghost:hover { --btn-bg: var(--clr-card-hover); --btn-fg: var(--text-primary); }
.btn-outline { --btn-bg: transparent; --btn-bd: var(--border-default); --btn-fg: var(--text-primary); }
.btn-outline:hover { --btn-bg: var(--clr-card-hover); --btn-bd: rgba(255, 255, 255, .18); }
.btn-sm { height: 31px; padding: 0 11px; font-size: 13px; gap: 6px; }
.btn-lg { height: 48px; padding: 0 22px; font-size: 15.5px; }
.btn-icon { width: 34px; padding: 0; }
.btn-sm.btn-icon { width: 30px; }
.btn .spinner { width: 14px; height: 14px; }
.spinner { border: 2px solid rgba(255,255,255,.25); border-top-color: currentColor; border-radius: 50%; animation: spin .7s linear infinite; display: inline-block; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------- app shell (§3.1) */
/* Topbar fixed 50px across the top; sidebar fixed 300px under it; main column max 1000px, 24px gutters. */
.shell { min-height: 100vh; }
.sidebar {
  position: fixed; z-index: 40; left: 0; top: var(--topbar-height); bottom: 0; width: var(--sidebar-width);
  background: var(--clr-canvas); box-shadow: inset -50px 0 50px 1px #1f252e;
  display: flex; flex-direction: column; overflow: hidden;
}
.sidebar-top { display: flex; gap: 6px; align-items: center; padding: 14px 12px 6px 14px; }
.sidebar-util {
  flex: 1; min-width: 0; display: inline-flex; align-items: center; justify-content: center; gap: 7px; height: 38px; padding: 0 8px;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); background: var(--clr-card-bg);
  color: var(--text-secondary); font-size: 13px; font-weight: 500; cursor: pointer; white-space: nowrap; text-decoration: none !important;
  transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}
.sidebar-util span { overflow: hidden; text-overflow: ellipsis; }
.sidebar-util:hover { background: var(--clr-card-hover); color: var(--text-primary); border-color: rgba(255, 255, 255, .14); }
.sidebar-util.active { color: #fff; background: rgba(255, 255, 255, .07); }
.sidebar-util.compact { flex: 0 0 auto; padding: 0 12px; }
.sidebar-util .icon { color: var(--text-tertiary); }
.sidebar-util.spinning .icon { animation: spin 600ms var(--ease-smooth) infinite; color: var(--clr-primary); }
.drawer-close { display: none; flex-shrink: 0; }
.server-switch {
  min-width: 0; max-width: 260px; display: flex; align-items: center; gap: 10px; height: 38px; padding: 0 10px 0 3px;
  background: rgba(255, 255, 255, .05); border: 1px solid var(--border-subtle); border-radius: var(--radius-full); cursor: pointer; text-align: left;
  transition: background-color 150ms ease, border-color 150ms ease;
}
.server-switch:hover, .server-switch[aria-expanded="true"] { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .14); }
.server-switch .name { font-weight: 600; font-size: 14px; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.server-switch .sub { display: none; }
.server-switch .grow { min-width: 0; flex: 1; }
.guild-avatar {
  width: 36px; height: 36px; border-radius: 11px; flex-shrink: 0; overflow: hidden;
  display: grid; place-items: center; background: var(--clr-input-bg); color: var(--text-secondary);
  font-family: var(--font-ui); font-weight: 700; font-size: 13px; letter-spacing: -.02em;
}
.guild-avatar img { width: 100%; height: 100%; object-fit: cover; }
.server-switch .guild-avatar { width: 32px; height: 32px; border-radius: 50%; font-size: 11.5px; }
.guild-avatar.lg { width: 64px; height: 64px; border-radius: 18px; font-size: 20px; }
.guild-avatar.xs { width: 24px; height: 24px; border-radius: 7px; font-size: 9.5px; }
.topbar-guild { position: relative; min-width: 0; display: flex; }
.topbar-guild .server-menu { left: 0; right: auto; width: 300px; top: calc(100% + 8px); max-height: min(420px, calc(100vh - 70px)); }
.server-menu .grow-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-sep { height: 1px; background: var(--border-default); margin: 6px 4px; }
.guild-avatar.xl { width: 84px; height: 84px; border-radius: 24px; font-size: 26px; }

.nav { flex: 1; overflow-y: auto; padding: 6px 14px 24px; scrollbar-width: thin; scrollbar-color: #3d4a5c transparent; }
.nav-search { position: relative; margin: 4px 0 12px; }
.nav-search input { width: 100%; height: 38px; border-radius: var(--radius-md); background: var(--clr-surface-dark); border: 1px solid var(--border-subtle); padding: 0 34px 0 34px; font-size: 14px; transition: border-color 150ms ease, box-shadow 150ms ease; }
.nav-search input::placeholder { color: var(--text-tertiary); }
.nav-search input:focus { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-color: var(--focus-ring); box-shadow: 0 0 0 3px var(--focus-ring-soft); }
.nav-search input::-webkit-search-cancel-button {
  -webkit-appearance: none; appearance: none; width: 14px; height: 14px; cursor: pointer; opacity: .7;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='none' stroke='%238596ad' stroke-width='2.4' stroke-linecap='round' viewBox='0 0 24 24'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E") center/14px no-repeat;
}
.nav-search input::-webkit-search-cancel-button:hover { opacity: 1; }
.nav-search .icon { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--text-tertiary); pointer-events: none; }
.nav-kbd { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); height: 20px; min-width: 20px; display: grid; place-items: center; padding: 0 5px; font-size: 11.5px; color: var(--text-tertiary); background: var(--clr-canvas); border: 1px solid var(--border-default); border-radius: 5px; pointer-events: none; }
.nav-empty { padding: 18px 12px; color: var(--text-tertiary); font-size: 13.5px; text-align: center; line-height: 1.5; }
.nav-empty strong { color: var(--text-secondary); font-weight: 600; word-break: break-word; }
.nav-group { margin-top: 16px; }
.nav-group:first-child { margin-top: 0; }
/* Section headers (§4.4 sidebar section): uppercase .77rem / 600 at 50% opacity */
.nav-heading { font-size: 0.77rem; font-weight: 600; line-height: 1.4; letter-spacing: .06em; text-transform: uppercase; color: rgba(255, 255, 255, .5); padding: 0 12px 6px; }
.nav-row { position: relative; }
.nav-link {
  display: flex; align-items: center; gap: 11px; height: 43px; padding: 0 12px; border-radius: var(--radius-md);
  color: var(--text-secondary); font-size: 15px; font-weight: 500; text-decoration: none !important; position: relative;
  transition: background-color 150ms ease, color 150ms ease;
}
.nav-link .icon { color: var(--text-tertiary); transition: color 150ms ease; }
.nav-link:hover { background: var(--clr-card-hover); color: var(--text-primary); }
.nav-link.active { background: rgba(255, 255, 255, .07); color: #fff; }
.nav-link.active::before { content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 2px; border-radius: 2px; background: var(--clr-primary); }
.nav-link.active .icon { color: var(--clr-primary); }
.nav-link .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-row.has-switch .nav-link { padding-right: 62px; }
.nav-row > .switch-sapphire { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); }
.nav-sub .nav-link { padding-left: 40px; height: 38px; font-size: 14px; }
.nav-sub .nav-link.active::before { left: 28px; }
.nav-fold { width: 100%; border: 0; background: transparent; cursor: pointer; text-align: left; font: inherit; }
.nav-fold .chevron-icon { transition: transform 200ms ease, color 150ms ease; }
.nav-fold[aria-expanded="true"] .chevron-icon { transform: rotate(90deg); }
/* status light kept for older markup; the sidebar now shows inline switches */
.led { width: 7px; height: 7px; border-radius: 50%; background: #3d4a5c; flex-shrink: 0; transition: background .2s, box-shadow .2s; }
.led.on { background: var(--clr-success); box-shadow: 0 0 0 3px var(--mint-soft); }
.sidebar-foot { border-top: 1px solid var(--border-subtle); padding: 8px 14px; position: relative; }
.sidebar-foot .nav-link { height: 38px; font-size: 14px; }
.avatar { width: 32px; height: 32px; border-radius: 50%; object-fit: cover; background: var(--clr-input-bg); flex-shrink: 0; }
.avatar-initial { display: inline-grid; place-items: center; color: var(--text-secondary); font-weight: 700; font-size: 12.5px; }
.user-btn { display: flex; align-items: center; gap: 9px; height: 38px; padding: 0 10px 0 3px; border: 1px solid var(--border-subtle); border-radius: var(--radius-full); background: rgba(255, 255, 255, .05); cursor: pointer; text-align: left; max-width: 220px; transition: background-color 150ms ease, border-color 150ms ease; }
.user-btn:hover, .user-btn[aria-expanded="true"] { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .14); }
.user-btn .who { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }
.user-btn .who .name { font-size: 14px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-btn .who .sub { display: none; }
.topbar-user { position: relative; }
.topbar-user .user-menu { left: auto; right: 0; width: 240px; top: calc(100% + 8px); }
.picker-opt.danger { color: var(--clr-danger-hover); }
.picker-opt.danger .icon { color: var(--clr-danger-hover); }
.picker-opt.danger:hover { background: var(--rose-soft); color: var(--clr-danger-hover); }
.picker-list form { margin: 0; }

.main { min-width: 0; margin-left: var(--sidebar-width); padding-top: var(--topbar-height); display: flex; flex-direction: column; }
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50; height: var(--topbar-height);
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 12px;
  padding: 0 14px; background: var(--clr-surface-dark); border-bottom: 1px solid var(--border-subtle);
}
.topbar-left, .topbar-right { display: flex; align-items: center; gap: 8px; min-width: 0; }
.topbar-right { justify-content: flex-end; }
/* Breadcrumb: Guild › Module › Sub-page, chevrons at .65em, non-final crumbs are links */
.crumbs { display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text-tertiary); min-width: 0; max-width: min(56vw, 640px); }
.crumbs a { color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex-shrink: 1; transition: color 150ms ease; }
.crumbs a:hover { color: var(--text-primary); text-decoration: none; }
.crumbs .icon { color: var(--text-tertiary); width: .65em; height: .65em; min-width: 9px; min-height: 9px; opacity: .8; }
.crumbs .current { color: var(--text-primary); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex-shrink: 0; max-width: 100%; }
.help-btn {
  width: 45px; height: 45px; display: inline-grid; place-items: center; padding: 0; flex-shrink: 0;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); background: rgba(255, 255, 255, .05); color: var(--text-secondary); cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
}
.help-btn:hover { background: rgba(255, 255, 255, .09); color: #fff; border-color: rgba(255, 255, 255, .14); }
.topbar .spacer { flex: 1; }
.menu-btn { display: none; }
.content { width: 100%; max-width: calc(var(--content-max-width) + 48px); margin: 0 auto; padding: 32px 24px 140px; animation: page-fade 200ms var(--ease-out), page-rise 200ms var(--ease-spring); }

/* ---------------------------------------------------------------- page header */
.page-head { display: flex; align-items: center; gap: 16px; margin-bottom: 26px; }
.page-head .glyph {
  width: 48px; height: 48px; border-radius: var(--radius-lg); display: grid; place-items: center; flex-shrink: 0;
  background: var(--clr-primary-soft); color: var(--clr-primary);
}
.page-head.is-on .glyph { color: var(--clr-success); background: rgba(34, 197, 94, .12); }
.page-head .titles { flex: 1; min-width: 0; }
.page-head .title-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.page-head .head-crumb { color: var(--text-tertiary); text-decoration: none !important; transition: color 150ms ease; }
.page-head .head-crumb:hover { color: var(--text-primary); }
.page-head .head-sep { color: var(--text-tertiary); width: .65em; height: .65em; }
.hub-tools { display: flex; justify-content: flex-end; margin: -8px 0 10px; }
.page-head h1 { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.page-head p { color: var(--text-secondary); margin-top: 4px; max-width: 64ch; }
.page-head .actions { display: flex; align-items: center; gap: 10px; }
.page-head .power { display: inline-flex; align-items: center; gap: 10px; }
.state-label { font-size: 13px; font-weight: 600; color: var(--text-tertiary); min-width: 22px; text-align: right; }
.state-label.on { color: var(--clr-success); }
.badge { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; background: var(--clr-input-bg); color: var(--text-secondary); font-family: var(--font-ui); letter-spacing: 0; }
.badge.mint { background: var(--mint-soft); color: var(--clr-success); }
.badge.iris { background: var(--clr-primary-soft); color: #6cb6ff; }
.badge.amber { background: var(--amber-soft); color: var(--clr-warning); }
.badge.rose { background: var(--rose-soft); color: var(--clr-danger-hover); }
.nav-link .nav-count { flex-shrink: 0; height: 19px; padding: 0 7px; background: var(--clr-danger); color: #fff; }

.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border-subtle); margin: -8px 0 26px; overflow-x: auto; }
.tab { padding: 10px 14px; color: var(--text-tertiary); font-weight: 500; font-size: 15px; border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; text-decoration: none !important; transition: color 150ms ease, border-color 150ms ease; }
.tab:hover { color: var(--text-primary); }
.tab.active { color: var(--text-primary); border-color: var(--clr-primary); }
.tab .tab-count { display: inline-grid; place-items: center; box-sizing: border-box; min-width: 18px; height: 18px; margin-left: 6px; padding: 0 4px; border-radius: 9px; background: #333e4c; color: #fff; font-size: 12px; font-weight: 700; line-height: 1; vertical-align: 1px; }

/* ---------------------------------------------------------------- settings document */
.doc { background: var(--clr-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); }
.doc + .doc { margin-top: 18px; }
.sec { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 28px; padding: 26px 28px; }
.sec + .sec { border-top: 1px solid var(--line-soft); }
.sec-rail h3 { display: flex; align-items: center; gap: 8px; font-size: 1.3rem; font-weight: 500; line-height: 1.3; }
.sec-rail h3 .icon { color: var(--muted); }
.sec-rail p { color: var(--text-secondary); font-size: 13.5px; margin-top: 6px; line-height: 1.5; }
.sec-body { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 18px 16px; align-content: start; }
.sec.stack { grid-template-columns: 1fr; gap: 16px; }
.fld { grid-column: span 6; min-width: 0; }
.fld.half { grid-column: span 3; }
.fld.third { grid-column: span 2; }
.fld > label, .fld-label { display: block; font-weight: 500; font-size: 15px; margin-bottom: 7px; color: var(--text); }
.fld-help { color: var(--text-tertiary); font-size: 13px; margin-top: 6px; line-height: 1.45; }
.fld-error { color: var(--rose); font-size: 12.5px; margin-top: 6px; line-height: 1.45; }
.fld-note { color: var(--amber); font-size: 12.5px; margin-top: 6px; line-height: 1.45; }
/* n/max under short capped text fields (VQ-BUG-004): quiet until the cap, then danger red. */
.fld-count { color: var(--text-tertiary); font-size: 12.5px; margin-top: 5px; line-height: 1.4; text-align: right; font-variant-numeric: tabular-nums; }
.fld-count.full { color: var(--clr-danger); }
/* show_if reveals (UX-BUG-10): 150 ms fade + 4px slide; reduced motion shows them at once (see the media query). */
.fld-enter { transition: opacity 150ms var(--ease-smooth), transform 150ms var(--ease-smooth); }
.fld-enter-from { opacity: 0; transform: translateY(-4px); }
.fld-enter-to { opacity: 1; transform: none; }
.input.invalid, .input.invalid:focus { border-color: var(--rose); box-shadow: 0 0 0 3px var(--rose-soft); }
/* MAR-4235: a focused invalid field used to draw exactly what an unfocused one drew. The rose border stays (the field
   still reads invalid) and the ring in the lighter rose says focused, at >=3:1 against both adjacent colours. */
.input.invalid:focus { outline: 2px solid var(--focus-ring-invalid); outline-offset: 2px; }
.fld-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; }
.fld-row .fld-text { min-width: 0; }
.fld-row .fld-label { margin-bottom: 2px; }
.fld-row .fld-help { margin-top: 0; }
.info-note {
  display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: 10px;
  background: var(--clr-card-bg); border: 1px solid var(--border-subtle); color: var(--text-2); font-size: 14px;
}
.info-note .icon { color: var(--sky); margin-top: 1px; }
.info-note .info-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.info-note .info-links .icon { color: currentColor; margin: 0; }
.info-note.warn { background: var(--amber-soft); border-color: rgba(255, 192, 97, .25); }
.info-note.warn .icon { color: var(--amber); }
.info-note.danger { background: var(--rose-soft); border-color: rgba(255, 107, 127, .25); }
.info-note.danger .icon { color: var(--rose); }

/* inputs */
.input, .select, .textarea {
  width: 100%; min-height: 40px; padding: 9px 12px; border-radius: var(--radius-md);
  background: var(--clr-input-bg); border: 1px solid var(--border-strong); color: var(--text); font-size: 15px;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
.input::placeholder, .textarea::placeholder { color: var(--text-tertiary); }
.input:hover, .select:hover, .textarea:hover { border-color: #46546a; }
.input:focus, .select:focus, .textarea:focus { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-color: var(--focus-ring); box-shadow: 0 0 0 3px var(--focus-ring-soft); }
.textarea { resize: vertical; min-height: 92px; line-height: 1.5; }
.select { appearance: none; padding-right: 34px; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%238596ad' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 11px center; cursor: pointer; }
.input-group { display: flex; align-items: stretch; }
.input-group .input { border-radius: var(--r-md) 0 0 var(--r-md); }
.input-group .addon { display: flex; align-items: center; padding: 0 12px; background: var(--ink-2); border: 1px solid var(--line); border-left: 0; border-radius: 0 var(--r-md) var(--r-md) 0; color: var(--muted); font-size: 13px; white-space: nowrap; }
.duration-input { max-width: 340px; }
.input-group .select.addon-select { width: auto; border-radius: 0 var(--r-md) var(--r-md) 0; border-left: 0; background-color: var(--ink-2); }
/* MAR-4616 (MAR-3773 r3, QA item 1): the controls of a group share one border -- `.addon` and `.addon-select` have
   `border-left: 0` and `.input-group` has no gap -- so the 2px ring at a 2px offset reaches 4px past the focused
   control into its neighbour's box, and the neighbour, painted after it, covers that part of the ring (QA measured 18
   covered ring sides over Verification, Welcome, Tickets and Logging, and the Welcome "Delete after" select touching
   the number input with no gap at all). Every control of a group therefore draws the same 2px ring *inside* its own
   box, 4px in, so 2px of the control's own background separates it from the neighbour's box: the ring stays inside
   the group, it never touches the neighbour and nothing is left to cover it. The soft glow of §4.2 is dropped here
   because a 3px spread would cross the same seam the ring used to cross. The colour is the one §4.2 already names, so
   the >=3:1 against the field and the surface stands (5.06:1 / 6.26:1). */
.input-group .input:focus, .input-group .select:focus, .input-group .textarea:focus,
.input-group .addon:focus-visible { outline-offset: -4px; box-shadow: none; }
input[type=number].input { -moz-appearance: textfield; }
input[type=number]::-webkit-inner-spin-button { opacity: .35; }

/* switch (older markup): same geometry and physics as .switch-sapphire (§5.2.4) */
.switch { position: relative; width: 45px; height: 24px; flex-shrink: 0; display: inline-block; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; z-index: 1; }
.switch .track { position: absolute; inset: 0; border-radius: var(--radius-full); background: rgba(255, 255, 255, 0.10); transition: background-color 200ms var(--ease-smooth); }
.switch .track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 9px; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.35); transition: transform 200ms var(--ease-spring), width 150ms ease; }
.switch input:checked + .track { background: var(--clr-success); }
.switch input:checked + .track::after { transform: translateX(21px); }
.switch input:active + .track::after { width: 21px; }
.switch input:checked:active + .track::after { transform: translateX(18px); }
.switch input:focus-visible + .track { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.switch input:disabled { cursor: default; }
.switch input:disabled + .track { opacity: .55; }

/* segmented control (small option sets) */
.segmented { display: inline-flex; background: var(--clr-surface-dark); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 3px; gap: 2px; flex-wrap: wrap; }
.segmented button { border: 0; background: transparent; color: var(--text-2); padding: 6px 12px; border-radius: var(--radius-sm); font-size: 14px; font-weight: 500; cursor: pointer; transition: background-color 150ms ease, color 150ms ease; }
.segmented button:hover { color: var(--text); }
.segmented button.on { background: #394556; color: #fff; box-shadow: 0 1px 0 rgba(255,255,255,.04) inset; }

/* D8: on a phone every option of a segmented group takes the full width of the group (one row each), so a group of
   long labels never wraps into a ragged two-line pill. .am-seg-inline is left in its row: it sits inside an automod
   sentence ("Trigger at [1] [spam] [in one message | over time]") and a full-width block there breaks the sentence. */
@media (max-width: 640px) {
  .segmented:not(.am-seg-inline) { display: flex; flex-direction: column; }
  .segmented button { min-height: 44px; }
}

/* multiple choice (multiselect fields): each option is a chip that toggles */
.choice-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.choice-chips button { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--ink-0); color: var(--text-2); font-size: 13.5px; font-weight: 600; cursor: pointer; transition: border-color .15s, background .15s, color .15s; }
.choice-chips button:hover { border-color: var(--ink-4); color: var(--text); }
.choice-chips button .icon { color: var(--muted); }
.choice-chips button.on { background: var(--clr-primary-soft); border-color: rgba(0, 132, 255, .55); color: #fff; }
.choice-chips button.on .icon { color: var(--iris); }

/* chips + pickers */
.picker { position: relative; }
.picker-control {
  min-height: 40px; width: 100%; display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  padding: 6px 34px 6px 8px; border-radius: var(--radius-md); background: var(--clr-input-bg); border: 1px solid var(--border-strong);
  cursor: pointer; text-align: left; position: relative; transition: border-color 150ms ease, box-shadow 150ms ease;
}
.picker-control:hover { border-color: #46546a; }
.picker.open .picker-control { border-color: var(--iris); box-shadow: 0 0 0 3px var(--iris-soft); }
.picker-control .caret { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); color: var(--muted); }
.picker-control .placeholder { color: var(--text-tertiary); padding-left: 4px; font-size: 14px; }
.picker-single { display: flex; align-items: center; gap: 8px; padding-left: 4px; font-size: 14px; min-width: 0; }
.picker-single span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 4px 0 9px; border-radius: 7px;
  background: var(--ink-2); border: 1px solid var(--line); font-size: 13px; font-weight: 500; max-width: 100%;
}
.chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip button { border: 0; background: transparent; color: var(--muted); width: 18px; height: 18px; border-radius: 4px; display: grid; place-items: center; cursor: pointer; padding: 0; }
.chip button:hover { background: var(--ink-4); color: var(--text); }
.role-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; background: #99aab5; }
.ch-ic { display: inline-flex; align-items: center; justify-content: center; width: 16px; flex-shrink: 0; color: var(--muted); }
.ch-ic .icon { width: 14px; height: 14px; }
.chip .ch-ic { width: 14px; margin-right: -2px; }
.picker-menu {
  position: absolute; z-index: 60; left: 0; right: 0; top: calc(100% + 6px); max-height: 320px; display: flex; flex-direction: column;
  background: var(--clr-surface-darker); border-radius: var(--radius-md); box-shadow: var(--shadow-pop); overflow: hidden;
  animation: pop-fade 150ms var(--ease-out);
}
.picker-menu.up { top: auto; bottom: calc(100% + 6px); }
.picker-search { padding: 8px; border-bottom: 1px solid var(--line); }
.picker-search input { width: 100%; height: 34px; border-radius: var(--radius-sm); background: var(--clr-input-bg); border: 1px solid var(--border-strong); padding: 0 10px; font-size: 14px; }
.picker-search input:focus { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-color: var(--focus-ring); }
.picker-list { overflow-y: auto; padding: 6px; scrollbar-width: thin; }
.picker-group { font-size: 11.5px; font-weight: 700; color: var(--muted); padding: 8px 8px 4px; }
.picker-opt { display: flex; align-items: center; gap: 9px; width: 100%; padding: 7px 8px; border: 0; background: transparent; border-radius: 7px; cursor: pointer; font-size: 14px; text-align: left; color: var(--text-2); text-decoration: none !important; }
.picker-opt:hover, .picker-opt.hl { background: rgba(255, 255, 255, .06); color: var(--text); }
.picker-opt.sel { color: #fff; }
.picker-opt .check { margin-left: auto; color: var(--iris); }
.picker-opt .icon { color: var(--muted); }
.picker-opt .warn { margin-left: auto; font-size: 11.5px; color: var(--amber); }
.picker-empty { padding: 14px; color: var(--muted); font-size: 13.5px; text-align: center; }

/* emoji picker */
.emoji-picker { max-width: 320px; }
.emoji-picker.fill { max-width: none; }
.emoji-picker .emoji-now { display: inline-flex; align-items: center; min-width: 0; max-width: 64px; overflow: hidden; }
.emoji-picker .emoji-now img { width: 20px; height: 20px; }
.picker-menu.emoji-menu { padding: 10px; max-height: 280px; overflow-y: auto; display: block; min-width: 264px; }
.emoji-grid { display: flex; flex-wrap: wrap; gap: 2px; }
/* A custom emoji whose image can't load (VQ-BUG-001): its :name: as a neutral chip on the same tile. */
.emoji-chip { display: inline-block; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 3px 7px; border: 1px solid var(--border-default); border-radius: 999px; font-size: 12px; font-weight: 500; line-height: 1.2; color: var(--text-tertiary); }
.emoji-grid .btn.has-emoji-chip { width: auto; padding: 0 4px; }
.emoji-picker .emoji-now .emoji-chip { max-width: 64px; padding: 2px 5px; }

/* tags input */
.tags { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 8px; min-height: 40px; border-radius: var(--radius-md); background: var(--clr-input-bg); border: 1px solid var(--border-strong); cursor: text; }
.tags:focus-within { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-color: var(--focus-ring); box-shadow: 0 0 0 3px var(--focus-ring-soft); }
.tags input { flex: 1; min-width: 120px; border: 0; background: transparent; outline: none; font-size: 14px; padding: 3px 4px; }

/* color input */
.color-input { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; container-type: inline-size; }
.color-swatch { width: 40px; height: 40px; border-radius: var(--r-md); border: 1px solid var(--line); position: relative; overflow: hidden; flex-shrink: 0; cursor: pointer; }
.color-swatch input { position: absolute; inset: -8px; width: 60px; height: 60px; opacity: 0; cursor: pointer; }
.swatches { display: flex; gap: 5px; flex-wrap: wrap; }
/* Narrow colour fields (half width, phones): the swatches get their own row instead of wrapping unevenly. */
@container (max-width: 480px) { .color-input .swatches { flex-basis: 100%; } }
.swatches button { width: 20px; height: 20px; border-radius: 6px; border: 1px solid rgba(255,255,255,.12); cursor: pointer; padding: 0; }

/* list editor (repeating items) */
.list { display: flex; flex-direction: column; gap: 10px; }
.list-item { border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--clr-card-bg); }
.list-item-head { display: flex; align-items: center; gap: 10px; padding: 10px 10px 10px 14px; cursor: pointer; user-select: none; }
.list-item-head .title { flex: 1; min-width: 0; font-weight: 600; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-item-head .title .muted { font-weight: 500; }
.list-item-head .chev { color: var(--muted); transition: transform 200ms ease; }
.list-item-head .li-actions { display: flex; gap: 2px; flex-shrink: 0; }
.list-item-head .li-off { flex-shrink: 0; }
.list-item.is-off > .list-item-head .title { color: var(--text-2); }
.li-more { display: none; position: relative; }
.li-menu { left: auto; right: 0; top: calc(100% + 4px); width: 180px; max-height: none; cursor: default; }
.picker-opt[disabled] { opacity: .45; pointer-events: none; }
.list-item.open .list-item-head .chev { transform: rotate(90deg); }
.list-item-body { padding: 4px 16px 18px; display: grid; grid-template-columns: repeat(6, minmax(0,1fr)); gap: 16px; border-top: 1px solid var(--line-soft); padding-top: 16px; }
.list-empty { border: var(--border-dashed); border-radius: var(--radius-lg); padding: 22px; text-align: center; color: var(--muted); font-size: 14px; }
.list-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* ---------------------------------------------------------------- message editor + Discord preview */
/* The editor is a size container so the preview can stack when the editor itself is narrow (a nested custom-command
   row), not only when the window is. The grid lives on .msg-layout because a container can't query its own size.
   No overflow:hidden anywhere here, so emoji/variable menus can open past the edges. */
.msg-editor { container-type: inline-size; border: 1px solid var(--line); border-radius: 12px; background: var(--ink-0); }
.msg-layout { display: grid; grid-template-columns: minmax(360px, 1fr) minmax(0, 604px); gap: 0; align-items: start; }
.msg-edit-pane { padding: 14px; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* Side variant: the frame is the right-hand cell and stays in view under the topbar while the controls scroll. */
.msg-layout > .dcf { position: sticky; top: calc(var(--topbar-height) + 16px); align-self: start; max-height: calc(100vh - 162px); display: flex; flex-direction: column; }
.msg-layout > .dcf > .dcf-body { overflow-y: auto; overscroll-behavior: contain; max-height: calc(100vh - 162px - 40px - 48px); }
.msg-editor.is-stacked .msg-layout { grid-template-columns: minmax(0, 1fr); }
.msg-editor.is-stacked .msg-layout > .dcf { position: static; max-height: none; align-self: stretch; }
.msg-editor.is-stacked .msg-layout > .dcf > .dcf-body { max-height: none; overflow-y: visible; }
@container (max-width: 899px) {
  .msg-layout { grid-template-columns: minmax(0, 1fr); }
  .msg-layout > .dcf { position: static; max-height: none; align-self: stretch; }
  .msg-layout > .dcf > .dcf-body { max-height: none; overflow-y: visible; }
}
.msg-limit { font-size: 12.5px; line-height: 1.45; color: var(--rose); }
.msg-limit.muted { color: var(--muted); }
.msg-vars-unknown { display: flex; flex-direction: column; gap: 2px; font-size: 12.5px; line-height: 1.45; color: var(--amber); }
.msg-toolbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.msg-toolbar .grow { flex: 1; }
.embed-card { border: 1px solid var(--line); border-radius: 10px; background: var(--ink-1); position: relative; }
.embed-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; border-radius: 10px 0 0 10px; background: var(--embed-color, var(--iris)); }
.embed-card-head { display: flex; align-items: center; gap: 8px; padding: 9px 10px 9px 16px; cursor: pointer; font-weight: 600; font-size: 13.5px; }
.embed-card-head .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.embed-card-body { padding: 4px 12px 14px 16px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.embed-card-body .full { grid-column: 1 / -1; }
.embed-card-body label { font-size: 12px; font-weight: 600; color: var(--muted); display: block; margin-bottom: 4px; }
.embed-card-body .input, .embed-card-body .textarea { min-height: 34px; padding: 7px 10px; font-size: 13.5px; }
.embed-card-body .textarea { min-height: 70px; }
.embed-fields { display: flex; flex-direction: column; gap: 8px; }
.embed-field { display: flex; flex-direction: column; gap: 6px; padding: 8px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--ink-0); }
.embed-field-top { display: flex; align-items: center; gap: 2px; }
.embed-field-top .input { flex: 1; min-width: 0; margin-right: 6px; }
.embed-inline { display: flex; align-items: center; gap: 6px; margin: 0 6px 0 0 !important; white-space: nowrap; font-size: 12.5px; color: var(--muted); cursor: pointer; }
.embed-card-body .textarea.embed-field-value { min-height: 34px; resize: none; }
.link-btn-row { display: grid; grid-template-columns: minmax(0, 1fr) 112px auto; gap: 6px; align-items: center; padding: 8px; border: 1px solid var(--line-soft); border-radius: 9px; background: var(--ink-0); }
.link-btn-row .lb-url { grid-column: 1 / -1; grid-row: 2; }
.emoji-picker.align-end .emoji-menu { left: auto; right: 0; }
.msg-tabs { display: none; }
.var-menu { position: relative; }
.var-pop { position: absolute; z-index: 70; top: calc(100% + 6px); right: 0; width: 320px; max-height: 340px; overflow-y: auto; background: var(--ink-2); border-radius: 12px; box-shadow: var(--shadow-pop); padding: 6px; }
.var-pop .grp { font-size: 11.5px; font-weight: 700; color: var(--muted); padding: 8px 8px 4px; text-transform: capitalize; }
.var-pop button { display: flex; width: 100%; gap: 10px; justify-content: space-between; align-items: baseline; padding: 6px 8px; border: 0; background: transparent; border-radius: 6px; cursor: pointer; text-align: left; color: var(--text-2); font-size: 13px; }
.var-pop button:hover { background: var(--ink-3); color: var(--text); }
.var-pop button code { background: transparent; border: 0; padding: 0; color: #b7adff; }
.var-pop button span { color: var(--muted); font-size: 12px; text-align: right; }
.char-count { font-size: 11.5px; color: var(--muted); text-align: right; }
.char-count.over { color: var(--rose); }

/* ---------------------------------------------------------------- Discord frame (.dcf) and message (.dc) */
/* Discord client, dark, build 622805 (MAR-395 §B.1). Scoped to the frame and the message mock, so none of these
   colours can leak into the dashboard; dashboard tokens (--clr-primary, --clr-success) aren't used in here.
   .cb-preview-col (MAR-667 ui §2.3, WP6) holds the Custom bot page's profile card and the 390 preview, outside any
   .dcf, so the scope selector includes it: the card reads exactly the tokens the frame does. */
.dcf, .dc, .cb-preview-col {
  --dc-chat-background: #313338;
  --dc-embed-background: #393a41;
  --dc-surface-higher: #3c3d45;
  --dc-text-default: #f3f3f4;
  --dc-text-strong: #ffffff;
  --dc-text-subtle: #c5c6ca;
  --dc-text-muted: #abacb2;
  --dc-chat-text-muted: #9d9ea5;
  --dc-text-link: #76aff6;
  --dc-text-code: #ffffff;
  --dc-background-code: rgba(88, 101, 242, .08);
  --dc-border-subtle: rgba(151, 151, 159, .12);
  --dc-border-normal: rgba(151, 151, 159, .20);
  --dc-mention-fg: #cdd7ff;
  --dc-mention-bg: rgba(88, 101, 242, .24);
  --dc-spine: #595a63;
  --dc-spoiler: #7d7e87;
  --dc-spoiler-hover: #9d9ea5;
  --dc-spoiler-revealed: rgba(151, 151, 159, .12);
  --dc-brand: #5865f2;
  --dc-btn-primary: #5865f2;   --dc-btn-primary-hover: #4452bb;   --dc-btn-primary-active: #3a48a3;
  --dc-btn-secondary: rgba(151, 151, 159, .12); --dc-btn-secondary-hover: rgba(151, 151, 159, .16); --dc-btn-secondary-active: rgba(151, 151, 159, .20);
  --dc-btn-success: #008545;   --dc-btn-success-hover: #006c37;
  --dc-btn-danger: #d22d39;    --dc-btn-danger-hover: #a9232e;
  --dc-btn-border: rgba(255, 255, 255, .08);
  --dc-input-bg: rgba(0, 0, 0, .08);
  --dc-input-border: rgba(151, 151, 159, .20);
  --dc-input-border-active: #5865f2;
  --dc-input-placeholder: #a4a5ab;
  --dc-reaction-bg: rgba(151, 151, 159, .12); --dc-reaction-bg-hover: rgba(151, 151, 159, .16);
  --dc-reaction-me-bg: rgba(88, 101, 242, .24); --dc-reaction-me-border: #5865f2;
  --dc-reaction-text: #c5c6ca;  --dc-reaction-me-text: #dfe4ff;
  --dc-ephemeral-bg: rgba(88, 101, 242, .05);
  --dc-embed-default: rgba(151, 151, 159, .20);
  /* frame chrome (ours, visual-spec §2) */
  --dcf-bar: #1e1f22;
  --dcf-border: #1e1f22;
}

/* Frame: dashboard bar, Discord channel header, message area. Variants: side | stacked | feature | modal | compact. */
.dcf { min-width: 0; background: var(--dc-chat-background); border: 1px solid var(--dcf-border); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, .35); overflow: visible; }
.dcf-feature { width: 100%; max-width: 720px; }
/* A feature preview whose first step is a pop-up (forms) keeps the feature width on every step. */
.cf-preview .dcf-modal { width: 100%; max-width: 720px; }
.dcf-bar { min-height: 40px; padding: 0 8px 0 14px; display: flex; align-items: center; gap: 8px; background: var(--dcf-bar); border-radius: 7px 7px 0 0; font-family: var(--font-ui); }
.dcf-label { font-size: 12px; font-weight: 600; line-height: 16px; color: var(--dc-text-strong); flex-shrink: 0; }
.dcf-note { font-size: 12px; font-weight: 400; line-height: 16px; color: var(--dc-text-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcf-controls { margin-left: auto; display: flex; align-items: center; gap: 6px; min-width: 0; }
.dcf-seg { display: inline-flex; height: 28px; padding: 2px; border-radius: 6px; background: rgba(0, 0, 0, .25); flex-shrink: 0; }
.dcf-seg button { height: 24px; padding: 0 10px; border: 0; border-radius: 4px; background: transparent; color: var(--dc-text-muted); font: 600 12px/24px var(--font-ui); white-space: nowrap; cursor: pointer; transition: background-color 120ms var(--ease-smooth), color 120ms var(--ease-smooth); }
.dcf-seg button:hover { background: rgba(151, 151, 159, .12); color: var(--dc-text-default); }
.dcf-seg button[aria-checked="true"], .dcf-seg button[aria-selected="true"] { background: rgba(151, 151, 159, .24); color: var(--dc-text-strong); }
.dcf-seg button:active { background: rgba(151, 151, 159, .32); }
.dcf-seg button:focus-visible, .dcf-link:focus-visible { outline: 2px solid var(--clr-primary); outline-offset: 1px; }
.dcf-link { padding: 0; border: 0; background: none; color: var(--dc-text-link); font: 600 12px/16px var(--font-ui); cursor: pointer; white-space: nowrap; }
.dcf-link:hover { text-decoration: underline; }
.dcf-channel { height: 48px; padding: 0 16px; display: flex; align-items: center; gap: 8px; background: var(--dc-chat-background); border-bottom: 1px solid var(--dc-border-subtle); font-family: var(--dc-font); min-width: 0; }
.dcf-channel svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--dc-chat-text-muted); }
.dcf-channel-name { font-size: 16px; font-weight: 600; line-height: 20px; color: var(--dc-text-strong); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dcf-channel-name.is-none { color: var(--dc-chat-text-muted); font-weight: 400; }
.dcf-body { position: relative; min-height: 120px; padding: 16px 16px 20px 0; background: var(--dc-chat-background); border-radius: 0 0 7px 7px; font-family: var(--dc-font); }
.dcf-body::-webkit-scrollbar { width: 8px; }
.dcf-body::-webkit-scrollbar-thumb { background: rgba(151, 151, 159, .24); border-radius: 4px; }
.dcf-body::-webkit-scrollbar-track { background: transparent; }
.dcf-body > .dc { padding: 0; background: transparent; }
.dcf-caption { padding: 0 0 8px 16px; font: 400 12px/16px var(--font-ui); color: var(--dc-text-muted); }
/* Caption + state switch (MAR-616 F1): one row with the caption's 16px inset; the children carry the 8px bottom space so a
   row whose children are all hidden takes no height, and a seg that doesn't fit drops under the caption at the same inset. */
.dcf-capline { display: flex; flex-wrap: wrap; align-items: center; gap: 0 8px; padding-left: 16px; min-width: 0; }
.dcf-capline > .dcf-caption { padding: 0; margin-bottom: 8px; min-width: 0; }
.dcf-capline > .dcf-seg { margin-bottom: 8px; }
/* A second message in the same frame (dc_follow); with its own channel header when the bot posts it elsewhere. */
.dcf-follow { margin-top: 17px; }
.dcf-follow > .dc { padding: 0; background: transparent; }
.dcf-follow.has-channel { margin-top: 20px; }
.dcf-follow > .dcf-channel { margin: 0 -16px 16px 0; border-top: 1px solid var(--dc-border-subtle); }
.dcf-foot { padding: 8px 0 0 16px; font: 400 12px/16px var(--font-ui); color: var(--dc-text-muted); }
.dcf-compact { box-shadow: none; }
.dcf-compact > .dcf-body { border-radius: 7px; min-height: 0; }
.dcf-modal > .dcf-body { display: grid; place-items: center; padding: 24px 16px; background: rgba(0, 0, 0, .7); }
.dcf-modal > .dcf-body > .dcf-caption { justify-self: stretch; padding-left: 0; }
/* Discord modal (visual-spec §3.9): verification math question, ticket questions, forms. */
.dc-modal { width: min(440px, calc(100% - 32px)); background: var(--dc-embed-background); border: 1px solid var(--dc-border-subtle); border-radius: 8px; box-shadow: 0 8px 24px rgba(0, 0, 0, .45); font-family: var(--dc-font); color: var(--dc-text-default); }
.dc-modal-h { display: flex; align-items: center; gap: 10px; padding: 16px 16px 4px; font-size: 20px; font-weight: 700; line-height: 24px; color: var(--dc-text-strong); min-width: 0; }
.dc-modal-h img { width: 24px; height: 24px; border-radius: 50%; flex-shrink: 0; }
.dc-modal-h span { min-width: 0; overflow-wrap: anywhere; }
.dc-modal-q { padding: 12px 16px 0; }
.dc-modal-q label { display: block; font-size: 16px; font-weight: 500; color: var(--dc-text-default); margin-bottom: 8px; }
.dc-modal-q .req { color: var(--dc-btn-danger); margin-left: 4px; }
.dc-modal-in { min-height: 40px; border-radius: 8px; background: var(--dc-input-bg); border: 1px solid var(--dc-input-border); color: var(--dc-input-placeholder); display: flex; align-items: center; padding: 0 12px; font-size: 16px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.dc-modal-in.long { min-height: 72px; align-items: flex-start; padding-top: 9px; white-space: normal; }
.dc-modal-f { display: flex; justify-content: flex-end; align-items: center; gap: 8px; padding: 16px; }
.dc-modal-cancel { display: inline-flex; align-items: center; height: 32px; padding: 0 16px; font-size: 14px; font-weight: 500; color: var(--dc-text-strong); }

/* Message row (visual-spec §3.2) */
.dc {
  font-family: var(--dc-font); background: var(--dc-chat-background); color: var(--dc-text-default); font-size: 16px; line-height: 1.375; padding: 16px 16px 20px 0; }
.dc-msg { display: flex; gap: 16px; padding-left: 16px; position: relative; }
.dc-msg + .dc-msg { margin-top: 17px; }
.dc-msg.is-ephemeral { background: var(--dc-ephemeral-bg); box-shadow: inset 2px 0 0 var(--dc-brand); padding-top: 2px; padding-bottom: 2px; }
.dc-avatar { width: 40px; height: 40px; margin-top: 2px; border-radius: 50%; flex-shrink: 0; background: var(--dc-brand); overflow: hidden; display: grid; place-items: center; }
.dc-avatar img { width: 100%; height: 100%; object-fit: cover; }
.dc-body { min-width: 0; flex: 1; }
.dc-head { display: flex; align-items: center; flex-wrap: wrap; gap: 0; line-height: 22px; }
.dc-name { font-weight: 500; color: var(--dc-text-strong); font-size: 16px; margin-right: 4px; }
.dc-app { display: inline-flex; align-items: center; gap: 2px; height: 15px; padding: 0 4.4px; margin-top: .2em; margin-right: 4px; border-radius: 4px; background: var(--dc-brand); color: #fff; font-size: 10px; font-weight: 600; line-height: 15px; text-transform: uppercase; }
.dc-app svg { width: 11px; height: 11px; }
.dc-time { color: var(--dc-chat-text-muted); font-size: 12px; line-height: 22px; }
.dc-content { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 16px; line-height: 22px; }
.dc-content.empty { color: var(--dc-text-muted); font-style: normal; font-size: 16px; }
.dc-content strong, .dc-embed strong { font-weight: 700; }
.dc-content em, .dc-embed em { font-style: italic; }
.dc-content u, .dc-embed u { text-decoration: underline; }
.dc-content s, .dc-embed s { text-decoration: line-through; }

/* Markdown (visual-spec §3.3) */
.dc code { background: var(--dc-background-code); border: 1px solid var(--dc-border-normal); border-radius: 4px; padding: 0 .2em; font-size: 85%; color: var(--dc-text-code); font-family: var(--dc-font-code); }
.dc pre { background: var(--dc-background-code); border: 1px solid var(--dc-border-normal); border-radius: 4px; padding: 7px; margin: 6px 0 0; max-width: 90%; white-space: pre-wrap; font-size: 12px; line-height: 16px; font-family: var(--dc-font-code); color: var(--dc-text-default); }
.dc pre code { border: 0; padding: 0; background: transparent; font-size: inherit; }
.dc a { color: var(--dc-text-link); text-decoration: none; }
.dc a:hover { text-decoration: underline; }
.dc-mention { background: var(--dc-mention-bg); color: var(--dc-mention-fg); border-radius: 3px; padding: 0 2px; font-weight: 500; }
.dc-quote { display: flex; margin: 4px 0; max-width: 90%; color: var(--dc-text-subtle); }
.dc-quote::before { content: ""; width: 4px; border-radius: 4px; background: var(--dc-spine); flex-shrink: 0; }
.dc-quote > div { padding: 0 8px 0 12px; min-width: 0; }
.dc-h1, .dc-h2, .dc-h3 { font-weight: 700; line-height: 1.375em; color: var(--dc-text-strong); margin: 16px 0 8px; }
.dc-h1 { font-size: 24px; } .dc-h2 { font-size: 20px; } .dc-h3 { font-size: 16px; }
.dc-content > .dc-h1:first-child, .dc-content > .dc-h2:first-child, .dc-content > .dc-h3:first-child,
.e-desc > .dc-h1:first-child, .e-desc > .dc-h2:first-child, .e-desc > .dc-h3:first-child { margin-top: 8px; }
.dc-small { font-size: 14px; line-height: 19.25px; color: var(--dc-text-subtle); }
.dc ul, .dc ol { margin: 4px 0 0 16px; padding: 0; white-space: normal; }
.dc ul { list-style: disc outside; } .dc ul ul { list-style: circle outside; } .dc ul ul ul { list-style: square outside; }
.dc ol { list-style: decimal outside; }
.dc li { margin-bottom: 4px; }
.dc-ts { background: rgba(151, 151, 159, .12); border-radius: 3px; padding: 0 2px; }
.dc-spoiler { background: var(--dc-spoiler); color: transparent; border-radius: 4px; cursor: pointer; }
.dc-spoiler:hover { background: var(--dc-spoiler-hover); }
.dc-spoiler.revealed { background: var(--dc-spoiler-revealed); color: inherit; cursor: auto; }
.dc-spoiler:not(.revealed) * { visibility: hidden; }
.dc-var-unknown { text-decoration: underline dotted var(--amber); text-underline-offset: 3px; }
/* Emoji: 22px in content, 48px when a message is only emoji, 18px in embeds, 16px in buttons. */
.dc-emo, .dc img.dc-emoji { display: inline-block; width: 1.375em; height: 1.375em; line-height: 1.375em; text-align: center; vertical-align: -.3em; object-fit: contain; }
.dc-content.dc-jumbo .dc-emo, .dc-content.dc-jumbo img.dc-emoji { width: 48px; height: 48px; line-height: 48px; font-size: 40px; vertical-align: middle; }
.dc-embed .dc-emo, .dc-embed img.dc-emoji { width: 18px; height: 18px; line-height: 18px; font-size: 15px; vertical-align: -.2em; }
.dc-button .dc-emo, .dc-button img.dc-emoji { width: 16px; height: 16px; line-height: 16px; font-size: 14px; vertical-align: middle; }

/* Embed (visual-spec §3.4). The left bar colour is set inline; --dc-embed-default is only the fallback. */
.dc-embed {
  margin-top: 8px; max-width: 516px; display: grid; grid-template-columns: minmax(0, 1fr); background: var(--dc-embed-background);
  border: 1px solid var(--dc-border-subtle); border-left: 4px solid var(--dc-embed-default); border-radius: 4px;
  padding: 2px 16px 16px 12px; font-size: 14px; line-height: 18px; position: relative;
}
.dc-embed.has-thumb { grid-template-columns: minmax(0, 1fr) 80px; column-gap: 16px; }
.dc-embed .e-main { min-width: 0; grid-column: 1; }
.dc-embed .e-author { display: flex; align-items: center; margin-top: 8px; font-size: 14px; font-weight: 600; color: var(--dc-text-strong); }
.dc-embed .e-author img { width: 24px; height: 24px; border-radius: 50%; margin-right: 8px; }
.dc-embed .e-author a { color: var(--dc-text-strong); }
.dc-embed .e-author > span, .dc-embed .e-author > a { min-width: 0; overflow-wrap: break-word; }
.dc-embed .e-title { margin-top: 8px; font-size: 16px; font-weight: 600; line-height: 22px; color: var(--dc-text-strong); overflow-wrap: break-word; }
.dc-embed .e-title a { color: var(--dc-text-link); }
.dc-embed .e-desc { margin-top: 8px; font-size: 14px; line-height: 18px; white-space: pre-line; color: var(--dc-text-default); overflow-wrap: break-word; }
.dc-embed .e-fields { display: grid; grid-template-columns: repeat(12, 1fr); gap: 8px; margin-top: 8px; }
.dc-embed .e-field { grid-column: 1 / 13; min-width: 0; }
.dc-embed .e-field.inline { grid-column: span 4; }
.dc-embed .e-field-name { font-weight: 600; color: var(--dc-text-strong); margin-bottom: 2px; font-size: 14px; line-height: 18px; }
.dc-embed .e-field-value { white-space: pre-line; font-size: 14px; line-height: 18px; color: var(--dc-text-default); overflow-wrap: break-word; }
.dc-embed .e-thumb { grid-column: 2; grid-row: 1 / 8; margin-top: 8px; margin-left: 0; max-width: 80px; max-height: 80px; border-radius: 4px; object-fit: contain; justify-self: end; }
.dc-embed .e-image { grid-column: 1 / -1; margin-top: 16px; border-radius: 4px; max-width: min(100%, 400px); max-height: 300px; object-fit: contain; justify-self: start; }
.dc-embed .e-footer { grid-column: 1 / -1; display: flex; align-items: center; margin-top: 8px; font-size: 12px; line-height: 16px; font-weight: 500; color: var(--dc-text-default); }
.dc-embed .e-footer img { width: 20px; height: 20px; border-radius: 50%; margin-right: 8px; }

/* Buttons, selects and reactions (visual-spec §3.5, §3.7) */
.dc-buttons { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.dc-button { display: inline-flex; align-items: center; justify-content: center; gap: 4px; height: 32px; min-width: 60px; max-width: 100%; padding: 2px 16px; border-radius: 8px; border: 1px solid var(--dc-btn-border);
  font-size: 14px; font-weight: 500; line-height: 16px; color: #fff; background: var(--dc-btn-secondary); white-space: nowrap; flex-shrink: 0; transition: background-color 120ms var(--ease-smooth); }
.dc-button > * { flex-shrink: 0; }
/* An overlong label ends in "…" inside the button, and the button never grows past its action row (Discord). */
.dc-button > .dc-label { flex-shrink: 1; min-width: 0; overflow: clip; overflow-clip-margin: 2px; text-overflow: ellipsis; }
.dc-button:hover { background: var(--dc-btn-secondary-hover); }
.dc-button:active { background: var(--dc-btn-secondary-active); }
.dc-button.primary { background: var(--dc-btn-primary); }
.dc-button.primary:hover { background: var(--dc-btn-primary-hover); }
.dc-button.primary:active { background: var(--dc-btn-primary-active); }
.dc-button.success { background: var(--dc-btn-success); }
.dc-button.success:hover { background: var(--dc-btn-success-hover); }
.dc-button.danger { background: var(--dc-btn-danger); }
.dc-button.danger:hover { background: var(--dc-btn-danger-hover); }
.dc-button svg { width: 16px; height: 16px; }
.dc-button.is-disabled, .dc-select.is-disabled { opacity: .5; cursor: not-allowed; }
.dc-select { margin-top: 8px; max-width: 400px; height: 40px; display: flex; align-items: center; justify-content: space-between; padding: 0 8px 0 12px; border-radius: 8px;
  background: var(--dc-input-bg); color: var(--dc-input-placeholder); font-size: 16px; border: 1px solid var(--dc-input-border); }
.dc-select.open { border-color: var(--dc-input-border-active); }
/* A string select's popout (visual-spec §3.5): 5 options of 40px + 8px before it scrolls. */
.dc-select-wrap { position: relative; max-width: 400px; margin-top: 8px; }
.dc-select-wrap .dc-select { margin-top: 0; cursor: pointer; }
.dc-caret { display: inline-flex; color: var(--dc-text-subtle); transition: transform .15s; }
.dc-caret.is-open { transform: rotate(180deg); }
.dc-menu { position: absolute; z-index: 5; left: 0; right: 0; top: calc(100% + 4px); background: var(--dc-embed-background); border: 1px solid var(--dc-border-normal); border-radius: 8px; max-height: 208px; overflow-y: auto; padding: 4px 0; }
.dc-menu-opt { display: flex; gap: 8px; align-items: center; min-height: 40px; padding: 8px 12px; cursor: pointer; }
.dc-menu-opt:hover, .dc-menu-opt.picked { background: var(--dc-surface-higher); }
.dc-menu-opt .l { color: var(--dc-text-default); font-size: 16px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dc-menu-opt .d { color: var(--dc-text-subtle); font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dc-menu-emoji { width: 22px; flex-shrink: 0; text-align: center; }
.dc-menu-emoji .dc-emo, .dc-menu-emoji img { width: 22px; height: 22px; }
.dc-menu-text { min-width: 0; }
.dc-reactions { display: flex; flex-wrap: wrap; margin-top: 4px; }
.dc-reaction { display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 2px 6px; margin: 0 4px 4px 0; border-radius: 8px; background: var(--dc-reaction-bg); border: 1px solid transparent; font-size: 14px; font-weight: 600; color: var(--dc-reaction-text); }
.dc-reaction:hover { background: var(--dc-reaction-bg-hover); }
.dc-reaction.me { background: var(--dc-reaction-me-bg); border-color: var(--dc-reaction-me-border); color: var(--dc-reaction-me-text); }

/* Ephemeral footer (visual-spec §3.8) */
.dc-eph { display: flex; align-items: center; gap: 4px; margin-top: 4px; font-size: 12px; line-height: 16px; color: var(--dc-chat-text-muted); }
.dc-eph svg { width: 16px; height: 16px; }
.dc-eph a { color: var(--dc-text-link); cursor: pointer; }
.dc-eph a:hover { text-decoration: underline; }

/* Interaction header (visual-spec §3.8): "Mika used /userinfo" above the reply, with the L-shaped spine. */
.dc-msg.has-used { flex-wrap: wrap; row-gap: 0; }
.dc-used { flex: 0 0 100%; position: relative; display: flex; align-items: center; gap: 4px; min-width: 0; margin-bottom: 2px; padding-left: 56px;
  font-size: 14px; line-height: 18px; color: var(--dc-text-subtle); white-space: nowrap; overflow: hidden; }
.dc-used img { width: 16px; height: 16px; border-radius: 50%; flex-shrink: 0; }
.dc-used-name { color: var(--dc-text-strong); font-weight: 500; }
.dc-used-cmd { color: var(--dc-text-link); }
.dc-used-spine { position: absolute; left: 20px; top: 9px; width: 32px; height: 9px; border-left: 2px solid var(--dc-spine); border-top: 2px solid var(--dc-spine); border-top-left-radius: 6px; }
.dc-link-icon { display: inline-flex; }
/* A server-drawn image attached to the message (welcome card, visual-spec P17/P18) with its loading and error states. */
.dc-card { position: relative; aspect-ratio: 1100 / 500; overflow: hidden; background: rgba(151, 151, 159, .12); }
.dc-embed .e-image.dc-card { width: min(100%, 400px); }
.dc-attach.dc-card { width: min(100%, 550px); }
.dc-card img { display: block; width: 100%; height: 100%; object-fit: contain; transition: opacity .2s var(--ease); }
.dc-card.is-loading img { opacity: .55; }
.dc-card-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 16px; text-align: center; font-size: 14px; line-height: 18px; color: var(--dc-text-muted); background: rgba(0, 0, 0, .35); }

/* Components V2 (visual-spec §3.6, UNVERIFIED values) */
.dc-container { margin-top: 8px; max-width: 516px; background: var(--dc-embed-background); border: 1px solid var(--dc-border-subtle); border-radius: 8px; padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.dc-container.has-accent { border-left-width: 4px; }
.dc-section { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 16px; align-items: start; }
.dc-section .dc-thumb { width: 85px; height: 85px; border-radius: 8px; object-fit: cover; }
.dc-sep { height: 8px; } .dc-sep.large { height: 16px; }
.dc-sep.divider { height: auto; margin: 4px 0; border-top: 1px solid var(--dc-border-normal); } .dc-sep.divider.large { margin: 8px 0; }
.dc-v2 > .dc-v2-part { margin-top: 8px; }
.dc-section-text { min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.dc-section > .dc-button { align-self: start; }
/* Media gallery: 1 item contained, 2 side by side, 3 = 1 large left + 2 stacked right, 4 = 2x2, 5-10 rows of 3 with
   the last rows of 2 where the count needs it (spans set per item on a 6-column grid). */
.dc-gallery { display: grid; gap: 4px; border-radius: 8px; overflow: hidden; width: 100%; max-width: 516px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.dc-gallery-item { position: relative; aspect-ratio: 1; min-width: 0; background: rgba(151, 151, 159, .12); }
.dc-gallery-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dc-gallery.n1 { grid-template-columns: minmax(0, 1fr); } .dc-gallery.n1 .dc-gallery-item { aspect-ratio: auto; max-height: 350px; } .dc-gallery.n1 img { max-height: 350px; object-fit: contain; }
.dc-gallery.n3 { grid-template-columns: 2fr 1fr; } .dc-gallery.n3 .dc-gallery-item:first-of-type { grid-row: 1 / 3; aspect-ratio: auto; }
.dc-gallery.many { grid-template-columns: repeat(6, minmax(0, 1fr)); } .dc-gallery.many .dc-gallery-item { grid-column: span 2; } .dc-gallery.many .dc-gallery-item.wide { grid-column: span 3; aspect-ratio: 3 / 2; }
/* Components V2 spoilers and alt text (MAR-424 spec §6.5, visual-r2 F5 / F10 / F11). UNVERIFIED until measured in a
   logged-in Discord client (G4). A spoiler blurs what it hides behind a centred pill that reveals it; the ALT badge sits
   on gallery images that have alt text. */
.dc-container.is-spoiler, .dc-thumb-wrap.is-spoiler, .dc-gallery-item.is-spoiler { position: relative; overflow: hidden; }
.dc-container.is-spoiler > .dc-v2-part, .dc-thumb-wrap.is-spoiler > img, .dc-gallery-item.is-spoiler > img { filter: blur(44px); pointer-events: none; }
.dc-thumb-wrap { position: relative; width: 85px; height: 85px; border-radius: 8px; overflow: hidden; }
.dc-thumb-wrap > .dc-thumb { display: block; }
.dc-spoiler-pill { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); height: 24px; padding: 0 10px; border: 0; border-radius: 20px; background: rgba(0, 0, 0, .60); color: #fff; font: 600 12px/24px var(--font-ui); white-space: nowrap; cursor: pointer; z-index: 1; }
.dc-spoiler-pill:focus-visible { outline: 2px solid var(--clr-primary); outline-offset: 2px; }
.dc-gallery-alt { position: absolute; left: 6px; bottom: 6px; height: 20px; padding: 0 6px; border-radius: 4px; background: rgba(0, 0, 0, .70); color: #fff; font: 700 11px/20px var(--font-ui); pointer-events: none; }
.dc-file { display: flex; align-items: center; gap: 8px; height: 64px; max-width: 432px; margin-top: 8px; padding: 10px 12px; background: var(--dc-embed-background); border: 1px solid var(--dc-border-subtle); border-radius: 8px; }
.dc-file .n { color: var(--dc-text-link); font-size: 16px; } .dc-file .s { color: var(--dc-chat-text-muted); font-size: 12px; }
.dc-file svg { width: 24px; height: 30px; flex-shrink: 0; color: var(--dc-text-subtle); }
.dc-file-text { min-width: 0; line-height: 20px; }
.dc-file-text .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dc-attach { display: block; margin-top: 8px; border-radius: 8px; max-width: min(100%, 550px); max-height: 350px; object-fit: contain; background: rgba(151, 151, 159, .12); }

/* ---------------------------------------------------------------- dirty bar (§5.2.2, §7.2) */
/* Centred over the main column, bottom 24px, min(740px, 100vw - 340px) wide, 56px high, radius 10px.
   .savebar is the older name; both get the same look. */
.savebar, .dirty-bar {
  position: fixed; z-index: 80; bottom: 24px; left: calc(50% + var(--sidebar-width) / 2);
  width: min(740px, calc(100vw - 340px)); height: 56px;
  display: flex; align-items: center; gap: 10px; padding: 0 10px 0 20px; border-radius: var(--radius-lg);
  background: var(--clr-dirtybar); border: 1px solid rgba(255, 255, 255, 0.12); box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8);
  transform: translate(-50%, calc(100% + 40px)); opacity: 0; pointer-events: none; visibility: hidden;
  transition: transform 250ms var(--ease-spring), opacity 200ms ease, background-color 150ms ease, visibility 0s linear 250ms;
}
.savebar.show, .dirty-bar.show { transform: translate(-50%, 0); opacity: 1; pointer-events: auto; visibility: visible; transition: transform 250ms var(--ease-spring), opacity 200ms ease, background-color 150ms ease, visibility 0s; }
@keyframes dirty-wiggle {
  0%, 100% { transform: translate(-50%, 0); }
  15%, 45%, 75% { transform: translate(calc(-50% - 8px), 0); }
  30%, 60%, 90% { transform: translate(calc(-50% + 8px), 0); }
}
.dirty-bar.shake, .savebar.shake {
  animation: dirty-wiggle 380ms var(--ease-spring);
  background-color: var(--clr-danger) !important; /* #ce3636 */
  transition: background-color 150ms ease;
}
.savebar .msg, .dirty-bar .msg { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; }
.savebar .headline, .dirty-bar .headline { font-weight: 600; font-size: 15px; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.savebar .msg small, .dirty-bar .msg small { display: block; font-weight: 500; color: var(--text-tertiary); font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.savebar .msg small.err, .dirty-bar .msg small.err { color: #ff8a8a; }
.dirty-bar.shake .msg small.err { color: #fff; }
.savebar .btn, .dirty-bar .btn { height: 38px; }
@keyframes shake { 0%,100% { transform: translate(-50%,0); } 20%,60% { transform: translate(calc(-50% - 7px),0); } 40%,80% { transform: translate(calc(-50% + 7px),0); } }

/* ---------------------------------------------------------------- toasts */
.toasts { position: fixed; top: 16px; right: 16px; z-index: 200; display: flex; flex-direction: column; gap: 8px; width: min(380px, calc(100vw - 32px)); pointer-events: none; }
.toast { pointer-events: auto; display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: var(--radius-lg); background: var(--clr-surface-darker); box-shadow: var(--shadow-pop); font-size: 14px; }
.toast .icon { margin-top: 1px; }
.toast.ok .icon { color: var(--mint); }
.toast.err .icon { color: var(--rose); }
.toast.info .icon { color: var(--sky); }
.toast .t-body { flex: 1; min-width: 0; }
.toast .t-title { font-weight: 700; }
.toast-enter { transition: transform 200ms var(--ease-spring), opacity 200ms var(--ease-out); }
.toast-enter-start { transform: translateY(-8px); opacity: 0; }

/* ---------------------------------------------------------------- modal (§5.2.3) */
/* Backdrop rgba(0,0,0,.8) + blur 4px, fades in 150ms ease-out / out 120ms ease-in; the dialog pops from
   scale(.95) translateY(12px) over 200ms spring. Dialog chrome radius is a literal 12px. */
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-pop { from { opacity: 0; transform: scale(0.95) translateY(12px); } to { opacity: 1; transform: scale(1) translateY(0); } }
@keyframes modal-shake {
  0%, 100% { transform: scale(1) translateX(0); }
  20%, 60% { transform: scale(1) translateX(-6px); }
  40%, 80% { transform: scale(1) translateX(6px); }
}
.modal-wrap, .modal-backdrop {
  position: fixed; inset: 0; z-index: 150; display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; padding: 16px;
  background: var(--clr-backdrop); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  animation: backdrop-in 150ms ease-out;
}
.modal, .modal-dialog {
  width: min(560px, 100%); max-height: calc(100vh - 32px); overflow-y: auto; justify-self: center;
  background: var(--clr-canvas); border: 1px solid var(--border-default); border-radius: 12px; box-shadow: 0 24px 64px rgba(0, 0, 0, .6);
  animation: modal-pop 200ms var(--ease-spring);
}
.modal-invalid { animation: modal-shake 280ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
/* Leaving (x-transition:leave="modal-leave" x-transition:leave-end="modal-leave-to" on the backdrop) */
.modal-leave { transition: opacity 120ms ease-in; }
.modal-leave-to { opacity: 0; }
.modal-leave .modal-dialog, .modal-leave .modal { transition: transform 150ms ease-in, opacity 150ms ease-in; }
.modal-leave-to .modal-dialog, .modal-leave-to .modal { transform: scale(0.97); opacity: 0; }
.modal.wide { width: min(880px, 100%); }
.modal-head { display: flex; align-items: center; gap: 12px; padding: 20px 24px 0; }
.modal-head h2 { flex: 1; font-size: 1.4rem; }
.modal-body { padding: 16px 24px 22px; }
.modal-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; padding: 14px 24px; border-top: 1px solid var(--border-subtle); background: var(--clr-surface-dark); border-radius: 0 0 12px 12px; }

/* Module intro dialog (§6.9): max 500px, padding 24px, 40px tinted icon, title = module name */
.intro-dialog { width: min(500px, 100%); padding: 24px; position: relative; overflow: visible; }
.intro-dialog .intro-close { position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; display: grid; place-items: center; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-tertiary); cursor: pointer; font-size: 16px; line-height: 1; transition: background-color 150ms ease, color 150ms ease; }
.intro-dialog .intro-close:hover { background: rgba(255, 255, 255, .07); color: #fff; }
.intro-dialog .intro-head { display: flex; align-items: center; gap: 14px; padding-right: 36px; }
.intro-dialog .intro-icon { width: 40px; height: 40px; border-radius: var(--radius-md); display: grid; place-items: center; flex-shrink: 0; background: var(--clr-primary-soft); color: var(--clr-primary); }
.intro-dialog h2 { font-size: 1.4rem; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
.intro-dialog .intro-body { margin-top: 16px; color: var(--text-secondary); font-size: 15px; line-height: 1.5; }
.intro-dialog .intro-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 24px; flex-wrap: wrap; }

/* ---------------------------------------------------------------- tables & data */
.table-wrap { position: relative; overflow-x: auto; border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--clr-card-bg); }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th { text-align: left; font-weight: 600; font-size: 13px; color: var(--text-tertiary); padding: 11px 14px; border-bottom: 1px solid var(--border-strong); white-space: nowrap; }
table.data td { padding: 11px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
table.data tr:last-child td { border-bottom: 0; }
table.data tr:hover td { background: rgba(255,255,255,.015); }
/* Command tables stack on phones: command + who on one line, the description under it. */
table.data .cmd-name code { white-space: nowrap; }
@media (max-width: 640px) {
  table.data.cmd-table thead { display: none; }
  table.data.cmd-table tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 11px 14px; border-bottom: 1px solid var(--line-soft); }
  table.data.cmd-table tr:last-child { border-bottom: 0; }
  table.data.cmd-table td { display: block; padding: 0; border: 0; min-width: 0; }
  table.data.cmd-table td.cmd-name code { white-space: normal; overflow-wrap: anywhere; }
  table.data.cmd-table td.cmd-desc { grid-column: 1 / -1; grid-row: 2; }
  table.data.cmd-table td.cmd-who { text-align: right; }
}
/* Empty states (§7.3): the spec title is the bold line, the helper sits under it. */
.empty-state { text-align: center; padding: 48px 20px; color: var(--text-tertiary); font-size: 15px; }
.empty-state .icon:not(.btn .icon) { color: #3d4a5c; margin-bottom: 10px; }
.empty-state h3, .empty-state strong, .empty-state .empty-state__title { display: block; color: var(--text-primary); font-size: 20px; font-weight: 700; line-height: 1.3; margin-bottom: 6px; }
.empty-state p { max-width: 46ch; margin: 0 auto; }
.empty-state .btn { margin-top: 16px; }
.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; }
.toolbar .grow { flex: 1; }
.pagination { display: flex; justify-content: flex-end; gap: 6px; margin-top: 12px; }

/* ---------------------------------------------------------------- overview */
.stat-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--clr-card-bg); margin-bottom: 26px; }
.stat { padding: 18px 20px; }
.stat + .stat { border-left: 1px solid var(--line-soft); }
.stat .v { font-family: var(--font-display); font-size: 26px; font-weight: 700; letter-spacing: -.02em; }
.stat .l { color: var(--muted); font-size: 13px; }
.module-board { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); background: var(--clr-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); overflow: hidden; }
.mod-tile { background: transparent; transition: background-color 150ms ease; padding: 16px 16px 16px 18px; display: flex; gap: 14px; align-items: flex-start; color: var(--text); text-decoration: none !important; box-shadow: 1px 0 0 var(--line-soft), 0 1px 0 var(--line-soft); }
.mod-tile:hover { background: var(--clr-card-hover); }
.mod-tile .glyph { width: 38px; height: 38px; border-radius: 11px; background: var(--ink-2); display: grid; place-items: center; color: var(--muted); flex-shrink: 0; }
.mod-tile.on .glyph { color: var(--mint); background: var(--mint-soft); }
.mod-tile.always .glyph { color: var(--iris); background: var(--iris-soft); }
.mod-tile .go { align-self: center; transition: transform 180ms var(--ease-spring), color 150ms ease; }
.mod-tile:hover .go { color: #fff; transform: translateX(3px); }
.mod-tile .t { flex: 1; min-width: 0; }
.mod-tile .name { font-weight: 700; font-size: 14.5px; display: flex; align-items: center; gap: 8px; }
.mod-tile .desc { color: var(--muted); font-size: 13px; margin-top: 3px; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.board-head { display: flex; align-items: center; gap: 12px; margin: 28px 0 12px; }
.board-head h2 { flex: 1; font-size: 18px; }
.activity { list-style: none; margin: 0; padding: 0; }
.activity li { display: flex; gap: 12px; align-items: center; padding: 11px 16px; font-size: 14px; }
.activity li + li { border-top: 1px solid var(--line-soft); }
.activity .avatar { width: 26px; height: 26px; }
.activity .when { margin-left: auto; color: var(--muted); font-size: 12.5px; white-space: nowrap; }

/* ---------------------------------------------------------------- public pages */
.site-nav { position: sticky; top: 0; z-index: 40; background: rgba(27, 32, 40, .86); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-bottom: 1px solid transparent; }
.site-nav.scrolled { border-color: var(--line-soft); }
.site-nav .inner { max-width: 1180px; margin: 0 auto; height: 66px; padding: 0 24px; display: flex; align-items: center; gap: 28px; }
.brand { display: flex; align-items: center; gap: 10px; font-family: var(--font-display); font-weight: 800; font-size: 20px; letter-spacing: -.03em; color: var(--text); text-decoration: none !important; }
.brand-mark { width: 30px; height: 30px; }
.site-nav .links { display: flex; gap: 22px; flex: 1; }
.site-nav .links a { color: var(--text-2); font-weight: 500; font-size: 14.5px; }
.site-nav .links a:hover { color: var(--text); text-decoration: none; }
.nav-actions { display: flex; gap: 10px; align-items: center; margin-left: auto; }
.nav-user { position: relative; }
.nav-user-btn { width: auto; padding: 4px 6px 4px 4px; gap: 6px; border-radius: 999px; }
.nav-user-menu { left: auto; right: 0; width: 240px; top: calc(100% + 8px); }
.nav-user-head { padding: 12px 14px 10px; border-bottom: 1px solid var(--line); }
.nav-user-head .name { font-weight: 700; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-only-phone, .nav-burger { display: none; }
.nav-sheet { display: none; }
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; }
/* Public pages: the footer sits at the bottom even when the page is short. */
body.public { min-height: 100vh; display: flex; flex-direction: column; }
.public-main { flex: 1 0 auto; }
.site-foot { border-top: 1px solid var(--line-soft); margin-top: 40px; }
.site-foot .wrap { display: flex; flex-wrap: wrap; gap: 12px 20px; align-items: center; padding-top: 28px; padding-bottom: 36px; }
.site-foot .grow { flex: 1; }

.servers-page { max-width: 1100px; margin: 0 auto; padding: 40px 24px 80px; }
.servers-head { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 26px; flex-wrap: wrap; }
.servers-head .grow { flex: 1; min-width: 240px; }
.servers-head p { color: var(--text-2); margin-top: 8px; }
.server-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
.server-card { background: var(--clr-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-xl); padding: 18px; display: flex; flex-direction: column; gap: 14px; color: var(--text); text-decoration: none !important; transition: border-color .15s, background .15s; position: relative; }
.server-card:hover { border-color: rgba(255, 255, 255, .14); background: var(--clr-card-hover); }
.server-card .top { display: flex; align-items: center; gap: 14px; min-width: 0; }
.server-card .top > div { min-width: 0; }
.server-card .name { font-weight: 700; font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.server-card .meta { color: var(--muted); font-size: 13px; display: flex; align-items: center; gap: 6px; white-space: nowrap; margin-top: 2px; }
.server-card .meta .badge { height: 20px; padding: 0 7px; font-size: 11.5px; }
.server-card .foot { display: flex; align-items: center; justify-content: space-between; }
.server-card .status { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; color: var(--muted); }
.server-card.has-bot .status { color: var(--mint); }
.server-card:not(.has-bot) { background: transparent; border-style: dashed; }

.error-page { min-height: 70vh; display: grid; place-items: center; text-align: center; padding: 40px 20px; }
.error-page .code { font-family: var(--font-display); font-size: 88px; font-weight: 800; letter-spacing: -.05em; color: var(--ink-4); line-height: 1; }
.error-page h1 { margin: 10px 0 8px; }
.error-page p { color: var(--text-2); max-width: 46ch; margin: 0 auto 22px; }

/* ---------------------------------------------------------------- shared v3 components (spec §4–§6) */
/* Page and tab enter: opacity 200ms ease-out + 6px rise on the spring; exit 150ms ease-in (§5.2.1). */
@keyframes page-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes page-rise { from { transform: translateY(6px); } to { transform: translateY(0); } }
@keyframes pop-fade { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.page-enter { animation: page-fade 200ms var(--ease-out), page-rise 200ms var(--ease-spring); }
/* For x-show panels: x-transition:enter="tab-enter" x-transition:enter-start="tab-enter-start"
   x-transition:leave="tab-leave" x-transition:leave-end="tab-leave-end" */
.tab-enter { transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-spring); }
.tab-enter-start { opacity: 0; transform: translateY(6px); }
.tab-leave { transition: opacity 150ms var(--ease-in); }
.tab-leave-end { opacity: 0; }

/* Cross-fading panes: the leaving pane leaves flow, so the container's height is
   driven by the entering pane only - no transient jump and no transient scrollbar. */
.tab-panes { position: relative; }
.tab-panes > .tab-leave { position: absolute; top: 0; left: 0; right: 0; pointer-events: none; }
/* The utility names used in the spec's Alpine example (§5.2.1), so that snippet works as written. */
.transition { transition-property: opacity, transform; }
.transition.ease-out { transition-timing-function: var(--ease-out), var(--ease-spring); }
.transition.ease-in { transition-timing-function: var(--ease-in); }
.transition.duration-200 { transition-duration: 200ms; }
.transition.duration-150 { transition-duration: 150ms; }
.opacity-0 { opacity: 0; }
.opacity-100 { opacity: 1; }
.translate-y-1\.5 { transform: translateY(6px); }
.translate-y-0 { transform: translateY(0); }

/* Path bars (§6.1 lists, §5.2.6 hover). Markup:
   <a class="path-bar" href="…"><span class="path-bar__icon">…</span><span class="path-bar__title">Title</span>
   <span class="path-bar__lede">Lede.</span><span class="path-bar__aside">controls</span>{{ icon('chevron-right', 18, 'chevron-icon') }}</a>
   Title and lede may also sit together in a .path-bar__body wrapper. */
.path-bar {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-areas: "icon title aside chev" "icon lede aside chev";
  align-items: center; column-gap: 0; width: 100%; min-height: 76px; padding: 16px 20px; text-align: left;
  background: var(--clr-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
  color: var(--text-primary); font: inherit; text-decoration: none !important; position: relative;
  transition: background-color 150ms ease, border-color 150ms ease;
}
a.path-bar, button.path-bar, .path-bar.is-link { cursor: pointer; }
.path-bar:hover { background: var(--clr-card-hover); border-color: rgba(255, 255, 255, 0.14); }
.path-bar + .path-bar { margin-top: 12px; }
.path-bar__icon { grid-area: icon; width: 40px; height: 40px; margin-right: 16px; border-radius: var(--radius-md); display: grid; place-items: center; background: rgba(255, 255, 255, .06); color: var(--text-secondary); flex-shrink: 0; }
.path-bar__body, .path-bar__text { grid-column: 2; grid-row: 1 / span 2; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.path-bar__title { grid-area: title; align-self: end; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 1.3rem; font-weight: 500; line-height: 1.3; color: var(--text-primary); min-width: 0; }
.path-bar__lede { grid-area: lede; align-self: start; font-size: 15px; line-height: 1.5; color: var(--text-secondary); min-width: 0; }
.path-bar__body .path-bar__title, .path-bar__body .path-bar__lede, .path-bar__text .path-bar__title, .path-bar__text .path-bar__lede { align-self: auto; }
.path-bar:not(:has(.path-bar__lede)) > .path-bar__title { grid-row: 1 / span 2; align-self: center; }
.path-bar__aside { grid-area: aside; margin-left: 16px; display: flex; align-items: center; gap: 10px; }
.path-bar > .chevron-icon { grid-area: chev; margin-left: 14px; }
.chevron-icon { color: var(--text-tertiary); flex-shrink: 0; transition: transform 180ms var(--ease-spring), color 150ms ease; }
.path-bar:hover .chevron-icon, .path-bar:focus-visible .chevron-icon { transform: translateX(3px); color: #ffffff; }
.path-bar-list { display: flex; flex-direction: column; gap: 12px; }
.path-bar-list > .path-bar + .path-bar { margin-top: 0; }

/* Accordions (§5.2.5): the drawer uses x-collapse.duration.200ms; its chevron turns 90° over 200ms. */
.accordion-trigger { display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; color: var(--text-secondary); font: inherit; font-weight: 500; cursor: pointer; padding: 6px 0; }
.accordion-trigger:hover { color: #fff; }
.accordion-trigger .chevron-icon { transition: transform 200ms ease, color 150ms ease; }
.accordion-trigger[aria-expanded="true"] .chevron-icon, .chevron-icon.rotate-90, .is-open > .chevron-icon { transform: rotate(90deg); color: var(--clr-primary); }

/* Featured cards (§6.1 top grid): 2 columns, min-height 168px, radius 15px */
.featured-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-bottom: 16px; }
.featured-card {
  min-height: 168px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 22px;
  background: var(--clr-card-bg); border: 1px solid var(--border-subtle); border-radius: var(--radius-xl);
  color: var(--text-primary); transition: background-color 150ms ease, border-color 150ms ease;
}
.featured-card:hover { border-color: rgba(255, 255, 255, 0.14); }
.featured-card__icon { width: 40px; height: 40px; border-radius: var(--radius-md); display: grid; place-items: center; background: rgba(255, 255, 255, .06); color: var(--text-secondary); margin-bottom: 4px; }
.featured-card h2, .featured-card h3, .featured-card__title { font-size: 1.3rem; font-weight: 500; line-height: 1.3; letter-spacing: 0; color: var(--text-primary); }
.featured-card p, .featured-card__lede { font-size: 15px; line-height: 1.5; color: var(--text-secondary); }
.featured-card > .btn:last-child, .featured-card__actions { margin-top: auto; }
.featured-card__actions { display: flex; gap: 10px; flex-wrap: wrap; }

/* Toggle switch (§5.2.4): 45×24 track, 18px knob sliding 21px, squashes to 21px while pressed; on = #22c55e */
.switch-sapphire { position: relative; display: inline-block; width: 45px; height: 24px; flex-shrink: 0; cursor: pointer; vertical-align: middle; }
.switch-sapphire input { position: absolute; inset: 0; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; z-index: 1; }
.switch-sapphire .track { position: absolute; inset: 0; border-radius: var(--radius-full); background: rgba(255, 255, 255, 0.10); transition: background-color 200ms var(--ease-smooth); }
.switch-sapphire .track::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 9px; background: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .35); transform: translateX(0);
  transition: transform 200ms var(--ease-spring), width 150ms ease;
}
.switch-sapphire input:checked + .track { background: var(--clr-success); }
.switch-sapphire input:checked + .track::after { transform: translateX(21px); }
.switch-sapphire input:active + .track::after { width: 21px; }
.switch-sapphire input:checked:active + .track::after { transform: translateX(18px); }
.switch-sapphire input:focus-visible + .track { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.switch-sapphire input:disabled { cursor: default; }
.switch-sapphire input:disabled + .track { opacity: .55; }

/* Segmented tabs (§6.5, §6.7): rail radius 8px, selected tab filled */
.seg-tabs { display: inline-flex; flex-wrap: wrap; gap: 4px; padding: 4px; margin-bottom: 20px; background: var(--clr-surface-dark); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); max-width: 100%; }
.seg-tab { height: 36px; padding: 0 16px; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-secondary); font: inherit; font-size: 15px; font-weight: 500; cursor: pointer; white-space: nowrap; transition: background-color 150ms ease, color 150ms ease; }
.seg-tab:hover { color: #fff; background: rgba(255, 255, 255, .04); }
.seg-tab.active, .seg-tab[aria-selected="true"] { background: #394556; color: #fff; }

/* BETA badge (§4.4 micro): red #ce3636, #e65b5b on hover */
.beta-pill { display: inline-flex; align-items: center; height: 20px; padding: 0 7px; border-radius: var(--radius-sm); background: var(--clr-danger); color: #fff; font-size: 0.75rem; font-weight: 700; line-height: 1.2; letter-spacing: .04em; text-transform: uppercase; vertical-align: middle; transition: background-color 150ms ease; }
.beta-pill:hover { background: var(--clr-danger-hover); }

/* Module header parts shared by hubs */
.module-help { width: 45px; height: 45px; display: inline-grid; place-items: center; padding: 0; flex-shrink: 0; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); background: rgba(255, 255, 255, .05); color: var(--text-secondary); cursor: pointer; transition: background-color 150ms ease, color 150ms ease; }
.module-help:hover { background: rgba(255, 255, 255, .09); color: #fff; }

/* ---------------------------------------------------------------- responsive */
@media (max-width: 1180px) {
  .sec { grid-template-columns: 1fr; gap: 14px; }
  /* Stacked variant: the frame goes full width under the controls, not sticky, and the page scrolls. */
  .msg-layout { grid-template-columns: minmax(0, 1fr); }
  .msg-layout > .dcf { position: static; max-height: none; align-self: stretch; }
  .msg-layout > .dcf > .dcf-body { max-height: none; overflow-y: visible; }
}
@media (max-width: 900px) {
  /* Sidebar becomes a drawer opened from the hamburger in the topbar. */
  .sidebar { z-index: 100; top: 0; width: min(300px, 86vw); transform: translateX(-102%); transition: transform 250ms var(--ease-spring); box-shadow: inset -50px 0 50px 1px #1f252e, 20px 0 60px rgba(0,0,0,.5); }
  .sidebar.open { transform: none; }
  .drawer-close { display: inline-flex; margin-left: auto; }
  /* With the close button in the row, Fetch roles/channels drops to its own full-width line so the label never clips. */
  .sidebar-top { flex-wrap: wrap; row-gap: 6px; }
  .sidebar-top .sidebar-util:not(.compact) { order: 3; flex: 1 0 100%; }
  .scrim { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 90; }
  .menu-btn { display: inline-flex; }
  .main { margin-left: 0; }
  .topbar { padding: 0 10px; grid-template-columns: auto minmax(0, 1fr) auto; }
  .crumbs { max-width: none; justify-content: center; }
  .topbar-guild .server-switch .name { max-width: 22vw; }
  .content { padding: 22px 16px 140px; }
  .savebar, .dirty-bar { left: 50%; width: calc(100vw - 24px); }
  .stat-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat:nth-child(3) { border-left: 0; }
  .stat:nth-child(n+3) { border-top: 1px solid var(--line-soft); }
  .site-nav .links { display: none; }
  .nav-only-phone { display: flex; }
  .nav-burger { display: inline-flex; }
  .nav-sheet { display: flex; flex-direction: column; padding: 6px 16px 14px; border-top: 1px solid var(--line-soft); background: var(--ink-0); }
  .nav-sheet a { padding: 11px 8px; color: var(--text-2); font-weight: 600; border-radius: 8px; }
  .nav-sheet a:hover { background: var(--ink-2); color: var(--text); text-decoration: none; }
}
@media (max-width: 640px) {
  .site-nav .inner { padding: 0 16px; gap: 12px; }
  .nav-login { display: none; }
  .wrap { padding: 0 16px; }
  .site-foot .grow { flex-basis: 100%; height: 0; }
}
/* Dirty bar below 830px: full width, headline hidden, buttons share the row (§7.2). */
@media (max-width: 830px) {
  .savebar, .dirty-bar { left: 50%; width: calc(100vw - 24px); bottom: 12px; padding: 0 9px; }
  /* Only the headline hides; a save error stays visible on its own line and the bar grows to fit it. */
  .savebar .headline, .dirty-bar .headline { display: none; }
  .savebar:not(.has-err) .msg, .dirty-bar:not(.has-err) .msg { display: none; }
  .savebar.has-err, .dirty-bar.has-err { height: auto; min-height: 56px; flex-wrap: wrap; padding: 9px; row-gap: 8px; }
  .savebar.has-err .msg, .dirty-bar.has-err .msg { flex: 1 0 100%; padding: 0 4px; }
  .savebar.has-err .msg small, .dirty-bar.has-err .msg small { white-space: normal; overflow: visible; }
  .savebar .btn, .dirty-bar .btn { flex: 1 1 0; }
  .featured-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .sec { padding: 20px 16px; }
  .fld.half, .fld.third { grid-column: span 6; }
  /* Phone top bar: the drawer already names the server, so the breadcrumb keeps only the page (and tab). */
  .topbar { gap: 6px; padding: 0 8px; }
  .crumbs .crumb-guild, .crumbs .crumb-guild + .icon { display: none; }
  .crumbs { font-size: 14.5px; justify-content: flex-start; }
  .topbar-guild .server-switch { padding-right: 3px; gap: 0; }
  .topbar-guild .server-switch .grow, .topbar-guild .server-switch > .icon { display: none; }
  .user-btn { padding-right: 3px; }
  .user-btn .who, .user-btn > .icon { display: none; }
  .topbar-guild .server-menu { position: fixed; left: 8px; right: 8px; width: auto; top: 54px; }
  h1 { font-size: 1.75rem; }
  .path-bar { padding: 14px 14px; grid-template-areas: "icon title chev" "icon lede chev" "aside aside aside"; grid-template-columns: auto minmax(0, 1fr) auto; }
  .path-bar__aside { margin: 10px 0 0; justify-content: flex-start; }
  .path-bar__icon { width: 36px; height: 36px; margin-right: 12px; }
  .path-bar__title { font-size: 1.15rem; }
  .seg-tabs { display: flex; flex-wrap: wrap; overflow: visible; max-width: 100%; }
  .page-head { flex-wrap: wrap; gap: 14px 16px; margin-bottom: 22px; }
  .page-head .glyph { width: 44px; height: 44px; }
  .page-head .actions { width: 100%; padding-top: 0; }
  .page-head .actions .btn-ghost:first-child { margin-left: -11px; }
  .page-head .power { margin-left: auto; }
  .board-head { flex-wrap: wrap; row-gap: 4px; }
  .board-head h2 { flex: 1 1 auto; }
  .color-input { flex-wrap: wrap; }
  .color-input > .input { flex: 1 1 0; min-width: 0; }
  .color-input .swatches { flex-basis: 100%; }
  .swatches button { width: 24px; height: 24px; }
  .embed-card-body { grid-template-columns: 1fr; }
  .msg-toolbar > .small { display: none; }
  .msg-edit-pane { padding: 12px; }
  /* Discord frame on phones (visual-spec §2.6): same message metrics as desktop, no shadow (the card frames it),
     a 44px channel header, and bar controls that move to a second row and scroll instead of wrapping again. */
  .dcf { box-shadow: none; }
  .dcf-bar { flex-wrap: wrap; row-gap: 0; }
  /* Row 1 keeps the 40px bar height when the controls wrap to a 36px second row (visual-spec §2.6). */
  .dcf-label, .dcf-note { line-height: 40px; }
  .dcf-controls { flex-shrink: 0; max-width: 100%; min-height: 36px; overflow-x: auto; scrollbar-width: none; }
  .dcf-controls::-webkit-scrollbar { display: none; }
  .dcf-channel { height: 44px; }
  .dcf-channel svg { width: 18px; height: 18px; }
  .dcf-channel-name { font-size: 15px; }
  .link-btn-row { grid-template-columns: minmax(0, 1fr) 104px auto; }
  /* Phones: switch between the editor and the preview instead of scrolling past a long editor. */
  .msg-tabs { display: flex; gap: 4px; padding: 8px 8px 0; border-bottom: 1px solid var(--line); background: var(--ink-1); border-radius: 11px 11px 0 0; }
  .msg-tabs button { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; border: 0; border-bottom: 2px solid transparent; background: transparent; color: var(--muted); font-weight: 600; font-size: 13.5px; cursor: pointer; }
  .msg-tabs button.on { color: var(--text); border-bottom-color: var(--iris); }
  .msg-editor:not(.show-preview) .msg-layout > .dcf { display: none; }
  .msg-editor.show-preview .msg-layout > .msg-edit-pane { display: none; }
  .list-item-body { padding: 14px 12px 16px; }
  /* Phones: the four item buttons move into one menu so the item's name has room. */
  .list-item-head { padding: 8px 6px 8px 12px; gap: 8px; }
  .li-actions .li-wide { display: none; }
  .li-more { display: inline-flex; }
}
/* Horizontal scrollers that overflow (UX-BUG-4/5): a 24px fade on the right edge until scrolled to the end.
   .fade-end is set by app.js; the mask only changes opacity, so no new colour. */
.fade-end { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent); }
/* ---------------------------------------------------------------- AI moderation (MAR-672 WP4)
   Sizes, states and colours follow the ui document of the MAR-671 spec. No colour below is new: every value is a token
   above or a literal already used in this file (#ff8a8a on .dirty-bar .err, #394556 on .segmented, #46546a on .input).
   Scoped to .ai-page (the root of the AI page) and .modal.ai-dialog, so the other module pages look as released. */

/* Setup progress. Moved unchanged from guild_home.html, where the server home "Get started" card still uses it. */
.steps-card { overflow: hidden; }
.steps-bar { height: 4px; background: var(--ink-3); }
.steps-bar > span { display: block; height: 100%; background: linear-gradient(90deg, var(--iris-btn), var(--iris)); border-radius: 0 4px 4px 0; }

/* Readable error red and sentence links (spec D15). .ai-link is the opt-in for the AI rows of Activity. */
.ai-page .fld-error, .modal.ai-dialog .fld-error { color: #ff8a8a; }
.ai-page a:not(.btn), .modal.ai-dialog a:not(.btn), .ai-link { color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }
.ai-page .badge.rose { color: #ff8a8a; }
.ai-page .badge .icon { width: 14px; height: 14px; }
.ai-badge-ic { display: inline-flex; }
/* The released .spinner is a border and takes its size from the button it sits in; on the page it needs its own (a 4px speck otherwise). */
.ai-page .spinner { width: 14px; height: 14px; vertical-align: -2px; flex: none; }
.ai-page .badge.amber .icon { color: currentColor; }

/* Segmented control, stack variant: one option per line on a phone. */
.segmented.stack button { min-height: 40px; }
@media (max-width: 900px) {
  .segmented.stack { flex-direction: column; width: 100%; }
  .segmented.stack button { width: 100%; text-align: left; }
}

/* Radio card (guided setup). One <button role="radio" aria-checked> per card inside one role="radiogroup" .ai-radios. */
.ai-radios { display: grid; gap: 8px; }
.ai-radio {
  display: grid; grid-template-columns: 18px minmax(0, 1fr); column-gap: 12px; align-items: start; width: 100%; min-height: 44px;
  padding: 12px 14px; border-radius: var(--radius-md); border: 1px solid var(--text-tertiary); background: transparent;
  color: var(--text-primary); text-align: left; cursor: pointer; transition: background-color 150ms ease, border-color 150ms ease;
}
.ai-radio:hover { background: var(--clr-card-hover); }
.ai-radio:active { background: rgba(255, 255, 255, .07); }
.ai-radio:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ai-radio[disabled] { opacity: .6; cursor: default; }
.ai-radio-ring { width: 18px; height: 18px; margin-top: 1px; border-radius: 50%; border: 2px solid var(--text-tertiary); background: transparent; box-sizing: border-box; transition: border-color 150ms ease, box-shadow 150ms ease; }
.ai-radio-body { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; min-width: 0; }
.ai-radio-name { font-size: 15px; font-weight: 600; line-height: 1.4; overflow-wrap: anywhere; }
.ai-radio-tag { display: inline-flex; align-items: center; height: 20px; padding: 0 7px; border-radius: var(--radius-sm); background: var(--clr-primary-soft); color: var(--text-primary); font-size: 12px; font-weight: 600; white-space: nowrap; }
.ai-radio-desc { flex: 1 0 100%; color: var(--text-secondary); font-size: 13.5px; font-weight: 400; line-height: 1.5; overflow-wrap: anywhere; }
.ai-radio.on, .ai-radio[aria-checked="true"] { background: var(--clr-primary-soft); border-color: var(--clr-primary); }
.ai-radio.on .ai-radio-ring, .ai-radio[aria-checked="true"] .ai-radio-ring { border-color: var(--clr-primary); background: var(--clr-canvas); box-shadow: inset 0 0 0 5px var(--clr-primary); }

/* Panel summary: <details class="doc"><summary class="ai-sum">. Marker hidden, chevron turns 90deg when open. */
details.doc > summary.ai-sum {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; min-height: 44px; padding: 14px 18px; list-style: none; cursor: pointer;
  border-radius: var(--radius-lg);
}
details.doc > summary.ai-sum::-webkit-details-marker { display: none; }
details.doc > summary.ai-sum::marker { content: ""; }
.ai-sum:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }
.ai-sum-chev { display: inline-flex; flex-shrink: 0; color: var(--text-tertiary); transition: transform 200ms var(--ease-smooth); }
details[open] > .ai-sum .ai-sum-chev { transform: rotate(90deg); }
.ai-sum-title { font-size: 1.3rem; font-weight: 500; line-height: 1.3; white-space: nowrap; color: var(--text-primary); }
.ai-sum .badge { white-space: nowrap; }
.ai-sum-purpose { flex: 1 0 100%; padding-left: 28px; color: var(--text-secondary); font-size: 13.5px; line-height: 1.5; }
.ai-sum-link { margin-left: auto; font-size: 15px; }
.ai-panel-body { padding: 0 18px 18px; }
@media (max-width: 900px) {
  .ai-sum-link { flex: 1 0 100%; margin-left: 28px; }
}
/* Phone width: the title takes its own line after the chevron, so a chip always lands under the title, not under the chevron. */
@media (max-width: 640px) {
  .ai-sum-title { flex: 1 1 calc(100% - 28px); white-space: normal; }
  .ai-sum .badge { margin-left: 28px; }
}

/* Page header and status strip. */
.ai-head { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 16px; margin-bottom: 18px; }
.ai-head > .info-note { flex: 1 0 100%; }
.ai-head h1 { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ai-head .power { display: inline-flex; align-items: center; gap: 10px; }
.ai-head-links { display: flex; gap: 8px 16px; margin-left: auto; }
.ai-mode { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 500; color: var(--text-secondary); }
.ai-mode.on { color: var(--clr-success); }
.ai-strip { padding: 14px 18px; display: grid; gap: 6px; margin-bottom: 18px; }
.ai-strip-facts { display: flex; flex-wrap: wrap; font-size: 14px; color: var(--text-secondary); }
.ai-strip-facts > span + span::before { content: "\00a0\00b7\00a0"; }
.ai-strip-note { font-size: 14px; color: var(--text-secondary); }
@media (max-width: 640px) {
  /* Title row, then the mode line with the switch at the right, whatever the length of the mode line (ui, Watching page). */
  .ai-head h1 { flex: 1 0 100%; }
  .ai-head .power { margin-left: auto; }
  .ai-try-foot .btn { flex: 1 0 100%; }
}
@media (max-width: 900px) {
  .ai-head-links { flex: 1 0 100%; flex-direction: column; margin-left: 0; }
  .ai-strip-facts { flex-direction: column; }
  .ai-strip-facts > span + span::before { content: none; }
}

/* Guided setup. .content.setup is a column so the sticky footer sits at the bottom edge of a short page. */
.ai-step-label { font-size: 13px; font-weight: 600; color: var(--text-secondary); margin-bottom: 8px; }
.ai-setup-grid { display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 16px; align-items: start; }
.ai-facts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px 12px; margin: 0; font-size: 14px; }
.ai-facts dt { color: var(--text-secondary); }
.ai-facts dd { margin: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.content.setup { display: flex; flex-direction: column; min-height: calc(100vh - var(--topbar-height)); padding-bottom: 0; }
/* The dock keeps the footer (and the save error right above it) at the bottom edge; the gap keeps 16px of canvas between the page and it. */
.ai-setup-gap { flex: 0 0 16px; }
.ai-setup-dock { position: sticky; bottom: 0; margin: auto -24px 0; z-index: 5; }
.ai-setup-dock.has-error { background: var(--clr-dirtybar); }
.ai-setup-dock > .info-note { margin: 12px 24px 0; }
.ai-setup-foot {
  position: sticky; bottom: 0; padding: 12px 24px; z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 12px 16px;
  background: var(--clr-dirtybar); border-top: 1px solid var(--border-subtle); border-radius: 0;
}
.ai-setup-dock.has-error .ai-setup-foot { border-top-color: transparent; }
.ai-nowrap { white-space: nowrap; }
.ai-setup-foot .msg { flex: 1; min-width: 0; font-size: 14px; color: var(--text-secondary); }
.ai-setup-foot .btn .spinner { margin-right: 2px; }
.ai-narrow { display: none; }
@media (max-width: 900px) {
  .ai-setup-grid { grid-template-columns: minmax(0, 1fr); }
  .content.setup { padding-bottom: 0; }
  .ai-setup-dock { margin: auto -16px 0; }
  .ai-setup-dock > .info-note { margin: 12px 16px 0; }
  .ai-setup-foot { padding: 12px 16px; }
  .ai-setup-foot .msg { flex: 1 0 100%; }
  .ai-setup-foot .btn { flex: 1 1 0; }
  .ai-wide { display: none; }
  .ai-narrow { display: inline; }
}

/* Preset card (Quick setups). */
.ai-presets { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
.ai-preset { display: flex; flex-direction: column; gap: 8px; padding: 14px; border-radius: var(--radius-lg); border: 1px solid var(--border-subtle); background: var(--clr-surface-dark); min-width: 0; }
.ai-preset-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
.ai-preset-name { font-size: 15px; font-weight: 600; line-height: 1.4; color: var(--text-primary); overflow-wrap: anywhere; }
.ai-preset-desc { flex: 1; font-size: 13.5px; font-weight: 400; line-height: 1.5; color: var(--text-secondary); overflow-wrap: anywhere; }
.ai-preset-tag { display: inline-flex; align-items: center; height: 20px; padding: 0 7px; border-radius: var(--radius-sm); background: var(--clr-input-bg); color: var(--text-secondary); font-size: 12px; font-weight: 600; white-space: nowrap; }
.ai-preset .btn { align-self: flex-start; }
@media (max-width: 900px) {
  .ai-presets { grid-template-columns: minmax(0, 1fr); }
  .ai-preset .btn { width: 100%; align-self: stretch; }
}

/* Own-rule row: name and sentence side by side on a desktop, stacked on a phone. Counter and remove share the .rule-meta row. */
.ai-rule { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 8px; }
.ai-rule .textarea { resize: none; overflow-wrap: anywhere; white-space: pre-wrap; overflow: hidden; }
.ai-rule .rule-meta, .rule-meta { grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ai-rule .fld-error { grid-column: 1 / -1; margin-top: 0; }
.ai-rule + .ai-rule { margin-top: 16px; }
@media (max-width: 900px) {
  .ai-rule { grid-template-columns: minmax(0, 1fr); }
}

/* Switch rows (built-in rules): text on the left, the switch on a shared right edge. */
.ai-switch-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 4px 18px; }
.ai-switch-row .fld-label { margin-bottom: 2px; }
.ai-switch-row .fld-help { margin-top: 0; }

/* Review item (Waiting for a person). */
.ai-review { padding: 16px 0; display: grid; gap: 8px; min-width: 0; }
.ai-review + .ai-review { border-top: 1px solid var(--border-subtle); }
.ai-review:first-child { padding-top: 0; }
.ai-review-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; font-size: 15px; }
.ai-review-head .who { font-weight: 600; overflow-wrap: anywhere; }
.ai-review-head .where, .ai-review-head .when { color: var(--text-secondary); font-size: 14px; }
.ai-review-head .when { margin-left: auto; }
.ai-review-line { overflow-wrap: anywhere; font-size: 15px; }
.ai-review-line b { font-weight: 600; }
.ai-review-excerpt { color: var(--text-secondary); overflow-wrap: anywhere; }
.ai-review-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.ai-review-done { color: var(--text-tertiary); font-size: 14px; }
@media (max-width: 900px) {
  .ai-review-actions .btn { flex: 1 1 calc(50% - 8px); }
}

/* Limits usage bar: the setup progress track with the used / limit ratio as the fill. */
.ai-usage { display: grid; gap: 8px; }
.ai-usage-label { font-size: 14px; color: var(--text-secondary); }

/* Dialog on the released .modal-wrap / .modal shell (partials/ai_dialog.html). */
.modal.ai-dialog { width: min(480px, 100%); position: relative; }
.ai-dialog .modal-head { padding-right: 60px; }
.ai-dialog-x { position: absolute; top: 18px; right: 18px; }
.ai-dialog .modal-body { display: grid; gap: 14px; }
.ai-dialog-rows { display: grid; gap: 12px; margin: 0; }
.ai-dialog-row { display: grid; gap: 2px; min-width: 0; }
.ai-dialog-row dt { font-size: 13.5px; color: var(--text-secondary); }
.ai-dialog-row dd { margin: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.ai-dialog-field { display: grid; gap: 6px; }
.ai-dialog-field .fld-help { margin-top: 0; }
.ai-dialog-check { display: flex; align-items: center; gap: 10px; min-height: 24px; cursor: pointer; }
.ai-dialog-check input { width: 18px; height: 18px; flex-shrink: 0; margin: 0; }
.ai-dialog-count { color: var(--text-tertiary); font-size: 12.5px; text-align: right; }
@media (max-width: 640px) {
  .modal-wrap.ai-wrap { padding: 0; }
  .modal.ai-dialog { width: 100%; max-height: 100vh; max-height: 100dvh; border-radius: 0; display: flex; flex-direction: column; overflow: hidden; }
  .modal.ai-dialog .modal-body { flex: 1 1 auto; overflow-y: auto; }
  .modal.ai-dialog .modal-foot { position: sticky; bottom: 0; flex-direction: column; flex-wrap: nowrap; border-radius: 0; }
  .modal.ai-dialog .modal-foot .btn { width: 100%; }
}

/* AI moderation page, composition rules (MAR-672 WP6): the few classes the page's own layout needs on top of the WP4
   vocabulary above. Tokens and literals already in this file only; scoped to .ai-page. */
.ai-page { min-width: 0; }
.content.setup > [x-data], .ai-setup-root, .ai-setup-flow { display: flex; flex-direction: column; flex: 1 1 auto; }
.ai-step-h { display: flex; align-items: center; gap: 10px; font-size: 1.5rem; font-weight: 600; line-height: 1.3; margin: 18px 0 8px; color: var(--text-primary); }
.ai-step-lede { color: var(--text-secondary); max-width: 66ch; margin-bottom: 18px; }
.ai-step-hint { margin-top: 12px; color: var(--text-secondary); }
.ai-step-pick { margin-top: 12px; }
.ai-aside { padding: 18px; }
.ai-aside-h { font-size: 1.3rem; font-weight: 500; line-height: 1.3; margin-bottom: 14px; color: var(--text-primary); }
.ai-aside-narrow { margin-top: 16px; }
.ai-summary { grid-template-columns: minmax(110px, 220px) minmax(0, 1fr); margin: 14px 0 18px; }
@media (max-width: 900px) { .ai-summary { grid-template-columns: minmax(0, 1fr); gap: 2px; } .ai-summary dd { margin-bottom: 10px; } }
.ai-details { margin-top: 12px; }
.ai-details p + p { margin-top: 8px; }
.ai-done-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 18px; }
/* A block, so an empty slot adds no space; each visible note has the same 18px under it as every panel and the strip. */
.ai-banners > .info-note { margin-bottom: 18px; }
.ai-notes { display: grid; gap: 12px; }
.ai-page .sec-body > .info-note { grid-column: 1 / -1; }
/* A banner with a button: the text and the button share the line, the button at the far right (wireframe 9.7); it wraps under the text when the line is short. */
.info-note.ai-note-row { flex-wrap: wrap; align-items: center; }
.info-note.ai-note-row .ai-note-text { flex: 1 1 240px; min-width: 0; }
.info-note.ai-note-row .info-links { margin: 0 0 0 auto; }
.ai-rule-note { margin-top: 8px; }
.ai-rule-add { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 12px; }
.ai-unsure-line { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; margin-top: 8px; }
.ai-unsure-line .fld-help { margin-top: 0; }
.ai-try-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; }
.ai-try-foot .muted { flex: 1 1 240px; min-width: 0; }
.ai-try-out { scroll-margin-bottom: 104px; }
.ai-try-result { display: grid; gap: 8px; padding: 14px 16px; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.ai-privacy { display: grid; gap: 0; }
.ai-privacy p { color: var(--text-secondary); overflow-wrap: anywhere; }
.ai-privacy p + p { margin-top: 8px; }
.ai-privacy-h { font-size: 15px; font-weight: 600; margin: 16px 0 4px; color: var(--text-primary); }
.ai-privacy-h:first-child { margin-top: 0; }
.ai-paste { resize: none; overflow: hidden; }
@media (max-width: 900px) {
  .ai-done-actions .btn { width: 100%; }
}

/* §5.3 reduced motion: every animation and transition swaps instantly */
@media (prefers-reduced-motion: reduce) {
  .fld-enter-from { transform: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .dirty-bar { transition: none !important; }
  .modal-dialog { transform: none !important; }
}

/* ---------------------------------------------------------------- Custom bot page (MAR-667, WP6) */
/* ui §2.1 to §2.9 and §3: the stepper (C1), the verification row (C2), the profile card (C3), the colour row (C5),
   the status header (P1, C6), the image upload (C7), the danger zone (C8), the alert banner (C9) and the layout. */
.cb-page .cb-progress { display: none; }
.cb-step { display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 14px; align-items: start; padding: 12px 14px;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md); background: var(--clr-card-bg); }
.cb-step .n { width: 30px; height: 30px; border-radius: var(--radius-full); display: grid; place-items: center;
  font-weight: 700; font-size: 14px; background: var(--clr-input-bg); color: var(--text-secondary); }
.cb-step h3 { font-family: var(--font-ui); font-size: 15.5px; font-weight: 600; line-height: 1.3; margin: 0 0 4px; }
.cb-step p { font-family: var(--font-ui); font-size: 14px; line-height: 1.45; color: var(--text-secondary); margin: 0; }
.cb-step.current { border-color: var(--clr-primary); background: var(--clr-primary-soft); }
.cb-step.current .n { background: transparent; color: var(--text-primary); box-shadow: 0 0 0 2px var(--clr-primary); }
.cb-step.current h3, .cb-step.current p { color: var(--text-secondary); }
.cb-step.done .n { background: var(--mint-soft); color: var(--clr-success); }
.cb-step.done h3 { color: var(--text-primary); }
.cb-steps { display: grid; gap: 14px; }
.cb-step .cb-verify { margin-top: 10px; }
/* R48: the page moves focus on its own, so the element it lands on shows the ring even when the focus did not come
   from the keyboard (the released :focus-visible rule stays silent after a click). */
.cb-step-title:focus, .cb-verify:focus, .cb-p1:focus { outline: 2px solid var(--border-focus); outline-offset: 2px; }
/* C2 — the verification row: icon and sentence, data-state and data-icon are the probe hooks. */
.cb-verify { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm); font-size: 13.5px; line-height: 1.4; color: var(--text-secondary);
  transition: border-color 200ms var(--ease-smooth), background-color 200ms var(--ease-smooth); }
.cb-verify[data-state="checking"] { border-color: var(--clr-primary); }
.cb-verify[data-state="passed"] { border-color: var(--clr-success); background: var(--mint-soft); }
.cb-verify[data-state="failed"] { border-color: rgba(255, 107, 127, .25); background: var(--rose-soft); }
.cb-verify .cb-vi { flex-shrink: 0; margin-top: 1px; color: var(--text-tertiary); }
.cb-verify[data-state="checking"] .cb-vi { color: var(--clr-primary); }
.cb-verify[data-state="passed"] .cb-vi { color: var(--clr-success); }
.cb-verify[data-state="failed"] .cb-vi { color: var(--clr-danger-text, #ff8a8a); }
.cb-verify .cb-vlinks { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.cb-verify .cb-vlinks .btn { height: 31px; }
/* In steps 3 and 4 the buttons sit under the verification row, as its sibling, so the row's own rule above never reaches
   them: this one keeps them 12px clear of the row (MAR-1034). */
.cb-verify + .cb-vlinks { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
/* The icon slot of C2 (.cb-vi) and the indicator of C6 (.cb-ind) are 16px boxes holding every glyph; the one the
   data-icon / data-shape value names is the only one drawn (ui §2.5, §2.6). */
.cb-vi, .cb-ind { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; flex-shrink: 0; }
.cb-vi > .g, .cb-ind > .g { display: none; }
.cb-vi .spinner, .cb-ind .spinner { width: 16px; height: 16px; }
.cb-vi[data-icon="check"] > .g-check, .cb-vi[data-icon="x"] > .g-x, .cb-vi[data-icon="spinner"] > .g-spinner,
.cb-vi[data-icon="unknown"] > .g-ring, .cb-vi[data-icon="stopped"] > .g-ring { display: inline-flex; }
.cb-ind[data-shape="x"] > .g-x, .cb-ind[data-shape="triangle"] > .g-triangle, .cb-ind[data-shape="ring"] > .g-ring,
.cb-ind[data-shape="spinner"] > .g-spinner, .cb-ind[data-shape="disc"] > .g-disc { display: inline-flex; }
.cb-ind > .g-ring, .cb-vi > .g-ring { width: 14px; height: 14px; box-sizing: border-box; border: 2px solid currentColor; border-radius: 50%; }
.cb-ind > .g-disc { width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 0 3px var(--mint-soft); }
/* C3 — the profile card, painted from the .dc-modal look; the --dc-* tokens come from the scope rule below. */
.cb-profile-card { background: var(--dc-embed-background); border: 1px solid var(--dc-border-subtle); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45); font-family: var(--dc-font); overflow: hidden; padding-bottom: 12px; }
/* MAR-1310 (addendum preview-correction, section 2.1), top to bottom: the banner (a 60px band without an image, 20:7 with
   one), the 80px avatar ring starting 40px above the banner's bottom edge, the name row 8px under it, the description
   and the activity card 12px apart, the card ending 12px under the last of them. Revision 4 and 5 (MAR-1460): the sizes
   and colours come from Discord's bot profile popout (document discord-profile-reference), the status dot is a 28px box
   (a 16px disc inside a 6px ring) 2px inside the avatar box, and the custom status is a bubble beside the avatar. */
.cb-profile-card .cb-pc-banner { height: 60px; width: 100%; background: var(--clr-discord); }
.cb-profile-card .cb-pc-banner.has-img { height: auto; aspect-ratio: 20 / 7; }
.cb-profile-card .cb-pc-banner img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The head row holds the avatar and, when there is one, the status bubble: 105px from the card's inner left edge and 7px
   above the banner's bottom edge (33px under the avatar box's top). It is in the flow, so a two-line bubble (54px, ending
   87px under the avatar box's top) pushes the name row down 7px and a one-line bubble (36px) changes nothing. */
.cb-pc-head { display: flex; align-items: flex-start; margin-top: -40px; padding-left: 16px; }
.cb-pc-avwrap { position: relative; flex: 0 0 80px; width: 80px; height: 80px; }
.cb-profile-card .cb-pc-av { display: block; width: 80px; height: 80px; border-radius: 50%; border: 4px solid var(--dc-embed-background);
  object-fit: cover; background: var(--dc-surface-higher); }
.cb-pc-dot { position: absolute; right: 2px; bottom: 2px; box-sizing: border-box; width: 28px; height: 28px; border-radius: 50%;
  border: 6px solid var(--dc-embed-background); }
.cb-pc-dot.online { background: var(--clr-success); }
.cb-pc-dot.idle { background: var(--clr-warning); }
.cb-pc-dot.dnd { background: var(--clr-danger-hover); }
.cb-pc-dot.invisible, .cb-pc-dot.offline { background: var(--dc-text-muted); }
.cb-pc-bubble { box-sizing: border-box; flex: 0 1 auto; min-width: 42px; max-width: min(181px, calc(100% - 105px)); margin: 33px 0 0 9px;
  background: #3f4048; border: 1px solid rgba(151, 151, 159, .04); border-radius: 16px; padding: 8px 12px;
  font-size: 14px; font-weight: 400; line-height: 18px; color: var(--dc-text-default); }
.cb-pc-bubble-text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }
.cb-pc-info { margin-top: 8px; padding: 0 16px; min-width: 0; }
/* The name wraps (anywhere, so a 32 character name of one word does too) to at most two lines, then an ellipsis; the
   APP tag keeps its size and sits on the first line, 6px after the name, centred on it (16px tall, 4px under the line's
   top). It has no check mark: the markup has none. */
.cb-pc-name { display: flex; align-items: flex-start; gap: 6px; font-size: 20px; font-weight: 700; line-height: 24px; color: var(--dc-text-default); }
.cb-pc-name .cb-pc-nm { min-width: 0; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.cb-pc-name .dc-app { flex-shrink: 0; height: 16px; margin: 4px 0 0; padding: 0 4px; font-size: 12px; line-height: 16px; }
/* The description sits on the card's own surface: no background, no border, no line above it. Its heading is .sr-only. */
.cb-pc-about { margin-top: 12px; padding: 0 16px; }
.cb-pc-about .cb-pc-text { font-size: 14px; line-height: 18px; color: var(--dc-text-strong); white-space: pre-wrap; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
/* The activity card (DEF-07): `#3f4048` is Discord's --neutral-60, written here and not as a shared token. 8 + 16 + 8 + 18 + 8 = 58px. */
.cb-pc-actcard { margin: 12px 16px 0; padding: 8px; background: #3f4048; border-radius: 8px; }
.cb-pc-act-type { font-size: 12px; font-weight: 500; line-height: 16px; color: var(--dc-text-strong); }
.cb-pc-act-name { margin-top: 8px; font-size: 14px; font-weight: 600; line-height: 18px; color: var(--dc-text-default); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* C5 — the colour row: swatch, hex, Reset on one row at 640 and below too. */
.cb-color .color-input { display: flex; align-items: center; gap: 8px; }
.cb-color .color-input .hex { width: 110px; }
.cb-color + .cb-color { margin-top: 16px; }
/* C6 — the P1 status header. The action group ends at the right edge of P1's content (ui §2.6, V28): with several
   buttons it wraps, and every wrapped row is right-aligned so the last button still ends on that edge. */
.cb-p1 { display: flex; align-items: center; gap: 18px; padding: 24px 28px; border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); background: var(--clr-card-bg); }
.cb-p1 .cb-profile { display: flex; gap: 18px; align-items: center; min-width: 0; }
.cb-p1 .cb-profile .av { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; background: var(--clr-input-bg); flex-shrink: 0; }
/* The name wraps inside the card, with no ellipsis and no line cap, so a name that does not fit (32 characters of one
   word) grows the card downwards instead of pushing the page sideways (MAR-1463, D2 of MAR-1459; the same treatment the
   C3 profile card's name has). Both flex parents already carry min-width: 0, so the name is what shrinks. */
.cb-p1 .cb-profile .nm { font-family: var(--font-ui); font-size: 22px; font-weight: 700; line-height: 1.25; overflow-wrap: anywhere; }
.cb-p1 .cb-state { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; margin-top: 4px; }
.cb-p1 .cb-state .led { width: 7px; height: 7px; }
.cb-p1 .cb-state .lbl { font-weight: 600; white-space: nowrap; }
/* The sentence sits under the state word on its own line (ui §2.6), so the label never shares a line with it or wraps. */
.cb-p1 .cb-line { flex: 1 1 100%; min-width: 0; color: var(--text-secondary); font-size: 14px; line-height: 1.45; overflow-wrap: anywhere; }
.cb-meta { font-size: 13px; font-weight: 400; line-height: 1.45; color: var(--text-tertiary); margin-top: 4px; }
/* Line 1 is who connected the bot and line 2 how many other servers it runs in (ui §2.6): two lines, not one run-on sentence. */
.cb-meta > span { display: block; }
.cb-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; justify-content: flex-end; }
/* MAR-1500 (MAR-1459 round 2, change 2): the card's text column is never narrower than 120px. Just above the 1100 break
   point the two-column layout gives the card only 368 to 472px, the action group took the rest of the row and the column
   measured 42.1px in S7 (34.2px in S6) and 100.9px in S3 at 1100, with the status sentence drawn one word per line; MAR-1463's
   overflow-wrap then broke the seed's own name inside the word there. The column keeps 120px as its floor, and one row
   needs 58px of card frame (2px border, 28px padding a side), the 72px avatar, an 18px gap, that 120px column, an 18px
   gap and the narrowest action row any state draws (168px, S6's "Open the Bot page" link) to hold both: 472px. The card
   is 368px wide at 1100 and gains a pixel per viewport pixel (card = viewport - 732), so the row cannot hold both
   between 1100 and 1204 and the action group takes the row under the text there, the arrangement the card already has
   at 390 (its card is 358px wide there and 368px at 1100). Above 1204 the row keeps them beside, as it always did; a
   media query rather than the card's own container query, because a container cannot lay itself out (app.css:505). */
.cb-p1 .cb-profile-info { min-width: 120px; }
@media (min-width: 1100px) and (max-width: 1204px) {
  .cb-p1 { flex-direction: column; align-items: flex-start; }
  .cb-p1 .cb-actions { width: 100%; margin-left: 0; }
}
.cb-ind[data-shape="x"] { color: var(--clr-danger-text, #ff8a8a); }
.cb-ind[data-shape="triangle"] { color: var(--clr-warning); }
.cb-ind[data-shape="ring"] { color: var(--text-tertiary); }
.cb-ind[data-shape="spinner"] { color: var(--clr-primary); }
.cb-ind[data-shape="disc"] { color: var(--clr-success); }
/* C7 — the image upload. */
.cb-upload { position: relative; overflow: hidden; display: inline-flex; }
.cb-upload input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.cb-upload:focus-within { outline: 2px solid var(--border-focus); outline-offset: 2px; border-radius: 4px; }
.cb-imgrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cb-imgrow .cb-imgprev { border-radius: var(--radius-sm); background: var(--clr-input-bg); overflow: hidden; flex-shrink: 0; }
.cb-imgrow .cb-imgprev.avatar { width: 64px; height: 64px; border-radius: 50%; }
.cb-imgrow .cb-imgprev.banner { width: 240px; aspect-ratio: 5 / 2; }
.cb-imgrow .cb-imgprev img { width: 100%; height: 100%; object-fit: cover; }
/* C8 — the danger zone. The stack spans the section body's grid (a .fld spans all six columns). */
.cb-danger-stack { display: flex; flex-direction: column; gap: 16px; grid-column: span 6; min-width: 0; }
.cb-danger-row { display: flex; align-items: center; gap: 16px; }
.cb-danger-row .cb-danger-text { flex: 1; min-width: 0; }
.cb-danger-row .btn-danger { align-self: center; }
/* C9 — the server-wide alert banner. */
.cb-alert { margin-bottom: 18px; }
/* The banner quotes the bot's name (up to 32 characters, no spaces): its text column shrinks and the name wraps at any character. */
.cb-alert > div { min-width: 0; overflow-wrap: anywhere; }
.info-note.cb-done { margin-bottom: 18px; }
/* The token row (W2, P6, take-over): the field and the button share a row at 1440. P5's test-message row and P7's
   alert row use the same shape: the select flexes like the token field (ui §2.6), the .btn-outline sits beside it. */
.cb-token-row { display: flex; gap: 10px; flex-wrap: wrap; }
.cb-token-row .input { flex: 1; min-width: 220px; }
.cb-token-row .select { flex: 1; min-width: 220px; }
.cb-token-row + .info-note { margin-top: 12px; }
.cb-loading .spinner { width: 22px; height: 22px; color: var(--clr-primary); }
.cb-invite { margin-top: 12px; }
.cb-profile-info { min-width: 0; }
/* The section bodies of this page are the released six-column grid, so every direct child spans it and only the
   .fld.half pair of P3 keeps its half; the same convention modules/tickets/_styles.html uses for .tk-grid. Without
   it the owner-only wrapper of a template and every health row would land in one 40px column and overflow the page. */
.cb-sb > * { grid-column: 1 / -1; }
.cb-sb > .fld.half { grid-column: span 3; }
/* The bot's own name is quoted in the verification sentences (C2), the info notes and the C8 help text, and a 32
   character name is one unbreakable word, so those rows were 424 to 443px wide and pushed the page to 517px at 320 and
   390 even with P1's own name wrapping (MAR-1463, D2 of MAR-1459). overflow-wrap inherits and only changes a line
   that has a word longer than it, so one rule here reaches all of them and every other sentence is drawn as before. */
.cb-sb { overflow-wrap: anywhere; }
/* The layout: P1, the preview column, then P2-P8 inside one .doc (ui §3). */
.content--wide { --content-max-width: 1240px; }
.cb-layout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; grid-template-areas: "p1 preview" "rest preview";
  column-gap: 24px; row-gap: 16px; align-items: start; }
.cb-layout > .cb-p1 { grid-area: p1; }
.cb-layout > .cb-preview-col { grid-area: preview; }
.cb-layout > .cb-rest { grid-area: rest; }
/* MAR-1310 (addendum preview-correction, section 1): the column has no height cap and no scroll of its own; nothing in
   it is cut. The cards are short enough to fit (C3 at most 388px, C4 at most 500px) and the viewport height picks the
   mode, so it never changes while typing. Single (720 to 1199px tall, the default here): the tabs and the selected card,
   pinned. Pair (1200px and taller): both cards, no tabs, pinned. Flow (below 720px): the column scrolls with the page.
   app.js (pvMode) reads the same three media queries. */
.cb-preview-col { position: sticky; top: calc(var(--topbar-height) + 16px); display: flex; flex-direction: column; align-items: flex-start; gap: 16px; }
.cb-preview-col > * { flex-shrink: 0; }
.cb-preview-col .cb-profile-card, .cb-preview-col .dcf { max-width: 360px; width: 100%; }
.cb-card-message { width: 100%; }
/* C4's author: one line that ends in an ellipsis; the APP tag and the time move to a second line when they no longer fit. */
.cb-preview-col .dcf-controls { overflow: visible; }  /* the shared frame scrolls its controls sideways on a phone; nothing in this column scrolls */
/* MAR-4414 (addendum revision 7, section 7.1): an emoji inline box is measured by the face of the text beside it,
   so the line it sits on grows 1px wherever that face is a fallback - the box's own baseline sits 9px inside its
   18px box beside Noto Sans and 8px beside Helvetica Neue, and the line that holds it is 20px or 21px (embeds 2, 3
   and 4, C4 482 -> 485 with no caption and 484/500 -> 487/503 under one). The box is taken out of that measurement
   instead of being given a face of its own, because a face is a property of the machine: top-aligned, with the 2px
   margin that keep it where it already was, its margin box is the 20px the line has beside the product's own faces
   and it no longer hangs under the baseline, so the line is that height whatever face draws the text and nothing
   moves while the product's faces are loaded. The shared `.dc-emo` rules are not edited. */
.cb-preview-col .dc-emo { vertical-align: top; margin-top: 2px; }
/* No caption (the sender is the custom bot, which the owner is editing): the name gets a whole line, shortens with an
   ellipsis at the width of the message body, and the APP tag and the time move to a second line when they no longer fit. */
.cb-card-message .dc-name { max-width: calc(100% - 4px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* MAR-1460 (addendum revision 5, 2.2, DEF-01): the custom bot's row stores no VERIFIED_BOT flag, so its APP tag shows no
   check mark. The class is set from the same getter as the sender (pvMsgIdentity); the shared frame's rule is untouched. */
.cb-msg-sender.is-custom .dc-app svg { display: none; }
/* Under a caption (the sender is Voinly's bot, named by the nickname saved for it) the author row is always one line,
   22px, wherever the card is 358px wide or more: the APP tag and the time stay whole beside the name and the name takes
   the width that is left and ends in an ellipsis (point 1 of revision 4, defects D3 and D4 of MAR-1311). A narrower card
   (a 320px screen) keeps the shared frame's wrapping row. */
.cb-card-message { container-type: inline-size; }
@container (min-width: 358px) {
  .cb-msg-sender:not(.is-custom) .dc-head { flex-wrap: nowrap; }
  .cb-msg-sender:not(.is-custom) .dc-name { flex: 0 1 auto; min-width: 0; max-width: none; }
  .cb-msg-sender:not(.is-custom) .dc-app, .cb-msg-sender:not(.is-custom) .dc-time { flex: none; white-space: nowrap; }
}
/* One card at a time (ui V24): the two switches are symmetric, so only the selected tab's card is drawn, at 1100 and above
   in the single and flow modes and below 1100 always. The pair mode below draws both. */
@media (min-width: 1100px) {
  .cb-preview-col .cb-profile-card { display: none; }
  .cb-preview-col .cb-profile-card.is-on { display: block; }
  .cb-preview-col .cb-card-message { display: none; }
  .cb-preview-col .cb-card-message.is-on { display: block; }
}
@media (min-width: 1100px) and (min-height: 1200px) {
  .cb-preview-col > .dcf-seg { display: none; }
  .cb-preview-col .cb-profile-card, .cb-preview-col .cb-profile-card.is-on { display: block; }
  .cb-preview-col .cb-card-message, .cb-preview-col .cb-card-message.is-on { display: block; }
}
@media (min-width: 1100px) and (max-height: 719.98px) {
  .cb-preview-col { position: static; }
}
@media (max-width: 1099px) {
  .cb-layout { grid-template-columns: minmax(0, 1fr); grid-template-areas: "p1" "preview" "rest"; }
  .cb-preview-col { position: static; }
  .cb-preview-col .cb-profile-card { display: none; }
  .cb-preview-col .cb-profile-card.is-on { display: block; }
  .cb-preview-col .cb-card-message { display: none; }
  .cb-preview-col .cb-card-message.is-on { display: block; }
  .cb-preview-col > .dcf-seg { height: 48px; }
  .cb-preview-col > .dcf-seg button { height: 44px; line-height: 44px; }
}
/* The Discord tokens exist outside .dcf/.dc for the profile card (ui §2.3). */

/* The Discord tokens exist outside .dcf/.dc for the profile card and the 390 preview column (ui §2.3): the scope
   selector gains .cb-preview-col, so the card reads the same tokens the frame does. */
/* 640 and below: the setup progress line shows, the P1 box and controls stack, everything stays 44px tall. */
@media (max-width: 640px) {
  .cb-page .cb-progress { display: block; font-size: 13px; font-weight: 500; line-height: 1.45; color: var(--text-secondary); margin: 0 0 10px; }
  /* Every control of this page is a 44px touch target at 390 (V2); the released .btn keeps its 38px desktop height. */
  .cb-page .input, .cb-page .select, .cb-page .textarea, .cb-page .btn, .cb-page .color-swatch { min-height: 44px; }
  .cb-page .btn { height: 44px; }
  .cb-page .color-swatch { width: 44px; height: 44px; }
  /* C5 at 640 and below (ui §2.5): the hex field grows so the swatch, the field and Reset of one colour share one
     row. The rule is this page's colour row only — at 641px and wider the field keeps its 110px width above and no
     other page's colour input is reached by it. */
  .cb-page .cb-color .color-input .hex { flex: 1 1 110px; min-width: 110px; width: auto; }
  .cb-p1 { padding: 20px 16px; flex-direction: column; align-items: flex-start; }
  .cb-p1 .cb-profile { flex-direction: column; gap: 14px; }
  .cb-p1 .cb-profile .av { width: 56px; height: 56px; }
  .cb-actions { width: 100%; margin-left: 0; }
  .cb-actions .btn { min-height: 44px; }
  .cb-token-row { flex-direction: column; align-items: stretch; }
  .cb-token-row .input, .cb-token-row .btn { min-height: 44px; }
  /* C7 at 390: the banner preview is the section-body width (ui §2.7, measured 324x129.59), the avatar keeps 64px. */
  .cb-imgrow .cb-imgprev.banner { width: 100%; flex: 1 1 100%; }
  .cb-imgrow .cb-imgprev.avatar { flex: 0 0 auto; }
  .cb-danger-row { flex-direction: column; align-items: flex-start; }
  .cb-danger-row .btn-danger { width: 100%; min-height: 44px; }
}
/* The Custom bot page's own error text: --clr-danger-text (ui §2.2), the token the decision log names. */
.cb-page .fld-error { color: var(--clr-danger-text); }
/* ui §1.7 — a text link on this page is --text-primary and underlined at rest. The blue shared button exception of
   the same section covers button fills, never a link inside a sentence: --clr-primary is 3.10:1 on the blue tint of
   the current setup step. The preview column keeps the Discord frame's own --dc-text-link, so it is not listed. */
.cb-page .cb-sb a:not(.btn), .cb-page .cb-alert a:not(.btn) {
  color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }
