/* Dovetail token system: near-black canvas + blueprint grid, Inter/JetBrains Mono,
   one indigo accent. Dark-only on purpose. */
:root {
  /* Makes the browser draw its own controls dark too: checkboxes, scrollbars
     and the native dropdown popup, which CSS cannot reach otherwise. */
  color-scheme: dark;

  --bg: #0a0a0a;
  --surface: #141414;      /* cards */
  --surface-2: #1e1e1e;    /* elevated: hover rows, nested chips */
  --border: #1e1e1e;
  --border-2: #313131;     /* hairline on nested elements */
  --border-strong: #454545;

  --text: #ffffff;
  --text-2: #a7a7a7;
  --text-3: #7c7c7c;

  --accent: #6798ff;
  --accent-soft: rgba(103, 152, 255, .12);
  --ok: #6798ff;           /* single chromatic note: positive = accent */
  --ok-soft: rgba(103, 152, 255, .12);
  --warn: #b98a3f;         /* functional punctuation only */
  --warn-soft: rgba(185, 138, 63, .12);
  --err: #e5626b;
  --err-soft: rgba(229, 98, 107, .12);

  --font: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
  --radius: 8px;
  --radius-lg: 8px;

  /* Every panel and the negative margins that let tables bleed to its edge read
     these. On a 375px screen 24px each side is 13% of the viewport, so the
     narrow layout shrinks them -- in one place, or the tables misalign. */
  --panel-pad-x: var(--s5);
  --panel-pad-y: var(--s4);
  --page-pad: var(--s6);
  --head-h: 76px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  color: var(--text);
  font: 14px/1.57 var(--font);
  letter-spacing: -.25px;
  font-feature-settings: "liga";
  -webkit-font-smoothing: antialiased;
  background-color: var(--bg);
  background-image: linear-gradient(#131313 1px, transparent 1px),
                    linear-gradient(90deg, #131313 1px, transparent 1px);
  background-size: 48px 48px;
}
a { color: var(--text); text-decoration: none; }

/* The browser's own [hidden] rule loses to any class that sets a display, and
   .btn sets inline-flex. Without this an empty pill sat under every short list
   and next to every selection bar. */
[hidden] { display: none !important; }
a:hover { color: var(--accent); text-decoration: none; }

.mono, .navlab, .badge, .pill, .tag, .kpi .label, th {
  font-family: var(--mono);
  letter-spacing: .85px;
  text-transform: uppercase;
}

/* --- shell ----------------------------------------------------------------- */

.shell { display: flex; min-height: 100vh; min-height: 100dvh; }

.side {
  display: flex; flex-direction: column;
  width: 248px; flex: 0 0 248px;
  border-right: 1px solid var(--border);
  background: var(--bg);
  position: sticky; top: 0; height: 100vh; height: 100dvh;
}
.brand { display: flex; align-items: center; gap: 11px; padding: 20px 16px 12px; }
.brand-mark {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border: 1px solid var(--border-2);
  border-radius: var(--radius); background: var(--surface); color: var(--accent);
}
.brand-mark svg { width: 16px; height: 16px; }
.brand-text { display: flex; flex-direction: column; }
.brand-text b { font-size: 15px; font-weight: 600; letter-spacing: -.3px; }
.brand-text small {
  font-family: var(--mono); font-size: 10px; letter-spacing: .85px;
  text-transform: uppercase; color: var(--text-3);
}

.navlab { padding: 20px 12px 8px; font-size: 11px; color: var(--text-3); }

/* --- automation switcher ----------------------------------------------------- */

.autoswitch { display: flex; flex-direction: column; gap: 6px; padding: 0 10px; }
#autoSection { display: flex; flex-direction: column; gap: 2px; }
.auto-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); font-size: 14px; letter-spacing: -.25px;
  color: var(--text); font-family: var(--font); cursor: pointer; text-align: left;
}
.auto-item:hover { background: var(--surface-2); }
.auto-item .auto-chev {
  width: 14px; height: 14px; flex: 0 0 14px; color: var(--text-3);
  transition: transform .15s ease;
}
.auto-block.collapsed .auto-chev { transform: rotate(-90deg); }
.auto-block.collapsed .auto-views { display: none; }

/* One block per automation, in the order the user dragged them into. */
#autoList { display: flex; flex-direction: column; gap: 2px; }
/* Above its neighbours while it is being carried, and never animated: the
   transform follows the pointer, a transition would lag behind it. */
.auto-block.dragging {
  opacity: .75; position: relative; z-index: 2; transition: none;
}
.auto-row { display: flex; align-items: stretch; gap: 4px; }
.auto-row .auto-item { flex: 1 1 auto; min-width: 0; }
/* Only rendered once there is a second automation to move this one past. */
.auto-grip {
  display: flex; align-items: center; justify-content: center;
  width: 24px; flex: 0 0 24px; padding: 0;
  border: 1px solid transparent; border-radius: var(--radius);
  background: none; color: var(--text-3); cursor: grab;
  /* Without this the phone scrolls the drawer instead of moving the block. */
  touch-action: none;
}
.auto-grip svg { width: 14px; height: 14px; }
.auto-grip:hover { color: var(--text); background: var(--surface); }
.auto-grip:active { cursor: grabbing; }
.auto-grip:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
.auto-item svg { width: 15px; height: 15px; color: var(--accent); flex: 0 0 15px; }
/* The icon is painted from the manifest, so it sits in a wrapper rather
   than directly in the button. */
.auto-item .auto-icon { display: inline-flex; flex: 0 0 15px; }
/* The grip takes width away from the row, so the name has to be allowed to
   shrink instead of wrapping onto a second line. */
.auto-item .auto-name {
  flex-grow: 1; font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* State as a dot rather than a word: with several automations the word ate the
   space the name needs, and the run bar on the dashboard says it in full. */
.auto-item .auto-dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-3);
}
.auto-item .auto-dot.running { background: #2fd971; }
.auto-item .auto-dot.paused { background: var(--warn); }
.auto-item .auto-dot.stopped { background: #ff5747; }
.auto-new {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border: 1px dashed var(--border-strong); border-radius: var(--radius);
  background: none; color: var(--text-3); cursor: pointer;
  font: 13.5px/1.4 var(--font); letter-spacing: -.25px; text-align: left;
}
.auto-new svg { width: 14px; height: 14px; flex: 0 0 14px; }
.auto-new:hover { color: var(--text); border-color: var(--text-3); }

.auto-new-wrap { position: relative; margin: 2px 0 4px; }
.auto-new { width: 100%; }
.auto-new svg { width: 14px; height: 14px; flex: 0 0 14px; }nav {
  display: flex; flex-direction: column; gap: 2px; padding: 4px 10px;
  flex: 1 1 auto; min-height: 0;
}
/* Only the automations scroll. The four pages above them stay put, and "New
   automation" stays directly under the last automation rather than being
   pushed to the bottom of the screen: it belongs to the list, not to the frame.
   So the block is sized by its content and only SHRINKS when there is not
   enough room, which is the moment the list starts scrolling. That is
   flex: 0 1 auto, not 1 1 auto, and it needs min-height: 0 or a flex child
   refuses to shrink below its content and pushes the button off the bottom
   anyway. */
#autoSection { display: flex; flex-direction: column; flex: 0 1 auto; min-height: 0; }
#autoSection[hidden] { display: none; }
#autoList { flex: 0 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.auto-new-wrap { flex: 0 0 auto; }
#autoList::-webkit-scrollbar { width: 6px; }
#autoList::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
#autoList { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
nav button {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 8px 12px; border: none; border-radius: var(--radius);
  background: none; color: var(--text-2); cursor: pointer;
  font: 14px/1.4 var(--font); letter-spacing: -.25px; text-align: left;
}
nav button svg { width: 17px; height: 17px; color: var(--text-3); flex: 0 0 17px; }
nav button:hover { color: var(--text); background: var(--surface); }
nav button.active { background: var(--surface-2); color: var(--text); font-weight: 500; }
nav button.active svg { color: var(--accent); }

/* The foot carries one number and a bar for it. A number alone says nothing
   about whether it is a lot; the bar says it at a glance, which is the whole
   reason for putting it somewhere you walk past every day. */
.side-foot {
  display: flex; flex-direction: column; gap: 7px; margin-top: auto;
  width: 100%; border: none; border-top: 1px solid var(--border);
  background: none; cursor: pointer; text-align: left;
  padding: 13px 18px 15px;
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .85px;
  text-transform: uppercase; color: var(--text-3);
}
.foot-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.foot-chev { width: 12px; height: 12px; flex: 0 0 12px; opacity: .55; }
.meter {
  display: block; height: 3px; border-radius: 2px; overflow: hidden;
  background: var(--surface-2);
}
.meter-fill {
  display: block; height: 100%; width: 0; background: var(--accent); border-radius: 2px;
}
.meter.big { height: 5px; margin: var(--s4) 0 6px; }
.side-foot.tight .meter-fill { background: var(--err); }

.maincol { flex-grow: 1; min-width: 0; display: flex; flex-direction: column; }

/* The sidebar becomes a drawer on a narrow screen. Both of these are dead
   weight on a desktop, so they only switch on inside the narrow query. */
.navtoggle {
  display: none; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex: 0 0 38px; margin-left: -6px;
  border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); color: var(--text); cursor: pointer;
}
.navtoggle svg { width: 18px; height: 18px; }
.navtoggle:hover { background: var(--surface-2); }
.nav-backdrop { display: none; }

/* --- topbar ----------------------------------------------------------------- */

header {
  display: flex; align-items: center; gap: var(--s3);
  padding-inline: max(var(--page-pad), calc((100% - 1440px) / 2 + var(--page-pad)));
  height: var(--head-h); flex: 0 0 var(--head-h);
  border-bottom: 1px solid var(--border);
  background: var(--bg); position: sticky; top: 0; z-index: 20;
}
.page-head { display: flex; flex-direction: column; gap: 3px; }
header h1 { margin: 0; font-size: 24px; font-weight: 500; letter-spacing: -.6px; line-height: 1.2; }
.spacer { flex: 1; }
.meta { color: var(--text-3); font-size: 12.5px; white-space: nowrap; }

.badge {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 3px 9px; border: 1px solid var(--border-2); border-radius: 4px;
  background: var(--surface-2); font-size: 11px; font-weight: 400;
}
.badge::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
/* One mapping for run state everywhere: the tile badge, the dot in the sidebar
   and the dot in the run bar all use these three, and they are the same green
   and the same red as the Start and Stop buttons. */
.badge.running { color: #2fd971; }
.badge.paused  { color: var(--warn); }
.badge.stopped { color: #ff5747; }

main { padding: var(--s5) var(--page-pad); max-width: 1440px; width: 100%; margin: 0 auto; }
.view { display: none; }
.view.active { display: block; }

/* --- buttons ---------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; gap: 8px;
  background: #ffffff; color: var(--bg); border: 1px solid #ffffff;
  border-radius: var(--radius); padding: 8px 16px; cursor: pointer;
  font: 500 14px/1.4 var(--font); letter-spacing: -.25px; white-space: nowrap;
}
.btn svg { width: 13px; height: 13px; }
.btn:hover:not(:disabled) { background: #e8e8e8; border-color: #e8e8e8; }
.btn.ghost {
  background: transparent; border-color: var(--border-strong); color: var(--text);
}
.btn.ghost:hover:not(:disabled) { background: var(--surface-2); }
.btn.small { padding: 5px 11px; font-size: 13px; }
.btn:disabled { opacity: .35; cursor: not-allowed; }
/* Loud on purpose, and still flat: a saturated green to start and a saturated
   coral to stop, both with near black label text so the colour carries the
   whole signal. No gradient, no glow, no animation -- a modelled button reads
   as a foreign body in this interface, the colour is what does the work. */
.btn.run { background: #2fd971; border-color: #2fd971; color: #04220e; }
.btn.run:hover:not(:disabled) { background: #45e483; border-color: #45e483; }
.btn.halt { background: #ff5747; border-color: #ff5747; color: #2b0600; }
.btn.halt:hover:not(:disabled) { background: #ff6c5e; border-color: #ff6c5e; }

/* --- cards & panels ---------------------------------------------------------- */

/* auto-fit on the container, not breakpoints on the window: the sidebar eats
   248px, so the window width says nothing about how much room these have. Four
   fit when there is room, two when there is not, and a card is never stretched
   across the whole page on its own. */
.kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(196px, 100%), 1fr));
  gap: var(--s4); margin-bottom: var(--s4);
}
.kpi {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px;
}
.kpi .label { font-size: 11px; color: var(--text-3); }
.kpi .value {
  font-size: 28px; font-weight: 500; line-height: 1.2; margin-top: 8px;
  font-variant-numeric: tabular-nums; letter-spacing: -.84px;
}
.kpi .sub { color: var(--text-3); font-size: 13px; margin-top: 4px; }
.kpi.ok .value { color: var(--accent); }
.kpi.warn .value { color: var(--warn); }
.kpi.err .value { color: var(--err); }

.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: var(--panel-pad-y) var(--panel-pad-x) var(--panel-pad-x);
  margin-bottom: var(--s4);
}
.panel > h2 {
  font-size: 16px; font-weight: 500; letter-spacing: -.25px; margin: 4px 0 var(--s4);
  display: flex; align-items: baseline; gap: var(--s2);
}
.panel > h2 .hint { color: var(--text-3); font-weight: 400; font-size: 12.5px; letter-spacing: -.1px; }
h2.section { font-size: 16px; font-weight: 500; letter-spacing: -.25px; margin: 0; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
/* Same min-content floor as the toolbar: a panel holding a wide table reports a
   huge minimum, and the tracks grew past the page instead of letting the table
   scroll inside its own box. */
.grid-2 > *, .panel-grid > * { min-width: 0; }

/* One plain sentence at the top of a page: what am I looking at. */
.panel.explain { background: none; border-style: dashed; }
.panel.explain p { margin: 0; color: var(--text-2); font-size: 13.5px; line-height: 1.6; max-width: 70ch; }

/* A blocking problem the user has to fix before anything works. */
.panel.alert { border-color: var(--warn); background: var(--warn-soft); }
.panel.alert h2 { color: var(--warn); }
.panel.alert p { margin: 0; color: var(--text-2); font-size: 13.5px; line-height: 1.6; max-width: 70ch; }
.panel.alert b { color: var(--text); font-family: var(--mono); font-size: 12.5px; }

/* --- onboarding ------------------------------------------------------------- */

.onboard { border-color: var(--border-2); }
.onboard ol { margin: 0; padding-left: 20px; color: var(--text-2); }
.onboard li { margin-bottom: var(--s2); }
.onboard li:last-child { margin-bottom: 0; }
.onboard b { color: var(--text); font-weight: 500; }

/* --- tables ------------------------------------------------------------------ */

.table-wrap { overflow-x: auto; margin: 0 calc(var(--panel-pad-x) * -1) calc(var(--panel-pad-x) * -1); }
.table-wrap table { margin: 0 var(--panel-pad-x); width: calc(100% - var(--panel-pad-x) * 2); }
table { border-collapse: collapse; font-size: 14px; letter-spacing: -.25px; width: 100%; }
th {
  text-align: left; color: var(--text-3); font-weight: 400; font-size: 11px;
  padding: 0 var(--s3) 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
td {
  padding: 12px var(--s3); border-bottom: 1px solid var(--border);
  vertical-align: middle; white-space: nowrap;
}
td.wrap, th.wrap { white-space: normal; max-width: 280px; min-width: 160px; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--surface-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.empty { color: var(--text-3); padding: var(--s4) var(--s3); }

/* --- forms -------------------------------------------------------------------- */

input, select, textarea {
  background: var(--bg); border: 1px solid var(--border-strong); color: var(--text);
  border-radius: var(--radius); padding: 8px 12px; font: 14px/1.4 var(--font);
  letter-spacing: -.25px;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
input::placeholder, textarea::placeholder { color: var(--text-3); }
textarea { width: 100%; min-height: 96px; resize: vertical; line-height: 1.6; }

/* Row selection: a bar that only appears once something is ticked. */
.selbar {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  margin-bottom: var(--s3); padding: 8px 12px;
  border: 1px solid var(--accent); border-radius: var(--radius);
  background: var(--accent-soft);
}
.sel-count { font-family: var(--mono); font-size: 11px; letter-spacing: .85px;
  text-transform: uppercase; color: var(--text); }
th.pick, td.pick { width: 34px; padding-right: 0; }
td.pick input, th.pick input { cursor: pointer; accent-color: var(--accent); margin: 0; }
.pickbox { display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }

/* Dropdowns: the native control cannot be styled past its own popup, so it is
   hidden and driven by a button plus a floating menu (see enhanceSelects).
   A select without JS still works, hence the fallback styling first. */
select {
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-color: var(--surface);
  border-color: var(--border-2);
  padding-right: 32px;
  min-width: 140px;
}
select[data-dd] { display: none; }

.dd-btn {
  display: inline-flex; align-items: center; gap: 10px;
  min-width: 140px; max-width: 100%;
  padding: 8px 12px; border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); color: var(--text); cursor: pointer;
  font: 14px/1.4 var(--font); letter-spacing: -.25px; text-align: left;
}
.dd-btn .dd-text { flex-grow: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd-btn svg { width: 14px; height: 14px; flex: 0 0 14px; color: var(--text-3); }
.dd-btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.dd-btn.open { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.dd-btn:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.field .dd-btn { width: 100%; }

.dd-menu {
  position: fixed; z-index: 200;
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px; border: 1px solid var(--border-2); border-radius: 12px;
  background: var(--surface-2);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .55);
  max-height: 320px; overflow-y: auto;
}
.dd-opt {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px; border: none; border-radius: var(--radius);
  background: none; color: var(--text-2); cursor: pointer;
  font: 14px/1.4 var(--font); letter-spacing: -.25px; text-align: left; white-space: nowrap;
}
.dd-opt span { flex-grow: 1; }
.dd-opt svg { width: 15px; height: 15px; flex: 0 0 15px; color: var(--text); }
.dd-opt:hover { background: #3a3a3a; color: var(--text); }
.dd-opt.on { color: var(--text); }

.row { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
label.check {
  display: inline-flex; align-items: center; gap: 7px; font-size: 13.5px;
  color: var(--text-2); cursor: pointer; user-select: none;
}
label.check input { margin: 0; cursor: pointer; accent-color: var(--accent); }
input[type="checkbox"] { accent-color: var(--accent); }
tr.contacts-row td { background: var(--surface-2); font-size: 13px; padding: 8px var(--s3); }
tr.contacts-row .who { color: var(--text-2); }
button.link {
  background: none; border: none; color: var(--accent); cursor: pointer;
  font: 13px/1.4 var(--font); padding: 0;
}
button.link:hover { text-decoration: underline; }

.fields {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
  gap: var(--s4); align-items: start;
}
/* Label, control and explanation each get their own row across the whole line,
   so a two line label does not push its input below the ones beside it. That
   crookedness is visible in every settings panel; subgrid is what fixes it
   without giving every label a fixed height. */
@supports (grid-template-rows: subgrid) {
  .fields { grid-auto-rows: min-content; }
  .fields > .field { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
}
.field-control { display: flex; flex-direction: column; }
.field label { display: block; font-size: 13.5px; font-weight: 500; margin-bottom: 6px; }
.field input, .field select { width: 100%; }
.field .desc { font-size: 12px; color: var(--text-3); margin-top: 6px; line-height: 1.5; }
.field .field-control > .keystate, .field .field-control > .desc.only { margin-top: 6px; }
.keystate {
  margin-top: 6px; font-family: var(--mono); font-size: 10px; letter-spacing: .85px;
  text-transform: uppercase;
}
.keystate.on { color: var(--accent); }
.keystate.off { color: var(--warn); }
/* A grid, not wrapped flex: equal columns line every box up under the one
   above it, instead of letting the longest label shove the next row out. */
.field .checks {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(200px, 100%), 1fr));
  gap: 2px; padding: 8px; border: 1px solid var(--border-2); border-radius: 8px;
  background: var(--surface);
}
.field .checks .check {
  display: flex; align-items: center; gap: 9px; min-width: 0;
  padding: 7px 9px; border-radius: 6px;
  font-size: 12.5px; color: var(--text-2); cursor: pointer; user-select: none;
}
.field .checks .check:hover { background: var(--surface-2); color: var(--text); }
.field .checks .check:has(input:checked) { color: var(--text); }
.field .checks .check input {
  width: 15px; height: 15px; margin: 0; flex: none; accent-color: var(--accent);
}
.field .checks .check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field:has(.checks) { grid-column: 1 / -1; }

details.advanced { margin-top: var(--s5); border-top: 1px solid var(--border); padding-top: var(--s4); }
details.advanced > summary {
  cursor: pointer; font-size: 14px; font-weight: 500; color: var(--text-2);
  list-style: none; display: flex; align-items: center; gap: 8px; user-select: none;
}
details.advanced > summary::-webkit-details-marker { display: none; }
details.advanced > summary::before { content: "▸"; color: var(--text-3); font-size: 11px; }
details.advanced[open] > summary::before { content: "▾"; }
details.advanced > summary:hover { color: var(--text); }
details.advanced .fields { margin-top: var(--s4); }
.panel > details.advanced { margin-top: 0; border-top: 0; padding-top: 0; }

/* --- bits ---------------------------------------------------------------------- */

.tag {
  display: inline-block; background: var(--surface-2); border: 1px solid var(--border-2);
  border-radius: 4px; padding: 1px 8px; font-size: 10.5px; color: var(--text-2);
  margin: 1px 4px 1px 0; max-width: 160px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}
/* An automation somebody shared, as opposed to one that ships with the app.
   The accent is the only colour in the palette that is not already a warning,
   so it carries "from someone else" without inventing a fifth hue. */
/* Four buttons do not fit across a card on a narrow screen, and a row that
   overflows silently hides the last one. */
.row.wrap { flex-wrap: wrap; }

.tag.community {
  color: var(--accent); border-color: rgba(103, 152, 255, .4);
  background: var(--accent-soft);
}

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 8px; border: 1px solid var(--border-2); border-radius: 4px;
  background: var(--surface-2); font-size: 10.5px; font-weight: 400;
}
.pill::before { content: ""; width: 4px; height: 4px; border-radius: 50%; background: currentColor; }
.pill.working, .pill.enriched, .pill.ok { color: var(--accent); }
.pill.idle, .pill.starting { color: var(--text-3); }
.pill.paused, .pill.credit_cap { color: var(--warn); }
.pill.stopped, .pill.failed { color: var(--err); }
.pill.new { color: var(--text-2); }
.muted { color: var(--text-3); }
.danger-text { color: var(--err); }
.ok-text { color: var(--accent); }

.chart { width: 100%; height: 150px; display: block; }
.chart .bar { fill: var(--accent); }
.chart .bar:hover { fill: #86adff; }
.chart .axis { stroke: var(--border-2); }
.chart text { fill: var(--text-3); font-size: 9.5px; font-family: var(--mono); letter-spacing: .4px; }

.log-line {
  font-family: var(--mono); font-size: 12px;
  padding: 6px 0; border-bottom: 1px solid var(--border); display: flex; gap: var(--s2);
}
.log-line:last-child { border-bottom: none; }
.log-line .lvl { flex: 0 0 46px; font-weight: 400; text-transform: uppercase; }
.log-line .ts { flex: 0 0 92px; color: var(--text-3); }
.lvl-error { color: var(--err); } .lvl-warn { color: var(--warn); }
.lvl-info { color: var(--accent); } .lvl-debug { color: var(--text-3); }

#toast {
  position: fixed; bottom: calc(var(--s5) + 58px); right: var(--s5); z-index: 100;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-strong); border-left: 2px solid var(--accent);
  border-radius: var(--radius); padding: 11px var(--s4);
  max-width: calc(100vw - var(--s5) * 2);
  opacity: 0; transform: translateY(6px); transition: opacity .18s, transform .18s;
  pointer-events: none; font-size: 13.5px;
}
#toast.show { opacity: 1; transform: none; }
#toast.error { border-left-color: var(--err); }

/* --- platform: home, templates, AI builder ------------------------------------ */

.hero { text-align: center; margin: 96px 0 40px; }
.hero h2 { margin: 0; font-size: 40px; font-weight: 500; letter-spacing: -.84px; line-height: 1.2; }
.hero p { margin: 12px 0 0; font-size: 16px; letter-spacing: -.25px; color: var(--text-2); }

.bigcards {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s4); max-width: 760px; margin: 0 auto;
}
.bigcard {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  padding: 28px 26px; border: 1px solid var(--border-2); border-radius: var(--radius);
  background: var(--surface); color: var(--text); cursor: pointer;
  font: 14px/1.57 var(--font); letter-spacing: -.25px; text-align: left;
}
.bigcard:hover { border-color: var(--border-strong); background: var(--surface-2); }
.bigcard svg { width: 24px; height: 24px; color: var(--accent); margin-bottom: 6px; }
.bigcard b { font-size: 20px; font-weight: 500; letter-spacing: -.42px; }
.bigcard span { color: var(--text-2); }

.panel-grid {
  /* 280, not 320: with one automation plus the two action tiles a wider
     track left a hole in the second row on a normal laptop screen. */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
  gap: var(--s4);
}
.tile {
  display: flex; flex-direction: column; gap: 10px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 22px 24px;
}
.tile-head { display: flex; align-items: center; gap: 10px; justify-content: space-between; }
/* The badge says where the entry came from, so it never gets cut in half to
   make room for a long name: the name wraps instead. */
.tile-head .tag { flex: 0 0 auto; max-width: none; }
.tile-head b { min-width: 0; }
.tile-head b { font-size: 16px; font-weight: 500; letter-spacing: -.25px; }
.tile p { margin: 0; color: var(--text-2); }
/* Two columns of figures rather than one line: four numbers in a row shrink to
   nothing, and the tile has the height to spare. */
.tile-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
  gap: 10px var(--s4); font-size: 13px; color: var(--text-3);
  padding-top: var(--s3); border-top: 1px solid var(--border);
}
.tile-stats span { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tile-stats b {
  color: var(--text); font-variant-numeric: tabular-nums;
  font-weight: 500; font-size: 19px; letter-spacing: -.3px;
}
.tile-stats i {
  font-style: normal; font-size: 12px; color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tile .row { margin-top: auto; padding-top: var(--s2); }

/* The two ways of adding an automation, as tiles in the same grid. Dashed, so
   they read as "add something" and not as an automation you already have. */
.tile.action {
  align-items: flex-start; text-align: left; cursor: pointer;
  border-style: dashed; border-color: var(--border-strong); background: none;
  font: 14px/1.57 var(--font); letter-spacing: -.25px; color: var(--text);
  gap: 8px;
}
.tile.action:hover { background: var(--surface); border-color: var(--text-3); }
.tile.action svg { width: 20px; height: 20px; color: var(--accent); }
.tile.action b { font-size: 16px; font-weight: 500; letter-spacing: -.25px; }
.tile.action p { color: var(--text-2); }

.chat {
  display: flex; flex-direction: column;
  max-width: 720px; margin: 0 auto;
  min-height: calc(100dvh - var(--head-h) - 96px);
}
.chat-log { flex-grow: 1; display: flex; flex-direction: column; gap: 12px; padding: 8px 0 24px; overflow-y: auto; }
.bubble {
  max-width: 82%; padding: 12px 16px; border-radius: var(--radius);
  font-size: 14px; line-height: 1.57; letter-spacing: -.25px;
}
.bubble.ai {
  align-self: flex-start; background: var(--surface);
  border: 1px solid var(--border); color: var(--text-2);
}
.bubble.me {
  align-self: flex-end; background: var(--surface-2);
  border: 1px solid var(--border-2); color: var(--text);
}
.chat-input { display: flex; gap: var(--s2); padding-bottom: var(--s5); }
.chat-input input { flex-grow: 1; padding: 11px 14px; }


/* --- settings subtabs ----------------------------------------------------------- */

.subtabs {
  display: flex; gap: 4px; margin-bottom: var(--s4);
  overflow-x: auto; scrollbar-width: none;
}
.subtabs::-webkit-scrollbar { display: none; }
.subtabs button { flex: 0 0 auto; white-space: nowrap; }
.subtabs button {
  padding: 8px 14px; border: none; border-radius: 9999px;
  background: none; color: var(--text-2); cursor: pointer;
  font: 14px/1.4 var(--font); letter-spacing: -.25px;
}
.subtabs button:hover { color: var(--text); }
.subtabs button.active { background: var(--surface-2); color: var(--text); font-weight: 500; }

.cfgtab { display: none; }
.cfgtab.active { display: block; }
.cfgtab > .row { margin-top: var(--s4); }

.desc-line { margin-top: 8px; font-size: 12px; color: var(--text-3); }
.btn svg { flex: 0 0 auto; }


/* --- term chips ------------------------------------------------------------------ */

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--s4); }
.chip {
  display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--border-2); border-radius: 6px; background: var(--surface-2);
  font-size: 13px; letter-spacing: -.25px; color: var(--text);
}
.chip button {
  background: none; border: none; color: inherit; cursor: pointer;
  font: inherit; padding: 5px 4px 5px 10px;
}
.chip .chip-x { display: inline-flex; align-items: center; padding: 5px 8px 5px 4px; color: var(--text-3); }
.chip .chip-x svg { width: 11px; height: 11px; }
.chip .chip-x:hover { color: var(--err); }
.chip.off { opacity: .45; }
.chip.off .chip-text { text-decoration: line-through; }

/* --- sidebar: nested automation views --------------------------------------------- */

.auto-views {
  display: flex; flex-direction: column; gap: 1px;
  margin: 2px 0 2px 15px; padding-left: 8px; border-left: 1px solid var(--border-2);
}
.auto-views button { padding: 7px 10px; font-size: 13.5px; }
.auto-views button svg { width: 15px; height: 15px; flex: 0 0 15px; }

/* --- AI chat window ----------------------------------------------------------------- */

.ai-chat {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 80;
  display: flex; flex-direction: column;
  width: 400px; max-width: calc(100vw - 32px); height: 500px;
  max-height: calc(100vh - 48px); max-height: calc(100dvh - 48px);
  background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--radius); overflow: hidden;
}
.ai-chat-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.ai-chat-title { font-size: 13.5px; font-weight: 550; letter-spacing: -.25px; }
.ai-chat-log {
  flex-grow: 1; display: flex; flex-direction: column; gap: 10px;
  padding: 14px; overflow-y: auto;
}
.ai-chat-log .bubble { max-width: 92%; padding: 10px 12px; font-size: 13.5px; }
.ai-chat .chat-input { padding: 10px 12px 12px; border-top: 1px solid var(--border); }
.ai-chat .chat-input input { padding: 9px 12px; }
.bubble.suggestions { display: flex; flex-wrap: wrap; gap: 6px; max-width: 100%; }
.bubble.suggestions .chip {
  cursor: pointer; padding: 4px 10px; user-select: none;
  border: 1px solid var(--border-2); background: var(--surface-2); color: var(--text-3);
}
.bubble.suggestions .chip.picked {
  border-color: var(--accent); background: var(--accent-soft); color: var(--text);
}
.bubble.suggestions .row { width: 100%; }

.modal-close {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: 1px solid var(--border-2); border-radius: 7px;
  background: none; color: var(--text-2); cursor: pointer;
}
.modal-close:hover { background: var(--surface-2); color: var(--text); }
.modal-close svg { width: 13px; height: 13px; }


/* --- term list (keywords / orte) --------------------------------------------------- */

/* Count on the left, both buttons as one group on the right. As a plain .row
   the spacer ate the first line and "delete all" dropped to the left below
   "copy all", which is what made the two panels look mismatched. */
.list-actions {
  display: flex; align-items: center; gap: var(--s2) var(--s3);
  flex-wrap: wrap; margin-top: var(--s4);
}
.list-actions > .muted { white-space: nowrap; }
.list-actions .list-actions-btns { display: flex; gap: var(--s2); margin-left: auto; }
.btn.danger { color: var(--err); border-color: rgba(229, 98, 107, .4); }
.btn.danger:hover:not(:disabled) { background: var(--err-soft); }

.term-list {
  margin-top: 8px; border: 1px solid var(--border); border-radius: var(--radius);
  /* overflow-y alone computes overflow-x to auto as well, which put a stray
     horizontal scrollbar under every list. */
  max-height: 380px; overflow-y: auto; overflow-x: hidden;
}
/* Clipped until opened. The 8 here is TERM_LIMIT in app.js; change both. */
.term-list.clipped { max-height: none; overflow: hidden; }
.term-list.clipped .term-row:nth-child(n+9) { display: none; }
.term-more { width: 100%; margin-top: 8px; justify-content: center; }
.term-row {
  display: flex; align-items: center; gap: 8px;
  border-bottom: 1px solid var(--border);
}
.term-row:last-child { border-bottom: none; }
.term-row:hover { background: var(--surface-2); }
.term-row .term-text {
  flex-grow: 1; background: none; border: none; color: var(--text); cursor: pointer;
  font: 13.5px/1.4 var(--font); letter-spacing: -.25px; text-align: left;
  padding: 8px 0 8px 12px; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
}
.term-row .term-count { font-size: 12px; flex: 0 0 auto; }
.term-row .chip-x {
  display: inline-flex; align-items: center; background: none; border: none;
  color: var(--text-3); cursor: pointer; padding: 8px 12px 8px 6px;
}
.term-row .chip-x svg { width: 11px; height: 11px; }
.term-row .chip-x:hover { color: var(--err); }
.term-row.off { opacity: .45; }
.term-row.off .term-text { text-decoration: line-through; }

/* --- settings: explanation, relevance, unsaved state ---------------------- */

/* One plain sentence per panel, above the fields it explains. */
.intro {
  margin: 0 0 var(--s4); max-width: 78ch;
  font-size: 13px; line-height: 1.65; color: var(--text-2);
}
.panel.intro-panel { padding: var(--panel-pad-y) var(--panel-pad-x); }
.panel.intro-panel .intro { margin: 0; }

/* A literal the user has to copy exactly: a header name, a model id. */
.desc code.mark, .intro code.mark {
  font-family: var(--mono); font-size: 11.5px; font-weight: 700;
  color: var(--warn); background: var(--warn-soft);
  padding: 1px 5px; border-radius: 4px; white-space: nowrap;
}

/* Dimmed, not hidden: the setting still exists, it just does nothing right
   now, and the reason line says which switch decides that. */
/* Direct children only: nesting the rule would multiply, and the checkboxes
   inside .checks would end up at .2 instead of .45. */
/* The control now sits in a wrapper, so the dimming has to reach through it.
   The reason line inside stays at full strength, it is the point of dimming. */
.field.muted > label, .field.muted > .desc,
.field.muted > .field-control > input, .field.muted > .field-control > select,
.field.muted > .field-control > .dd-btn,
.field.muted > .field-control > .checks { opacity: .45; }
.field .desc.only { color: var(--warn); opacity: 1; margin-top: 8px; }
/* A provider that is not selected takes its whole block with it. */
.field.gone { display: none; }
.field:not(.muted) .desc.only { display: none; }

.field .keystate .linkbtn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent); text-decoration: underline;
}
.field .keystate .linkbtn:hover { color: var(--text); }

/* Nothing else on the page says that settings need saving. This does. */
.saverow { align-items: center; gap: var(--s3); }
.dirtyhint { display: none; font-size: 12px; color: var(--warn); }
.dirty .dirtyhint { display: inline; }

.maint { display: grid; gap: var(--s4); }
.maint-row { display: grid; grid-template-columns: 220px 1fr; gap: var(--s4); align-items: center; }
.maint-row .desc { font-size: 12px; color: var(--text-3); line-height: 1.55; }

/* The danger zone reads as one block, so nothing in it is clicked by reflex. */
.panel.danger { border-color: rgba(229, 98, 107, .35); }
.panel.danger > h2 { color: var(--err); }
.btn.ghost.danger { border-color: rgba(229, 98, 107, .45); color: var(--err); }
.btn.ghost.danger:hover { background: var(--err-soft); border-color: var(--err); }

/* --- connection overview -------------------------------------------------- */

/* A grid, not a table: the four columns line up down the page, and a missing
   required key is the one thing that has to be visible without reading. */
.statuslist { display: grid; gap: 1px; }
.statusrow {
  display: grid; grid-template-columns: 10px minmax(0, 1fr) auto minmax(0, 1.4fr);
  gap: var(--s3); align-items: center;
  padding: 10px 0; border-bottom: 1px solid var(--border);
}
.statusrow:last-child { border-bottom: none; }
.statusdot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-3); }
.statusdot.on { background: var(--accent); }
.statusdot.missing { background: var(--warn); }
.statusname { font-size: 13.5px; font-weight: 500; }
.statusstate {
  font-family: var(--mono); font-size: 10px; letter-spacing: .85px;
  text-transform: uppercase; color: var(--text-3); white-space: nowrap;
}
.statusstate.on { color: var(--accent); }
.statusstate.missing { color: var(--warn); }
.statusnote { font-size: 12.5px; color: var(--text-3); }
@media (max-width: 620px) {
  /* Four columns in 343px would leave a word per line. Two rows instead:
     name and state on top, the reason underneath. */
  .statusrow { grid-template-columns: 10px minmax(0, 1fr) auto; }
  .statusnote { grid-column: 2 / -1; }
}

/* --- table toolbars ------------------------------------------------------- */

/* Two deliberate rows instead of one wrapping line. The search field spans the
   full width on top, everything else sits on the row below: filters left,
   count and actions right. One wrapping .row produced a different ragged shape
   at every window width, which is what made it look crooked. */
.toolbar { display: grid; gap: var(--s3); }
/* Grid children default to a min-content floor, so two action buttons that
   cannot fit on one line pushed the whole toolbar past the screen edge. */
.toolbar > * { min-width: 0; }
/* The search field and the hit count it describes, one line, count on the right. */
.toolbar-search { display: flex; align-items: center; gap: var(--s3); }
.toolbar-search input { flex: 1 1 auto; min-width: 0; }
.toolbar-search .muted { flex: none; white-space: nowrap; }
.toolbar-row {
  display: flex; align-items: center; gap: var(--s2) var(--s4); flex-wrap: wrap;
}
.toolbar-filters { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
/* Auto margin per flex line: the actions stay on the right edge whether they
   share the row with the filters or get pushed onto their own line. */
.toolbar-actions {
  display: flex; align-items: center; gap: var(--s2); margin-left: auto;
  flex-wrap: wrap;
}
/* Every control the same height, so the row reads as one line, not a stack. */
.toolbar input, .toolbar .btn, .toolbar .dd-btn { height: 38px; }
/* The toolbar is its own box above the table's box, same as on the Domains
   page. No divider needed: the gap between the panels is the divider. */
.panel.toolbar-panel { padding: var(--panel-pad-y) var(--panel-pad-x); }

/* --- run bar: state and control, where the workers are -------------------- */

.runbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap; padding: var(--s4) var(--s5);
}
.runbar-state { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.runbar-title { font-size: 15px; font-weight: 500; letter-spacing: -.25px; }
.runbar-dot {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--text-3);
}
.runbar-dot.running { background: #2fd971; }
.runbar-dot.paused { background: var(--warn); }
.runbar-dot.stopped { background: #ff5747; }

/* --- confirmation dialog ------------------------------------------------- */

/* The browser's own confirm() is suppressed inside embedded windows: it returns
   false immediately and shows nothing, which turned every delete in this app
   into a button that did nothing. This one is ours, so it always appears. */
.modal {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s4); background: rgba(0, 0, 0, .65);
}
.token-input {
  width: 100%; margin-top: var(--s3);
  background: var(--bg); border: 1px solid var(--border-2); color: var(--text);
  border-radius: var(--radius); padding: 10px 12px;
  font: 14px/1.4 var(--mono); letter-spacing: 0;
}
.token-input:focus { outline: none; border-color: var(--border-strong); }

.modal-card {
  width: 100%; max-width: 460px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: var(--s5);
}
.modal-card h2 {
  margin: 0 0 var(--s3); font-size: 17px; font-weight: 500; letter-spacing: -.25px;
}
.modal-card p {
  margin: 0; color: var(--text-2); font-size: 13.5px; line-height: 1.6;
}
.modal-actions { margin-top: var(--s5); justify-content: flex-end; }
.modal-card.danger { border-color: rgba(229, 98, 107, .45); }
.modal-card.danger h2 { color: var(--err); }

/* A dialog that has something to read or something to paste. It stays a dialog
   rather than becoming a page: wider, and scrolling inside itself, so a long
   manifest never pushes the buttons off the bottom of the screen. */
.modal-card:has(#askInput:not([hidden])),
.modal-card:has(#askBody:not(:empty)) { max-width: 620px; }
#askBody:not(:empty) { margin-top: var(--s4); max-height: 46vh; overflow-y: auto; }
#askBody h3 {
  margin: var(--s4) 0 var(--s2); font-size: 12px; font-weight: 500;
  letter-spacing: .3px; text-transform: uppercase; color: var(--text-3);
}
#askBody h3:first-child { margin-top: 0; }
#askBody ul { margin: 0; padding-left: 18px; color: var(--text-2); font-size: 13.5px; }
#askBody li { margin-bottom: var(--s2); line-height: 1.6; }
#askBody p { margin: 0; }
#askInput {
  display: block; width: 100%; margin-top: var(--s4);
  font-family: var(--mono); font-size: 12px; line-height: 1.55;
  resize: vertical; min-height: 120px;
}
/* --- choosing a model ------------------------------------------------------ */

/* The field and its picker on one line. The field still takes anything typed
   into it, so the button never replaces it, it only saves the typing. */
.withpick { display: flex; gap: 8px; align-items: stretch; }
.withpick input { flex: 1 1 auto; min-width: 0; }
.withpick .btn { flex: 0 0 auto; }

.modelpicker { display: flex; flex-direction: column; gap: var(--s3); }
.modelbar { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }
.modelbar input[type="text"], .modelbar #modelSearch { flex: 1 1 200px; min-width: 0; }
.modelrow {
  display: flex; align-items: center; gap: 4px;
  border-bottom: 1px solid var(--border);
}
.modelrow:last-child { border-bottom: none; }
.modelrow.current { background: var(--surface-2); }
.modelrow.gone { padding: 8px 4px; color: var(--text-3); }
.modelpick {
  display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0;
  padding: 8px 6px; border: none; background: none; cursor: pointer;
  text-align: left; color: var(--text);
}
.modelpick:hover { background: var(--surface-2); }
.modelid { font-family: var(--mono); font-size: 12.5px; word-break: break-all; }
.modelmeta { font-size: 12px; color: var(--text-3); }
.starbtn {
  flex: 0 0 auto; width: 26px; height: 26px; padding: 0; line-height: 1;
  border: none; background: none; cursor: pointer; color: var(--text-3);
  font-size: 15px;
}
.starbtn.on { color: var(--warn); }
.starbtn:hover { color: var(--text); }

.side-foot:hover { background: var(--surface); color: var(--text-2); }
/* A disk that is nearly full is the one number worth shouting about: this app
   has filled one before. */
.side-foot.tight { color: var(--err); }
/* Inside a dialog the cards sit closer together and the numbers are smaller:
   this is a panel about the machine, not a dashboard. */
#askBody .kpis { margin: 0; gap: var(--s3); }
#askBody .kpi { padding: 12px 14px; }
#askBody .kpi .value { font-size: 20px; margin-top: 4px; letter-spacing: -.4px; }
#askBody .kpi .sub { font-size: 12px; }
/* The slash list, sitting on top of the input it belongs to. Above, not below:
   the input is at the bottom of the panel and a list under it would be off
   screen. */
.chat-input { position: relative; }
.slashbox {
  position: absolute; left: 0; right: 0; bottom: calc(100% + 8px);
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--radius); overflow: hidden; z-index: 5;
  max-height: 260px; overflow-y: auto;
}
.slashrow {
  display: flex; align-items: baseline; gap: var(--s3); justify-content: space-between;
  padding: 9px 12px; border: none; background: none; cursor: pointer;
  color: var(--text); text-align: left; font: 400 13.5px/1.4 var(--font);
}
.slashrow.on, .slashrow:hover { background: var(--surface-2); }
.slashrow .desc { flex: 0 0 auto; }
/* Which automation the assistant is currently talking about. */
.scopechip {
  display: inline-flex; align-items: center; flex: 0 0 auto;
  padding: 4px 9px; border: 1px solid var(--accent); border-radius: var(--radius);
  color: var(--accent); font-family: var(--mono); font-size: 10.5px;
  letter-spacing: .6px; text-transform: uppercase;
}

.sysrow { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.sysrow code { flex: 1 1 240px; word-break: break-all; }

.problems { margin: 0; padding-left: 18px; }
.problems code.mark { margin-right: 4px; }

/* --- template lists ------------------------------------------------------- */

/* The heading over a group of cards. Quiet on purpose: it separates two lists,
   it does not compete with the names inside them. */
.listhead {
  margin: var(--s5) 0 var(--s3); font-size: 12px; font-weight: 500;
  letter-spacing: .3px; text-transform: uppercase; color: var(--text-3);
}
.listhead:first-of-type { margin-top: 0; }
/* An automation that exists but has not been offered to anybody yet. Quiet:
   it is a state, not a warning. */
/* The whole card opens its detail, so it has to look like it does something
   and the two controls on it have to stay reachable. */
.tile.card-open { cursor: pointer; }
.tile.card-open:hover { border-color: var(--text-3); }
.tile.card-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* A like is a bookmark with a count next to it, so it is shaped like the other
   buttons in the row rather than like a stray icon. Pushed to the right, so the
   two things you do to an automation stay together on the left. */
.tile-actions { align-items: stretch; }
.likebtn {
  display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  padding: 8px 12px; border: 1px solid var(--border-strong);
  border-radius: var(--radius); background: transparent; cursor: pointer;
  color: var(--text-3); font: 500 13px/1 var(--font);
}
.likebtn .heart { font-size: 14px; line-height: 1; }
.likebtn:hover { background: var(--surface-2); color: var(--text); }
.likebtn.on { color: var(--err); border-color: rgba(229, 98, 107, .45); }
.detail-actions { margin-top: var(--s4); }

/* --- the builder saying what it is doing ---------------------------------- */

/* The one place in this app that moves. Everything else is flat on purpose,
   but a build takes two to four calls to a model and a still screen for that
   long reads as a hang. One dot pulses, and only the step that is running. */
.bubble.steps { display: flex; flex-direction: column; gap: 8px; }
.stepline { display: flex; align-items: center; gap: 10px; font-size: 13.5px; }
.stepline .desc { display: inline; }
.stepdot {
  flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent); animation: stepwork 1.1s ease-in-out infinite;
}
.stepline.done { color: var(--text-3); }
.stepline.done .stepdot { background: var(--text-3); animation: none; }
@keyframes stepwork { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .stepdot { animation: none; }
}
#tplPager { margin-top: var(--s3); }

.tile.empty-tile {
  color: var(--text-3); font-size: 13.5px; line-height: 1.6;
  border-style: dashed; justify-content: center;
}
@media (max-width: 620px) {
  /* Full width buttons, and the safe one first under the thumb. */
  .modal-actions { flex-direction: column-reverse; }
  .modal-actions .btn { width: 100%; justify-content: center; }
}

/* --- assistant ------------------------------------------------------------ */

/* One launcher, bottom right. It steps aside whenever a chat panel is open, so
   it never sits on top of the suggestions widget that lives in the same corner. */
.agent-launcher {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 79;
  width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border-strong); cursor: pointer;
}
.agent-launcher svg { width: 19px; height: 19px; }
.agent-launcher:hover { background: var(--surface); border-color: var(--text-3); }
.chat-open .agent-launcher { display: none; }
.ai-chat.agent { z-index: 81; }

/* What the assistant changed, listed under its answer: it says what it did, in
   the app's own words, not only what the model claims it did. */
.bubble .changes {
  margin: 8px 0 0; padding: 8px 10px; list-style: none;
  border-left: 2px solid var(--accent); background: var(--accent-soft);
  border-radius: 0 6px 6px 0; font-size: 12.5px; color: var(--text-2);
}
.bubble .changes li + li { margin-top: 4px; }


/* --- narrow screens ---------------------------------------------------------
   Three gates, each for a different reason:
   900px   the sidebar no longer fits beside the content, so it becomes a drawer
   620px   one column of everything, and the chat panels take the whole screen
   coarse  a finger, not a mouse: bigger hit areas and 16px form text
   Above 900px nothing in here applies, so the desktop layout is untouched. */

@media (max-width: 900px) {
  :root { --page-pad: var(--s4); --head-h: 60px; }

  .navtoggle { display: inline-flex; }

  /* Out of the flow entirely, so the content gets the full width instead of
     being pushed down by a sidebar that is as tall as its own contents. */
  .side {
    position: fixed; top: 0; left: 0; z-index: 90;
    width: 272px; flex: 0 0 272px; max-width: 84vw;
    height: 100vh; height: 100dvh; overflow-y: auto;
    transform: translateX(-101%);
    transition: transform .18s ease;
  }
  body.nav-open .side { transform: none; }
  body.nav-open { overflow: hidden; }
  .nav-backdrop {
    display: block; position: fixed; inset: 0; z-index: 89;
    background: rgba(0, 0, 0, .6);
  }

  header { gap: var(--s3); }
  header h1 { font-size: 19px; }
  main { padding: var(--s4) var(--page-pad); }

  .grid-2 { grid-template-columns: 1fr; }
  .maint-row { grid-template-columns: 1fr; }
}

@media (max-width: 620px) {
  :root { --panel-pad-x: var(--s4); }

  /* Two per row rather than four stacked cards: at one card per row the four
     numbers alone fill a phone screen and the charts start below the fold. */
  .kpis { grid-template-columns: repeat(auto-fit, minmax(min(148px, 100%), 1fr)); }

  .bigcards { grid-template-columns: 1fr; }
  .hero { margin: 40px 0 28px; }
  .hero h2 { font-size: 30px; }
  .hero p { font-size: 15px; }

  .kpi { padding: 14px 16px; }
  .kpi .value { font-size: 24px; }

  /* A control that decides whether the workers run should not be a small pill
     wedged into a corner of a phone screen. */
  .runbar .btn { width: 100%; justify-content: center; }
  .toolbar-actions .btn, .saverow .btn { flex: 1 1 auto; justify-content: center; }
  .toolbar-actions { width: 100%; }

  /* Full sheet: a 400px box plus an on screen keyboard leaves nothing of the
     conversation visible. */
  .ai-chat {
    top: 0; left: 0; right: 0; bottom: auto;
    width: auto; max-width: none; height: 100dvh; max-height: none;
    border-width: 0; border-radius: 0;
  }
  .agent-launcher { right: var(--s4); bottom: var(--s4); }
  #toast { right: var(--s4); left: var(--s4); max-width: none; bottom: calc(var(--s4) + 58px); }
}

/* Touch, at any width: Apple and Google both put the smallest comfortable
   target at around 44px, and half the controls here were under 30. */
@media (pointer: coarse) {
  /* Anything under 16px makes mobile Safari zoom the page on focus and leave
     it zoomed, which is where the sideways scrolling on iPhones comes from. */
  input, select, textarea, .dd-btn, .dd-opt { font-size: 16px; }
  input, select, .dd-btn { padding-top: 10px; padding-bottom: 10px; }

  .btn { padding: 11px 16px; }
  .btn.small { padding: 12px 13px; }
  .toolbar input, .toolbar .btn, .toolbar .dd-btn { height: 44px; }

  nav button { padding: 12px; }
  .auto-views button { padding: 12px 10px; }
  .auto-item { padding: 12px; }
  .auto-new { padding: 12px; }
  .navtoggle { width: 44px; height: 44px; flex-basis: 44px; }
  .subtabs button { padding: 12px 14px; }

  .modal-close { width: 44px; height: 44px; }
  .term-row .term-text { padding-top: 13px; padding-bottom: 13px; }
  .term-row .chip-x { padding: 16px 16px 16px 8px; }
  .chip button { padding: 12px 4px 12px 11px; }
  .chip .chip-x { padding: 12px 10px 12px 4px; }
  .field .checks .check { padding: 12px 9px; }
  button.link, .field .keystate .linkbtn { padding: 12px 0; }

  .pickbox { padding: 12px 10px; }
  th.pick, td.pick { width: 44px; }
  td.pick input, th.pick input { width: 20px; height: 20px; }
}

/* Phone held sideways: 375px of height, and the hero alone claimed 136 of it. */
@media (max-height: 520px) {
  .hero { margin: 24px 0 20px; }
  .hero h2 { font-size: 26px; }
  .chat { min-height: 0; }
}
