/* Participant styling. Mobile first: the phone layout is the base and the wider
   screen is the one media query, rather than the other way round.

   Colour comes from tokens.css and nothing here writes a literal. That matters
   most for the accent, which is the neon mint on a dark ground and a deep
   readable green on a light one; the token already swaps, so using --accent is
   always right and a hex never is. */

/* The title and the tabs stay put at the top, so anything scrolled to has to
   stop below them rather than at the top of the window. Set here, once, because
   every scroll in the app hits it: a new question being shown, a citation
   opening the question it refers to, a jump to an anchor. The alternative is
   each caller subtracting the bar's height itself, which is how one of them
   ends up not doing it. --chrome-height is measured, and the fallback is only
   for the moment before the first measurement. */
html {
  scroll-padding-top: calc(var(--chrome-height, 6rem) + .5rem);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--q-font);
  font-size: var(--step-0);
  margin: 0;
  -webkit-text-size-adjust: 100%;
}

 /* One width for the whole app, on every screen and every page. The bar, the
    title and the tab row live in here, and they must not move when somebody
    changes tab: a navigation bar that shifts by two hundred pixels between the
    questions and the map is the single most unsettling thing the app did.

    What varies is the tab content, which is a separate question answered by
    `.reading` below. The shell used to be 42rem for a discussion and 64 or 76
    for the map and the manager screens, so the whole frame resized under the
    pointer. */
.shell {
  max-width: 64rem;
  margin: 0 auto;
  min-height: 100dvh;
}

/* The reading column, inside the fixed frame. A question and an answer box do
   not want a wide measure, so the panels that are mostly words keep 42rem and
   centre themselves; the map, the setup form and the transcript table use the
   whole frame. Applied to the content rather than to the shell, which is what
   keeps the bar still. */
.reading {
  max-width: 42rem;
  margin-inline: auto;
}

.pad {
  padding: 1rem 1rem 4rem;
}

/* The participant screens are built for a phone and stay narrow on a desktop,
   because a question and an answer box do not want a wide column.

   The manager screens, the help and the map are wider, but still a column with
   the window either side of it. They used to run to 76rem, which on any
   ordinary laptop is the whole screen, so those pages read as a left-aligned
   website while the discussion beside them read as an app. One shape for the
   whole thing is worth more than the extra inch: 64rem still fits the setup
   form with its assistant panel beside it, and a table wider than that scrolls
   inside its own box rather than stretching the page. */
.shell-wide { max-width: 64rem; }

/* --- chrome ---------------------------------------------------------------- */

.topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .75rem 1rem;
  padding-top: max(.75rem, env(safe-area-inset-top));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.topbar-title {
  font-size: var(--step-1);
  font-weight: 600;
  margin: 0;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.icon-btn {
  background: none;
  border: 0;
  color: var(--text-dim);
  font-size: var(--step-2);
  line-height: 1;
  padding: .25rem .4rem;
  border-radius: var(--r-sm);
}

.icon-btn:hover { color: var(--text); background: var(--surface-2); }

.back-labelled {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  flex: none;
}

.back-labelled span {
  font-size: var(--step--1);
  font-weight: 600;
}

/* Bottom left, out of the way. Top right it sat on the theme toggle. */
.dev-badge {
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 20;
  background: var(--pink);
  color: var(--ink);
  font-size: var(--step--1);
  font-weight: 700;
  padding: .15rem .5rem;
  border-top-right-radius: var(--r-sm);
}

/* --- one navigation bar, everywhere ---------------------------------------- */

/* The app's own areas. Everything else is a subscreen of one of these, and the
   discussion's own tabs sit under it rather than beside it: this bar moves
   between parts of the app, those move between parts of one discussion. */
.appnav {
  display: flex;
  align-items: center;
  gap: .2rem;
  padding: .45rem .8rem;
  padding-top: max(.45rem, env(safe-area-inset-top));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}

.appnav::-webkit-scrollbar { display: none; }

/* Until the server has said who is asking. The bar was drawn as a participant's
   and then grew an item at a time as each answer arrived: the owner items, the
   project list, the account. One change instead of four, and a browser that has
   been here before guesses right and never flickers at all. The delay is a
   fraction of a second and nothing moves during it. */
.appnav.settling .appnav-owner,
.appnav.settling .account { visibility: hidden; }

.appnav-brand {
  font-weight: 700;
  color: var(--text);
  text-decoration: none;
  margin-right: .8rem;
  white-space: nowrap;
}

/* An owner's bar carries a project selector and five items, which on anything
   short of a wide desktop leaves the last of them off the edge. The furniture
   gives way first: Projects and Help keep their icons and lose their words, and
   the brand shortens to one. What never gives way is Questions, Set up and
   Transcript, which are what the bar is for. */
.appnav-brand-short { display: none; }
.appnav-item .bi { margin-right: .3rem; }

@media (max-width: 62rem) {
  .appnav-brand-full { display: none; }
  .appnav-brand-short { display: inline; }
  .appnav-label { display: none; }
  .appnav-item .bi { margin-right: 0; }
  .appnav-project { max-width: 9rem; }
}

.appnav-brand:hover { color: var(--accent); text-decoration: none; }

.appnav-item {
  flex: none;
  padding: .3rem .7rem;
  border-radius: var(--r-pill);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  text-decoration: none;
  white-space: nowrap;
}

.appnav-item:hover { color: var(--accent); text-decoration: none; }

/* A closed discussion. Faded and struck through rather than removed: it is
   still the way back to what was written, and a control that vanishes when a
   project closes reads as a bug. */
.shut { opacity: .5; text-decoration: line-through; }

.appnav-item.on {
  color: var(--accent-ink);
  background: var(--accent);
}

.appnav-end {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: .3rem;
  padding-left: .6rem;
}

/* The account. Last on the bar, where an account always is. */
.account { position: relative; }

.account-face {
  display: flex;
  align-items: center;
  gap: .3rem;
  padding: .25rem .5rem;
  border-radius: var(--r-pill);
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  list-style: none;
}

.account-face::-webkit-details-marker { display: none; }
.account-face:hover { color: var(--accent); }
.account[open] .account-face { color: var(--accent); }

/* Fixed rather than absolute, and placed by script when it opens. The bar
   scrolls sideways on a narrow screen, and an element that scrolls clips
   anything hanging out of it: the menu was being cut off at the bar's own
   bottom edge, which reads as a menu hidden behind the page. Taking it out of
   the flow entirely is the only fix that does not cost the bar its scrolling. */
.account-menu {
  position: fixed;
  z-index: 60;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  padding: .3rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
}

.account-note {
  margin: .2rem .6rem .4rem;
  font-size: var(--step--2);
  max-width: 16rem;
  line-height: 1.4;
}

.account-email {
  margin: .2rem .5rem .4rem;
  font-size: var(--step--1);
  color: var(--text-faint);
  word-break: break-all;
}

.account-item {
  border: 0;
  background: none;
  border-radius: var(--r-sm);
  padding: .35rem .5rem;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.account-item:hover { background: var(--surface-2); color: var(--accent); }

/* Removing what you wrote, in the menu with the other things that are about
   you. A rule above it, because it is not one of the account items and should
   not be pressed while reaching for Sign out. Not red until the pointer is on
   it: a standing red line in an open menu is the loudest thing on the screen,
   which is the fault this was moved twice to fix. */
.account-item-danger {
  margin-top: .35rem;
  padding-top: .5rem;
  border-top: 1px solid var(--border);
  border-radius: 0;
  color: var(--text-faint);
}

.account-item-danger:hover { background: none; color: var(--bad); }

/* On a phone the name goes and the icon stays: an address is half the bar. */
@media (max-width: 48rem) {
  .account-who { display: none; }
}

/* The discussion's name, under the bar rather than in it: the bar is the same
   on every screen and this changes with the discussion. */
.discussion-title {
  font-size: var(--step-1);
  font-weight: 600;
  margin: 0;
  padding: .5rem 1rem .35rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A page's own heading row, for the screens that are not a discussion. */
.page-head {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .8rem 1rem .2rem;
}

/* The title and the tabs, as one block that stays put while a panel scrolls.
   Sticky on the block rather than on each part, so there is one height to
   clear and nothing can slide out from under anything else. */
.chrome {
  position: sticky;
  top: 0;
  z-index: 6;
  background: var(--surface);
}

.chrome .topbar { position: static; }

/* Classic tabs: the row is the navigation and the selected one is marked on the
   row itself, so there is never a question of where you are or how to get
   anywhere else. It replaces four buttons that each opened a screen with its
   own way back. */
.tabs {
  display: flex;
  align-items: stretch;
  gap: .1rem;
  padding: 0 .5rem;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}

.tabs::-webkit-scrollbar { display: none; }

.tab {
  flex: none;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  padding: .55rem .8rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.tab:hover { color: var(--accent); }
.tab.on { color: var(--accent); border-bottom-color: var(--accent); }

/* Something landed in a tab nobody is looking at. A dot and a short pulse
   rather than a permanent colour: it has to catch the eye once and then stop,
   because a tab that stays lit is a badge nobody reads after the first day. It
   goes when the tab is opened, which is the only thing anybody should have to
   do about it. */
.tab.fresh::after {
  content: '';
  display: inline-block;
  width: .45rem;
  height: .45rem;
  margin-left: .35rem;
  vertical-align: .1rem;
  border-radius: 50%;
  background: var(--accent);
  animation: tab-fresh 1.6s ease-in-out 4;
}

/* A crowd answering now. Its own mark rather than `fresh`, because the two say
   different things: fresh means something arrived while you were looking away
   and clears when you look, and this means it is working at this moment and has
   to stay while somebody watches it. */
.tab.tab-running::after {
  content: '';
  display: inline-block;
  width: .45rem;
  height: .45rem;
  margin-left: .35rem;
  vertical-align: .1rem;
  border-radius: 50%;
  background: var(--accent);
  animation: tab-fresh 1.6s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .tab.tab-running::after { animation: none; }
}

@keyframes tab-fresh {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .35; transform: scale(1.35); }
}

@media (prefers-reduced-motion: reduce) {
  .tab.fresh::after { animation: none; }
}

/* Leaves the discussion rather than switching a panel, so it is set apart from
   the tabs and never takes the selected mark. */
.tab-away { margin-left: .6rem; opacity: .85; }

/* A rule between what an owner does to the discussion and the discussion
   itself, drawn on Questions and only while the owner's tabs are showing. A
   participant's row starts at Questions with nothing to its left, so the rule
   would be a line hanging off the edge. */
#tab-rehearse:not([hidden]) + #tab-list {
  margin-left: .5rem;
  padding-left: 1rem;
  border-left: 1px solid var(--border);
}
.tab-away::before { content: '↗'; margin-right: .25rem; }

/* Who is here and how much has been said. On the tab row rather than in a bar
   of its own: it is one line of context, not a place to be. */
.tabs-pulse {
  margin-left: auto;
  align-self: center;
  padding-left: .8rem;
  font-size: var(--step--1);
  color: var(--text-faint);
  white-space: nowrap;
}

@media (max-width: 30rem) {
  /* Two things compete for a narrow row, and the tabs are the ones you press. */
  .tabs-pulse { display: none; }
}

/* Positioned, because the map's furniture hangs off it: the search box, the
   node panel, the zoom controls and the key are all absolute, and without a
   positioned parent they resolve against the window instead. On a screen wider
   than the app that put the search box off the left edge and the node panel off
   the right, outside the app entirely. */
.tab-panel { display: block; position: relative; }

/* A panel's own actions sit at its top, where a screen of its own would have
   put them in a header. */
.panel-actions {
  display: flex;
  justify-content: flex-end;
  gap: .4rem;
  margin-bottom: .8rem;
  min-height: 1.6rem;
}

/* A pill for a row of actions. An anchor wearing it has to stop looking like a
   link: a pill with underlined text inside reads as two controls, one of which
   appears not to work. */
.action-btn {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: .2rem .8rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-dim);
  text-decoration: none;
  cursor: pointer;
}

.action-btn:hover {
  color: var(--accent);
  border-color: var(--accent);
  text-decoration: none;
}

/* --- help ------------------------------------------------------------------ */

/* The measure belongs to the prose, not to the article: capping the article
   would cap the diagrams inside it too, and a diagram cut off at the width of a
   paragraph is a diagram nobody sees the end of. */
.help-body { line-height: 1.55; }

.help-body > p,
.help-body > ul,
.help-body > ol,
.help-body > blockquote,
.help-body > h1,
.help-body > h2,
.help-body > h3 { max-width: 38rem; }

.help-body h1 { font-size: var(--step-3); margin: 0 0 1rem; }
.help-body h2 { font-size: var(--step-2); margin: 2rem 0 .6rem; }
.help-body h3 { font-size: var(--step-1); margin: 1.4rem 0 .4rem; }
.help-body p, .help-body li { color: var(--text-dim); }
.help-body strong { color: var(--text); }
.help-body a { color: var(--accent); }

.help-body blockquote {
  margin: 1rem 0;
  padding: .6rem .9rem;
  border-left: 3px solid var(--accent-muted);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}

.help-body blockquote p:last-child { margin-bottom: 0; }
.help-body ul { padding-left: 1.2rem; }
.help-body li { margin-bottom: .4rem; }

/* Which half of the document is showing. Only somebody signed in as an owner
   sees this, and only because they are the ones who need to read both. */
.help-audience {
  max-width: 38rem;
  margin-bottom: 1.2rem;
  padding-bottom: .6rem;
  border-bottom: 1px solid var(--border);
  font-size: var(--step--1);
  color: var(--text-faint);
}

/* --- the help: contents beside the document -------------------------------- */

.help-layout { display: block; }

@media (min-width: 62rem) {
  .help-layout {
    display: grid;
    grid-template-columns: 15rem minmax(0, 1fr);
    align-items: start;
  }
}

.help-toc {
  display: none;
  position: sticky;
  top: 3.5rem;
  max-height: calc(100dvh - 5rem);
  overflow-y: auto;
  padding: 1rem .5rem 2rem 1rem;
  font-size: var(--step--1);
}

@media (min-width: 62rem) { .help-toc { display: block; } }

.help-toc ul { list-style: none; margin: 0; padding: 0; }
.help-toc li { margin: .18rem 0; }
.help-toc .toc-sub { padding-left: .8rem; }

.help-toc a {
  color: var(--text-faint);
  text-decoration: none;
  display: block;
  padding: .1rem .4rem;
  border-left: 2px solid transparent;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}

.help-toc a:hover { color: var(--text); background: var(--surface-2); }

/* Where the reader is. A contents list on a long page that does not say where
   you are is only half of one. */
.help-toc a.here {
  color: var(--accent);
  border-left-color: var(--accent);
  font-weight: 600;
}

/* --- the templates, in the help --------------------------------------------- */

/* Tabs rather than a stack of cards: these are alternatives to choose between,
   and comparing two of them should not mean scrolling past four others. */
.template-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  margin: 1rem 0 0;
  border-bottom: 1px solid var(--border);
  padding-bottom: .5rem;
}

.template-tab {
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-pill);
  padding: .25rem .8rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  cursor: pointer;
  white-space: nowrap;
}

.template-tab:hover { color: var(--text); background: var(--surface-2); }

.template-tab.on {
  color: var(--accent);
  border-color: var(--accent);
  background: rgba(var(--accent-rgb), .1);
}

.template-panel {
  width: min(58rem, 100%);
  padding: 1.1rem 0 0;
}

.template-blurb { margin: 0 0 1rem; color: var(--text); font-size: var(--step-0); }
.template-when { margin: .9rem 0; color: var(--text-dim); }
/* The examples after the first, under the buttons rather than beside
   them: two large buttons would say the examples are the point. */
.template-more-examples {
  margin: .6rem 0 0; font-size: .9rem; color: var(--text-dim);
}
.template-more-examples a { color: var(--accent); }
.template-label { margin: 0 0 .2rem; color: var(--text-faint); font-size: var(--step--1); }

.template-starters {
  margin: 1.2rem 0;
  padding-left: .9rem;
  border-left: 2px solid var(--accent-muted);
}

.template-starters p { margin: .15rem 0; color: var(--text-dim); font-style: italic; }

/* Wider than the reading column on purpose, so it scrolls inside its own box
   rather than making the page scroll sideways. */
/* The map a template draws: the first few columns of the picture the Overview
   tab draws of a real discussion. Every colour here is the one the full map
   uses for the same thing, so that a card teaches somebody how to read the app
   rather than teaching them a second notation. */
.template-diagram { overflow-x: auto; padding: .2rem 0 .4rem; }
.template-map { max-width: 100%; height: auto; }

/* A question is the AI writing, and nobody has answered it yet: white, and
   dashed because it is open. */
.map-question rect {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-width: 1.2;
  stroke-dasharray: 5 3;
}

/* An answer is somebody's own words, so it is the one filled box. */
.map-answer rect {
  fill: var(--accent-muted);
  fill-opacity: .3;
  stroke: var(--accent-muted);
  stroke-width: 1.4;
}

/* A synthesis is the AI writing about the whole room: white like a question,
   because it is answerable, and a solid heavier border because it is settled
   rather than open. Steve, 19 August 2026. */
.map-synthesis rect {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-width: 2.2;
}

/* A report is written for somebody outside the discussion, so it is the one box
   that is not part of it, and the one box not in the discussion's colour. */
.map-report rect {
  fill: var(--gold-fill);
  stroke: var(--gold);
  stroke-width: 1.6;
}

.map-report .map-glyph { stroke: var(--gold); }
.map-report .map-glyph .map-filled { fill: var(--gold); }

/* Three rows starting at three different points, in the key. */
.map-ragged-bar { fill: var(--accent-muted); fill-opacity: .45; }

/* Live, or people arriving in their own time, said above the picture. */
.template-tempo {
  margin: 0 0 .15rem;
  font-size: var(--step--1);
  color: var(--text-faint);
}

.template-tempo-live { color: var(--accent); font-weight: 600; }

/* Where the thread runs on past the edge of the picture. */
.map-more rect { fill: none; stroke: var(--border-strong); stroke-dasharray: 2 3; }
.map-dot { fill: var(--text-faint); }

/* Two questions about one answer. */
.map-stacked {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-width: 1.2;
  stroke-dasharray: 5 3;
  opacity: .55;
}

/* Something that happens only when somebody asks for it. */
.map-ghost { opacity: .55; }

/* A later wave of people, in a colour of its own. The two colour tokens are
   redefined on the group, so every rule above goes on saying var(--accent) and
   none of them has to know that waves exist. */
.map-wave-1 {
  --accent: var(--wave-2);
  --accent-muted: var(--wave-2-muted);
}

/* The person in the margin belongs to a wave as much as their boxes do, and
   the plain glyph takes its colour from the text rather than from the accent,
   so it needs saying here or the row label stays the first wave's colour. */
.map-glyph.map-wave-1 { stroke: var(--wave-2); }
.map-glyph.map-wave-1 .map-filled { fill: var(--wave-2); }

/* Where somebody running the discussion stops it and starts it again. */
.map-band-divider line {
  stroke: var(--text-faint);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
}

/* The column where everything else closes and the room answers one thing. */
.map-band-focus rect {
  fill: var(--accent);
  fill-opacity: .07;
  stroke: none;
}

.map-band-label {
  font-size: 9px;
  fill: var(--text-faint);
}

.map-glyph { fill: none; stroke: var(--text-dim); stroke-width: 1.1; }
.map-glyph circle { fill: none; }
.map-glyph-ai { stroke: var(--accent); }
.map-glyph .map-filled { fill: var(--text-dim); stroke: none; }
.map-glyph-ai .map-filled { fill: var(--accent); stroke: none; }

.map-rule { stroke: var(--text-faint); stroke-width: 1.6; opacity: .35; }

.map-wire { fill: none; stroke: var(--border-strong); stroke-width: 1.2; }

.map-wire-synthesises {
  stroke: var(--accent);
  stroke-width: 1.3;
  stroke-dasharray: 4 3;
  opacity: .75;
}

.map-wire-draws { stroke: var(--text-faint); stroke-width: 1.2; }

/* A sealed lane: nobody in it sees anybody else's answers, and the one thing
   that crosses it is a synthesis. */
.map-seal {
  stroke: var(--text-dim);
  stroke-width: 1.5;
  stroke-dasharray: 6 4;
}

/* The steps in words, under the picture. Numbered, on one line each on a phone
   and flowing across on anything wider. */
.template-steps {
  display: flex;
  flex-wrap: wrap;
  gap: .15rem .9rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
  font-size: var(--step--1);
  color: var(--text-dim);
}

.template-steps li { counter-increment: step; }

.template-steps li::before {
  content: counter(step);
  display: inline-block;
  min-width: 1.1em;
  margin-right: .3em;
  color: var(--accent);
  font-weight: 700;
}

/* The key. One per page, because the marks mean the same thing on every card. */
.template-key {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: .9rem 1rem;
  margin: 1.4rem 0 0;
  background: var(--surface);
}

.template-key-title {
  margin: 0 0 .6rem;
  font-weight: 700;
  font-size: var(--step-0);
}

.template-key-list {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: .25rem .8rem;
  margin: 0;
  font-size: var(--step--1);
  color: var(--text-dim);
}

/* Near enough the scale the maps themselves come out at, so a box in the key
   and the same box on a card look like the same thing. */
.template-key-list dt .template-map { width: 46px; height: auto; }

.template-key-list dt { line-height: 0; }
.template-key-list dd { margin: 0; }

/* Two pairs abreast where the key has a page to itself rather than a cell of a
   grid, so it does not run down half the front page. */
@media (min-width: 900px) {
  .home-templates .template-key-list {
    grid-template-columns: auto minmax(0, 1fr) auto minmax(0, 1fr);
  }
}

.template-key-note {
  margin: .7rem 0 0;
  font-size: var(--step--1);
  color: var(--text-faint);
  max-width: 40rem;
}

.template-dials {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .1rem .7rem;
  margin: 1.2rem 0 0;
  font-size: var(--step--1);
}

.template-dials dt { color: var(--text-faint); }
.template-dials dd { margin: 0; color: var(--text-dim); }

/* --- the report ------------------------------------------------------------- */

.report-standing {
  padding: .7rem .9rem;
  background: var(--surface-2);
  border-radius: var(--r);
  margin-bottom: 1.4rem;
  font-size: var(--step--1);
}

.report-version { margin: 0 0 .2rem; color: var(--text-dim); }
.report-changelog { margin: .4rem 0 0; color: var(--text-faint); }

/* Behind the discussion. Not an error, but the reader has to know before
   deciding what to do with what follows. */
.report-stale { margin: 0; color: var(--text); font-weight: 600; }

.report-section { margin-bottom: 2rem; }
.report-section h2 { font-size: var(--step-1); margin: 0 0 .5rem; }
.report-section p { color: var(--text-dim); line-height: 1.55; }

.report-flag {
  margin-left: .5rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--accent);
  text-transform: lowercase;
}

.report-evidence-toggle {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: var(--step--1);
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}

.report-evidence-toggle:disabled {
  color: var(--text-faint);
  text-decoration: none;
  cursor: default;
}

.report-evidence {
  margin-top: .7rem;
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

.evidence {
  padding: .6rem .8rem;
  border-left: 3px solid var(--border-strong);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--step--1);
}

.evidence-synthesis { border-left-color: var(--accent); }
.evidence-who { margin: 0; font-weight: 700; color: var(--accent); }
.evidence-question { margin: .2rem 0 .3rem; color: var(--text-faint); font-style: italic; }
.evidence-text { margin: 0 0 .5rem; color: var(--text); }

.evidence-changed {
  margin: 0 0 .5rem;
  color: var(--text-faint);
  font-style: italic;
}

.evidence-parents {
  margin: 0 0 .6rem;
  padding-left: .8rem;
  border-left: 1px solid var(--border);
}

.evidence-parents p { margin: .2rem 0; color: var(--text-dim); }

/* --- chat ------------------------------------------------------------------- */

.chat-room {
  /* The room fills what the sticky title and tabs leave. Measured
     rather than guessed: the bar is one line on a wide screen and
     wraps on a narrow one, and a guess is wrong on one of them. */
  height: calc(100dvh - var(--chrome-height, 6rem) - 3.5rem);
  overflow-y: auto;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
}

.chat-msg {
  max-width: 85%;
  padding: .45rem .7rem;
  border-radius: var(--r);
  background: var(--surface-2);
  align-self: flex-start;
}

.chat-mine { align-self: flex-end; background: rgba(var(--accent-rgb), .14); }
.chat-ai { border-left: 3px solid var(--accent); }

.chat-who {
  display: block;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--accent);
}

.chat-to { margin-left: .4rem; font-weight: 400; color: var(--text-faint); }
.chat-text { margin: .1rem 0 0; white-space: pre-wrap; }

/* The names offered while an @ is being typed. Above the box rather than below
   it, because the box sits at the bottom of the window and a list under it
   would open off the screen. */
.chat-form { position: relative; }

.mention-list {
  position: absolute;
  bottom: calc(100% + .3rem);
  left: 0;
  z-index: 20;
  min-width: 12rem;
  max-height: 14rem;
  overflow-y: auto;
  margin: 0;
  padding: .25rem;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
}

.mention-item {
  padding: .3rem .5rem;
  border-radius: var(--r-sm);
  font-size: var(--step--1);
  cursor: pointer;
}

.mention-item:hover,
.mention-item.on { background: var(--surface-2); color: var(--accent); }

.chat-form {
  display: flex;
  gap: .5rem;
  padding: .7rem 1rem 1rem;
  border-top: 1px solid var(--border);
}

.chat-form input { flex: 1; }

/* Three dots where the reply will be, rather than a spinner somewhere else. */
.chat-dots i {
  display: inline-block;
  width: .4rem;
  height: .4rem;
  margin-right: .2rem;
  border-radius: 50%;
  background: var(--accent);
  animation: chat-dot 1.2s infinite ease-in-out;
}

.chat-dots i:nth-child(2) { animation-delay: .2s; }
.chat-dots i:nth-child(3) { animation-delay: .4s; }

@keyframes chat-dot {
  0%, 60%, 100% { opacity: .25; }
  30% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .chat-dots i { animation: none; opacity: .6; }
}

/* --- what the AI is doing, and the manager list ----------------------------- */

.thinking-actions {
  display: flex;
  gap: .6rem;
  align-items: center;
  flex-wrap: wrap;
  margin: .2rem 0 1rem;
  font-size: var(--step--1);
}

.thinking-now {
  padding: .8rem 1rem;
  background: var(--surface-2);
  border-radius: var(--r);
  margin-bottom: 1.4rem;
}

.thinking-line { margin: 0 0 .4rem; color: var(--text-dim); }
.thinking-line:last-child { margin-bottom: 0; }

.thinking-label {
  display: inline-block;
  min-width: 5.5rem;
  color: var(--text-faint);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.thinking-pass {
  padding: .9rem 0;
  border-top: 1px solid var(--border);
}

.thinking-when { margin: 0 0 .3rem; color: var(--text-faint); font-size: var(--step--1); }
.thinking-why { margin: 0 0 .6rem; color: var(--text); }

/* What the AI has worked out about the discussion. Set apart from the chips,
   because it is prose that goes into the instructions and gets read every time
   a question is written, not a value that changed. */
.thinking-learned-panel {
  border: 1px solid var(--accent-muted);
  border-radius: var(--r);
  padding: .9rem 1rem;
  margin-bottom: 1.2rem;
  background: color-mix(in srgb, var(--accent-muted) 8%, var(--surface));
}

.thinking-learned { margin: .2rem 0 .6rem; }

.thinking-learned-text {
  margin: .2rem 0 .5rem;
  white-space: pre-wrap;
  color: var(--text);
}

.thinking-source {
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  padding: .05rem .5rem;
  font-size: var(--step--1);
  color: var(--text-dim);
}

.thinking-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
  align-items: baseline;
  margin-bottom: .35rem;
}

.thinking-decision, .thinking-observation {
  font-size: var(--step--1);
  padding: .1rem .45rem;
  border-radius: var(--r-sm);
}

.thinking-decision {
  background: rgba(var(--accent-rgb), .16);
  color: var(--text);
}

.thinking-observation { background: var(--surface-2); color: var(--text-faint); }

.manage-card {
  padding: .9rem 0;
  border-top: 1px solid var(--border);
}

.manage-card-head { display: flex; gap: .6rem; align-items: baseline; }
.manage-card-meta { color: var(--text-faint); font-size: var(--step--1); margin: .2rem 0 .5rem; }
.manage-card-actions { display: flex; flex-wrap: wrap; gap: .4rem; }

/* Pressing a row opens it. The cursor says so, because a row that does
   something and looks inert is a thing nobody presses. */
.manage-card-openable { cursor: pointer; }
.manage-card-openable:hover { background: var(--surface-2); }
.manage-card-openable.open { background: var(--surface-2); }
.manage-card-detail {
  margin: .7rem 0 .2rem;
  padding: .7rem .9rem;
  border-left: 2px solid var(--accent);
  background: var(--surface);
  cursor: auto;
}
.manage-card-facts {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .25rem .9rem;
  margin: 0;
  font-size: var(--step--1);
}
.manage-card-facts dt { color: var(--text-faint); }
.manage-card-facts dd { margin: 0; }
.manage-card-description {
  margin: .6rem 0 0;
  color: var(--text-dim);
  font-size: var(--step--1);
}

.tag {
  font-size: var(--step--1);
  padding: .05rem .4rem;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-faint);
}

/* A demonstration, in a list beside real discussions. Given the accent rather
   than another shade of grey, because that is the whole job: three identical
   chips in a row is the same as no chip at all. Outlined rather than filled, so
   it marks without shouting over the title beside it. */
   `--accent` already swaps per theme, deep mint on white and neon on the dark
   ground, so it is readable both ways and the neon never lands on white.
   `--accent-muted` is the token for an outline rather than a pop. */
.tag-demo {
  background: transparent;
  border: 1px solid var(--accent-muted);
  color: var(--accent);
  font-weight: 600;
}

.tag-demo .bi { margin-right: .25rem; }

.link-btn {
  background: none;
  border: 0;
  padding: 0;
  margin-left: .5rem;
  color: var(--accent);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* --- the overview canvas ---------------------------------------------------- */

/* The reading column is right for questions and wrong for a network, so the
   overview widens the shell. It widens to the manager screens' column rather
   than to the whole window: with no limit at all, the bars ran the full width
   of a large monitor and the brand, the project and the tabs ended up alone on
   the far left, a screen's width from anything they act on. One centred column
   for the bars and the map keeps them together. */
/* Nothing to do any more: the shell is one width and the panels decide their
   own. Kept as a hook for anything that genuinely needs the wide layout. */

.net-host {
  position: relative;
  width: 100%;
  /* Room for the key below, which belongs on screen with the map: a legend you
     have to scroll to is a legend nobody reads, and the page scrolling at all
     on this tab is wrong. The count that used to sit above it has gone, so the
     map takes the line back. */
  height: calc(100dvh - var(--chrome-height, 6rem) - 5rem);
  overflow: hidden;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  cursor: grab;
  touch-action: none;      /* pinch and drag are ours, not the browser's */
  /* Or a double-click to zoom selects the word under it and leaves it
     highlighted on the map. There is nothing here anybody wants to copy. */
  user-select: none;
  -webkit-user-select: none;
}

.net-host.grabbing { cursor: grabbing; }

.net { width: 100%; height: 100%; display: block; }

/* Replaying the discussion. The map is the one screen that flattens time, so
   this puts it back: a cursor over the discussion's own clock, showing only
   what had been written by then. Floated over the picture for the same reason
   as the note below, and centred because it is a transport control and that is
   where people reach for one. */
/* Replay, in the key row beneath the map beside the synthesis navigation.
   It floated over the picture, which is the one thing a control over a map must
   not do: it covered the bottom of the thing it is for. Both of these move you
   through the discussion, so they belong together and out of the way. */
.net-replay {
  display: inline-flex;
  align-items: center;
  gap: .15rem;
}

.net-replay-btn {
  border: 0;
  background: none;
  color: var(--text-dim);
  cursor: pointer;
  padding: 0.1rem 0.25rem;
  border-radius: var(--r-sm);
  /* One glyph per button, and never allowed to break: the first two were a bar
     and a triangle side by side, which the browser wrapped onto two lines. */
  font-size: 0.8rem;
  line-height: 1.4;
  white-space: nowrap;
}

.net-replay-btn:hover { color: var(--accent); background: var(--surface-2); }

.net-replay-scrub {
  width: min(22vw, 9rem);
  accent-color: var(--accent-muted);
  height: 0.75rem;
}

/* Editor dials: the settings a tab governs, in the tab row beside it.
   Steve, 9 August 2026, twice. Tiny icons, because a select carrying its value
   as words made the controls wider than the tabs. And clearly different from
   everything around them, because these are the only controls on the screen
   that change the discussion for everybody rather than moving this reader
   around it. A participant never sees one; an editor should never mistake one
   for a tab.

   The difference is a dashed outline and the muted accent. Dashed reads as
   "not part of the row", and nothing else in the app uses it. */
/* The dial belongs to the tab on its left, and has to look it. The row spaces
   tabs evenly, so an icon sitting midway between two of them belongs to
   neither: it read as its own item in the row rather than as part of "Report".
   Steve, 9 August 2026. So it pulls back against its own tab, the padding on
   that side of the tab goes, and the gap before the next tab is what separates
   the pairs. */
.tab-dial-holder {
  display: inline-flex;
  align-items: center;
  margin-left: -.55rem;
  margin-right: .55rem;
}

/* And it takes the state of the tab it belongs to, so the pair lights together
   rather than the label changing colour beside a mark that does not. */
.tab.on + .tab-dial-holder .editor-dial-btn {
  color: var(--accent);
  border-color: var(--accent);
}

.editor-dial { position: relative; display: inline-flex; }

.editor-dial-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  padding: 0;
  border: 1px dashed var(--accent-muted);
  border-radius: 50%;
  background: transparent;
  color: var(--accent-muted);
  font-size: .68rem;
  line-height: 1;
  cursor: pointer;
}

.editor-dial-btn:hover { color: var(--accent); border-color: var(--accent); }

/* The feature this dial governs is switched off, said on the icon so a glance
   along the row shows which are on. */
.editor-dial.is-off .editor-dial-btn { opacity: .45; text-decoration: line-through; }
.editor-dial.saving .editor-dial-btn { opacity: .4; }
.editor-dial.failed .editor-dial-btn { border-color: var(--bad); color: var(--bad); }

.editor-dial-menu {
  /* Fixed, not absolute. The tab row scrolls sideways and a scrolling element
     clips whatever hangs out of it, so an absolutely positioned menu was cut
     off inside the bar. `place()` in tabsetting.js puts it where the icon is. */
  position: fixed;
  z-index: 40;
  min-width: 12rem;
  padding: .3rem;
  border: 1px dashed var(--accent-muted);
  border-radius: var(--r-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
  text-align: left;
}

/* What this group of choices decides. It has to win against the choices under
   it, and it was losing: same weight, nearly the same size, and the faintest
   colour in the palette, so a menu of seven read as one list of twenty-two
   things. Steve, 9 August 2026. So the heading gets the weight and the choices
   get smaller, which separates them by contrast rather than by adding rules and
   padding to a menu that has to fit on a laptop. */
.editor-dial-heading {
  margin: 0 0 .3rem;
  padding: 0 .35rem;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-dim);
}

.editor-dial-menu button {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  background: none;
  color: var(--text);
  padding: .18rem .35rem;
  border-radius: var(--r-sm);
  font-size: .7rem;
  line-height: 1.35;
  cursor: pointer;
}

/* The trigger while its menu is open, so the pair reads as one thing. */
.editor-dial.open .editor-dial-btn {
  color: var(--accent);
  border-style: solid;
  border-color: var(--accent);
  background: var(--surface);
}

/* What this menu is. The tab's own label plus "settings", so it can never
   disagree with the tab it hangs from. */
.editor-dial-title {
  margin: 0 0 .45rem;
  padding: 0 .35rem .35rem;
  border-bottom: 1px solid var(--border);
  font-size: .72rem;
  font-weight: 700;
  color: var(--text);
}

.editor-dial-menu.is-wide .editor-dial-title { grid-column: 1 / -1; }

/* A radio button per choice, which is what these are: one from a short list.
   The whole line is the target, so it works with a thumb. */
.editor-dial-option {
  display: flex;
  align-items: flex-start;
  gap: .45rem;
  padding: .2rem .35rem;
  border-radius: var(--r-sm);
  font-size: .7rem;
  line-height: 1.35;
  cursor: pointer;
}

.editor-dial-option:hover { background: var(--surface-2); }
/* The dot takes the app's accent rather than the browser's default blue or
   whatever the platform theme thinks. */
.editor-dial-option input { margin: .18rem 0 0; flex: 0 0 auto; accent-color: var(--accent); }
.editor-dial-option input:checked + span { color: var(--accent); font-weight: 600; }

/* The join to the icon that opened it: a notch under the icon's middle, placed
   by `place()` in tabsetting.js because the menu is positioned against the
   window and the icon is not. */
.editor-dial-menu::before {
  content: '';
  position: absolute;
  top: -6px;
  left: calc(var(--notch-x, 1rem) - 6px);
  width: 11px;
  height: 11px;
  transform: rotate(45deg);
  /* Solid, because a dashed 11-pixel diamond reads as nothing at all. */
  border-left: 1px solid var(--accent-muted);
  border-top: 1px solid var(--accent-muted);
  background: var(--surface);
}

/* Two decisions abreast once there are more than three of them, so a menu of
   six does not run off the bottom of a laptop screen. One column is still the
   default: two narrow columns of two options each read worse than one. */
.editor-dial-menu.is-wide {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .1rem .5rem;
  min-width: 24rem;
}

.editor-dial-section + .editor-dial-section { margin-top: .45rem; }
.editor-dial-menu.is-wide .editor-dial-section { margin-top: 0; }

.net-replay-rate {
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  border-radius: var(--r-sm);
  font-size: 0.68rem;
  padding: 0.05rem 0.1rem;
  margin-left: 0.25rem;
}

.net-replay-clock {
  color: var(--text-dim);
  font-size: 0.7rem;
  min-width: 4.5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Not written yet, at the point the cursor is sitting on. Kept in the layout
   rather than removed, so the picture does not jump about as it fills in and
   somebody can see the space a later answer is going to occupy. */
.net .not-yet { opacity: 0; pointer-events: none; }
.net .node, .net .wire { transition: opacity .18s ease; }

/* Floated over the map rather than given a row of its own, because the host has
   a fixed height and a note above the picture would push the picture off the
   bottom. Only drawn when something has actually been removed. */
.net-note {
  position: absolute;
  top: 0.5rem;
  left: 0.75rem;
  z-index: 2;
  margin: 0;
  padding: 0.25rem 0.6rem;
  border-radius: 0.4rem;
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: 0.8rem;
  pointer-events: none;
}

/* Wires under everything, and quiet: the shape is carried by where the boxes
   are, and heavy lines at this density read as a scribble. */
.wire {
  fill: none;
  stroke: var(--border-strong);
  stroke-width: 1.2;
}

.wire-synthesises {
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-dasharray: 4 3;
  opacity: .75;
}

/* Told apart by fill as well as by outline, because at a distance an outline is
   all anybody can see and two outlines the same colour are one thing. */
.node rect {
  fill: var(--surface);
  stroke: var(--border-strong);
  stroke-width: 1.5;
}

/* Three things on this map and they should be three things at a glance: what
   was asked, what people said, and what the AI made of it.

   A question is white with a dashed outline. One look for all of them: the map
   used to draw an editor's question, an AI question and an opening question
   differently, and two of those three are not categories the app has. Nothing
   has set an editor's question since starter questions were removed, and an
   opening question is a question that happens to be first.
   An answer is filled, because it is somebody's own words and the substance of
   the map.
   A synthesis is filled in the accent and outlined heavily: it is the AI
   speaking about several people at once, which is the one thing here nobody
   said. */
.node-answer rect {
  fill: var(--accent-muted);
  fill-opacity: .3;
  stroke: var(--accent-muted);
  stroke-width: 1.5;
}

.node-question rect {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-dasharray: 5 3;
}

/* White like an AI question, because a synthesis is the AI writing and is
   answerable, and a solid heavy border rather than the question's dashes,
   because it is a statement rather than an open question. Steve, 19 August
   2026; it was a tinted accent fill, which read as a third kind of thing. */
.node-synthesis rect {
  fill: var(--surface);
  stroke: var(--accent);
  stroke-width: 2.5;
}

.node { cursor: pointer; }

.node.chosen rect {
  stroke: var(--focus);
  stroke-width: 3.5;
  fill: var(--focus);
  fill-opacity: .3;
}

.node.chosen .node-label { fill: var(--text); }

/* The ring around whatever is open in the side panel. Drawn over every node, so
   the choice is findable after a pan rather than only where it was clicked. */
.net-halo {
  fill: none;
  stroke: var(--focus);
  stroke-width: 3;
  pointer-events: none;
}

/* Tracing a line of descent. Everything not related to the node under the
   pointer steps back, so what is left is where that node sits in the
   conversation, which is impossible to follow by eye across seven hundred
   boxes. Stepping back, not disappearing: at .12 the rest of the map went
   blank, and losing the surroundings loses the thing the trace was for. */
.net-host.tracing .node { opacity: .38; }
.net-host.tracing .wire { opacity: .22; }
.net-host.tracing .node.kin { opacity: 1; }
.net-host.tracing .node.chosen { opacity: 1; }

/* The wires between the lit boxes light too. Boxes alone leave the reader to
   work out what connects to what, which is the question the trace answers. */
.net-host.tracing .wire.kin {
  opacity: 1;
  stroke: var(--focus);
  stroke-width: 2;
}

.node.hovered rect {
  stroke: var(--focus);
  stroke-width: 3;
}

.net-tip {
  position: fixed;
  z-index: 40;
  max-width: 24rem;
  padding: .55rem .7rem;
  background: var(--surface);
  border: 1px solid var(--accent-muted);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
  font-size: var(--step--1);
  line-height: 1.4;
  color: var(--text-dim);
  pointer-events: none;
}

.net-tip strong {
  display: block;
  color: var(--accent);
  margin-bottom: .15rem;
}

.net-tip em {
  display: block;
  margin-top: .4rem;
  font-style: normal;
  color: var(--text-faint);
}

.node-label {
  font-size: 10px;
  fill: var(--text-dim);
  pointer-events: none;
}

/* --- reading a node --------------------------------------------------------- */

.net-panel {
  position: absolute;
  top: 4.5rem;
  right: 1rem;
  bottom: 7rem;
  width: min(26rem, calc(100vw - 2rem));
  overflow-y: auto;
  /* Above the screen panel beside it: on a phone that panel takes the whole
     width, and a card a press opened must be what that press produced. */
  z-index: 4;
  padding: 1rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}

.net-panel-close {
  float: right;
  border: 0;
  background: none;
  color: var(--text-faint);
  font-size: var(--step-2);
  line-height: 1;
  cursor: pointer;
}

.net-panel-close:hover { color: var(--text); }

/* Removing a synthesis, at the bottom of the panel and behind a rule: it is the
   one destructive thing on this screen and it should not sit among the links
   somebody is clicking to read their way around. */
.net-panel-actions {
  margin-top: .8rem;
  padding-top: .7rem;
  border-top: 1px solid var(--border);
}

.net-panel-note {
  margin: .4rem 0 0;
  font-size: var(--step--1);
  color: var(--text-faint);
}

.net-panel-kind {
  margin: 0 0 .25rem;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--accent);
}

/* When it was written: quiet, under the kind and above the words, so it is
   there to be read and never competes with them. */
.net-panel-when {
  margin: 0 0 .6rem;
  font-size: var(--step--1);
  color: var(--text-faint);
}

.net-panel-text {
  margin: 0 0 1rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

/* --- the tracks on the map ------------------------------------------------
   One band per person, behind everything, so a row says whose it is. Until
   23 August 2026 a row was a thread and meant nothing you could name. */
.net-track-strip {
  fill: var(--surface-2);
  fill-opacity: .45;
  stroke: var(--border);
  stroke-width: 1;
}
.net-track:hover .net-track-strip { fill-opacity: .8; stroke: var(--border-strong); }
.net-track.chosen .net-track-strip {
  fill-opacity: .9;
  stroke: var(--accent);
  stroke-width: 1.6;
}
.net-track-name {
  fill: var(--text-dim);
  font-family: var(--q-font);
  font-size: 15px;
  font-weight: 600;
  text-anchor: start;
  pointer-events: none;
}
.net-track.chosen .net-track-name { fill: var(--accent); }

/* --- one person's screen, beside the map ----------------------------------
   Steve, 24 August 2026: the point is the experience, which is the other
   questions standing open beside the one being answered and the syntheses
   arriving among them. So this is the Questions tab, in a column of its own,
   drawn by the Questions tab's own renderer. It takes width from the map
   rather than sitting on top of it, because a screen shown over the thing it
   is a screen of is two pictures fighting for one space. */
.net-screen {
  position: absolute;
  top: 0;
  right: 0;
  width: var(--screen-width, 25rem);
  height: calc(100dvh - var(--chrome-height, 6rem) - 5rem);
  z-index: 3;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-left: 1px solid var(--border-strong);
}

/* The map gives up the width rather than being covered by it. */
#panel-overview.with-screen .net-host {
  width: calc(100% - var(--screen-width, 25rem));
}
#panel-overview.with-screen .net-controls {
  right: calc(var(--screen-width, 25rem) + 1rem);
}

/* Reading one node is still a card over the map, and it moves along with
   everything else rather than opening underneath the screen. Above it in any
   case, because on a phone the screen has the whole width and a card that a
   press opened must be the thing that press produced. */
#panel-overview.with-screen .net-panel {
  right: calc(var(--screen-width, 25rem) + 1rem);
}

/* On a phone there is no width to divide, so the screen takes the whole of it.
   Which is right anyway: this is what a phone showed one person, at the size a
   phone showed it. */
@media (max-width: 46rem) {
  .net-screen { width: 100%; border-left: 0; }
  #panel-overview.with-screen .net-host { width: 100%; }
  #panel-overview.with-screen .net-controls { right: 1rem; }
  #panel-overview.with-screen .net-panel { right: 1rem; }
}

.net-screen-bar {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  padding: .6rem .9rem .4rem;
  border-bottom: 1px solid var(--border);
}

.net-screen-who {
  margin: 0;
  flex: 1;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--accent);
}

/* The screen itself scrolls; the name above it and the caveat below it do not,
   so neither can be scrolled out of sight while the replay runs. */
.net-screen-page {
  flex: 1;
  overflow-y: auto;
  padding: .8rem .9rem;
}

.net-screen-lead {
  margin: 0 0 .6rem;
  color: var(--text-dim);
}

.net-screen-empty {
  margin: .4rem 0 0;
  color: var(--text-faint);
}

/* Their own words, as their message, for the one step where they sent them. */
.net-screen-answer { margin-bottom: .9rem; }
.net-screen-sent {
  margin: 0 0 .2rem;
  font-size: .72rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-faint);
  text-align: right;
}
.net-screen-said {
  margin-left: auto;
  max-width: 86%;
  width: fit-content;
  background: var(--accent);
  color: var(--surface);
  border-radius: 12px 12px 3px 12px;
  padding: .5rem .7rem;
  line-height: 1.45;
  white-space: pre-wrap;
}

.net-screen-caveat {
  margin: 0;
  padding: .5rem .9rem .6rem;
  border-top: 1px solid var(--border);
  font-size: .76rem;
  color: var(--text-faint);
}

/* A row in here is read, not answered: there is no drawer to open and no box to
   type in, so nothing should invite a press. */
.net-screen .q-row { cursor: default; }
.net-screen .q-cue { display: none; }
.net-screen .fold-line { cursor: default; }

.net-panel-heading {
  margin: 1.2rem 0 .4rem;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-faint);
}

.net-panel-link {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--border);
  background: var(--surface-2);
  border-radius: var(--r-sm);
  padding: .5rem .6rem;
  margin-bottom: .4rem;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-dim);
  line-height: 1.4;
  cursor: pointer;
}

.net-panel-link:hover { border-color: var(--accent); color: var(--text); }

.net-panel-who {
  display: block;
  font-weight: 700;
  color: var(--accent);
  margin-bottom: .15rem;
}

.net-jump {
  border: 1px solid var(--accent);
  background: none;
  color: var(--accent);
  border-radius: var(--r-pill);
  padding: .1rem .7rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  cursor: pointer;
}

.net-jump:hover { background: rgba(var(--accent-rgb), .12); }

.net-where {
  font-size: var(--step--1);
  color: var(--text-faint);
  white-space: nowrap;
}

.node-who { font-weight: 700; fill: var(--accent); }

/* Zoomed out far enough that labels are a grey smear rather than words, so
   they come off and the shape is left to speak. */
.net.far .node-label { display: none; }

/* Inside the canvas rather than over the key beneath it, which is where they
   landed when the section was not a positioning context. */
#view-overview { position: relative; }

.net-controls {
  position: absolute;
  right: 1rem;
  bottom: 4rem;
  display: flex;
  gap: .3rem;
  z-index: 2;
}

/* Top left, where reading starts, and over the canvas rather than above it, so
   the map keeps its full height. */
.net-search {
  position: absolute;
  left: 1rem;
  top: 2.6rem;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: .3rem;
}

.net-search input {
  width: 14rem;
  max-width: 40vw;
  font-size: var(--step--1);
  padding: .25rem .5rem;
}

.net-search-count {
  font-size: var(--step--1);
  color: var(--text-dim);
  white-space: nowrap;
}

/* What was found, and what was not. The misses fade rather than disappear, so
   the shape of the discussion is still there to place the matches in. */
.node.found rect {
  stroke: var(--focus);
  stroke-width: 3;
}

.node.dimmed { opacity: .2; }

/* "Mine" on: somebody's own nodes at full strength and outlined, the rest
   faded but still there, so the shape of the discussion is still the thing on
   screen and their thread through it is legible against it. Fainter than the
   search dimming, which is looking for one node rather than a path. */
.mine-on .node:not(.is-mine) { opacity: .28; }
.mine-on .node.is-mine rect { stroke: var(--focus); stroke-width: 2.5; }

.key-mine {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  cursor: pointer;
}

.net-btn {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text-dim);
  border-radius: var(--r-sm);
  min-width: 2rem;
  padding: .3rem .5rem;
  font: inherit;
  font-size: var(--step-0);
  cursor: pointer;
}

.net-btn:hover { color: var(--accent); border-color: var(--accent); }

.net-key {
  display: flex;
  align-items: center;
  gap: .9rem;
  flex-wrap: wrap;
  margin: 0;
  padding: .5rem 1rem;
  font-size: var(--step--1);
  color: var(--text-faint);
}

/* The base first, then the three that differ from it. It was the other way
   round, so this rule overrode both of them and the key drew three identical
   white squares against three labels that promised three different things.
   Steve, 10 August 2026, asking what the first three were. */
.key::before {
  content: "";
  display: inline-block;
  width: .7rem;
  height: .7rem;
  border-radius: 3px;
  margin-right: .35rem;
  vertical-align: -1px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
}

.key-question::before {
  border: 1.5px dashed var(--accent);
}

/* The swatches have to be the colours actually on the canvas, or the key is
   telling the reader something untrue. */
/* The star is the mark itself rather than a swatch, so the key shows the thing
   that is on the map. */
.key-knew::before { content: none; }
.key-knew { color: var(--accent); }

.node-knew {
  font-size: 11px;
  fill: var(--accent);
}

.key-answer::before {
  background: color-mix(in srgb, var(--accent-muted) 22%, var(--surface));
  border-color: var(--accent-muted);
}

.key-synthesis::before {
  background: var(--surface);
  border-color: var(--accent);
  border-width: 2px;
}

/* Left of the controls, not under them. */
.net-hint { margin-left: 1rem; }

/* --- placeholder text -------------------------------------------------------

   Every placeholder, everywhere, in one rule. Italic, fainter than the text
   around it and never larger, so nothing anybody typed can be mistaken for a
   suggestion and nothing suggested can be mistaken for what they typed.
   Steve, 8 August 2026. The size is inherited rather than set, because a
   placeholder larger than its own field is the fault this exists to stop. */
::placeholder {
  font-style: italic;
  color: var(--text-faint);
  opacity: 1;                /* Firefox dims placeholders again on top of the colour */
  font-size: inherit;
}

/* --- placeholders ---------------------------------------------------------- */

/* Deliberately plain and deliberately labelled. A stub dressed up as a feature
   wastes somebody's time before they discover it does nothing. */
.stub {
  border: 1px dashed var(--border-strong);
  border-radius: var(--r);
  padding: 1.1rem;
  color: var(--text-dim);
  line-height: 1.5;
}

.stub p { margin: 0 0 .8rem; }
.stub p:last-child { margin-bottom: 0; }

.stub-title {
  font-size: var(--step-1);
  font-weight: 700;
  color: var(--text);
}

.stub-note {
  font-size: var(--step--1);
  color: var(--text-faint);
}

.busy {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(var(--accent-rgb), .05);
  z-index: 30;
}

/* --- join ------------------------------------------------------------------ */

.field-label {
  display: block;
  font-weight: 600;
  margin-bottom: .35rem;
}

.join-consent {
  margin: .6rem 0 0;
  font-size: var(--step--1);
  color: var(--text-dim);
  text-align: center;
}

.consent summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--text-dim);
}

.consent-list {
  margin: .75rem 0 0;
  padding-left: 1.1rem;
  color: var(--text-dim);
  font-size: var(--step--1);
}

.consent-list li { margin-bottom: .5rem; }

/* --- the question list ----------------------------------------------------- */

.lead-line {
  font-size: var(--step-1);
  font-weight: 600;
  margin: 0 0 .75rem;
}

/* The button under the sentence that explains it. Room above it, because a
   primary action pressed against the prose reads as part of the paragraph. */
.lead-action { margin-top: 1.1rem; }

.limit {
  display: flex;
  align-items: center;
  gap: .3rem;
  margin-bottom: .8rem;
}

.limit-btn {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: .1rem .6rem;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-dim);
  cursor: pointer;
}

.limit-btn:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}

/* At the end of the ladder: still there, so the reader can see the setting
   exists and that they are already at one end of it. */
.limit-btn:disabled {
  opacity: .4;
  cursor: default;
}

.question-list {
  display: flex;
  flex-direction: column;
  gap: .6rem;
}

.q-row {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  transition: border-color .12s ease, box-shadow .12s ease,
              transform .12s ease, background-color .12s ease;
}

/* The pressable part. The card itself is no longer a button, because it now
   contains a form and a button cannot hold one. */
.q-head {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  border-radius: var(--r);
  padding: .8rem .9rem;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* Whose thread this question belongs to. Yours carries a solid edge in the
   accent; anything that grew out of somebody else's answer carries a faint one,
   so the two read apart at a glance rather than only where the separator falls.
   An edge rather than a background, because the row already uses its background
   for open, new and compulsory. */
/* Yours against everybody else's. The first attempt was a three-pixel edge in
   the muted accent against one in the border colour, which is a difference you
   have to be told about to see, and Steve could not. A question in your own
   thread now carries the full accent down its edge and a tint of it behind,
   and anything that grew from somebody else's answer carries neither. */
.q-row.q-mine {
  border-left: 4px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}

.q-row.q-theirs { border-left: 4px solid transparent; }

/* A synthesis belongs to nobody in particular, so it keeps its own edge. */
/* A synthesis belongs to nobody in particular, so it keeps its own edge and
   takes no tint from whose thread it landed in. */
.q-row.q-synthesis.q-mine, .q-row.q-synthesis.q-theirs {
  border-left-color: var(--accent-muted);
  background: var(--surface-2);
}

.q-row.open {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

/* The drawer, where the answer is written. Opening in place keeps the rest of
   the list in view, which is the difference between a discussion and a form. */
.q-drawer {
  padding: 0 .9rem .9rem;
  border-top: 1px dashed var(--border);
  margin-top: -.2rem;
  padding-top: .8rem;
}

.q-form .answer-box { font-size: var(--step-0); }

.q-others { margin-top: 1.2rem; }

.q-others .bubble-other { font-size: var(--step--1); }

/* The card has to look like it does something. Four small changes together
   read as one: it lifts, the border takes the accent, the shadow deepens and
   the cue appears. Any one of them on its own is easy to miss. */
.q-row:hover, .q-row.open {
  border-color: var(--accent);
  background: var(--surface);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}

.q-head:focus-visible {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 .2rem rgba(var(--accent-rgb), .25);
}

.q-head:active {
  transform: translateY(0);
  box-shadow: var(--shadow-sm);
}

.q-text {
  display: block;
  font-size: var(--step-1);
  line-height: 1.35;
}

.q-footer {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  margin-top: .5rem;
  min-height: 1.1rem;
}

.q-meta {
  font-size: var(--step--1);
  color: var(--text-faint);
}

.q-cue {
  margin-left: auto;
  flex: none;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--accent);
  opacity: 0;
  transition: opacity .12s ease;
}

.q-row.open .q-cue { display: none; }

.q-cue::after {
  content: "\00a0\2192";
}

.q-row:hover .q-cue,
.q-head:focus-visible .q-cue { opacity: 1; }

/* No hover on a phone, so the cue would never appear. Show it always, quieter,
   since on touch the whole card being tappable is the more obvious reading. */
@media (hover: none) {
  .q-cue { opacity: .75; }
}

/* A synthesis has no spine, so it cannot have a breadcrumb. This is what it has
   instead: the answers it was written from, one press away, in the same place
   and the same shape as the crumb on a question. */
.drawn-from { display: block; margin-top: .25rem; }

.drawn-toggle {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-faint);
  text-decoration: underline dotted;
  cursor: pointer;
}

.drawn-toggle:hover { color: var(--accent); }

.drawn-detail {
  display: block;
  margin-top: .35rem;
  padding-left: .7rem;
  border-left: 2px solid var(--border);
  font-size: var(--step--1);
  color: var(--text-dim);
}

.drawn-step { display: block; margin: .2rem 0; }

/* The breadcrumb.

   Collapsed it is one full-width line naming where the question came from. The
   full trail on a deep question is longer than the question itself, so it opens
   on hover, and on tap for touch screens where there is no hover. The collapsed
   line is truncated by CSS, so only one copy of the text is ever stored or
   sent. */

.crumb {
  display: block;
  position: relative;
  margin-bottom: .4rem;
}

.crumb-line {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  width: 100%;
  font-size: var(--step--1);
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-bottom: 1px dashed var(--border);
  padding-bottom: .3rem;
}

/* The whole of the trail's trigger. Small, and the only thing that opens the
   panel: hovering the card opened it before, so a pointer crossing the list
   left a trail of panels behind it. */
.crumb-icon {
  flex: none;
  cursor: help;
  color: var(--accent-muted);
  padding: 0 .15rem;
}

.crumb-icon:hover { color: var(--accent); }

.crumb-summary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Pushed to the right so the trail length is readable at a glance without
   competing with the text for the start of the line. */
.crumb-depth {
  margin-left: auto;
  flex: none;
  font-size: var(--step--1);
  color: var(--text-faint);
  opacity: .75;
}

/* The trail floats, rather than expanding in the flow, because growing the card
   would make every card underneath jump as the mouse travelled down the list.

   It opens upwards. The trail is what came before, so it belongs above, and
   more to the point opening downwards covered the question itself: the one
   thing on the card that has to stay readable and obviously clickable. */
.crumb-detail {
  display: none;
  position: absolute;
  bottom: calc(100% + .3rem);
  /* Pulled out by its own padding, so the text inside starts exactly where the
     question below it starts. Aligned to the card rather than merely near it. */
  left: -.7rem;
  right: -.7rem;
  z-index: 4;
  padding: .55rem .7rem;
  background: var(--surface);
  border: 1px solid var(--accent-muted);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow);
}

/* Hovering the icon, or having tapped it. Nothing else opens the trail. */
.crumb:has(.crumb-icon:hover) .crumb-detail,
.crumb-open .crumb-detail { display: block; }

/* The summary hides while the trail is open, because the panel already shows
   that same last step and reading it twice is confusing. Hidden rather than
   removed, so the card does not resize under the pointer. The icon itself stays
   visible: hiding it would take the hover away from under the mouse and the
   panel would flicker open and shut. */
.crumb:has(.crumb-icon:hover) .crumb-summary,
.crumb:has(.crumb-icon:hover) .crumb-depth,
.crumb-open .crumb-summary,
.crumb-open .crumb-depth { visibility: hidden; }

/* The hovered card and its floating trail have to sit above their neighbours,
   and the lift transform makes a stacking context, so the card is raised too.
   Above the sticky topbar as well, at 5, or the trail on the first card in the
   list would open upwards and disappear behind it. */
.q-row { position: relative; }
.q-row:hover { z-index: 6; }

/* Who in one column and what they said in another, so a wrapped line lines up
   under the text rather than under the name. */
.crumb-step {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .4rem;
  font-size: var(--step--1);
  line-height: 1.45;
  padding: .18rem 0;
  color: var(--text-dim);
}

/* A step with no name still starts at the left edge. */
.crumb-step:not(:has(.crumb-who)) { grid-template-columns: 1fr; }

/* Questions are the rungs of the chain and answers are what was said, so the
   two are told apart by weight rather than by another colour. */
.crumb-step-question,
.crumb-step-synthesis {
  color: var(--text-faint);
  font-style: italic;
}

.crumb-step-answer { color: var(--text-dim); }

.crumb-who {
  font-weight: 600;
  font-style: normal;
  color: var(--accent);
  margin-right: .4rem;
}

/* Hover is not available on a phone, so the whole trail would never open. Show
   it open by default there instead, capped so a deep chain cannot push the
   question itself off the screen. */
@media (hover: none) {
  .crumb-line { display: none; }
  .crumb-detail {
    display: block;
    position: static;
    max-height: 6.5rem;
    overflow-y: auto;
    box-shadow: none;
    border: 0;
    border-left: 2px solid var(--accent-muted);
    border-radius: 0;
    padding: 0 0 0 .7rem;
    background: none;
  }
}

.q-compulsory {
  border-color: var(--accent);
  border-width: 2px;
}

.q-synthesis {
  background: var(--surface-2);
  border-left: 3px solid var(--accent-muted);
}

/* Above the statement, so the change of voice is read before the words are.
   Small caps rather than a badge: this is a heading on the card, not a status. */
.q-to-everyone {
  display: block;
  font-size: var(--step--1);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent-muted);
  margin-bottom: .35rem;
}

/* --- panes -------------------------------------------------------------
   Three scopes, three headed columns down the screen, replacing one ranked
   list whose every row carried a badge saying which scope it was. A heading
   says it once for the whole pane. */
.pane {
  margin: 0 0 1.5rem;
  padding-top: .2rem;
}

.pane-title {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin: 0 0 .55rem;
  padding-bottom: .3rem;
  border-bottom: 1px solid var(--border);
}

/* The pane whose questions grew from this person's own answers, and the one
   holding a group's, each keep the colour their badge used to carry. */
.pane-you > .pane-title { color: var(--accent-muted); border-bottom-color: var(--accent-muted); }
.pane-group > .pane-title { color: var(--wave-2); border-bottom-color: var(--wave-2); }

/* Held back while a focus stands elsewhere. Dimmed and said in words, because
   a pane that has gone quiet and one the app is holding look identical, and
   only one of them is a thing happening. */
.pane-collapsed { opacity: .45; }

.pane-held {
  font-size: var(--step--1);
  color: var(--text-faint);
  margin: .2rem 0 0;
}

/* The pane's own record: a line you open, not a block you scroll past. An
   inner scrolling box is unpleasant on a touch screen and unreachable from a
   keyboard, and these are short enough to open in place. */
.pane-record {
  margin-bottom: .6rem;
}

.record-line {
  display: inline-block;
  background: none;
  border: 0;
  padding: .15rem 0;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-faint);
  cursor: pointer;
  text-decoration: underline dotted;
}

.record-line:hover { color: var(--text-dim); }

.pane-record .a-row {
  box-shadow: none;
  background: var(--surface-2);
  opacity: .9;
}

.pane-record .a-row:hover { box-shadow: var(--shadow-sm); opacity: 1; }

/* The fold: one honest line saying how many wait below, in place of the old
   per-reader cap that hid them. */
.fold-line {
  display: block;
  width: 100%;
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-dim);
  font-size: var(--step--1);
  padding: .45rem .75rem;
  margin-top: .5rem;
  cursor: pointer;
}

.fold-line:hover { color: var(--text); border-color: var(--accent-muted); }

.q-new {
  border-color: var(--accent);
}

/* A new question arriving among thirty others is easy to miss, so for a couple
   of seconds everything else steps back and only the new ones are lit. The
   effect ends on its own: a highlight that stayed would read as a permanent
   badge rather than as something that has just happened. */
.question-list.spotlight .q-row:not(.q-new) {
  opacity: .25;
  filter: saturate(.4);
}

.question-list.spotlight .q-new {
  border-width: 2px;
  box-shadow: 0 0 0 .28rem rgba(var(--accent-rgb), .22), var(--shadow);
}

.question-list .q-row {
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease,
              background-color .12s ease, opacity .5s ease, filter .5s ease;
}

/* Dimming and undimming is the whole effect, so with motion turned down keep
   the contrast and drop the movement rather than removing the cue. */
@media (prefers-reduced-motion: reduce) {
  .question-list .q-row { transition: opacity .01s, filter .01s; }
}

/* A real break, not a label. What follows is a different kind of thing, so the
   list stops, says what the rest is and that none of it is expected, and starts
   again on a recessed ground. */
.list-separator {
  margin: 2rem -1rem .9rem;
  padding: .9rem 1rem .8rem;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.list-separator-heading {
  margin: 0 0 .25rem;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: .06em;
}

.list-separator-note {
  margin: 0;
  font-size: var(--step-0);
  color: var(--text-dim);
  line-height: 1.4;
}

/* Everything after the break is visibly a lower priority than the questions
   above it. */
.list-separator ~ .q-row {
  background: transparent;
  border-style: dashed;
  box-shadow: none;
}

/* --- the record of your own answers ---------------------------------------- */

/* A section heading in the list of questions. Quiet: it names where you are
   rather than competing with the questions under it. */
.q-section {
  margin: 1.2rem 0 .5rem;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.q-section:first-child { margin-top: 0; }

/* --- the record, above the questions --------------------------------------- */

.a-row {
  display: block;
  width: 100%;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: .8rem .9rem;
  margin-bottom: .6rem;
  box-shadow: var(--shadow-sm);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}

.a-row:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
  transform: translateY(-1px);
}

.a-row:hover .q-cue { opacity: 1; }

.a-question {
  display: block;
  font-size: var(--step--1);
  color: var(--text-faint);
  font-style: italic;
  line-height: 1.35;
  margin-bottom: .35rem;
}

.a-text {
  display: block;
  font-size: var(--step-0);
  line-height: 1.4;
}

.edited-flag {
  border: 0;
  background: var(--surface-2);
  border-radius: var(--r-pill);
  padding: .1rem .5rem;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-dim);
  cursor: pointer;
}

.edited-flag:hover { color: var(--text); }

/* Kept out of sight until asked for, because most of the time the current text
   is the one people want, and a permanent double of every edited answer would
   make the list twice as long for no gain. */
.a-previous {
  display: none;
  margin-top: .5rem;
  padding: .5rem .6rem;
  border-left: 2px solid var(--border-strong);
  background: var(--surface-2);
  border-radius: var(--r-sm);
  font-size: var(--step--1);
  color: var(--text-dim);
  line-height: 1.4;
}

.show-previous .a-previous { display: block; }



.edit-note {
  margin: 0 0 .8rem;
  padding: .6rem .7rem;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border-left: 3px solid var(--warn);
  font-size: var(--step--1);
  color: var(--text-dim);
  line-height: 1.45;
}

.previous-versions { margin-top: 1.5rem; }

.bubble-previous {
  background: var(--surface-2);
  border-left: 2px solid var(--border-strong);
  font-size: var(--step-0);
  color: var(--text-dim);
}

/* --- the waiting screen ---------------------------------------------------- */

.waiter {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  min-height: 100dvh;
  padding: 3rem 1.25rem 2rem;
  text-align: center;
}

.waiter-top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .6rem;
}

.waiter-top .spinner-border {
  color: var(--accent);
  width: 2rem;
  height: 2rem;
}

.waiter-title {
  margin: 0;
  font-size: var(--step-2);
  font-weight: 600;
}

.waiter-stats {
  margin: 0;
  font-size: var(--step--1);
  color: var(--text-dim);
}

.waiter-feed {
  width: 100%;
  max-width: 34rem;
  flex: 1;
}

/* Every recent answer is on the track at once, one card per view, so the timer
   and the reader's thumb move the same thing. No gap between cards: the index
   maths in waiting.js is scrollLeft / clientWidth, and a gap would drift it. */
.waiter-carousel {
  animation: waiter-in .35s ease;
}

.waiter-track {
  display: flex;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}

.waiter-track::-webkit-scrollbar { display: none; }

.waiter-card {
  flex: 0 0 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 1rem;
  text-align: left;
  box-shadow: var(--shadow-sm);
  /* Answers run to a few thousand characters, so cap the card and let the long
     ones scroll inside rather than setting the height of the whole track. */
  max-height: 45dvh;
  overflow-y: auto;
}

.waiter-dots {
  display: flex;
  justify-content: center;
  gap: .5rem;
  padding: .9rem 0 .2rem;
}

.waiter-dot {
  width: .5rem;
  height: .5rem;
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--border-strong);
  transition: background .2s ease, transform .2s ease;
}

.waiter-dot.is-on {
  background: var(--accent);
  transform: scale(1.35);
}

/* The dot itself is small, so give the thumb a target around it. */
.waiter-dot::before {
  content: '';
  position: absolute;
  inset: -.6rem;
}

.waiter-dot { position: relative; }

@keyframes waiter-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .waiter-carousel { animation: none; }
  .waiter-dot { transition: none; }
}

.waiter-label {
  margin: 0 0 .5rem;
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--text-faint);
}

.waiter-question {
  margin: 0 0 .5rem;
  font-size: var(--step--1);
  font-style: italic;
  color: var(--text-faint);
  line-height: 1.4;
}

.waiter-answer {
  margin: 0;
  font-size: var(--step-0);
  line-height: 1.45;
  color: var(--text);
}

.waiter-who {
  display: block;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--accent);
  margin-bottom: .2rem;
}

.btn-ghost {
  background: none;
  border: 1px solid var(--border-strong);
  color: var(--text-dim);
  border-radius: var(--r-pill);
  padding: .45rem 1.1rem;
  font: inherit;
  font-size: var(--step--1);
}

.btn-ghost:hover { color: var(--text); border-color: var(--accent-muted); }

.empty-note {
  color: var(--text-dim);
  margin-top: 1rem;
}

/* The end of somebody's part in the discussion. Marked out rather than left as
   another dim line, because it says something different from "nothing just now":
   this is the one that means they are done. */
.closing {
  margin-top: 1.5rem;
  padding: 1.25rem;
  border: 1px solid var(--accent-muted);
  border-radius: var(--r);
  background: var(--surface);
}

.closing p { margin: 0 0 1rem; }
.closing a:last-child { margin-bottom: 0; }

/* --- the focused question -------------------------------------------------- */

.breadcrumb-trail {
  font-size: var(--step--1);
  color: var(--text-faint);
  margin-bottom: .5rem;
  line-height: 1.4;
}

.bubble {
  border-radius: var(--r);
  padding: .8rem .9rem;
  line-height: 1.4;
}

.bubble-question {
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: var(--step-2);
  margin-bottom: .9rem;
  box-shadow: var(--shadow-sm);
}

.answer-box {
  font-size: var(--step-1);
  border-radius: var(--r);
  border-color: var(--border-strong);
  background: var(--surface);
  color: var(--text);
}

.answer-box:focus {
  border-color: var(--focus);
  box-shadow: 0 0 0 .2rem rgba(var(--accent-rgb), .2);
}

.answer-actions {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-top: .75rem;
}

.hint {
  font-size: var(--step--1);
  color: var(--text-faint);
}

.other-answers { margin-top: 2rem; }

.others-heading {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.bubble-other {
  background: var(--surface-2);
  margin-bottom: .5rem;
  font-size: var(--step-0);
}

.bubble-other .who {
  display: block;
  font-size: var(--step--1);
  color: var(--text-faint);
  margin-bottom: .2rem;
}

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

.btn-accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}

.btn-accent:hover,
.btn-accent:focus {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  filter: brightness(1.08);
}

.btn-accent:disabled { opacity: .6; }

.thinking-heading {
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin: 2rem 0 .6rem;
}

/* Writing on the board. Below the two buttons, because those are one press and
   this is a sentence somebody has to compose. */
.board-write {
  margin-top: .8rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .4rem;
  max-width: 44rem;
}
.board-write textarea { width: 100%; }

/* What the producer has stopped. Marked rather than left as ordinary text,
   because it is the one line on this screen that explains why nothing is
   happening. */
.thinking-held {
  margin: .6rem 0 .2rem;
  padding: .5rem .7rem;
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}

.told-set {
  border-top: 1px solid var(--border);
  padding: .6rem 0;
  font-size: var(--step--1);
}

.told-set summary { cursor: pointer; color: var(--text-dim); font-weight: 600; }
.told-what { margin: .7rem 0 .2rem; color: var(--text-faint); font-weight: 600; }

.told-text {
  margin: 0;
  padding: .6rem .7rem;
  background: var(--surface-2);
  border-radius: var(--r-sm);
  color: var(--text-dim);
  white-space: pre-wrap;
  font: inherit;
  font-size: var(--step--1);
  max-height: 18rem;
  overflow-y: auto;
}

/* Answering by speaking: one button beside Send, the same size as it, so the
   two read as two ways of doing one thing rather than a feature bolted on. */
.record { display: inline-flex; align-items: center; gap: .5rem; }

.record-btn {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: .35rem .9rem;
  font: inherit;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
}

.record-btn:hover { color: var(--accent); border-color: var(--accent); }

.record-btn.recording {
  color: var(--accent-ink);
  background: var(--bad);
  border-color: var(--bad);
  animation: record-pulse 1.4s ease-in-out infinite;
}

@keyframes record-pulse {
  50% { opacity: .72; }
}

@media (prefers-reduced-motion: reduce) {
  .record-btn.recording { animation: none; }
}

/* How long is left, from the moment recording starts rather than appearing
   near the end. A limit sprung on somebody mid-sentence loses the sentence. */
.record-clock {
  font-variant-numeric: tabular-nums;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-dim);
}

.record-clock.warning { color: var(--bad); }

.record-note { font-size: var(--step--1); color: var(--text-faint); }

/* --- a new build arriving -------------------------------------------------- */

.updating {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
}

.updating-box { text-align: center; padding: 2rem; }
.updating-title { margin: 1rem 0 .3rem; font-weight: 600; }
.updating-note { margin: 0; color: var(--text-faint); font-size: var(--step--1); }
.text-accent { color: var(--accent); }

/* The Advisor: a chathead in the corner that fills the setup form in.
   Three states, all of them this one element. Closed is the bubble on its own.
   Docked is a panel above the bubble, which is where somebody adjusting one
   thing wants it. Centred is for the one occasion where it is the whole job in
   front of you, a discussion that has just been created and has nothing in it
   yet, and only then does it take the screen. */
.advisor { position: fixed; inset: 0; pointer-events: none; z-index: 1080; }


.advisor-head {
  position: absolute;
  right: 1.2rem;
  bottom: 1.2rem;
  pointer-events: auto;
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 1.4rem;
  line-height: 1;
  box-shadow: 0 .3rem .9rem rgba(0, 0, 0, .28);
  cursor: pointer;
}

.advisor-head:hover { filter: brightness(1.08); }

/* The wiggle, for the one thing it is worth interrupting somebody about: a
   discussion that cannot be opened, or a reply nobody has the panel open to
   read. It stops the moment the panel is opened. */
.advisor-wiggle { animation: advisorWiggle 1.05s ease-in-out infinite; }

@keyframes advisorWiggle {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(1); }
  20% { transform: translateY(-3px) rotate(-9deg) scale(1.1); }
  50% { transform: translateY(0) rotate(9deg) scale(1.1); }
  80% { transform: translateY(-2px) rotate(-6deg) scale(1.06); }
}

.advisor-backdrop { display: none; }

.advisor-centred .advisor-backdrop {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: auto;
  background: rgba(0, 0, 0, .45);
}

.advisor-panel {
  display: none;
  position: absolute;
  right: 1.2rem;
  bottom: 5.2rem;
  pointer-events: auto;
  width: min(24rem, calc(100vw - 2.4rem));
  max-height: min(32rem, calc(100vh - 8rem));
  flex-direction: column;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface-2);
  box-shadow: 0 .5rem 1.6rem rgba(0, 0, 0, .3);
  overflow: hidden;
}

.advisor-open .advisor-panel { display: flex; }

.advisor-centred .advisor-panel {
  right: auto;
  bottom: auto;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(42rem, calc(100vw - 2.4rem));
  max-height: min(38rem, calc(100vh - 4rem));
  animation: advisorCentreIn .35s ease;
}

@keyframes advisorCentreIn {
  from { opacity: 0; transform: translate(-50%, -50%) scale(.95); }
  to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.advisor-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .5rem .8rem;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

.advisor-name { font-weight: 700; }

.advisor-shrink,
.advisor-resize {
  border: 0;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}

.advisor-shrink:hover,
.advisor-resize:hover { color: var(--text); }

/* The name takes the space, so the two controls sit together on the right. */
.advisor-name { margin-right: auto; }

.advisor-said {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  padding: .8rem;
  overflow-y: auto;
}

.advisor-line {
  margin: 0;
  padding: .4rem .6rem;
  border-radius: var(--r-sm);
  font-size: var(--step--1);
  background: var(--surface);
}

.advisor-you { background: rgba(var(--accent-rgb), .12); }
.advisor-note { color: var(--text-faint); }

/* The link to send people, printed where the Advisor says it is finished. It
   breaks anywhere, because an address in a narrow panel that cannot wrap pushes
   the whole panel wider than the screen. */
.advisor-link {
  display: inline-block;
  margin-top: .3rem;
  color: var(--accent);
  overflow-wrap: anywhere;
}

/* What to press next, in the panel that has just said what it is. */
.advisor-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  background: none;
  padding: 0 .6rem;
}

.advisor-form {
  display: flex;
  flex-direction: column;
  gap: .5rem;
  padding: 0 .8rem;
}

.advisor-hint {
  margin: .4rem .8rem .8rem;
  font-size: var(--step--1);
  color: var(--text-faint);
}

/* Marked where the Advisor has written, and left marked until somebody keeps it
   or throws it away. A mark that fades after four seconds is gone before the
   sentence explaining it has been read. */
.advisor-filled {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Why the discussion cannot be opened, said on the control that opens it. */
.status-blocked {
  margin: .3rem 0 0;
  font-size: var(--step--1);
  color: var(--warn);
}

/* Folded settings: everything real, and only the everyday ones in the way. */
.more-settings {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: .7rem 1rem;
  width: 100%;
}

.more-settings summary {
  cursor: pointer;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.more-settings .setup-dials { margin-top: .8rem; }

/* One folded section per group of settings, stacked. Each closes on its own, so
   somebody adjusting how the AI asks questions is not scrolling past the report
   and the demonstrations to reach it. */
.more-groups {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  width: 100%;
}

/* Open, it is a section with a heading. Shut, it is a line in a list, so a
   column of seven of them reads as a contents page rather than seven boxes. */
.more-groups .more-settings:not([open]) { padding: .45rem 1rem; }

.more-groups .more-settings[open] summary { margin-bottom: .2rem; }

/* What this discussion is, which is the one thing on the join screen somebody
   has not read already. It was the dimmer of the two blocks and the standing
   note about demonstrations below it was the brighter, so the new information
   was faint and the part repeated from the front page was bold. */
.join-description {
  font-size: var(--step-0);
  color: var(--text);
  line-height: 1.5;
  margin-bottom: 1rem;
}

/* And the note is the secondary one: true, worth saying before anybody types,
   and the same words on every demonstration. */
.demo-note {
  font-size: var(--step--1);
  color: var(--text-dim);
  line-height: 1.5;
  margin-bottom: 1rem;
}

.appnav-project {
  flex: none;
  max-width: 14rem;
  margin-right: .8rem;
  padding: .2rem .5rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text-dim);
  font: inherit;
  font-size: var(--step--1);
}

/* --- the manager's screens ------------------------------------------------- */

.manage-who {
  font-size: var(--step--1);
  color: var(--text-faint);
  margin: 0 0 1rem;
  text-align: right;
}

.manage-panel-head { display: flex; justify-content: flex-end; margin-bottom: .6rem; }

.setup-header { margin-bottom: 1.4rem; }
.setup-header h2 { margin: 0 0 .2rem; }

/* The address, the copy button and the counts on one line, wrapping on a phone
   rather than pushing the counts off the side. */
.setup-header-line {
  display: flex;
  align-items: center;
  gap: .35rem;
  flex-wrap: wrap;
}

/* The way into the discussion, an icon and a few words rather than the address
   written out. The gap is for the icon, which sits inside the link. */
.setup-link {
  align-items: center;
  color: var(--accent);
  display: inline-flex;
  gap: .3rem;
  text-decoration: none;
}
.setup-link:hover { text-decoration: underline; }
.setup-command {
  font-size: var(--step--1);
  color: var(--text-faint);
  background: var(--surface-2);
  padding: .2rem .5rem;
  border-radius: var(--r-sm);
}

.setup-form { display: flex; flex-direction: column; gap: 1.4rem; align-items: flex-start; }

.setup-group {
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: .8rem 1rem 1rem;
  width: 100%;
}

.setup-group legend {
  float: none;
  width: auto;
  padding: 0 .4rem;
  font-size: var(--step--1);
  font-weight: 700;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.setup-field { margin-bottom: .9rem; }
.setup-field .field-label { display: block; margin-bottom: .2rem; }
/* A chevron beside every label, and any one of them turns them all. Quiet
   until the pointer is near it: twenty of these have to sit on the form without
   becoming the first thing anybody sees. */
.notes-chevron {
  border: 0;
  background: none;
  padding: 0 .25rem;
  font: inherit;
  font-size: var(--step--1);
  color: var(--text-faint);
  opacity: .55;
  cursor: pointer;
}

.notes-chevron:hover { opacity: 1; color: var(--accent); }

/* `.form-text` is the note under a field, `.dial-what` the one beside a
   setting, and `.group-note` the one under a group heading. */
body.notes-hidden .setup-field .form-text,
body.notes-hidden .group-note,
body.notes-hidden .dial-what { display: none; }

.setup-note { font-size: var(--step--1); margin: 0; }
.group-note { font-size: var(--step--1); margin: 0 0 .6rem; }

/* Save stays on screen, because this form is several screens long and a Save
   button below the fold is how an afternoon's editing gets lost. It sits at the
   bottom rather than the top: it is the last thing done, and the bottom edge is
   where a thumb already is on a phone. */
.save-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: .8rem;
  width: 100%;
  padding: .7rem 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* Something is unsaved. The bar takes the accent rather than showing a warning
   colour: nothing is wrong, there is just something to do. */
.save-bar.unsaved {
  border-top-color: var(--accent);
  box-shadow: 0 -.4rem .8rem -.4rem rgba(var(--accent-rgb), .4);
}

.save-bar.unsaved .setup-note { color: var(--accent); font-weight: 600; }

.setup-starters { display: flex; flex-direction: column; gap: .5rem; align-items: flex-start; }

.starter-row {
  display: flex;
  gap: .5rem;
  align-items: flex-start;
  width: 100%;
  flex-wrap: wrap;
}

.starter-text { flex: 1 1 22rem; }
.starter-section { flex: 0 0 9rem; align-self: flex-start; font-size: var(--step--1); }
.starter-flag { font-size: var(--step--1); color: var(--text-faint); display: flex; gap: .3rem; align-items: center; }
.starter-count { font-size: var(--step--1); }

/* One row per setting: name, control, and what it does. The description sits
   beside the control rather than under it, so the column of names reads as a
   list of what can be changed. */
.setup-dials { display: flex; flex-direction: column; gap: .4rem; }

/* Label, control, what it does, and where its value came from. The last column
   is often empty, which is the point: a value nothing explains and a value a
   dimension decided should not look the same. */
.dial-row {
  display: grid;
  grid-template-columns: 14rem 9rem 1fr auto;
  gap: .6rem;
  align-items: center;
}

@media (max-width: 720px) {
  .dial-row { grid-template-columns: 1fr; }
}

/* Two abreast where there is room for it. Steve, 9 August 2026: none of these
   is a text box, so none of them needs the width of the form, and a column of
   nine is a screen of scrolling to compare two things that belong together.
   Which two sit side by side is declared in `modes.EVERYDAY`, in pairs.

   The row rearranges rather than shrinking: name and control on one line, what
   it does underneath, where the value came from under that. Squeezing the
   four-column row into half the width would put a nine-rem control next to a
   fourteen-rem label and leave nothing for the description. */
@media (min-width: 64rem) {
  .setup-dials {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 2.5rem;
    row-gap: .7rem;
    align-items: start;
  }

  /* One thing per line inside the cell. Name, control, what it does, where the
     value came from. Keeping the name and the control on one line was tried
     first and the two collided: a select showing "Every so often, as the
     discussion goes" cannot share half a form with a label. */
  .dial-row {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'label' 'control' 'what' 'from';
    gap: .15rem;
  }

  .dial-label { grid-area: label; }
  .dial-row > :nth-child(2) { grid-area: control; justify-self: start; min-width: 0; max-width: 100%; }
  .dial-what { grid-area: what; }
  .dial-from { grid-area: from; }
}

.dial-label { font-size: var(--step--1); font-weight: 600; }
.dial-what { font-size: var(--step--1); }
.dial-control { justify-self: start; }

@media (max-width: 48rem) {
  .dial-row { grid-template-columns: 1fr; gap: .1rem; margin-bottom: .8rem; }
}

.template-choices { display: flex; flex-direction: column; gap: .5rem; }

.template-choice {
  display: flex;
  gap: .5rem;
  align-items: baseline;
  padding: .4rem .6rem;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  cursor: pointer;
}

.template-choice:has(input:checked) { border-color: var(--accent); }

.built-in {
  margin-bottom: 1rem;
  font-size: var(--step--1);
}

.built-in summary { cursor: pointer; color: var(--text-dim); font-weight: 600; }

.built-in-text {
  margin: .5rem 0 0;
  padding: .7rem .8rem;
  background: var(--surface-2);
  border-radius: var(--r-sm);
  color: var(--text-faint);
  white-space: pre-wrap;
  font: inherit;
  font-size: var(--step--1);
  max-height: 22rem;
  overflow-y: auto;
}

.rehearse { display: flex; flex-direction: column; gap: .6rem; align-items: flex-start; }
.rehearse-note { font-size: var(--step--1); margin: 0; }
.rehearse-line { display: flex; gap: 1rem; flex-wrap: wrap; }
.rehearse-buttons { display: flex; gap: .4rem; flex-wrap: wrap; }
.rehearse-said { font-size: var(--step--1); margin: 0; }

/* What the crowd is doing, under the buttons. Empty when there is nothing to
   say, so a discussion with no simulated people in it carries no line at all. */
.rehearse-live {
  font-size: var(--step--1);
  margin: 0;
  color: var(--text-dim);
  min-height: 1.4em;
}

/* A dot while anything is running. The count changing every few seconds says it
   is working; the dot says so in the seconds between two counts that are the
   same, which is most of a round. */
.rehearse-live.is-running::before {
  content: '';
  display: inline-block;
  width: .5rem;
  height: .5rem;
  margin-right: .4rem;
  border-radius: 50%;
  background: var(--accent);
  animation: rehearse-pulse 1.2s ease-in-out infinite;
}

@keyframes rehearse-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .25; }
}

@media (prefers-reduced-motion: reduce) {
  .rehearse-live.is-running::before { animation: none; }
}
.rehearse-field { display: flex; gap: .4rem; align-items: center; font-size: var(--step--1); color: var(--text-dim); }
.rehearse-field input { max-width: 5.5rem; }

/* The room, in words: a whole line, because a sentence about who should be in
   it does not fit beside a number. `align-self` is what makes it a whole line:
   the block above sets `align-items: flex-start`, so without it the row shrinks
   to its content and the box inside it has almost nothing to fill. */
.rehearse-field-wide { display: flex; gap: .5rem; align-items: center; align-self: stretch; margin-bottom: .6rem; }
.rehearse-field-wide span { flex: none; }
.rehearse-field-wide input,
.rehearse-field-wide textarea { flex: 1; max-width: 34rem; }

.list-controls {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.list-select-all,
.list-archived-toggle {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: var(--step--1);
  color: var(--text-dim);
  cursor: pointer;
}

/* Pushed to the far end: it changes what the list is, rather than acting on
   what is in it. */
.list-archived-toggle { margin-left: auto; }

.transcript-controls {
  display: flex;
  gap: .4rem;
  flex-wrap: wrap;
  margin: .6rem 0 1rem;
}

/* Removal is not the ordinary thing to do on this screen, so it does not look
   like the ordinary button. It is still plainly a button: a control somebody
   cannot find is how a discussion ends up full of robots. */
.action-btn.danger { border-color: var(--bad); color: var(--bad); }
.action-btn.danger:hover:not(:disabled) { background: var(--bad); color: var(--surface); }
.action-btn:disabled { opacity: .45; cursor: default; }

/* The table can outgrow a phone, so it scrolls inside its own box rather than
   making the whole page scroll sideways. */
.table-scroll { overflow-x: auto; }

.transcript-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--step--1);
}

.transcript-table th,
.transcript-table td {
  text-align: left;
  padding: .4rem .6rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.transcript-table th { color: var(--text-faint); font-weight: 600; }
.transcript-row:hover { background: var(--surface-2); }
.transcript-row.on { background: rgba(var(--accent-rgb), .1); }

.transcript-thread:not(:empty) {
  margin-top: 1.2rem;
  padding-top: .8rem;
  border-top: 2px solid var(--accent);
}

.transcript-person {
  border-top: 1px solid var(--border);
  padding: .9rem 0;
}

.transcript-head { display: flex; gap: .6rem; align-items: baseline; margin-bottom: .4rem; }
.transcript-question { margin: .6rem 0 .1rem; color: var(--text-faint); font-style: italic; }
.transcript-answer { margin: 0; color: var(--text); }
.transcript-edited { font-size: var(--step--1); margin: .1rem 0 0; }

/* --- wider screens --------------------------------------------------------- */

@media (min-width: 48rem) {
  .pad { padding: 1.5rem 1.5rem 4rem; }
  .shell { border-inline: 1px solid var(--border); }
}


/* --- the front page ---------------------------------------------------------
   One template at a time, filling what is left of the window under the bar.
   The titles along the top are the navigation, so the other eight are visible
   without being read. */

.home-pad { padding-top: .8rem; }

.home-lead {
  margin: 0 0 1rem;
  font-size: var(--step-0);
  color: var(--text-dim);
  max-width: 46rem;
}

.home-templates { display: flex; flex-direction: column; }

.home-titles {
  display: flex;
  gap: .2rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: .4rem;
  border-bottom: 1px solid var(--border);
}

.home-titles::-webkit-scrollbar { display: none; }

.home-title {
  flex: none;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  padding: .4rem .7rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  white-space: nowrap;
  cursor: pointer;
}

.home-title:hover { color: var(--accent); }
.home-title.on { color: var(--accent); border-bottom-color: var(--accent); }

/* Tall enough that moving between templates does not make the page jump, and no
   taller: it used to fill the window, which left the card in the top half and a
   screen of nothing under it. */
.home-stage {
  min-height: 26rem;
  padding: 1.2rem 0 1.5rem;
}

/* Sits under the carousel, so it reads as a footnote to the templates rather
   than as a pitch above them. */
.home-angle {
  border-top: 1px solid var(--border);
  padding: 1.4rem 0 2rem;
  max-width: 46rem;
}

.home-angle-title {
  margin: 0 0 .8rem;
  font-size: var(--step-0);
  color: var(--text-dim);
  font-weight: 600;
}

.home-angle-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: .9rem;
}

.home-angle-list strong { color: var(--accent); }

/* The form's own sections, one at a time, with the titles as the way between
   them. Same arrangement as the front page's templates, because it is the same
   problem: several things to compare, and a reader who should not have to
   scroll past four of them to reach the fifth. */
.form-nav {
  display: flex;
  gap: .2rem;
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border);
  margin-bottom: 1rem;
}

.form-nav::-webkit-scrollbar { display: none; }

.form-nav-item {
  flex: none;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  padding: .45rem .7rem;
  font: inherit;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-faint);
  white-space: nowrap;
  cursor: pointer;
}

.form-nav-item:hover { color: var(--accent); }
.form-nav-item.on { color: var(--accent); border-bottom-color: var(--accent); }

/* The seven dimensions, above the settings they decide. Laid out like the dials
   below them, because they are the same kind of thing one level up, and a
   different layout would suggest they were unrelated. */
.setup-dims { display: flex; flex-direction: column; gap: .5rem; }

/* Label and slider on one line, what the position means on the next. The second
   line is where the overflow went: "Only what the AI says about the group" does
   not fit in a column beside a control and does not need to. */
.dim-row {
  display: grid;
  grid-template-columns: 9rem minmax(10rem, 16rem);
  gap: .2rem .8rem;
  align-items: center;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border);
}

.dim-label {
  font-weight: 600;
  text-transform: capitalize;
}

.dim-control { width: 100%; }

/* Both of these sit under the pair above, across the full width. */
.dim-said,
.dim-detail { grid-column: 1 / -1; }

.dim-said {
  margin: 0;
  font-size: var(--step--1);
  color: var(--text-dim);
}

/* The notches, drawn by the browser from the datalist. Small enough not to
   shout, present enough that the slider does not pretend to be continuous. */
.dim-control { accent-color: var(--accent); }

.dim-detail summary {
  cursor: pointer;
  font-size: var(--step--2);
  color: var(--text-faint);
  letter-spacing: .03em;
}

.dim-decides {
  margin: .3rem 0 .2rem 1rem;
  padding-left: .8rem;
  border-left: 2px solid var(--border);
}

.dim-decides-line { margin: 0 0 .15rem; font-size: var(--step--1); }
.dim-decides-line code { font-size: .9em; }
.dim-decides-note { margin: .35rem 0 0; font-size: var(--step--2); }

.dim-what { margin: 0; font-size: var(--step--1); }

/* A value the dimensions decided rather than somebody typed. Said in words on
   the row, not signalled by a colour: colour alone is not an explanation, and
   the first attempt at this was a one-pixel border nobody could see. */
.from-shape { border-color: var(--accent); }

/* A setting that is doing nothing, because another one has switched it off.
   Faded and disabled, with the reason on the row: greying a control without
   saying why is a dead end somebody has to go and find the answer to.

   The comment used to sit between `.from-shape-row` and `.inert`, and a browser
   strips the comment and reads what is left as one descendant selector. So
   nothing was faded at all unless it happened to be inside a row a dimension
   had just written to, and the form went on taking values for settings nothing
   reads while looking entirely live. `.from-shape-row` is a hook nothing
   styles. */
.inert { opacity: .45; }

/* The same, on a dial beside a tab. The heading and the buttons fade together
   and the reason goes under them, so a menu says which of its decisions are
   live at this moment rather than offering four that are and three that are
   not. */
.editor-dial-section.inert .editor-dial-option { pointer-events: none; }
.editor-dial-why {
  display: block;
  font-size: var(--step--2);
  font-style: italic;
  opacity: .9;
}

.dial-inert {
  font-size: var(--step--2);
  font-style: italic;
  white-space: nowrap;
  justify-self: start;
  grid-column: 3;
}

.dial-from {
  background: rgba(var(--accent-rgb), .14);
  border-radius: var(--r-pill);
  padding: .1rem .5rem;
  color: var(--text);
  font-style: normal;
}

.dial-from {
  font-size: var(--step--2);
  font-style: italic;
  white-space: nowrap;
  justify-self: start;
}

/* And when one actually moves, it flashes once. Marking every governed setting
   the same way says which are governed, not which just changed under your hand,
   and a form where nothing visibly moves is one somebody presses twice. */
@keyframes just-changed {
  from { background: rgba(var(--accent-rgb), .35); }
  to   { background: transparent; }
}

.just-changed { animation: just-changed 1.1s ease-out; border-radius: var(--r-sm); }

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

/* Whether an instruction can argue with a setting: a norm is written into the
   AI's instructions and left to its judgement, a rule is applied in code. Three
   of the twenty-eight are norms, which is worth being able to see. */
.force {
  margin-left: .35rem;
  font-size: .8em;
  cursor: help;
}

/* The norm is the one worth spotting: three settings out of twenty-eight, and
   the only three an instruction can argue with. So it takes the accent and the
   padlocks stay quiet. */
.force-norm { color: var(--accent); }
.force-rule { color: var(--text-faint); opacity: .55; }

@media (max-width: 640px) {
  .dim-row { grid-template-columns: 1fr; }

  /* The row under the map holds four different things: what the colours mean,
     the way to the next synthesis, the replay controls and a line of advice
     about a mouse. On a phone they wrapped into each other, and what fell off
     the end was the replay controls, which are the only ones that do anything.
     So the advice goes, the colour key scrolls sideways on one line, and the
     controls take a line of their own. */
  .net-hint { display: none; }

  /* The colour key goes too. Five labels is two lines on a phone, it explains
     colours somebody can see, and what it pushed off the screen was the replay
     controls. Keeping the legend and losing the controls is the wrong way
     round. */
  .net-key .key { display: none; }

  .net-key {
    gap: .4rem .5rem;
    padding: .4rem .6rem;
  }

  /* Clear of the zoom buttons, which sit over the canvas at the bottom right. */
  .net-controls { bottom: 3.2rem; }

  /* 40vw of a phone is 156 pixels, which cut the placeholder off at "Find words
     in the discu". Nothing else is up there to make room for. */
  .net-search input { max-width: 62vw; }

  /* Two columns of settings need 24rem, which is wider than the screen. One
     column and a scroll, rather than a menu with its right-hand half off the
     edge. */
  .editor-dial-menu.is-wide {
    grid-template-columns: 1fr;
    min-width: 0;
    width: calc(100vw - 1.5rem);
    max-height: 70vh;
    overflow-y: auto;
  }

  /* Back to one column, so the space between groups has to come back with it:
     the two-column rule takes it away because the grid gap provides it. */
  .editor-dial-menu.is-wide .editor-dial-section + .editor-dial-section {
    margin-top: .5rem;
  }
}

/* A template's example discussion, under the box it is an example for. Smaller,
   greyer and italic, so nobody reads it as the title they are about to give
   their own project. */
.field-example {
  margin: .25rem 0 0;
  font-size: var(--step--1);
  font-style: italic;
  color: var(--text-faint);
}

.field-example-tag { font-style: normal; font-weight: 600; }

.home-card { display: flex; flex-direction: column; gap: .7rem; }

/* The movement between templates, so the page turning itself is visible rather
   than a card that has silently become a different card. Further and slower
   than it was: at 1.2rem over .45s the change read as a flicker, and what it
   has to say is that this is one of a set being paged through. */
@keyframes home-card-in {
  from { opacity: 0; transform: translateX(2.5rem); }
  to   { opacity: 1; transform: none; }
}

.home-card-in { animation: home-card-in .55s cubic-bezier(.2, .7, .3, 1); }

@media (prefers-reduced-motion: reduce) {
  .home-card-in { animation: none; }
}

.home-card-title { margin: 0; font-size: var(--step-2); }

.home-blurb { margin: 0; max-width: 46rem; }
.home-when { margin: 0; color: var(--text-dim); max-width: 46rem; }

.home-actions {
  display: flex;
  gap: .6rem;
  align-items: center;
  flex-wrap: wrap;
  margin-top: .4rem;
}

/* Back, the titles, forward, and where you are, on one line. The controls belong
   with the labels they move between: at the foot of a full-height stage they
   were a screen away from them and read as page furniture. */
.home-nav {
  display: flex;
  align-items: center;
  gap: .5rem;
  border-bottom: 1px solid var(--border);
}

.home-nav .home-titles { flex: 1; min-width: 0; border-bottom: 0; }

.home-arrow {
  border: 1px solid var(--border-strong);
  background: var(--surface);
  border-radius: var(--r-pill);
  padding: .2rem .8rem;
  font: inherit;
  color: var(--text-dim);
  cursor: pointer;
}

.home-arrow:hover { color: var(--accent); border-color: var(--accent); }

/* One blob per template, under the card. The mark every carousel carries: how
   many, which one, and a way to any of them. Big enough to press on a phone,
   which is why the target is larger than the blob drawn inside it. */
.home-blobs {
  display: flex;
  justify-content: center;
  gap: .1rem;
  margin-top: 1.2rem;
}

.home-blob {
  border: 0;
  background: none;
  padding: .45rem .3rem;
  line-height: 0;
  cursor: pointer;
}

.home-blob::before {
  content: '';
  display: block;
  width: .55rem;
  height: .55rem;
  border-radius: 50%;
  background: var(--border-strong);
  transition: background .2s, transform .2s;
}

.home-blob:hover::before { background: var(--text-dim); }

.home-blob.on::before {
  background: var(--accent);
  transform: scale(1.35);
}

/* How far through the discussion is, above the questions.
   A thin line rather than a chunky bar: it is an estimate somebody glances at,
   not a download. The words beside it carry the meaning, because a bar at forty
   per cent of nothing in particular says nothing on its own. */
.progress-line {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 .6rem;
}

.progress-track {
  flex: 1 1 auto;
  height: 4px;
  border-radius: 2px;
  background: var(--surface-2);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  border-radius: 2px;
  background: var(--accent);
  transition: width .8s ease;
}

.progress-note {
  flex: 0 0 auto;
  font-size: var(--step--1);
  color: var(--text-faint);
}

@media (max-width: 640px) {
  /* The words go under the line rather than squeezing it to a stub. */
  .progress-line { flex-wrap: wrap; gap: .2rem .6rem; }
  .progress-track { flex: 1 0 100%; }
}

/* The options on a closed question. Big enough to hit with a thumb, and the
   whole line is the target rather than the little circle: a radio button on a
   phone is otherwise a test of aim. */
.q-choices { display: flex; flex-direction: column; gap: .1rem; margin-bottom: .6rem; }

.q-choice {
  display: flex;
  align-items: flex-start;
  gap: .55rem;
  padding: .45rem .5rem;
  border-radius: var(--r-sm);
  cursor: pointer;
}

.q-choice:hover { background: var(--surface-2); }
.q-choice input { margin-top: .15rem; flex: 0 0 auto; }
.q-choice span { line-height: 1.4; }

/* Set up, two fields abreast where the fields allow it. Same rule and same
   breakpoint as the settings panel: anything you type sentences into keeps the
   whole width, anything that is a choice shares a line. */
@media (min-width: 64rem) {
  .setup-group-pairs {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 2.5rem;
    align-items: start;
  }

  /* The heading and the note about the whole group span it. */
  .setup-group-pairs > legend,
  .setup-group-pairs > .group-note,
  .setup-group-pairs > .setup-field-wide,
  .setup-group-pairs > .setup-dials,
  .setup-group-pairs > .more-groups,
  .setup-group-pairs > details,
  .setup-group-pairs > .dim-grid { grid-column: 1 / -1; }
}

/* The one question before a discussion is made: from scratch, or like one of
   the templates. A native dialog, so the browser owns the backdrop, the focus
   and Escape. Sized to be read on a phone first: the eight templates are a
   scrolling column rather than a grid, since a grid of eight on a narrow screen
   is a column with worse line lengths. */
.template-chooser {
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
  padding: 1.1rem;
  width: min(34rem, calc(100vw - 2rem));
  max-height: min(80vh, 44rem);
  overflow-y: auto;
}
.template-chooser::backdrop { background: rgba(20, 40, 32, .45); }
.template-chooser h2 { font-size: var(--step-2); margin: 0 0 .8rem; }
.template-chooser-or {
  color: var(--text-dim);
  font-size: var(--step--1);
  margin: 1rem 0 .5rem;
}

.template-pick {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: inherit;
  padding: .6rem .75rem;
  cursor: pointer;
}
.template-pick:hover { border-color: var(--accent); }
.template-pick strong { display: block; }
.template-pick span {
  display: block;
  color: var(--text-dim);
  font-size: var(--step--1);
}

/* The template and the way into its worked example, side by side, so reading
   one is a step off the choice rather than a different screen. */
.template-chooser-row {
  display: flex;
  align-items: center;
  gap: .5rem;
  margin-bottom: .4rem;
}
.template-chooser-row > .template-pick { flex: 1 1 auto; }
.template-choice-see {
  flex: 0 0 auto;
  font-size: var(--step--1);
  color: var(--accent);
  white-space: nowrap;
}


/* --- the wall chart: every template at once, three across -------------------
   For a slide or a printed sheet rather than for the site, so it is laid out to
   fit a screen in one go: no long prose, one line of description, and the
   pictures carrying the argument. Eight templates and a key make nine cells. */
/* The whole window, not the 64rem reading column the rest of the app uses.
   Three cells of a 64rem page are 314 pixels each, which drew every map at a
   quarter of its size and left the key's own samples looking four times bigger
   than the same box on a card. A wall chart is not a reading column. */
.wall { max-width: none; }

.wall-heading {
  font-size: var(--step-3);
  margin: .2rem 0 1.1rem;
}

.wall-sub {
  display: block;
  margin-top: .35rem;
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--text-dim);
  max-width: 52rem;
}

.wall-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  /* Each cell hugs what is in it. Stretched to a common height, the two short
     templates on the bottom row grew a hand's width of empty card apiece,
     following the key beside them. */
  align-items: start;
}

.wall-cell {
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  padding: .8rem .9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  min-width: 0;
}

.wall-title { margin: 0; font-size: var(--step-1); }

.wall-blurb {
  margin: 0;
  font-size: var(--step--1);
  color: var(--text-dim);
}

/* The map fits its cell rather than scrolling inside it: on a wall chart a
   picture somebody has to drag sideways is a picture nobody sees. Its width is
   set on the element by `setScale`, as its share of the widest map on the page,
   so there is nothing to say here about width. */
.wall-cell .template-diagram { overflow: visible; }

/* Resting on a card opens it at a size where the boxes can be read. Fixed
   rather than positioned against the card, because a card in the right-hand
   column has nowhere to grow into. */
.wall-lightbox {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem;
  background: rgba(10, 20, 16, .45);
  backdrop-filter: blur(2px);
  /* The pointer passes straight through to the card underneath. Without this
     the panel covers the card the moment it opens, the card is sent
     pointerleave, and the thing closes itself in the same frame it appeared. */
  pointer-events: none;
}

.wall-large {
  width: min(1180px, 94vw);
  max-height: 92vh;
  overflow: auto;
  box-shadow: var(--shadow);
  padding: 1.4rem 1.6rem 1.6rem;
  gap: .8rem;
}

.wall-large .wall-title { font-size: var(--step-2); }
.wall-large .wall-blurb { font-size: var(--step-0); }
.wall-large .template-steps { font-size: var(--step-0); }

/* At this size the map fills the panel, whatever its share of the widest one. */
.wall-large .template-map { width: 100% !important; }

@media (prefers-reduced-motion: no-preference) {
  .wall-lightbox { animation: wall-lightbox-in .12s ease-out; }
}

@keyframes wall-lightbox-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* The sample of a break in the key. */
.map-band-divider-sample {
  stroke: var(--text-faint);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
}

/* The key already carries its own border on the front page, and a second one
   inside a cell is a box in a box. */
.wall-key .template-key {
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
}

@media (max-width: 1100px) {
  .wall-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .wall-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Printed, or saved as a PDF for a talk: the bar and the page furniture go, and
   a cell is never split across two pages. */
@media print {
  .wall .chrome, .wall .appnav { display: none; }
  .wall-grid { gap: .6rem; }
  .wall-cell {
    break-inside: avoid;
    page-break-inside: avoid;
    background: none;
  }
}

/* The way to the wall chart, small and under the lead: it is for showing the
   range to a room rather than for a visitor working out what this is. */
.home-all { margin: -.4rem 0 .6rem; font-size: var(--step--1); }
