/* Palette. Two themes, one set of names. Nothing below this block names a colour directly, so a
   theme change is an edit here and nowhere else. */
:root{
  --bg:#fbfbfa;--fg:#1c1c1a;--dim:#6b6b66;--line:#e2e2dd;--accent:#8a5a00;--panel:#fff;
  --mine:#fdf3dd;          /* the reader's own message, warmer than the page */
  --theirs:#fff;
  --hover:#f4f3ef;
  --live:#2f7d32;          /* a colleague who can answer right now */
  --danger:#b3311c;        /* an irreversible action, never the palette's own accent */
  --shadow:0 1px 2px rgba(28,28,26,.06);
}
@media (prefers-color-scheme:dark){
  :root{
    --bg:#161614;--fg:#eceae4;--dim:#9a9a92;--line:#2e2e29;--accent:#e0a94b;--panel:#1e1e1b;
    --mine:#2a2419;--theirs:#1e1e1b;--hover:#262622;--live:#7fc784;--danger:#e0685a;
    --shadow:0 1px 2px rgba(0,0,0,.35);
  }
}
*{box-sizing:border-box}

/* The site header's height, one number: the header holds at least this much, and the Mission
   page's sticky counts strip sticks right under it. Measured, not guessed: a strip placed higher
   slides under the header while the page scrolls. */
:root{--bar-h:4.25rem}
@media (max-width:820px){:root{--bar-h:3.75rem}}

/* Type scale. 17px base rather than 15px: this is a page people read for minutes at a time, and
   the older size made a long thread tiring. Everything else is expressed in rem so the whole
   scale moves together. */
body{
  margin:0;background:var(--bg);color:var(--fg);
  font:17px/1.62 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--accent)}
h1{font-size:2rem;line-height:1.2;margin:.2rem 0 1rem;letter-spacing:-.01em}
h2{font-size:1.2rem;margin:1.4rem 0 .5rem}
h3{font-size:1rem;margin:.9rem 0 .3rem}

/* Header. Sticky, because on a long thread the way back to the board list should not require
   scrolling to the top first. */
.bar{
  display:flex;align-items:center;gap:1rem;padding:.7rem 1.1rem;min-height:var(--bar-h);
  border-bottom:1px solid var(--line);background:var(--panel);
  position:sticky;top:0;z-index:10;
}
.bar .brand{font-weight:650;text-decoration:none;font-size:1.05rem}
.bar nav{display:flex;gap:1rem}
.bar nav a{text-decoration:none;padding:.2rem .1rem;border-bottom:2px solid transparent;white-space:nowrap}
.bar button{white-space:nowrap}
.bar nav a:hover{border-bottom-color:var(--accent)}
.bar .right{margin-left:auto;display:flex;align-items:center;gap:.7rem}
.who{color:var(--dim);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* On a phone the header holds the two lists and Sign out; the name would only push them onto a
   second line. */
@media (max-width:560px){.bar{gap:.7rem;padding:.5rem .9rem}.bar nav{gap:.7rem}.who{display:none}}

/* app-body pairs the persistent board list with whatever page it is showing beside. The sidebar is
   a flex item rather than sticky: sticking it under the header would need a hardcoded offset equal
   to the header's own height, which is set by its padding and line height, not a fixed number. */
.app-body{display:flex;align-items:stretch}
/* width alongside the flex-basis shorthand is belt-and-suspenders: flex-shrink:0 already keeps
   this from shrinking, but an explicit width is what stops long unbroken board titles from ever
   nudging the column wider than the fixed rail this is supposed to be. min-width:0 lets its own
   children (the title links) shrink for their own ellipsis instead of forcing the column to grow
   to their content size. */
.sidebar{flex:0 0 15rem;width:15rem;min-width:0;padding:1.4rem 1rem 1rem 1.1rem;border-right:1px solid var(--line);position:relative;transition:width .15s ease,flex-basis .15s ease}
.sidebar .search{margin:0 0 .6rem;max-width:none}
.sidebar .search-empty{font-size:.85rem;margin:.4rem 0}
.sidebar .sidebar-boards{list-style:none;margin:0;padding:0}
.sidebar .sidebar-boards li{display:flex;align-items:center;gap:.4rem;padding:.3rem 0;min-width:0}
/* Same reason as ul.boards li[hidden] below: the li rule above sets display:flex, which beats the
   browser's own [hidden]{display:none} on specificity, so a row sidebar-search.js hides would
   otherwise stay rendered with empty content. */
.sidebar .sidebar-boards li[hidden]{display:none}
.sidebar .sidebar-boards li.active a{color:var(--fg);font-weight:600}
.sidebar .sidebar-boards a{text-decoration:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1 1 auto;display:block}
.sidebar .empty{color:var(--dim);font-size:.92rem;margin:2rem 0 0}
.sidebar .find{display:inline-block;margin-top:.6rem;font-size:.92rem}

/* The toggle sits top-right of the rail in both states, a fixed reference point rather than one
   that jumps sides when folded. Its own glyph (« / ») is set by sidebar-toggle.js, which
   also keeps aria-expanded in sync; this rule is layout only. */
.sidebar-toggle{
  position:absolute;top:.7rem;right:.6rem;width:1.7rem;height:1.7rem;
  border:1px solid var(--line);border-radius:.3rem;background:var(--panel);color:var(--fg);
  cursor:pointer;font-size:1rem;line-height:1;padding:0;
}
.sidebar-toggle:hover{border-color:var(--accent);color:var(--accent)}

/* Folded: a thin rail with only the toggle, and main regains the width the rail gave up. The
   class lives on <html> (set by the inline script in layout.gohtml and toggled by
   sidebar-toggle.js) so it reaches .sidebar and main no matter which is defined first. */
html.sidebar-folded .sidebar{flex:0 0 2.75rem;width:2.75rem;padding:1.4rem .4rem 1rem;overflow:hidden}
html.sidebar-folded .sidebar .search,
html.sidebar-folded .sidebar .search-empty,
html.sidebar-folded .sidebar .sidebar-boards,
html.sidebar-folded .sidebar .empty,
html.sidebar-folded .sidebar details.archived-boards,
html.sidebar-folded .sidebar .find{display:none}
html.sidebar-folded .sidebar-toggle{position:static;margin:0 0 0 auto}

main{flex:1 1 auto;min-width:0;padding:1.4rem 1.1rem 5rem}
main.narrow{max-width:34rem}

@media (max-width:820px){
  .app-body{flex-direction:column}
  .sidebar{flex:none;width:100%;border-right:none;border-bottom:1px solid var(--line);padding:1rem 1.1rem}
  html.sidebar-folded .sidebar{width:100%;padding:.6rem 1.1rem}
}

button{
  font:inherit;font-weight:500;padding:.45rem .95rem;border:1px solid var(--line);
  border-radius:7px;background:var(--panel);color:var(--fg);cursor:pointer;
  transition:border-color .12s,background .12s;
}
button:hover{border-color:var(--accent);background:var(--hover)}
button:disabled{opacity:.45;cursor:not-allowed}
button:disabled:hover{border-color:var(--line);background:var(--panel)}
button.primary{background:var(--accent);border-color:var(--accent);color:var(--bg);font-weight:600}
button.primary:hover{filter:brightness(1.08)}
input,textarea{
  font:inherit;width:100%;padding:.55rem .7rem;border:1px solid var(--line);
  border-radius:7px;background:var(--panel);color:var(--fg);
}
::placeholder{color:var(--dim);opacity:1}
a:focus-visible,button:focus-visible,summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
input:focus,textarea:focus{outline:2px solid var(--accent);outline-offset:-1px;border-color:var(--accent)}
label{display:block;color:var(--dim);font-size:.92rem;margin-bottom:.25rem}
.error{color:#b3261e;font-weight:500}
@media (prefers-color-scheme:dark){.error{color:#ff9a90}}
.disclosure{color:var(--dim);font-size:.92rem;max-width:44rem}
.code{color:var(--dim);font-size:.88rem}

/* The announcement page. A text is whatever a colleague wrote, so a long address or an unbroken
   word wraps inside the column instead of widening the page. */
main.narrow .text,main.narrow li,main.narrow .board{overflow-wrap:anywhere}
main.narrow .text{white-space:pre-wrap}
form.ack{display:flex;flex-wrap:wrap;gap:.6rem;margin-top:1.2rem}
form.ack input[name=note]{flex:1 1 14rem;width:auto}

/* The sign-in page. Two columns on a wide screen — the words where reading starts, the lattice
   where the artwork's own weight already is — and one column, words first, on a narrow one. The
   image is never cropped: it is drawn at its own proportions so the hexagons stay hexagons. */
main.hero-page{
  max-width:64rem;margin:0 auto;display:grid;grid-template-columns:minmax(0,22rem) minmax(0,1fr);
  gap:2.5rem;align-items:center;min-height:62vh;
}
.hero-copy h1{font-size:2.6rem;margin:0 0 .6rem}
.hero-copy .lede{color:var(--dim);font-size:1.15rem;line-height:1.5;margin:0 0 1.6rem}
.hero-art img{display:block;width:100%;height:auto}
button.big{padding:.65rem 1.3rem;font-size:1.05rem}
/* The create forms hold one line of text and one button; a field as wide as the window reads as
   the whole page's input. */
form.create,form.search-create{max-width:34rem}
form.create button,form.search-create button{margin-top:.6rem}
@media (max-width:820px){
  main.hero-page{grid-template-columns:1fr;gap:1.2rem;min-height:0}
  .hero-art{order:-1}
  .hero-art img{max-height:9rem;object-fit:cover;object-position:right center}
  .hero-copy h1{font-size:2.1rem}
}
.banner{margin:-.4rem 0 1.2rem;overflow:hidden;border-radius:10px}
.banner img{display:block;width:100%;height:5.5rem;object-fit:cover;object-position:right center;opacity:.85}

/* Board lists. A row is a whole click target, which is how every chat application behaves. */
.search{margin:1rem 0 .6rem;max-width:26rem}
.search label{margin-bottom:.2rem}
.search-empty{color:var(--dim);margin:.6rem 0}

ul.boards{list-style:none;padding:0;margin:1rem 0}
ul.boards li{
  display:flex;align-items:center;gap:.8rem;padding:.75rem .9rem;margin:.35rem 0;
  border:1px solid var(--line);border-radius:9px;background:var(--panel);
  transition:border-color .12s,background .12s;
}
/* A filtered-out row must actually disappear. `ul.boards li{display:flex}` above outranks the
   browser's own [hidden]{display:none} on specificity alone, so without this rule board-search.js
   setting `.hidden = true` would leave the row rendered — same width, same border, just invisible
   content — which is worse than not filtering at all. */
ul.boards li[hidden]{display:none}
ul.boards li:hover{border-color:var(--accent);background:var(--hover)}
ul.boards li form{margin:0}
ul.boards li form.leave{margin-left:auto}
/* The title and, under it, the dim "Created by <name> · <time>" line share one column that grows to
   fill the row, so the unread badge and the dots stay at the far end. min-width:0 lets a long title
   wrap and the meta line truncate instead of pushing the dots off a phone-width row. */
ul.boards .board-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:.15rem}
ul.boards .board-main>a,ul.boards .name{font-weight:550;font-size:1.05rem;text-decoration:none}
ul.boards .board-main>a:hover{text-decoration:underline}
.board-created{margin:0;font-size:.82rem;line-height:1.3;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.board-created .by{font-weight:500}

/* The three dots at the right end of a board row. The row's title grows to fill the line, so the
   unread badge and then the dots sit at its far end; margin-left:auto keeps the dots there even in
   a row with nothing else to grow. The open menu is positioned against the dots and floats over the
   rows below it, right-aligned, so opening one never moves a row. Its forms get back the spacing
   that `ul.boards li form{margin:0}` takes from the Join and Leave buttons. */
ul.boards li .board-menu{margin-left:auto}
ul.boards li .board-menu .menu-body{position:absolute;right:0;top:2.4rem;z-index:20;text-align:left}
ul.boards li .board-menu .menu-body form{margin:0 0 .8rem}
ul.boards li .board-menu .menu-body form.board-delete{margin-bottom:0}
/* On a list row the rename form opens the menu, so it has nothing above it to be divided from. */
ul.boards li .board-menu .menu-body form.rename{border-top:none;padding-top:0}
ul.boards li .board-menu .menu-body .hint{margin:0}

/* Archived boards: a closed fold under the list, in the page and in the sidebar. */
details.archived-boards{margin:1.2rem 0}
details.archived-boards > summary{cursor:pointer;color:var(--dim);font-size:.95rem;padding:.3rem 0;user-select:none}
details.archived-boards > summary:hover,details.archived-boards[open] > summary{color:var(--fg)}
details.archived-boards ul.boards{margin:.4rem 0 0}
.sidebar details.archived-boards{margin:.6rem 0 0}
.sidebar details.archived-boards > summary{font-size:.9rem}

/* Unread pill. Slack's one honest attention signal: a count, on the thing that has the count,
   and nowhere else. */
.badge{
  background:var(--accent);color:var(--bg);border-radius:999px;padding:.05rem .55rem;
  font-size:.85rem;font-weight:650;min-width:1.5rem;text-align:center;
}

/* Board participants. One line of compact chips — a status dot plus a name — carries at a glance
   what a vertical panel took a whole card to say; email and last-seen detail move into the
   tooltip, and a board with more chats than fit scrolls sideways inside the header row. */
.participants-bar .dim{color:var(--dim);font-size:.9rem;margin:0}
.participants-bar .chips{
  list-style:none;margin:0;padding:0 1.25rem 0 0;display:flex;gap:.5rem;
  overflow-x:auto;overflow-y:hidden;white-space:nowrap;-webkit-overflow-scrolling:touch;
  /* The last chip fades out at the right edge, which says "more this way"; the padding lets the
     final chip scroll fully clear of the fade. */
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 1.25rem),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 1.25rem),transparent);
}
.participants-bar .chip{
  display:inline-flex;align-items:center;flex:none;
  padding:0;border:1px solid var(--line);border-radius:999px;
  background:color-mix(in srgb,var(--c) 16%,var(--panel));
  border-color:color-mix(in srgb,var(--c) 45%,var(--line));font-size:.85rem;font-weight:550;
}

/* The whole chip is the link, dot included, so the target is the chip and not just its letters. */
.participants-bar .chip a,.participants-bar .chip .in{display:inline-flex;align-items:center;gap:.35rem;padding:.15rem .6rem;color:inherit;text-decoration:none}
.participants-bar .chip a:hover{text-decoration:underline}
.participants-bar .chip .chip-state{margin-right:.6rem;font-size:.8em;opacity:.75}

/* Board page layout, Slack-style: the page is exactly one viewport tall and nothing but the thread
   scrolls. The header (title, Thread / Mission tabs, participant chips, "⋯" menu) is one row above
   the thread and the composer is one block below it, both plain flex items rather than sticky
   boxes, so neither can be scrolled away or covered. The site header and the sidebar keep their
   own place; the sidebar's own list scrolls if it is long. */
body:has(main.board-thread){height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
body:has(main.board-thread) .app-body{flex:1 1 auto;min-height:0;align-items:stretch}
body:has(main.board-thread) .sidebar{overflow-y:auto}
main.board-thread{display:flex;flex-direction:column;min-height:0;overflow:hidden;padding:0 1.1rem}
.board-head{
  display:flex;flex-wrap:nowrap;align-items:center;gap:.9rem;flex:none;
  padding:.45rem 0;border-bottom:1px solid var(--line);
}
.board-head h1{font-size:1.15rem;line-height:1.3;margin:0;flex:0 1 auto;min-width:0;max-width:40%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.board-head .tabs{flex:none;margin:0;border-bottom:none;gap:.9rem}
.board-head .tabs a{padding:.2rem .1rem}
.board-head .participants-bar{flex:1 1 0;min-width:0}
.board-menu{flex:none;position:relative}
.board-menu summary{
  list-style:none;cursor:pointer;width:2rem;height:2rem;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:7px;background:var(--panel);font-size:1.2rem;line-height:1;user-select:none;
}
.board-menu summary::-webkit-details-marker{display:none}
.board-menu summary:hover,.board-menu[open] summary{border-color:var(--accent);color:var(--accent)}
.board-menu .menu-body{
  position:absolute;right:0;top:2.4rem;z-index:20;width:min(22rem,86vw);padding:.9rem 1rem;
  background:var(--panel);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.25);
  max-height:70vh;overflow-y:auto;
}
main.board-thread > .stale{flex:none;margin:.5rem 0 0}
.thread-scroll{flex:1 1 auto;min-height:0;overflow-y:auto;padding:.6rem .2rem .4rem 0;overscroll-behavior:contain}
main.board-thread form.compose{position:static;flex:none;margin:0;padding:.6rem 0 .7rem}
main.board-thread form.compose textarea{max-height:30vh}
@media (max-width:820px){
  body:has(main.board-thread) .sidebar{flex:none;max-height:35vh}
  .board-head{flex-wrap:wrap;gap:.4rem .8rem}
  .board-head h1{max-width:calc(100% - 3rem)}
  .board-head .participants-bar{flex:1 1 100%;order:5}
  .board-menu{margin-left:auto}
}

.panels{display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:1.4rem}
.panel{flex:1 1 17rem;border:1px solid var(--line);border-radius:10px;padding:.5rem 1rem 1rem;background:var(--panel);box-shadow:var(--shadow)}
.panel h2{margin-top:.7rem;font-size:1.05rem}
.panel ul{list-style:none;padding:0;margin:.3rem 0}
.panel li{padding:.2rem 0;overflow-wrap:anywhere}
.panel .ack-inline{margin:.3rem 0 .2rem}
/* An announcement can hold a whole paragraph; the panel shows its first lines and the link opens the rest. */
.panel li > a{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;line-clamp:3;overflow:hidden}
.panel .repo,.panel .ago,.pending{color:var(--dim);font-size:.9rem}
.dim li{color:var(--dim)}

/* Presence. A filled dot means "can answer now" — the single piece of state WhatsApp taught
   everyone to read without a legend. */
.dot{display:inline-block;width:.55rem;height:.55rem;border-radius:50%;background:var(--live);margin-right:.45rem;vertical-align:.05rem}
.dot.away{background:transparent;box-shadow:inset 0 0 0 2px var(--dim)}

/* Thread. Bubbles, avatars and author grouping. The three together are what make a wall of text
   read as a conversation between people instead of a log file.

   Density and alignment follow Slack/iMessage, not the old WhatsApp-style layout: every bubble,
   including the reader's own, sits on the left edge, one under another — English reads
   left-to-right, and a staggered left/right column reads as a mirror of a phone messenger, not a
   left-to-right log. A bubble is sized to its content, capped at ~70% of the column, so a short
   message is not stretched into a wide, padded box. */
ul.items{list-style:none;padding:0;margin:0}
.item{
  display:grid;grid-template-columns:2.4rem 1fr;column-gap:.5rem;
  padding:.1rem 0;margin:.15rem 0;position:relative;
}
.item .av{
  grid-row:1;align-self:start;width:2.4rem;height:2.4rem;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-size:.85rem;font-weight:650;color:#fff;user-select:none;
  background:color-mix(in srgb,#000 22%,var(--c));
}
.item .bubble{
  background:color-mix(in srgb,var(--c) 16%,var(--theirs));
  border:1px solid color-mix(in srgb,var(--c) 40%,var(--line));border-radius:10px;
  border-top-left-radius:4px;padding:.3rem .6rem;box-shadow:var(--shadow);
  min-width:0;width:fit-content;max-width:70%;justify-self:start;
  line-height:1.35;
}
/* The reader's own messages keep a distinct tint so "which of these did I write" is still
   answered at a glance, but the layout column and left alignment are the same as anyone else's —
   this is a left-to-right thread, not a two-sided messenger. */
.item.mine .bubble{border-top-left-radius:4px}

.item.kind-announcement .bubble{border-left:3px solid var(--accent)}
.item.kind-question .bubble{border-left:3px solid #3b6ea5}
.item.kind-answer .bubble{border-left:3px solid #3a7d44}

/* A run of messages from one person collapses into one turn: the avatar and the name appear
   once, at the top of the run. */
.item.grouped{margin-top:-.2rem}
.item.grouped .av{visibility:hidden}
.item.grouped .bubble{border-top-left-radius:10px}

.item .meta{display:flex;flex-wrap:wrap;gap:0 .6rem;align-items:baseline;color:color-mix(in srgb,var(--fg) 30%,var(--dim));font-size:.82rem;margin-bottom:.05rem}
.item .author{white-space:nowrap;font-weight:650;color:var(--fg);font-size:.9rem}
/* Every bubble sits on the left whoever wrote it, so the reader's own are marked in words. */
.item.mine .author::after{content:" · you";color:var(--dim);font-weight:400}
.item .chat{color:var(--accent);font-size:.82rem;font-weight:500;overflow-wrap:anywhere}
.item .kind{text-transform:lowercase}
.item .addressed{color:var(--accent);font-size:.82rem;font-weight:500;overflow-wrap:anywhere}
.item .text{white-space:pre-wrap;overflow-wrap:anywhere;margin:.05rem 0;font-size:.94rem;line-height:1.35}

@media (max-width:820px){.item .bubble{max-width:85%}}

/* An html post: the page is a white sandboxed frame inside the bubble. The bubble is as wide as the
   column allows (up to a readable maximum) because a diagram has no natural width. The frame is
   white whatever the theme, since a page written for a white background must stay readable; the
   person can drag its bottom-right corner to make it taller, and "Open full size" shows it alone. */
.item.kind-html .bubble{width:min(100%,56rem);max-width:100%}
.item .html-frame{
  display:block;width:100%;height:360px;min-height:120px;max-height:90vh;margin:.3rem 0 0;
  background:#fff;border:1px solid var(--line);border-radius:6px;resize:vertical;overflow:auto;
}
.item .html-actions{margin:.2rem 0 .1rem;font-size:.82rem}
main.html-view{display:flex;flex-direction:column;min-height:calc(100vh - var(--bar-h));padding:.8rem 1rem}
main.html-view p{margin:0 0 .6rem}
.html-full{flex:1 1 auto;width:100%;min-height:70vh;background:#fff;border:1px solid var(--line);border-radius:6px}

/* Answer sits on the header line of a bubble and takes no row of its own. A continuation of a run has
   no header line, so its link floats over the bubble's top-right corner and shows on hover or
   keyboard focus only; it is out of the layout flow, so a bubble never reserves height for it. */
.item .acts{margin-left:auto;display:inline-flex;gap:.8rem;font-size:.82rem}
.item .acts a{color:var(--dim)}
.item .acts a:hover{color:var(--accent)}
.item .bubble{position:relative}
.item .actions{
  position:absolute;top:-.7rem;right:.4rem;z-index:1;font-size:.82rem;padding:0 .5rem;
  background:var(--panel);border:1px solid var(--line);border-radius:6px;box-shadow:var(--shadow);
  opacity:0;pointer-events:none;transition:opacity .12s;
}
.item:hover .actions,.item:focus-within .actions{opacity:1;pointer-events:auto}
@media (hover:none){.item .actions{display:none}}

/* Compose. Pinned to the bottom of the viewport, where both Slack and WhatsApp put it, so the
   place you type never moves. */
form.compose{
  position:sticky;bottom:0;background:var(--bg);padding:.8rem 0 .9rem;
  margin:1.4rem 0 .4rem;border-top:1px solid var(--line);
}
form.compose textarea{resize:vertical;min-height:3.2rem}
form.compose .row{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem 1.2rem;margin-top:.55rem}
form.compose .hint,form.compose .note{color:var(--dim);font-size:.88rem}
form.compose .note{margin:.4rem 0 0}
.replying{color:var(--dim);font-size:.93rem;margin:0 0 .4rem}
/* Whom a message is for and whether it is a question: two small controls on the Post line. */
form.compose label.to,form.compose label.check{display:flex;align-items:center;gap:.5rem;margin:0;color:var(--fg);font-size:.92rem}
form.compose label.check input{width:auto;margin:0}
/* HTML mode: a page needs a tall box and a code font, and its own send shortcut (compose.js). The
   mode is a checkbox, so the box can follow it with no script. */
form.compose:has(input[name="html"]:checked) textarea,main.board-thread form.compose:has(input[name="html"]:checked) textarea{
  min-height:14rem;max-height:60vh;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.88rem;
}
form.compose .hint-html{display:none}
form.compose:has(input[name="html"]:checked) .hint-html{display:inline}
form.compose:has(input[name="html"]:checked) .hint-text{display:none}
@media (hover:none){form.compose:has(input[name="html"]:checked) .hint-html{display:none}}
form.compose select{font:inherit;font-size:.92rem;padding:.3rem .5rem;border:1px solid var(--line);border-radius:7px;background:var(--panel);color:var(--fg);max-width:14rem}

form.markread,form.archive,form.leave,form.rename{display:block;margin:0 0 .8rem}
form.rename{max-width:30rem}
form.rename .field{display:flex;gap:.4rem}
form.rename .field input{flex:1 1 auto;min-width:0}
/* The check-mark that saves the new name: as tall as the field beside it, named by aria-label. */
button.save{flex:none;width:2.75rem;padding:0;color:var(--accent);font-size:1.15rem;font-weight:700;line-height:1}
form.leave button{color:var(--dim)}
form.archive .hint{color:var(--dim);font-size:.9rem;margin:.3rem 0 0}
/* The two groups of the board menu. "Only you" and "For everyone" are captions, not buttons: they
   say who an action reaches before the person presses it. */
.board-menu .menu-caption{margin:0 0 .5rem;font-size:.78rem;font-weight:650;letter-spacing:.04em;text-transform:uppercase;color:var(--dim)}
.board-menu .menu-divider{border:0;border-top:1px solid var(--line);margin:.2rem 0 .9rem}
.board-menu .menu-body > .hint{color:var(--dim);font-size:.9rem;margin:0 0 .8rem}
/* What the last action did. It sits under the page header and goes away by itself. */
.notice{margin:.6rem auto;max-width:60rem;padding:.55rem .85rem;border:1px solid var(--accent);border-radius:8px;background:var(--mine);font-weight:500}
.notice[hidden]{display:none}
.archived-note{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem .8rem}
.archived-note form.inline{margin:0}
form.danger{display:block;margin-top:.4rem;max-width:30rem}
form.danger .hint{color:var(--dim);font-size:.9rem;margin:.2rem 0 .7rem}
button.danger{background:transparent;border-color:var(--danger);color:var(--danger)}
button.danger:hover{background:var(--danger);color:var(--panel)}

/* Empty states. An empty board should read as an invitation, not as a report that nothing is
   there — the difference decides whether a person posts the first message. */
.empty{border:1px dashed var(--line);border-radius:10px;padding:1.6rem 1.2rem;text-align:center;color:var(--dim)}
.empty strong{display:block;color:var(--fg);font-size:1.1rem;margin-bottom:.3rem}
.empty p{margin:.3rem 0}

/* The outage notice on an open thread. Kept visible rather than subtle: a thread that has
   stopped updating must not read as a quiet board. */
.stale{background:var(--mine);border:1px solid var(--accent);border-radius:8px;padding:.6rem .85rem;font-weight:500}

/* The initials are white, so the avatar is the chat's colour a little darker: white on the plain
   colour fails AA for several of the twelve, and the meta line is dim mixed toward the text colour
   for the same reason on the tinted bubble (TestTextOnTintedBubblesAndAvatarsPassesAA). */

/* Avatar colours. A fixed set, chosen by a hash of the address, because the Content-Security-Policy
   has no 'unsafe-inline' for style: a per-person colour has to be a class, not a style attribute.
   Same person, same colour, every board, every session — which is what makes an avatar readable
   at a glance instead of decorative. */
.av-0{background:#8a5a00}.av-1{background:#3b6ea5}.av-2{background:#3a7d44}.av-3{background:#8d4a6b}
.av-4{background:#5c5a9e}.av-5{background:#a1592e}.av-6{background:#2f7276}.av-7{background:#7a6a2f}

/* Chat colours. One per chat name (hash, see chatClass), so every participant of a board is its own
   colour in the thread bubbles, avatars and participant chips. --c is the strong colour; bubbles
   and chips mix a little of it into the theme background, so both themes stay readable. */
.chat-a{--c:#b8860b}.chat-b{--c:#3b6ea5}.chat-c{--c:#3a7d44}.chat-d{--c:#a64d79}
.chat-e{--c:#6a5acd}.chat-f{--c:#c0562b}.chat-g{--c:#1f8a8a}.chat-h{--c:#8a8a1f}
.chat-i{--c:#d1495b}.chat-j{--c:#2e86c1}.chat-k{--c:#7d5a3c}.chat-l{--c:#5f8a2e}

/* Board tabs. Thread and Mission are two views of one board, so they sit under its title. */
.tabs{display:flex;gap:1.2rem;margin:-.4rem 0 1.2rem;border-bottom:1px solid var(--line)}
.tabs a{text-decoration:none;color:var(--dim);padding:.3rem .1rem;border-bottom:2px solid transparent;margin-bottom:-1px}
.tabs a[aria-current="page"]{color:var(--fg);border-bottom-color:var(--accent);font-weight:600}

/* A task action in the thread is one line; the Mission tab is where tasks are read. */
.item.kind-task,.item.kind-event{display:flex;align-items:center;flex-wrap:wrap;gap:.5rem;margin:.2rem 0 .2rem 3.1rem;color:var(--dim);font-size:.9rem}
.item.kind-event .who{font-weight:600}
.item.kind-event .what{overflow-wrap:anywhere}
.item.kind-task .ico{width:1.2rem;height:1.2rem}
.item.kind-task .text{font-size:.9rem;margin:0}
.item.kind-task .task-link{overflow-wrap:anywhere}

/* Where unread starts: a line across the thread with one word on it. */
.jump{position:sticky;bottom:.5rem;display:block;margin:.3rem 0 0 auto;border-color:var(--accent);background:var(--panel);color:var(--accent);box-shadow:var(--shadow)}
.jump[hidden]{display:none}
.new-divider{display:flex;align-items:center;gap:.7rem;margin:.6rem 0;list-style:none;color:var(--accent);font-size:.8rem;font-weight:650;letter-spacing:.04em;text-transform:uppercase}
.new-divider::before,.new-divider::after{content:"";flex:1 1 auto;border-top:1px solid var(--accent)}

.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* The Mission view. Icons carry the picture and text is kept to a card's one-line title; every
   icon names itself in the tooltip. */
.ico{width:1.9rem;height:1.9rem;flex:none;display:inline-block;vertical-align:middle;border-radius:7px}
.ico:focus-visible,.agent:focus-visible,.task:focus-visible,.count:focus-visible,.more:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.counts{
  display:flex;flex-wrap:wrap;gap:.5rem;position:sticky;top:var(--bar-h);z-index:5;
  background:var(--bg);padding:.5rem 0;margin-bottom:.6rem;border-bottom:1px solid var(--line);
}
.count{display:inline-flex;align-items:center;gap:.35rem;padding:.2rem .55rem .2rem .25rem;border:1px solid var(--line);border-radius:999px;background:var(--panel)}
.count .ico{width:1.5rem;height:1.5rem}
.count b{font-variant-numeric:tabular-nums;min-width:1.2ch;text-align:right}
.count .count-name{font-size:.8rem;color:var(--dim);text-transform:lowercase}

.rail{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;min-height:2.8rem;margin:.4rem 0 1rem}
.agent{display:inline-flex;align-items:center;gap:.35rem;padding:.15rem .5rem .15rem .15rem;border-radius:999px;color:#fff;font-size:.82rem;font-weight:600;max-width:12rem}
.agent .ico{width:1.7rem;height:1.7rem}
.agent .agent-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.agent .agent-away{flex:none;font-size:.72rem;font-weight:500;font-style:italic;opacity:.9}
.agent.lead{box-shadow:0 0 0 2px var(--accent)}
.agent.away{opacity:.8;filter:grayscale(1) brightness(.7);outline:1px dashed currentColor;outline-offset:-1px}
.agent.human{background:transparent;padding:0 .5rem 0 0;color:var(--fg)}
.rail-label{font-size:.82rem;color:var(--dim)}

.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(15rem,1fr));gap:.7rem}
.task{
  display:grid;grid-template-columns:auto 1fr;grid-template-areas:"state title" "result result" "comps comps" "holder holder";
  gap:.4rem .55rem;align-items:center;padding:.6rem .7rem;min-height:7.2rem;
  border:1px solid var(--line);border-left:4px solid var(--line);border-radius:10px;background:var(--panel);box-shadow:var(--shadow);
}
.task .state{grid-area:state;display:flex}
.task .title{grid-area:title;font-size:.88rem;line-height:1.3;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow-wrap:anywhere;min-width:0}
.task .result{grid-area:result;font-size:.82rem;line-height:1.3;color:var(--dim);overflow-wrap:anywhere}
.task .comps{grid-area:comps;display:flex;flex-wrap:wrap;gap:.3rem;align-items:center}
.task .comps .ico{width:1.6rem;height:1.6rem}
.task .more{font-size:.8rem;color:var(--dim);border:1px solid var(--line);border-radius:6px;padding:0 .3rem}
.task>*{min-width:0}
.task .holder{grid-area:holder;min-height:2rem;display:flex;justify-content:flex-start;align-items:center}
.task .holder .agent{padding:.15rem .5rem .15rem .15rem;max-width:min(12rem,100%)}
.task.state-claimed{border-left-color:var(--accent)}
.task.state-in_progress{border-left-color:#3b6ea5}
.task.state-passed{border-left-color:var(--live)}
.task.state-blocked{border-left-color:#a1592e}
.task.state-failed{border-color:var(--danger);border-left-color:var(--danger);background:color-mix(in srgb,var(--danger) 9%,var(--panel))}
.task.trimmed{border-style:dashed}

.mission-empty{grid-column:1/-1;display:flex;justify-content:center;padding:3rem 0;border:1px dashed var(--line);border-radius:12px}
.mission-empty .ico{width:5rem;height:5rem;opacity:.85}

.replay{display:flex;align-items:center;gap:.7rem;margin-top:1.4rem}
.replay input[type=range]{flex:1 1 auto;accent-color:var(--accent)}
.replay{flex-wrap:wrap}
.replay label{flex:1 1 100%;font-size:.88rem;color:var(--dim)}
.replay .end{font-size:.8rem;color:var(--dim)}
.mission-empty{flex-direction:column;align-items:center;gap:.8rem;color:var(--dim);text-align:center}
.mission-empty p{margin:0;max-width:34rem}
.replay output{font-variant-numeric:tabular-nums;color:var(--dim);font-size:.88rem;min-width:4.5rem}
.replay .live{border-color:var(--live);color:var(--live)}
.mission.replaying .cards{outline:2px dashed var(--line);outline-offset:.4rem;border-radius:10px}

.tip{
  position:fixed;z-index:50;max-width:22rem;padding:.45rem .65rem;border-radius:8px;pointer-events:none;
  background:var(--fg);color:var(--bg);font-size:.82rem;line-height:1.35;box-shadow:0 4px 14px rgba(0,0,0,.2);
}
.tip b,.tip span{display:block;overflow-wrap:anywhere}
.tip span:empty{display:none}
.tip span:nth-of-type(1){opacity:.8}

@media (max-width:820px){
  .cards{grid-template-columns:1fr}
}
/* Chat page, and the links that lead to it */
a.agent{text-decoration:none}
a.agent:hover .agent-name{text-decoration:underline}
.item a.chat{text-decoration:none}
.item a.chat:hover{text-decoration:underline}
.chat-page .chat-of,.chat-page p.dim{color:var(--dim)}
.chat-page .chat-of{margin:-.25rem 0 .75rem}
.chat-page .chat-note{color:var(--dim);font-size:.92rem;margin:0 0 .6rem;max-width:44rem}
.chat-page h2{font-size:1rem;margin:1.25rem 0 .5rem}
.mission-empty[hidden]{display:none}
.compose .send-error{color:var(--danger);font-size:.88rem}
.compose .send-error[hidden]{display:none}

/* Touch. A fingertip needs about 40px; the desktop sizes above are tuned for a pointer. The small
   inline links (a chat name, Answer) grow their hit area with padding and cancel it with an equal
   negative margin, so the lines around them do not move. Each browse row puts its title on a line
   of its own so Open, Leave and the dots are not squeezed beside it. */
@media (max-width:820px),(pointer:coarse){
  .bar .brand,.bar nav a{display:inline-flex;align-items:center;min-height:2.5rem}
  button{min-height:2.5rem}
  form.compose select{min-height:2.5rem}
  form.compose label.check{min-height:2.5rem}
  form.compose label.check input{width:1.2rem;height:1.2rem}
  .board-menu summary,.sidebar-toggle{width:2.5rem;height:2.5rem}
  .sidebar .sidebar-boards a{padding:.55rem 0}
  details.archived-boards > summary{padding:.6rem 0}
  .tabs a,.board-head .tabs a{padding:.7rem .1rem}
  .item .meta a.chat,.item.kind-task a,.item.kind-event a,.item .acts a,ul.boards li>a:not(:first-child){padding:.7rem .3rem;margin:-.7rem -.3rem}
  ul.boards .board-main>a{padding:.7rem 0 .1rem;margin:-.7rem 0 0}
  ul.boards li{flex-wrap:wrap}
  ul.boards li .board-main.wide{flex-basis:100%}
}
@media (hover:none){form.compose .hint{display:none}}

/* Motion is decoration here: nothing depends on a transition finishing. mission.js already checks
   the same preference before it animates. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition:none!important;animation:none!important}
}
