.dt-tenant-layer {
  --dt-void: #010306;
  --dt-ink: #eef8ff;
  --dt-ink-soft: #c6d6e2;
  --dt-muted: #71879a;
  --dt-copper-hot: #ffc18a;
  --dt-energy: #76ff68;
  --dt-energy-hot: #ddffd8;
  --dt-danger: #ff7569;
  --dt-success: #7cff8e;
  --dt-line: rgba(139, 205, 244, .26);
  position: relative;
  z-index: 2147480000;
  color: var(--dt-ink);
  font: 400 15px/1.48 "Bahnschrift", "Segoe UI Variable", "Aptos", system-ui, sans-serif;
  letter-spacing: .01em;
}

.dt-tenant-layer, .dt-tenant-layer * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
.dt-tenant-layer button { color: inherit; font: inherit; touch-action: manipulation; }
.dt-tenant-layer button:focus-visible { outline: 1px solid rgba(128,235,255,.88); outline-offset: 2px; }
.dt-tenant-layer [hidden] { display: none !important; }

.dt-guide-pill {
  position: fixed;
  z-index: 2147480100;
  right: max(12px, env(safe-area-inset-right));
  bottom: max(12px, env(safe-area-inset-bottom));
  min-height: 38px;
  padding: 7px 13px;
  border: 1px solid rgba(126,255,111,.3);
  border-radius: 0;
  color: #c9ffc3;
  background: linear-gradient(145deg, rgba(31,102,35,.78), rgba(2,17,11,.9));
  box-shadow: 0 14px 35px rgba(0,0,0,.55), 0 0 18px rgba(118,255,104,.1), inset 0 1px 0 rgba(255,255,255,.12), inset 0 -2px 0 rgba(0,0,0,.68);
  clip-path: polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px);
  cursor: pointer;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .085em;
  text-transform: uppercase;
  transition: transform .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.dt-guide-pill:hover { transform: translateY(-2px); color: #fff; border-color: rgba(139,231,255,.48); box-shadow: 0 16px 40px rgba(0,0,0,.6), 0 0 22px rgba(57,187,255,.13), inset 0 1px 0 rgba(255,255,255,.14); }
.dt-guide-pill:active { transform: translateY(1px); box-shadow: inset 0 3px 7px rgba(0,0,0,.55); }

.dt-overlay {
  position: fixed;
  inset: 0;
  z-index: 2147480200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(12px, env(safe-area-inset-top)) max(10px, env(safe-area-inset-right)) max(12px, env(safe-area-inset-bottom)) max(10px, env(safe-area-inset-left));
  background: rgba(0,3,8,.76);
  backdrop-filter: blur(8px) saturate(1.15);
}

.dt-sheet {
  position: relative;
  width: min(880px, 100%);
  max-height: min(92vh, 860px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--dt-line);
  color: var(--dt-ink-soft);
  background: linear-gradient(180deg,rgba(255,255,255,.075),transparent 9%),linear-gradient(125deg,rgba(7,20,32,.96),rgba(1,8,15,.94) 52%,rgba(7,19,30,.96));
  box-shadow: 0 30px 90px rgba(0,0,0,.8),0 5px 18px rgba(0,0,0,.65),inset 0 1px 0 rgba(239,251,255,.19),inset 0 -1px 0 rgba(0,0,0,.92),inset 1px 0 0 rgba(121,192,236,.08);
  clip-path: polygon(12px 0,100% 0,100% calc(100% - 12px),calc(100% - 12px) 100%,0 100%,0 12px);
}
.dt-sheet::before {
  content: "";
  position: absolute;
  inset: 3px;
  z-index: 0;
  pointer-events: none;
  border: 1px solid rgba(215,240,255,.075);
  clip-path: polygon(10px 0,calc(100% - 10px) 0,100% 10px,100% calc(100% - 8px),calc(100% - 8px) 100%,8px 100%,0 calc(100% - 8px),0 10px);
}
.dt-sheet::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: 12%;
  top: 0;
  width: 250px;
  height: 2px;
  background: linear-gradient(90deg,transparent,rgba(170,233,255,.8),#fff,rgba(106,255,93,.72),transparent);
  box-shadow: 0 0 7px rgba(146,231,255,.8),0 0 22px rgba(66,185,255,.45);
}
.dt-sheet > * { position: relative; z-index: 1; }

.dt-sheet-head {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 17px 17px 13px;
  border-bottom: 1px solid rgba(139,204,241,.13);
  background: linear-gradient(180deg,rgba(255,255,255,.03),transparent);
}
.dt-sheet-title-wrap { min-width: 0; flex: 1; }
.dt-sheet-kicker { color: #91a9bb; font-size: 9px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
.dt-sheet-title { margin: 4px 0 0; color: #effaff; font-size: clamp(18px,4.7vw,24px); line-height: 1.12; font-weight: 720; letter-spacing: .045em; text-transform: uppercase; }
.dt-close-button {
  flex: 0 0 36px;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(137,202,239,.16);
  border-radius: 0;
  color: #a5bdcd;
  background: rgba(1,9,15,.55);
  clip-path: polygon(6px 0,100% 0,100% calc(100% - 6px),calc(100% - 6px) 100%,0 100%,0 6px);
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
}
.dt-close-button:hover { color: #fff; border-color: rgba(126,231,255,.4); }

.dt-consent-body { overflow-y: auto; padding: 19px 17px 7px; scrollbar-width: thin; scrollbar-color: rgba(102,255,91,.25) transparent; }
.dt-consent-copy {
  margin: 0;
  padding: 14px;
  border: 1px solid rgba(139,202,239,.13);
  color: #c4d4df;
  background: rgba(0,8,15,.58);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.dt-consent-actions { flex: 0 0 auto; display: flex; justify-content: flex-end; padding: 13px 17px 17px; }

.dt-primary-button, .dt-secondary-button, .dt-tab-button {
  min-height: 40px;
  padding: 8px 13px;
  border: 1px solid rgba(126,255,111,.25);
  border-radius: 0;
  color: #c9ffc3;
  background: linear-gradient(145deg,rgba(31,102,35,.34),rgba(2,17,11,.52));
  box-shadow: inset 0 1px 0 rgba(255,255,255,.09),inset 0 -2px 0 rgba(0,0,0,.58);
  clip-path: polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px);
  cursor: pointer;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .065em;
  text-transform: uppercase;
  transition: transform .15s ease,color .15s ease,border-color .15s ease,box-shadow .15s ease,background .15s ease;
}
.dt-primary-button:hover, .dt-secondary-button:hover { color: #fff; border-color: rgba(139,231,255,.45); box-shadow: 0 0 20px rgba(57,187,255,.12),inset 0 1px 0 rgba(255,255,255,.1); }
.dt-primary-button:active, .dt-secondary-button:active { transform: translateY(1px); box-shadow: inset 0 3px 7px rgba(0,0,0,.55); }
.dt-primary-button:disabled { cursor: not-allowed; opacity: .48; }
.dt-secondary-button { border-color: rgba(141,204,239,.18); color: #a7b9c6; background: rgba(1,9,15,.55); }

.dt-tutorial-grid { min-height: 0; flex: 1; display: flex; flex-direction: column; }
.dt-tutorial-nav {
  flex: 0 0 auto;
  display: flex;
  gap: 4px;
  padding: 9px 10px;
  overflow-x: auto;
  border-bottom: 1px solid rgba(139,204,241,.11);
  background: rgba(0,7,13,.42);
  scrollbar-width: thin;
  scrollbar-color: rgba(102,255,91,.22) transparent;
}
.dt-tab-button {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 7px 10px;
  border-color: rgba(139,202,239,.12);
  color: #778d9e;
  background: rgba(0,8,15,.55);
  white-space: nowrap;
}
.dt-tab-button[aria-selected="true"] { color: #d9f8ff; border-color: rgba(118,255,104,.3); box-shadow: inset 0 -2px 0 rgba(118,255,104,.55); }
.dt-tutorial-content { min-height: 0; flex: 1; overflow-y: auto; padding: 19px 17px; scrollbar-width: thin; scrollbar-color: rgba(102,255,91,.25) transparent; }
.dt-section-title { margin: 0 0 12px; color: #effaff; font-size: 18px; font-weight: 700; letter-spacing: .045em; text-transform: uppercase; }
.dt-section-body { margin: 0; color: #c4d4df; white-space: pre-wrap; overflow-wrap: anywhere; }
.dt-point-list { display: grid; gap: 8px; margin: 14px 0 0; padding: 0; list-style: none; }
.dt-point-item { position: relative; padding: 9px 10px 9px 26px; border: 1px solid rgba(139,202,239,.11); color: #b9cbd7; background: rgba(0,8,15,.55); }
.dt-point-item::before { content: ""; position: absolute; left: 10px; top: 15px; width: 6px; height: 6px; transform: rotate(45deg); border: 1px solid rgba(151,255,141,.65); box-shadow: 0 0 8px rgba(118,255,104,.24); }
.dt-tier-cards { display: grid; gap: 8px; margin-top: 15px; }
.dt-tier-card { padding: 12px; border: 1px solid rgba(255,181,115,.22); color: #cbd8e1; background: linear-gradient(145deg,rgba(63,30,14,.27),rgba(0,8,15,.62)); clip-path: polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px); box-shadow: inset 0 1px 0 rgba(255,255,255,.06); }
.dt-tier-card strong { display: block; color: var(--dt-copper-hot); font-size: 12px; letter-spacing: .08em; text-transform: uppercase; }
.dt-tier-card p { margin: 7px 0 0; color: #9db0bd; font-size: 12px; white-space: pre-wrap; }
.dt-tutorial-actions { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 9px; padding: 12px 17px 17px; border-top: 1px solid rgba(139,204,241,.11); }
.dt-tutorial-actions-buttons { display: flex; align-items: center; gap: 7px; }
.dt-progress-label { color: #718797; font: 600 9px/1.2 ui-monospace, "Cascadia Mono", Consolas, monospace; letter-spacing: .08em; text-transform: uppercase; }

.dt-toast-rack {
  position: fixed;
  z-index: 2147480300;
  right: max(12px, env(safe-area-inset-right));
  bottom: max(62px, calc(env(safe-area-inset-bottom) + 54px));
  width: min(370px, calc(100vw - 24px));
  display: grid;
  gap: 8px;
  pointer-events: none;
}
.dt-toast {
  position: relative;
  padding: 12px 13px;
  border: 1px solid rgba(139,202,239,.26);
  color: #c6d6e2;
  background: linear-gradient(180deg,rgba(255,255,255,.055),transparent 25%),rgba(2,10,19,.95);
  box-shadow: 0 18px 42px rgba(0,0,0,.62),inset 0 1px 0 rgba(239,251,255,.14),inset 0 -1px 0 rgba(0,0,0,.9);
  clip-path: polygon(7px 0,100% 0,100% calc(100% - 7px),calc(100% - 7px) 100%,0 100%,0 7px);
  font-size: 12px;
  animation: dt-toast-enter .22s ease-out both;
}
.dt-toast-success { border-color: rgba(118,255,104,.36); color: #caffc5; box-shadow: 0 18px 42px rgba(0,0,0,.62),0 0 20px rgba(118,255,104,.08),inset 0 1px 0 rgba(239,251,255,.14); }
.dt-toast-neutral { border-color: rgba(141,204,239,.24); color: #b8cbd8; }
.dt-toast-error { border-color: rgba(255,117,105,.4); color: #ffaaa2; }

@keyframes dt-toast-enter { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }

@media (min-width: 720px) {
  .dt-tutorial-grid { flex-direction: row; }
  .dt-tutorial-nav { flex: 0 0 190px; flex-direction: column; padding: 13px 10px; overflow-x: visible; overflow-y: auto; border-right: 1px solid rgba(139,204,241,.11); border-bottom: 0; }
  .dt-tab-button { width: 100%; text-align: left; white-space: normal; }
  .dt-tab-button[aria-selected="true"] { box-shadow: inset 2px 0 0 rgba(118,255,104,.55); }
  .dt-tutorial-content { padding: 24px 25px; }
  .dt-tier-cards { grid-template-columns: repeat(3,minmax(0,1fr)); }
}

@media (max-width: 480px) {
  .dt-overlay { align-items: flex-end; padding-inline: 0; padding-bottom: 0; }
  .dt-sheet { width: 100%; max-height: 94dvh; clip-path: polygon(11px 0,100% 0,100% 100%,0 100%,0 11px); }
  .dt-sheet-head { padding: 15px 13px 12px; }
  .dt-consent-body, .dt-tutorial-content { padding: 15px 13px; }
  .dt-consent-actions, .dt-tutorial-actions { padding: 11px 13px max(13px,env(safe-area-inset-bottom)); }
  .dt-primary-button { min-height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  .dt-tenant-layer *, .dt-tenant-layer *::before, .dt-tenant-layer *::after { animation: none !important; scroll-behavior: auto !important; transition-duration: .01ms !important; }
}

/* ---- One-time install prompt card ---- */
.dt-install {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 2147483000; display: flex; align-items: center; gap: 14px;
  width: min(480px, calc(100vw - 24px)); padding: 13px 15px;
  background: linear-gradient(180deg, rgba(4, 16, 24, .98), rgba(1, 7, 12, .98));
  border: 1px solid rgba(143, 208, 244, .4);
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
  box-shadow: 0 22px 54px rgba(0, 0, 0, .72), 0 0 22px rgba(112, 255, 130, .1), inset 0 1px 0 rgba(210, 240, 255, .12);
}
.dt-install-copy { flex: 1; min-width: 0; }
.dt-install-copy b { display: block; color: #dff2ff; font-size: 13px; letter-spacing: .04em; }
.dt-install-copy span { display: block; margin-top: 2px; color: #8fb0c4; font-size: 11.5px; line-height: 1.45; }
.dt-install-actions { display: flex; gap: 8px; flex: none; }

/* ---- Setup access (Fred, 2026-07-18): the ONE lime-electric element in the rail — the door to
   connectors + credits, deliberately unmissable for owner and guests alike. ---- */
.sb-setup {
  display: flex; align-items: center; gap: 10px; width: calc(100% - 24px); margin: 10px 12px 4px;
  padding: 12px 14px; border-radius: 12px; cursor: pointer;
  background: linear-gradient(160deg, rgba(60, 255, 120, 0.10), rgba(8, 28, 14, 0.7));
  border: 1px solid rgba(102, 255, 140, 0.55); color: #b8ffcf;
  font: inherit; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; font-size: 11.5px;
  box-shadow: 0 0 14px rgba(80, 255, 130, 0.3), inset 0 0 10px rgba(80, 255, 130, 0.1);
  animation: sbSetupPulse 2.2s ease-in-out infinite;
}
.sb-setup:hover { filter: brightness(1.18); }
.sb-setup-core {
  width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; background: #66ff8c;
  box-shadow: 0 0 12px #66ff8c, 0 0 26px rgba(102, 255, 140, 0.8);
}
@keyframes sbSetupPulse {
  0%, 100% { box-shadow: 0 0 10px rgba(80, 255, 130, 0.22), inset 0 0 8px rgba(80, 255, 130, 0.08); }
  50% { box-shadow: 0 0 24px rgba(80, 255, 130, 0.6), inset 0 0 14px rgba(80, 255, 130, 0.22); }
}
/* The rail's New button now carries a full sentence — keep it on one graceful line-or-two. */
.sb-head .sb-new span { font-size: 11px; letter-spacing: 0.05em; line-height: 1.3; text-align: left; white-space: normal; }

/* Privacy explainer card (appears under the header when the dial is touched or changed). */
.privacy-note {
  position: fixed; top: 74px; left: 50%; transform: translateX(-50%);
  z-index: 2147480200; max-width: min(480px, calc(100vw - 24px));
  padding: 12px 16px; border-radius: 12px; cursor: pointer;
  background: linear-gradient(165deg, rgba(10, 24, 38, 0.96), rgba(4, 10, 18, 0.96));
  border: 1px solid rgba(120, 200, 255, 0.35); color: #cfe6f5;
  font-size: 12.5px; line-height: 1.5; letter-spacing: 0.02em;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6), 0 0 18px rgba(90, 180, 255, 0.15);
  transition: opacity 0.4s ease;
}
.privacy-note b { color: #eaf6ff; }
.privacy-note.gone { opacity: 0; }
/* Settings sheet helper text (persona vs Operating Mode). */
.frow-note {
  margin: 6px 2px 10px; color: #8fb0c4; font-size: 11.5px; line-height: 1.55; letter-spacing: 0.02em;
}
.frow-note em { color: #cfe6f5; font-style: normal; font-weight: 600; }

/* ==== Badass Cyber Mech pass (Fred, 2026-07-18) ==================================== */

/* Model selection ceremony: green laser races the trigger border, then the name flares. */
@property --laser-a { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
.model-trigger { position: relative; }
.model-trigger.laser::after {
  content: ""; position: absolute; inset: -2px; border-radius: 12px; padding: 2px;
  background: conic-gradient(from var(--laser-a), transparent 0deg 318deg, rgba(102, 255, 140, 0.25) 330deg, #66ff8c 352deg 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: laserRace 0.9s linear 1 forwards;
  pointer-events: none;
  filter: drop-shadow(0 0 6px rgba(102, 255, 140, 0.8));
}
@keyframes laserRace { from { --laser-a: 0deg; } to { --laser-a: 720deg; } }
.model-current.name-flare { animation: nameFlare 1.7s ease-out 1; }
@keyframes nameFlare {
  0% { color: #eafff0; text-shadow: 0 0 14px rgba(102, 255, 140, 0.95), 0 0 30px rgba(102, 255, 140, 0.5); }
  55% { color: #a8ffc4; text-shadow: 0 0 8px rgba(102, 255, 140, 0.55); }
  100% { color: inherit; text-shadow: none; }
}

/* Dominion Prime: the core breathes green while the four bars ignite one at a time and then
   pulse in step with it — the "connecting to something bigger" look. */
.prime-module .prime-core { position: relative; }
.prime-module .prime-core .glyph, .prime-module .prime-core { animation: primeBreathe 3.2s ease-in-out infinite; }
@keyframes primeBreathe {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(102, 255, 140, 0.35)); }
  50% { filter: drop-shadow(0 0 14px rgba(102, 255, 140, 0.95)) drop-shadow(0 0 30px rgba(102, 255, 140, 0.45)); }
}
.prime-bars i {
  background: #66ff8c !important;
  opacity: 0.15;
  animation: barIgnite 3.2s ease-in-out infinite;
  box-shadow: 0 0 8px rgba(102, 255, 140, 0.7);
}
.prime-bars i:nth-child(1) { animation-delay: 0s; }
.prime-bars i:nth-child(2) { animation-delay: 0.22s; }
.prime-bars i:nth-child(3) { animation-delay: 0.44s; }
.prime-bars i:nth-child(4) { animation-delay: 0.66s; }
@keyframes barIgnite {
  0% { opacity: 0.15; }
  18% { opacity: 1; }
  45% { opacity: 0.85; }
  60% { opacity: 1; }
  100% { opacity: 0.15; }
}

/* GUIDE pill: never sit on the send arrow (mobile) and never cover the rail's Prime bars. */
@media (max-width: 620px) {
  .dt-guide-pill { bottom: max(96px, calc(env(safe-area-inset-bottom) + 84px)); }
}
.rail-open .dt-guide-pill { opacity: 0; pointer-events: none; transition: opacity 0.2s ease; }

/* Fresh-start ignition: one green scanline sweeps the chat surface after "Start a New
   Conversation or Project". */
#neural-glass { position: relative; }
.nc-ignite::before {
  content: ""; position: absolute; inset: 0; z-index: 40; pointer-events: none;
  background: linear-gradient(180deg, transparent 0%, rgba(102, 255, 140, 0.14) 46%, rgba(102, 255, 140, 0.5) 50%, rgba(102, 255, 140, 0.14) 54%, transparent 100%);
  background-size: 100% 220%;
  animation: ncSweep 0.9s ease-out 1 forwards;
  mix-blend-mode: screen;
}
@keyframes ncSweep { from { background-position: 0 -120%; opacity: 1; } to { background-position: 0 120%; opacity: 0; } }

/* Guide pill, docked in the composer next to the paperclip (Fred, 2026-07-18). The fixed-floater
   rules above still cover the fallback case; docked mode goes static and compact. */
.bar-left { display: flex; align-items: flex-end; gap: 6px; min-width: 0; }
.dt-guide-pill.dt-guide-docked {
  position: static; right: auto; bottom: auto;
  height: 43px; padding: 0 14px; border-radius: 12px; flex: 0 0 auto;
}
@media (max-width: 620px) {
  .bar-left { grid-column: 1; grid-row: 2; justify-self: start; }
  .dt-guide-pill.dt-guide-docked { bottom: auto; }
}
/* Docked pill never needs the rail-open fade (the drawer overlays the composer naturally). */
.rail-open .dt-guide-pill.dt-guide-docked { opacity: 1; pointer-events: auto; }

/* Voice mode, found (Fred, 2026-07-18): the speaker toggle was display:none on phones — spoken
   answers were invisible exactly where they matter. Dock it bottom-left with the paperclip.

   FIXED 2026-07-19: #speak and .bar-left were BOTH assigned grid-column 1 / grid-row 2, one
   start-aligned and one end-aligned. Two items in one grid cell overlap by definition, and they
   did: once bar-left grew to four controls (paperclip, forge dial, Forge Images, GUIDE) it ran
   198px wide into a ~180px cell and the GUIDE pill sat on top of the speaker button. Adding
   buttons to bar-left will do this again unless each control owns a column, so the mobile bar now
   has a dedicated column per right-hand control and bar-left keeps column 1 to itself. */
@media (max-width: 620px) {
  .bar { grid-template-columns: minmax(0, 1fr) 43px 43px 55px; }
  #speak { display: flex; grid-column: 2; grid-row: 2; justify-self: center; margin-right: 0; }
  #mic { grid-column: 3; grid-row: 2; }
  .send { grid-column: 4; grid-row: 2; }
  /* The left cluster must never push into its neighbours again: it shrinks instead. */
  .bar-left { min-width: 0; gap: 5px; overflow: hidden; }
  /* GUIDE goes icon-only on phones. The word cost 63px in a row that did not have 63px to give,
     and the pill is recognisable without it. Full label returns above 620px. */
  .dt-guide-pill.dt-guide-docked { padding: 0; width: 36px; min-width: 36px; justify-content: center; font-size: 0; }
  .dt-guide-pill.dt-guide-docked::before { content: "?"; font-size: 15px; font-weight: 800; letter-spacing: 0; }
}

/* Per-answer speak buttons (bubble corners) + the auto-speak master toggle state. */
.msg.ai .bubble.has-speak { position: relative; padding-right: 46px; }
.bspeak {
  position: absolute; right: 8px; width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 8px; cursor: pointer; font-size: 14px; opacity: 0.7;
  background: rgba(10, 30, 16, 0.55); border: 1px solid rgba(102, 255, 140, 0.35); color: #b8ffcf;
}
.bspeak:hover { opacity: 1; box-shadow: 0 0 10px rgba(102, 255, 140, 0.4); }
.bspeak-top { top: 8px; }
.bspeak-bottom { bottom: 8px; }
#speak { display: flex; align-items: center; justify-content: center; gap: 2px; }
#speak.speakon {
  background: linear-gradient(160deg, rgba(60, 255, 120, 0.2), rgba(8, 28, 14, 0.75)) !important;
  border-color: rgba(102, 255, 140, 0.7) !important; color: #b8ffcf;
  box-shadow: 0 0 14px rgba(80, 255, 130, 0.5), inset 0 0 8px rgba(80, 255, 130, 0.15);
}
#speak .spk-state { font-size: 9px; font-weight: 800; letter-spacing: 0.05em; color: #66ff8c; }

/* ---------- what's new / release notes dialog (Fred, 2026-07-19) ----------
   Rides the existing .dt-sheet chrome so it reads as part of the app. The body scrolls on its own
   because the sheet is height-capped: on a phone this content is well past a screen, and a dialog
   that scrolls the page behind it is how you lose the dismiss button. */
.dt-release-body {
  min-height: 0;
  flex: 1;
  overflow-y: auto;
  padding: 18px 17px 4px;
  scrollbar-width: thin;
  scrollbar-color: rgba(102, 255, 91, .25) transparent;
}
.dt-release-standfirst {
  margin: 0 0 20px;
  color: #c4d4df;
  font-size: 15px;
  line-height: 1.55;
}
.dt-release-head {
  margin: 22px 0 10px;
  color: #effaff;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .17em;
  text-transform: uppercase;
}
.dt-release-head::after {
  content: "";
  display: block;
  width: 46px;
  height: 1px;
  margin-top: 7px;
  background: linear-gradient(90deg, rgba(118, 255, 104, .9), transparent);
}
.dt-release-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }
.dt-release-list li { position: relative; padding-left: 19px; }
.dt-release-list li::before {
  content: "";
  position: absolute; left: 0; top: .55em;
  width: 6px; height: 6px;
  background: var(--dt-energy);
  box-shadow: 0 0 7px rgba(118, 255, 104, .5);
  transform: rotate(45deg);
}
.dt-release-list b { display: block; color: #eaf6ff; font-size: 14px; font-weight: 700; letter-spacing: .01em; }
.dt-release-list span { display: block; margin-top: 2px; color: #a9bfcd; font-size: 13.5px; line-height: 1.5; }
.dt-release-horizon {
  margin: 26px 0 6px;
  padding: 15px 16px;
  border: 1px solid rgba(255, 193, 138, .3);
  background: linear-gradient(140deg, rgba(255, 193, 138, .08), rgba(1, 9, 15, .5) 60%);
}
.dt-release-horizon b { display: block; color: var(--dt-copper-hot); font-size: 13px; font-weight: 700; letter-spacing: .04em; }
.dt-release-horizon span { display: block; margin-top: 6px; color: #b6c6d2; font-size: 13.5px; line-height: 1.5; }
@media (max-width: 620px) {
  .dt-release-body { padding: 15px 14px 4px; }
  .dt-release-standfirst { font-size: 14.5px; }
  .dt-release-list b { font-size: 13.5px; }
  .dt-release-list span { font-size: 13px; }
}

/* ---------- voice transport + mic status (Fred, 2026-07-19) ----------
   Three fixes in one surface. Speaking used to be invisible and unstoppable: no sign the request
   was in flight, no control once audio started. The bar below IS the progress indicator and the
   stop button, which is why it shows from the moment the request leaves, not when audio arrives.
   It sits above the composer and below the modals so it never blocks a control you might need. */
.voice-bar {
  position: fixed;
  left: 50%;
  bottom: max(14px, calc(env(safe-area-inset-bottom) + 10px));
  transform: translateX(-50%);
  z-index: 2147480050;
  width: min(430px, calc(100vw - 24px));
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border: 1px solid rgba(102, 255, 140, .34);
  background: linear-gradient(150deg, rgba(9, 34, 20, .95), rgba(2, 12, 8, .96));
  box-shadow: 0 18px 44px rgba(0, 0, 0, .6), 0 0 22px rgba(102, 255, 140, .1), inset 0 1px 0 rgba(255, 255, 255, .1);
  clip-path: polygon(9px 0, 100% 0, 100% calc(100% - 9px), calc(100% - 9px) 100%, 0 100%, 0 9px);
}
.voice-bar[hidden] { display: none; }
.voice-bar .vb-body { flex: 1; min-width: 0; display: grid; gap: 7px; }
.voice-bar .vb-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.voice-bar .vb-state { color: #b8ffcf; font: 700 10px/1 "Cascadia Mono", "Consolas", monospace; letter-spacing: .16em; }
.voice-bar .vb-time { color: #7fa793; font: 500 10px/1 "Cascadia Mono", "Consolas", monospace; }
.voice-bar .vb-track { height: 4px; overflow: hidden; background: rgba(102, 255, 140, .12); }
.voice-bar .vb-track i { display: block; width: 0; height: 100%; background: linear-gradient(90deg, #2f9d4a, #b8ffcf); box-shadow: 0 0 8px rgba(102, 255, 140, .6); transition: width .15s linear; }
/* Preparing has no duration to report yet, so the bar sweeps. A frozen 0% bar reads as broken. */
.voice-bar[data-state="preparing"] .vb-track i { width: 34%; animation: vb-indeterminate 1.05s ease-in-out infinite; transition: none; }
@keyframes vb-indeterminate { 0% { transform: translateX(-105%); } 100% { transform: translateX(320%); } }
.voice-bar[data-state="paused"] .vb-state { color: #ffd9a0; }
.voice-bar .vb-play, .voice-bar .vb-stop {
  flex: none; display: grid; place-items: center; cursor: pointer;
  border: 1px solid rgba(102, 255, 140, .34); color: #b8ffcf; background: rgba(3, 18, 9, .8);
}
.voice-bar .vb-play { width: 40px; height: 40px; font-size: 13px; }
.voice-bar .vb-stop { width: 34px; height: 34px; font-size: 13px; color: #ffb0a0; border-color: rgba(255, 138, 112, .38); }
.voice-bar .vb-play:disabled { opacity: .45; cursor: default; }
.voice-bar .vb-play:hover:not(:disabled), .voice-bar .vb-stop:hover { box-shadow: 0 0 14px rgba(102, 255, 140, .3); }
.voice-bar .vb-stop:hover { box-shadow: 0 0 14px rgba(255, 138, 112, .3); }
/* The bubble button that started playback stays lit, so you can see WHICH answer is talking. */
.bspeak.bspeak-active { opacity: 1; color: #eaffef; border-color: rgba(102, 255, 140, .85); box-shadow: 0 0 12px rgba(102, 255, 140, .55); }

/* Mic phases. Recording carries a running clock so a live mic is distinguishable from a dead one;
   transcribing carries a moving spinner. Silence during either phase is what read as "hanging". */
.mic-status {
  display: flex; align-items: center; gap: 9px; margin: 0 0 7px;
  padding: 7px 11px; border: 1px solid rgba(102, 255, 140, .28); background: rgba(4, 20, 11, .72);
  color: #b8ffcf; font: 700 10px/1 "Cascadia Mono", "Consolas", monospace; letter-spacing: .12em;
}
.mic-status[hidden] { display: none; }
.mic-status span { flex: 1; min-width: 0; }
.mic-status b { color: #eafff0; font-weight: 700; }
.mic-status-dot { width: 9px; height: 9px; border-radius: 50%; background: #ff6a5a; box-shadow: 0 0 9px rgba(255, 106, 90, .8); animation: mic-dot-pulse 1.1s ease-in-out infinite; }
.mic-status[data-mode="work"] .mic-status-dot { border-radius: 0; background: none; border: 2px solid rgba(102, 255, 140, .3); border-top-color: #b8ffcf; width: 12px; height: 12px; box-shadow: none; animation: mic-spin .7s linear infinite; }
@keyframes mic-dot-pulse { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
@keyframes mic-spin { to { transform: rotate(360deg); } }
/* .busy previously only dimmed the mic, which looks the same as "disabled and broken". */
#mic.busy { position: relative; opacity: .85; }
#mic.busy::after {
  content: ""; position: absolute; inset: -3px; border: 2px solid rgba(102, 255, 140, .25);
  border-top-color: #b8ffcf; border-radius: 50%; animation: mic-spin .7s linear infinite;
}

.voice-try-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 4px; }
.voice-try {
  min-height: 34px; padding: 0 14px; cursor: pointer;
  border: 1px solid rgba(102, 255, 140, .4); color: #b8ffcf; background: rgba(102, 255, 140, .08);
  font: 700 10px/1 "Cascadia Mono", "Consolas", monospace; letter-spacing: .12em;
}
.voice-try:hover { box-shadow: 0 0 14px rgba(102, 255, 140, .25); }
.voice-try:disabled { opacity: .5; cursor: default; }
.voice-try-note { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .voice-bar[data-state="preparing"] .vb-track i, .mic-status-dot, #mic.busy::after { animation: none !important; }
}

/* ---- machine-grant mark (OWNER ONLY) ---------------------------------------------------------
   Fred, 2026-07-21: every model that actually holds the machine grant — full read/write on the
   laptop's C/F/G/Z through the elevated node, plus admin PowerShell/cmd/Terminal — renders its
   name in red and bold, everywhere a name appears. At a glance he knows which pick can reach his
   machines and which is only a conversation.

   The wall is server-side, not here: /api/models only sends `broadAccess` to the owner's payload,
   so a guest's rows never carry .has-machine-grant and no CSS of theirs can conjure it. This file
   loads after dominion-ui.css (and its cinematic/rendered @imports), so these win the cascade
   without !important. */
:root { --dt-grant: #ff4a3d; --dt-grant-glow: rgba(255, 74, 61, 0.42); }

.mr-name .mr-text.has-machine-grant,
.model-current.has-machine-grant {
  color: var(--dt-grant);
  font-weight: 800;
  text-shadow: 0 0 10px var(--dt-grant-glow);
}

/* The selection ceremony flares green on the way in. For a granted model that would settle from
   green into red and read as a glitch, so it gets its own flare in the same key as the mark. */
.model-current.has-machine-grant.name-flare { animation: nameFlareGrant 1.7s ease-out 1; }
@keyframes nameFlareGrant {
  0%   { color: #fff0ee; text-shadow: 0 0 14px rgba(255, 74, 61, 0.95), 0 0 30px rgba(255, 74, 61, 0.5); }
  55%  { color: #ff8d82; text-shadow: 0 0 8px rgba(255, 74, 61, 0.55); }
  100% { color: var(--dt-grant); text-shadow: 0 0 10px var(--dt-grant-glow); }
}

/* The native <select> list (keyboard nav, some mobile browsers). Native option rows paint on the
   OS menu surface, which ignores text-shadow and may be light, so this is flat red on no glow. */
#model option[data-grant="1"] { color: #d21f10; font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  .model-current.has-machine-grant.name-flare { animation: none; }
}

/* ---- model eras: numbered engine ledger + transcript boundaries ------------------------------
   Up to three model choices sit side-by-side in a sticky vertical ledger. Names are measured in
   app.js against the available screen height, so even long catalog names remain fully visible.
   Bright green rules pin the exact transcript boundary where model 2 or 3 begins. */
.model-ledger {
  position: sticky;
  top: 18px;
  height: 0;
  z-index: 0;
  pointer-events: none;
  user-select: none;
}
.model-ledger-list {
  position: absolute;
  inset: 0 auto auto 0;
  display: flex;
  align-items: flex-start;
  height: var(--model-span);
}
.model-ledger-entry {
  --model-hue: 132;
  position: relative;
  flex: 0 0 auto;
  height: var(--model-span);
}
.model-ledger-index {
  position: absolute;
  top: 0;
  left: 50%;
  z-index: 1;
  min-width: 19px;
  height: 19px;
  padding: 0 4px;
  transform: translateX(-50%);
  border: 1px solid #35ff88;
  border-radius: 999px;
  background: rgba(0, 18, 10, .88);
  color: #72ffac;
  box-shadow: 0 0 12px rgba(53, 255, 136, .55);
  font: 850 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace;
  text-align: center;
}
.model-ledger-name {
  position: absolute;
  left: 50%;
  top: var(--model-span);
  width: var(--model-span);
  overflow: visible;
  white-space: nowrap;
  transform: rotate(-90deg) translateY(-50%);
  transform-origin: 0 0;
  color: hsl(var(--model-hue) 88% 70%);
  opacity: .25;
  font-family: "Domine", Georgia, serif;
  font-weight: 950;
  line-height: .9;
  letter-spacing: .025em;
  text-align: center;
  text-transform: uppercase;
  text-shadow: 0 0 20px hsl(var(--model-hue) 90% 55% / .45);
}
.model-ledger-entry.pending .model-ledger-index {
  border-style: dashed;
  opacity: .72;
}
.model-era {
  position: relative;
  isolation: isolate;
  min-width: 0;
}
.model-era-turns {
  position: relative;
  z-index: 1;
  min-width: 0;
  padding-left: var(--model-rail-width, 54px);
}
.model-era-turns > .turn:last-child { margin-bottom: 18px; }
.model-era-divider {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  margin: 8px 12px 20px var(--model-rail-width, 54px);
  color: #78ffad;
  filter: drop-shadow(0 0 7px rgba(36, 255, 124, .8));
}
.model-era-divider::before,
.model-era-divider::after {
  content: "";
  height: 3px;
  flex: 1 1 auto;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent, #20ff79 18%, #7dffb1 50%, #20ff79 82%, transparent);
  box-shadow: 0 0 10px #20ff79, 0 0 22px rgba(32, 255, 121, .75);
}
.model-era-divider span {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin: 0 9px;
  border: 2px solid #4dff95;
  border-radius: 50%;
  background: #001b0c;
  color: #caffdc;
  font: 900 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
}

@media (max-width: 700px) {
  .model-ledger { top: 10px; }
  .model-ledger-index { min-width: 17px; height: 17px; font-size: 10px; line-height: 15px; }
  .model-era-divider { margin-right: 5px; margin-bottom: 14px; }
}

/* ---- The composer strip, phones (Fred, 2026-07-30 evening, correcting the Option 1 read):
   "I never decided to remove all of them. I wanted to remove the fire alarm button and make sure
   that they were all the same size and didn't overlap each other. That's it."

   So every control comes back. Only the Fire Alarm stays gone (removed from the markup itself;
   POST /chat/fire-alarm survives as the API escape hatch), and the whole strip is one tile size.

   NO HARD-CODED COLUMNS, deliberately. This layout has been rebuilt four times and has produced
   the same overlap twice, because every version assigned controls to numbered grid cells: the
   2026-07-19 fix gave each control its own column, Option 1 then re-cut the grid to five columns,
   and the pill was later parked in column 3 which is the speaker's cell. Two items, one cell,
   overlap by definition. The count of controls is also not constant, so numbered cells cannot be
   right: #ide-trigger only paints when engaged (#ide-trigger.on) and #dfi-trigger arrives from
   JS, meaning the strip legitimately holds seven or eight tiles at different moments. A fixed
   grid answers that with a hole or a collision.

   A single flex row with `flex: 1 1 0` gives every tile an identical computed width whatever the
   count, wraps instead of stacking if it ever runs out of room, and cannot place two controls in
   one cell. Adding the ninth control later needs no CSS change at all. ---- */
@media (max-width: 720px) {
  html body .bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  /* display:contents so #bar-left's children are flex items of the strip itself, not of a nested
     box that would size them independently and break the shared basis. */
  html body #bar-left { display: contents; }
  html body #input { flex: 0 0 100%; order: 1; min-height: 50px; }
  html body #bar-mid { order: 0; }
  /* Every tile, one size. flex-basis 0 means the free space is split evenly, so eight tiles are
     identical and seven tiles are identical-and-slightly-wider. max-width keeps them from growing
     into slabs on a 720px tablet; the 1/1 ratio derives height so nothing is ever set twice. */
  html body #attach,
  html body #forge-trigger,
  html body #dfi-trigger,
  html body #ide-trigger,
  html body .dt-guide-pill.dt-guide-docked,
  html body #speak,
  html body #mic,
  html body #send {
    order: 2;
    flex: 1 1 0; min-width: 0; max-width: 54px;
    box-sizing: border-box; width: auto; height: auto; aspect-ratio: 1 / 1;
    margin: 0; padding: 0; border-radius: 10px;
    align-items: center; justify-content: center; place-items: center;
  }
  /* Display is left to each control's own rules on purpose: #ide-trigger is display:none until it
     carries .on, and forcing it visible here would put the Crucible opener on screen while the
     Crucible is not engaged. Restoring these three is about un-hiding them, not overriding them. */
  html body #attach, html body #speak, html body #mic {
    border: 1px solid rgba(212, 130, 60, .45); background: rgba(10, 6, 2, .55);
    display: flex;
  }
  html body #attach svg, html body #speak svg, html body #mic svg,
  html body #forge-trigger svg, html body #dfi-trigger svg, html body #ide-trigger svg {
    width: 21px; height: 21px;
  }
  /* The pill's text does not fit a square tile, so it uses the compact "?" face already defined
     for narrow phones below. */
  html body .dt-guide-pill.dt-guide-docked {
    padding: 0; min-width: 0; min-height: 0; font-size: 0; justify-content: center;
  }
  html body .dt-guide-pill.dt-guide-docked::before { content: "?"; font-size: 15px; font-weight: 800; letter-spacing: 0; }
}

/* ---- Crucible stage, phones: the dock floats over the stage's last 82px, and the stage's own
   36px tail padding left the Build It button and status line shaved to a sliver at full scroll
   (Fred's phone, Vibe Coder, 2026-07-30). The stage now reserves the dock's full height plus the
   safe area below its content, matching the reserve the chat body already keeps. ---- */
@media (max-width: 720px) {
  html body #ide-root .ide-stage { padding-bottom: calc(40px + 88px + env(safe-area-inset-bottom)); }
}

/* ---- Send to Crucible on phones (Fred, 2026-07-30). The Option 1 strip above hides #bar-mid,
   because on a phone that middle slot was dead space between five tiles. The hand-off button now
   lives there, so the container comes back — but as its own full-width row ABOVE the input rather
   than a sixth tile squeezed among five. This block sits after the strip block on purpose: same
   specificity, later file, so it wins the display fight it exists to win. ---- */
@media (max-width: 720px) {
  html body #bar-mid { display: flex; grid-column: 1 / -1; order: -1; justify-content: center; }
  html body #bar-mid:empty, html body #bar-mid:has(> #to-crucible[hidden]) { display: none; }
  html body #to-crucible { width: 100%; justify-content: center; margin: 0 0 6px; padding: 9px 12px; font-size: 12px; }
}

/* ---- SCHEDULED WORK ORDERS (Fred, 2026-07-31). A pop-up layer that is both the dashboard and the
   order form, appended to <body> rather than the drawer: a fixed-position child of a TRANSFORMED
   ancestor positions against that ancestor instead of the viewport, and the drawer is transformed.
   That trap has already cost this app one round of debugging. ---- */
.sb-orders {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; margin: 4px 0 0; box-sizing: border-box;
  font: inherit; font-size: 12.5px; letter-spacing: .03em; text-align: left; cursor: pointer;
  color: var(--ink, #dfe9f0); background: rgba(9, 18, 26, .5);
  border: 1px solid rgba(185, 107, 50, .3); border-radius: 9px;
}
.sb-orders:hover { border-color: rgba(232, 160, 74, .6); }
.sb-orders-glyph { width: 17px; height: 17px; flex: 0 0 auto; color: #e8a04a; }
/* The dot: something ran since you last looked. It is the only reason to open the panel unprompted,
   so it has to be visible without being loud. */
.sb-orders.has-new { position: relative; }
.sb-orders.has-new::after {
  content: ""; position: absolute; top: 8px; right: 10px; width: 7px; height: 7px; border-radius: 50%;
  background: radial-gradient(circle, #d4ffc4 0%, #79ff52 55%, #1c8f3c 100%);
  box-shadow: 0 0 7px 1px rgba(121, 255, 82, .8);
}

.wo-layer { position: fixed; inset: 0; z-index: 4000; display: flex; align-items: center; justify-content: center; }
.wo-layer[hidden] { display: none; }
.wo-scrim { position: absolute; inset: 0; background: rgba(2, 5, 9, .72); backdrop-filter: blur(4px); }
.wo-panel {
  position: relative; width: min(560px, calc(100vw - 24px)); max-height: min(84vh, 780px);
  display: flex; flex-direction: column; overflow: hidden;
  border-radius: 14px; border: 1px solid rgba(185, 107, 50, .45);
  background: linear-gradient(160deg, rgba(16, 22, 30, .98), rgba(6, 9, 13, .99));
  box-shadow: 0 24px 70px rgba(0, 0, 0, .7), inset 0 1px 0 rgba(255, 220, 170, .1);
}
.wo-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 10px; border-bottom: 1px solid rgba(185, 107, 50, .28); }
.wo-title { font-size: 13px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #e8c9a4; }
.wo-x { background: none; border: 0; color: var(--muted, #8ba0ae); font-size: 24px; line-height: 1; cursor: pointer; padding: 0 4px; }
.wo-x:hover { color: #fff; }
.wo-body { overflow-y: auto; padding: 14px 16px 18px; }
.wo-h { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--muted, #8ba0ae); margin: 0 0 8px; }
.wo-empty { margin: 0 0 16px; font-size: 12.5px; line-height: 1.5; color: var(--muted, #8ba0ae); }

.wo-row { padding: 10px 12px; margin: 0 0 8px; border-radius: 10px; border: 1px solid rgba(185, 107, 50, .26); background: rgba(9, 15, 21, .6); }
.wo-row.off { opacity: .55; }
.wo-row-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; color: var(--ink, #dfe9f0); }
.wo-row-sub { font-size: 11.5px; color: var(--muted, #8ba0ae); margin-top: 2px; word-break: break-all; }
.wo-row-last { font-size: 11.5px; color: #cfe6d2; margin-top: 5px; }
.wo-dim { opacity: .6; }
.wo-badge { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; border: 1px solid currentColor; }
.wo-badge.ok { color: #79ff52; } .wo-badge.dry { color: #e8c9a4; }
.wo-badge.wait { color: #7fb6ff; } .wo-badge.bad { color: #ff8a6a; }
.wo-row-btns { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.wo-row-btns button {
  font: inherit; font-size: 11px; padding: 5px 9px; border-radius: 7px; cursor: pointer;
  color: #d8ecff; border: 1px solid rgba(139, 205, 244, .34); background: rgba(12, 24, 36, .8);
}
.wo-row-btns button:hover { border-color: rgba(157, 255, 146, .5); color: #fff; }
.wo-row-btns .wo-del { color: #ff9c85; border-color: rgba(255, 120, 90, .38); }

.wo-new { margin-top: 16px; padding-top: 14px; border-top: 1px solid rgba(185, 107, 50, .24); }
.wo-f { display: flex; flex-direction: column; gap: 4px; margin: 0 0 10px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted, #8ba0ae); }
.wo-f input, .wo-f select {
  font: inherit; font-size: 13px; text-transform: none; letter-spacing: 0; padding: 8px 10px; border-radius: 8px;
  color: var(--ink, #dfe9f0); background: rgba(9, 18, 26, .85); border: 1px solid rgba(185, 107, 50, .38);
}
.wo-when { display: flex; gap: 10px; flex-wrap: wrap; }
.wo-when .wo-f { flex: 1 1 140px; min-width: 0; }
.wo-blurb, .wo-note { margin: -4px 0 10px; font-size: 11.5px; line-height: 1.5; color: var(--muted, #8ba0ae); }
.wo-note { border-left: 2px solid rgba(185, 107, 50, .45); padding-left: 10px; }
.wo-actions { display: flex; justify-content: flex-end; }
.wo-send {
  font: inherit; font-size: 13px; font-weight: 700; letter-spacing: .05em; padding: 10px 18px; border-radius: 9px; cursor: pointer;
  color: #eafff0; border: 1px solid rgba(120, 220, 120, .5);
  background: linear-gradient(145deg, rgba(38, 118, 44, .9), rgba(3, 30, 16, .95));
  box-shadow: 0 0 16px rgba(100, 255, 90, .16), inset 0 1px 0 rgba(226, 255, 221, .22);
}
.wo-send:hover { filter: brightness(1.1); }
.wo-status { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: #cfe6d2; min-height: 1em; }
.wo-status.bad { color: #ff9c85; }

@media (max-width: 720px) {
  .wo-panel { width: 100vw; max-height: 100vh; height: 100%; border-radius: 0; border-left: 0; border-right: 0; }
  .wo-layer { align-items: stretch; }
  .wo-body { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
}

/* ---- The fast lane (OpenAI service_tier "fast", 2026-07-30). Quiet when off, unmistakable when
   armed, because the difference between those two states is the difference between a normal bill
   and a doubled one. Amber rather than green: in this app green means free or safe, and paying
   twice as much is neither. ---- */
.fast-lane {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin: 0 0 8px; padding: 7px 11px; box-sizing: border-box;
  font: inherit; font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-align: left;
  cursor: pointer; border-radius: 9px;
  color: var(--muted, #8ba0ae);
  border: 1px solid rgba(185, 107, 50, .34);
  background: linear-gradient(145deg, rgba(24, 15, 6, .7), rgba(6, 4, 2, .9));
  transition: color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.fast-lane[hidden] { display: none; }
.fast-lane .fl-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; background: rgba(185, 107, 50, .5); }
.fast-lane:hover { color: #e8d3ba; border-color: rgba(232, 160, 74, .6); }
.fast-lane.on {
  color: #ffd9a6; border-color: rgba(255, 176, 74, .75);
  box-shadow: inset 0 1px 0 rgba(255, 224, 186, .18), 0 0 16px rgba(255, 150, 40, .18);
}
.fast-lane.on .fl-dot {
  background: radial-gradient(circle, #ffe6c2 0%, #ff9a3a 55%, #a34a08 100%);
  box-shadow: 0 0 7px 1px rgba(255, 154, 58, .85);
}

/* ---- Reclaiming the phone screen (Fred, 2026-07-30: "there is an awful lot of screen taken up by
   the feature icons. can we lay this out better?").

   MEASURED, not guessed. On a 393x852 phone with a conversation running, the chat itself had 258px
   and the surrounding controls had 573px. Two thirds of the screen was chrome.

   The cause was structural rather than decorative. The strip above is a six-column grid whose third
   column is `1fr`, which on a 393px screen is 72px of deliberately empty space between the two
   left tiles and the three right ones. The GUIDE pill is a .bar-left child, .bar-left is
   display:contents here, so the pill auto-placed straight PAST that empty cell and opened a fourth
   row of its own to sit in alone. A 43px row plus its gap, spent on one small button, directly
   beside a 72px hole.

   So the pill was given the hole explicitly, by number: grid-column 3.

   SUPERSEDED 2026-07-30 evening. That number was read off the six-column grid described above,
   but the grid in force by then was Option 1's five columns, whose column 3 is the SPEAKER. The
   pill was parked on top of #speak, which is the same collision the 2026-07-19 comment near line
   382 of this file records and "fixed" the same way, with numbers. Twice is a pattern, so the
   strip above no longer uses numbered cells at all and the pill needs no placement of its own.
   Only the input sizing is kept here. ---- */
@media (max-width: 720px) {
  /* An empty single-line box does not need to look like a paragraph field. It still grows with
     what is typed, so nothing is lost above one line. */
  html body #input { min-height: 50px; }
}

/* ---- The dock, phones. "VIDEO GENERATION" wrapped to two lines and dragged "SOON" down with it,
   which made the whole dock 91px on Fred's phone against 69px in the rig. The labels are set to
   fit on one line at every phone width instead of being allowed to grow the bar they sit in. ---- */
@media (max-width: 720px) {
  html body #dock-nav { padding: 4px 6px calc(4px + env(safe-area-inset-bottom)); }
  html body #dock-nav .rail-dest { padding: 5px 1px; gap: 2px; }
  html body #dock-nav .rail-dest b { font-size: 8px; letter-spacing: .04em; white-space: nowrap; }
  html body #dock-nav .rail-dest small { font-size: 6.5px; letter-spacing: .03em; }
  html body #dock-nav .glyph { width: 17px; height: 17px; }
  /* The reserve must match the bar it reserves for. It was 58px against a dock that measured 69px,
     so the last 11px of the page was already sliding under the dock before any of this. The dock
     is 60px now and the page keeps 64px, which is the first time these two numbers have agreed. */
  html body { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
}
