/* console.css: Bot Manners School console layout.
   Every value resolves to a --29d-* token from the canonical tokens.css.
   Components come from the canonical components.css (.ds-*); this file
   only arranges them.

   TOKEN SOURCE RULING (Lane 2, C3, 2026-10-25): exactly ONE token
   source serves this console: enroll/assets/tokens.css + components.css,
   the vendored files that are byte-synced to the canon at
   ideas.29court.com/design-system/ (verified 2026-10-25: components.css
   byte-identical to canon; tokens.css identical except the sync header
   comment). The merged deploy serves those same physical files at
   /enroll/assets/* and inlines them as TOKENS_CSS / COMPONENTS_CSS, so
   enroll and the console can never drift apart. Tide tokens.css
   (tide.29court.com) is a different dialect that re-points --29d-*
   values; it is NOT a source for this surface. Never link a second
   token file here. */

body { margin: 0; background: var(--29d-bg); color: var(--29d-text-1); font-family: var(--29d-font-ui); }

.ds-root *, .ds-root *::before, .ds-root *::after { box-sizing: border-box; }
/* The hidden attribute must win over layout display rules (.gate-wrap is
   display:grid, which silently un-hides the gate). Caught in rendered
   proof, 2026-10-24: gate stacked above the app after sign-in while
   every DOM check passed. */
.ds-root [hidden] { display: none !important; }
.ds-root .ds-input, .ds-root .ds-select, .ds-root .ds-textarea { font-size: 16px; }
/* Console touch floor: small buttons still land a finger at phone size. */
.ds-root .ds-btn-sm { height: 40px; }
/* Console text floor (UI skill: UI text at 13px or larger). The canonical
   badge and table-header styles compute at 12px and 11px; inside the
   console only, they are raised to the 13px floor token. Same scoped-
   override pattern as the touch floor above; the canonical files are
   not forked. Caught by the Lane 2 gate, 2026-10-25. */
.ds-root .ds-badge { font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); }
.ds-root .ds-table th { font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); }
.ds-root .ds-overline { font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); }
/* .num is emitted by console.js for the rules count in a card title;
   it was used but never defined (Lane 2 gate, 2026-10-25). */
.num { font-variant-numeric: tabular-nums; }
/* Grid children must be allowed to shrink below content width, or a wide
   table stretches the whole page past the phone. */
.stack > *, .grid-2 > *, .grid-3 > *, .stat-row > *, .check-form > * { min-width: 0; }
.ds-table-wrap { max-width: 100%; }
@media (max-width: 640px) {
  .console-account, .brand-sub, .stub-badge { display: none; }
  .console-top { padding: 0 var(--29d-s-3); gap: var(--29d-s-2); }
  .brand-name { font-size: var(--29d-text-md); }
}

/* ---------- gate ---------- */
.gate-wrap {
  min-height: 100vh; min-height: 100dvh;
  display: grid; place-items: center;
  padding: var(--29d-s-4);
  padding-top: max(var(--29d-s-4), env(safe-area-inset-top));
  padding-bottom: max(var(--29d-s-4), env(safe-area-inset-bottom));
}
.gate-card { max-width: 440px; width: 100%; padding: var(--29d-s-8); }
.gate-title {
  font-family: var(--29d-font-display); font-weight: var(--29d-weight-xbold);
  font-size: var(--29d-text-3xl); line-height: var(--29d-lh-3xl);
  margin: var(--29d-s-2) 0 var(--29d-s-2); letter-spacing: -0.01em;
}
.gate-lede { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); color: var(--29d-text-2); margin: 0 0 var(--29d-s-6); }
.gate-form { display: grid; gap: var(--29d-s-4); }
.gate-submit { width: 100%; height: 44px; }
.gate-note { display: flex; gap: var(--29d-s-2); align-items: flex-start; font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); color: var(--29d-text-3); margin: var(--29d-s-6) 0 0; }

/* ---------- app chrome ---------- */
.console-top { position: sticky; top: 0; z-index: 20; gap: var(--29d-s-3); }
.console-brand { display: flex; align-items: baseline; gap: var(--29d-s-2); min-width: 0; }
.brand-name { font-family: var(--29d-font-display); font-weight: var(--29d-weight-xbold); font-size: var(--29d-text-lg); white-space: nowrap; }
.brand-sub { font-size: var(--29d-text-sm); color: var(--29d-text-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--29d-weight-semibold); }
.console-top-right { margin-left: auto; display: flex; align-items: center; gap: var(--29d-s-2); }
.console-account { font-size: var(--29d-text-sm); color: var(--29d-text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 180px; }
.console-nav {
  display: flex; gap: var(--29d-s-4); overflow-x: auto;
  padding: 0 var(--29d-page-pad);
  background: var(--29d-surface); border-bottom: 1px solid var(--29d-border);
  position: sticky; top: 56px; z-index: 19;
  scrollbar-width: none;
}
.console-nav::-webkit-scrollbar { display: none; }
.console-nav .ds-navlink { white-space: nowrap; padding-top: var(--29d-s-3); padding-bottom: var(--29d-s-3); min-height: 40px; display: inline-flex; align-items: center; }

/* ---------- mobile tab bar + desktop rail (UI-3 C2, Lane 2 2026-10-25) ----------
   One chrome per width: under 960px the bottom tab bar carries the four
   routes and the top nav row stands down; at 960px and up the same top
   nav becomes a left rail and the tab bar stands down. Same links, same
   order, same aria-current treatment in both chromes. */
.console-tabbar {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  grid-template-columns: repeat(4, 1fr);
  background: var(--29d-surface); border-top: 1px solid var(--29d-border);
  padding-bottom: env(safe-area-inset-bottom);
}
.console-tabbar a {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--29d-s-0); min-height: 56px; padding: var(--29d-s-1);
  color: var(--29d-text-2); text-decoration: none;
  font-size: var(--29d-text-sm); font-weight: var(--29d-weight-medium);
}
.console-tabbar a:active { background: var(--29d-wash-2); }
.console-tabbar .tab-dot { width: 20px; height: 3px; border-radius: var(--29d-pill); background: transparent; }
.console-tabbar a[aria-current="page"] { color: var(--29d-text-1); font-weight: var(--29d-weight-semibold); }
.console-tabbar a[aria-current="page"] .tab-dot { background: var(--29d-coral-600); }
@media (max-width: 959px) {
  .console-nav { display: none; }
  .console-tabbar { display: grid; }
  .console-main { padding-bottom: calc(var(--29d-s-12) + 64px); }
  .console-foot { padding-bottom: calc(var(--29d-s-8) + 64px); }
  .console-toasts { bottom: max(72px, env(safe-area-inset-bottom)); }
}
@media (min-width: 960px) {
  #appView {
    display: grid;
    grid-template-columns: 208px minmax(0, 1fr);
    grid-template-areas: "top top" "nav main" "nav foot";
    align-items: start;
  }
  .console-top { grid-area: top; }
  .console-nav {
    grid-area: nav;
    flex-direction: column; align-items: stretch; gap: var(--29d-s-1);
    overflow-x: visible;
    padding: var(--29d-s-4) var(--29d-s-3);
    border-bottom: none; border-right: 1px solid var(--29d-border);
    top: 56px; height: calc(100vh - 56px); height: calc(100dvh - 56px);
  }
  .console-nav .ds-navlink {
    min-height: 44px; padding: var(--29d-s-2) var(--29d-s-3);
    border-bottom: none; border-radius: var(--29d-r-2); margin-bottom: 0;
  }
  .console-nav .ds-navlink[aria-current="page"] { background: var(--29d-wash-2); }
  @media (hover: hover) {
    .console-nav .ds-navlink:hover { background: var(--29d-wash); }
  }
  .console-main { grid-area: main; width: 100%; }
  .console-foot { grid-area: foot; width: 100%; }
}
.console-main { max-width: 1120px; margin: 0 auto; padding: var(--29d-s-6) var(--29d-page-pad) var(--29d-s-12); outline: none; }
.console-foot {
  max-width: 1120px; margin: 0 auto; padding: 0 var(--29d-page-pad) var(--29d-s-8);
  display: flex; justify-content: space-between; gap: var(--29d-s-3); flex-wrap: wrap;
  font-size: var(--29d-text-sm); color: var(--29d-text-3);
}
.console-toasts { position: fixed; right: var(--29d-s-4); bottom: max(var(--29d-s-4), env(safe-area-inset-bottom)); z-index: 50; display: grid; gap: var(--29d-s-2); }

/* ---------- finish-setup banner + account settings rows (R6) ----------
   The banner is the first thing on the page when a setup step is owed
   (R7 async return lands here too). Coral tint, one message, one
   action. The settings rows are the grouped-settings grammar: the
   words keep the wide column (flex 1, min-width 0), the action is
   shrinkable furniture that wraps below on a narrow screen. */
#viewRoot { outline: none; }
.setup-banner {
  display: flex; align-items: center; gap: var(--29d-s-4); flex-wrap: wrap;
  background: var(--29d-coral-50); border: 1px solid var(--29d-coral-600);
  border-radius: var(--29d-r-3); padding: var(--29d-s-4); margin-bottom: var(--29d-s-6);
}
.setup-banner-text { flex: 1 1 auto; min-width: 0; }
.setup-banner h2 { font-size: var(--29d-text-lg); line-height: var(--29d-lh-lg); margin: 0; letter-spacing: -0.01em; overflow-wrap: anywhere; }
.setup-banner p { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); color: var(--29d-text-2); margin: var(--29d-s-1) 0 0; overflow-wrap: anywhere; }
.setup-banner .ds-btn { flex: 0 1 auto; }
.setting-row {
  display: flex; align-items: center; gap: var(--29d-s-3); flex-wrap: wrap;
  padding: var(--29d-s-3) 0; border-bottom: 1px solid var(--29d-border);
}
.setting-row:last-child { border-bottom: none; }
.setting-text { flex: 1 1 auto; min-width: 0; }
.setting-text b { display: block; font-size: var(--29d-text-md); line-height: var(--29d-lh-md); }
.setting-text span { display: block; font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); color: var(--29d-text-2); margin-top: var(--29d-s-0); overflow-wrap: anywhere; }
.setting-row .ds-btn { flex: 0 1 auto; }

/* ---------- delete group + deleted receipt (R6K) ----------
   The danger group is the last group in the Your account panel: its
   own separated block, prose column defended like every setting row
   (flex 1, min-width 0), actions shrinkable and wrapping below. The
   two step-2 actions carry equal weight: same size, same row, the
   way back styled as a real button, never a faint link. */
.danger-group { border-top: 1px solid var(--29d-red-500); margin-top: var(--29d-s-3); padding-top: var(--29d-s-3); }
.danger-group .setting-text b { color: var(--29d-red-700); }
.danger-step p { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); color: var(--29d-text-2); margin: 0 0 var(--29d-s-3); overflow-wrap: anywhere; }
.danger-step p b { color: var(--29d-text-1); }
.danger-line { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); font-weight: var(--29d-weight-semibold); color: var(--29d-text-1) !important; }
.delete-actions { display: flex; gap: var(--29d-s-2); flex-wrap: wrap; align-items: center; }
.delete-actions .ds-btn { flex: 0 1 auto; }
.danger-step .ds-field { margin-bottom: var(--29d-s-3); max-width: 320px; }
.deleted-counts { display: grid; gap: var(--29d-s-1); margin: 0 0 var(--29d-s-4); }
.deleted-counts p { margin: 0; font-size: var(--29d-text-md); line-height: var(--29d-lh-md); color: var(--29d-text-2); overflow-wrap: anywhere; }
.deleted-kept-title { font-size: var(--29d-text-lg); line-height: var(--29d-lh-lg); margin: 0 0 var(--29d-s-2); }
.deleted-kept { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); color: var(--29d-text-2); margin: 0 0 var(--29d-s-3); overflow-wrap: anywhere; }
#deletedView .gate-submit { margin-top: var(--29d-s-2); }
/* Plan link inside the delete step + the deleted receipt (R6M): the
   words sit in the prose line, but the link itself carries the
   console touch floor — an inline-flex box at least 40px tall,
   never a 17px prose sliver a finger cannot land on. */
.plan-link { display: inline-flex; align-items: center; min-height: 40px; }

/* ---------- shared view pieces ---------- */
.view-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--29d-s-4); flex-wrap: wrap; margin-bottom: var(--29d-s-4); }
.view-title { font-size: var(--29d-text-2xl); line-height: var(--29d-lh-2xl); font-weight: var(--29d-weight-bold); margin: 0; letter-spacing: -0.01em; }
.view-sub { font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); color: var(--29d-text-2); margin: var(--29d-s-1) 0 0; max-width: 62ch; }
.stack { display: grid; gap: var(--29d-s-4); }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--29d-s-4); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--29d-s-4); }
@media (max-width: 860px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } }

.stat-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--29d-s-4); }
@media (max-width: 860px) { .stat-row { grid-template-columns: 1fr 1fr; } }
.stat-num { font-size: var(--29d-text-3xl); line-height: var(--29d-lh-3xl); font-weight: var(--29d-weight-bold); font-variant-numeric: tabular-nums; }
.stat-label { font-size: var(--29d-text-sm); color: var(--29d-text-3); text-transform: uppercase; letter-spacing: 0.06em; font-weight: var(--29d-weight-semibold); margin-top: var(--29d-s-1); }
.stat-sub { font-size: var(--29d-text-sm); color: var(--29d-text-2); margin-top: var(--29d-s-1); }

.bot-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--29d-s-3); }
.bot-label { font-size: var(--29d-text-lg); line-height: var(--29d-lh-lg); font-weight: var(--29d-weight-bold); margin: 0; overflow-wrap: anywhere; }
.bot-meta { font-size: var(--29d-text-sm); color: var(--29d-text-3); margin-top: var(--29d-s-1); }
.bot-stats { display: flex; gap: var(--29d-s-6); margin-top: var(--29d-s-4); flex-wrap: wrap; }
.bot-stat b { display: block; font-size: var(--29d-text-xl); line-height: var(--29d-lh-xl); font-variant-numeric: tabular-nums; }
.bot-stat span { font-size: var(--29d-text-sm); color: var(--29d-text-3); text-transform: uppercase; letter-spacing: 0.05em; font-weight: var(--29d-weight-semibold); }
.card-actions { display: flex; gap: var(--29d-s-2); margin-top: var(--29d-s-4); flex-wrap: wrap; }

.key-line { display: flex; align-items: center; gap: var(--29d-s-2); flex-wrap: wrap; min-width: 0; }
.key-line > a { overflow-wrap: anywhere; min-width: 0; display: inline-flex; align-items: center; min-height: 40px; }
.key-code {
  font-family: var(--29d-font-mono); font-size: var(--29d-text-sm);
  background: var(--29d-sunken); border: 1px solid var(--29d-border);
  border-radius: var(--29d-r-2); padding: var(--29d-s-2) var(--29d-s-3);
  overflow-wrap: anywhere; word-break: break-all;
}
.reveal-panel { border: 1px solid var(--29d-green-600); background: var(--29d-green-100); border-radius: var(--29d-r-3); padding: var(--29d-s-4); }
.reveal-panel p { margin: 0 0 var(--29d-s-2); font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); color: var(--29d-text-1); }

/* ---------- rules ---------- */
.rule-row { display: grid; gap: var(--29d-s-2); padding: var(--29d-s-3) 0; border-bottom: 1px solid var(--29d-border); }
.rule-row:last-child { border-bottom: none; }
.rule-top { display: flex; align-items: center; gap: var(--29d-s-2); flex-wrap: wrap; }
.rule-n { font-weight: var(--29d-weight-bold); font-variant-numeric: tabular-nums; }
.rule-text { font-size: var(--29d-text-md); line-height: var(--29d-lh-md); margin: 0; overflow-wrap: anywhere; }
.rule-text.retired { color: var(--29d-text-3); }
.rule-actions { display: flex; gap: var(--29d-s-2); flex-wrap: wrap; }
.rule-edit { display: grid; gap: var(--29d-s-2); }

/* ---------- check playground ---------- */
.score-line { display: flex; align-items: center; gap: var(--29d-s-4); flex-wrap: wrap; }
.score-num { font-size: var(--29d-text-4xl); line-height: var(--29d-lh-4xl); font-weight: var(--29d-weight-bold); font-variant-numeric: tabular-nums; }
.score-sub { font-size: var(--29d-text-sm); color: var(--29d-text-2); }
.violation { border: 1px solid var(--29d-border); border-left: 3px solid var(--29d-red-500); border-radius: var(--29d-r-2); padding: var(--29d-s-3) var(--29d-s-4); display: grid; gap: var(--29d-s-1); }
.violation b { font-size: var(--29d-text-sm); }
.violation .why { font-size: var(--29d-text-sm); color: var(--29d-text-2); line-height: var(--29d-lh-sm); }
.violation .ex { font-size: var(--29d-text-sm); font-style: italic; color: var(--29d-text-3); overflow-wrap: anywhere; }
.violation .fix { font-size: var(--29d-text-sm); }
.instruction { font-size: var(--29d-text-sm); line-height: var(--29d-lh-sm); color: var(--29d-text-2); background: var(--29d-sunken); border: 1px solid var(--29d-border); border-radius: var(--29d-r-2); padding: var(--29d-s-3) var(--29d-s-4); }
.sample-row { display: flex; gap: var(--29d-s-2); flex-wrap: wrap; }
.check-form { display: grid; gap: var(--29d-s-3); }
.check-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: var(--29d-s-3); align-items: end; }
@media (max-width: 860px) { .check-row { grid-template-columns: 1fr; } }
.toggle-line { display: flex; align-items: center; gap: var(--29d-s-2); font-size: var(--29d-text-sm); font-weight: var(--29d-weight-medium); min-height: var(--29d-control-h); }
/* The checkbox glyph stays 20px; its hit box is padded to the 40px
   touch floor (border-box total 40px, glyph centred in the content
   box). Lane 2 gate, 2026-10-25: the bare 20px input failed the
   target floor even inside its 40px label row. */
.toggle-line input { width: 40px; height: 40px; padding: 10px; accent-color: var(--29d-coral-600); flex: none; }

.create-row { display: grid; grid-template-columns: 1fr auto; gap: var(--29d-s-3); align-items: end; }
@media (max-width: 860px) { .create-row { grid-template-columns: 1fr; } }
.bot-picker { display: flex; align-items: center; gap: var(--29d-s-3); flex-wrap: wrap; }
.bot-picker .ds-select { min-width: 220px; max-width: 100%; }
.mono { font-family: var(--29d-font-mono); }
.nowrap { white-space: nowrap; }
td .excerpt { display: block; max-width: 340px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 860px) { td .excerpt { max-width: 150px; } }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}
