/* BrainRise Ops — one stylesheet, no framework.
   Same palette as the workbook: slate + blue accent, status chips. */

:root {
  --dark: #131a2b;
  --accent: #2f6bed;
  --accent-soft: #eef4ff;
  --bg: #f7f8fb;
  --card: #ffffff;
  --border: #e7eaf1;
  --muted: #6b7488;
  /* Body ink is NOT black. Near-black text at heavy weights is what makes an
     interface look shouted rather than written; #2a3346 reads as firmly as
     #0f172a and stops the page from feeling stamped on. --ink is the darkest
     we ever go, and only headings get it. */
  --text: #2a3346;
  --ink: #171d2b;
  --danger: #d92d20;
  --danger-soft: #fef2f2;
  --back: #a855f7;
  --back-soft: #f6eefe;
  --ok: #12996a;
  --ok-soft: #e9f8f1;
  --warn-soft: #fef6e7;
  --r: 10px;
  --shadow: 0 1px 2px rgb(16 24 40 / .05);
  --shadow-lift: 0 4px 12px rgb(16 24 40 / .08);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  /* Segoe UI Variable is Windows 11's optically-sized face — noticeably finer
     than plain Segoe UI at both ends of the scale. Text for running copy,
     Display for headings, and the old stack behind them for everyone else. */
  font: 15px/1.55 "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system,
        Roboto, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -.003em;
  background: var(--bg); color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
button { font: inherit; cursor: pointer; letter-spacing: inherit; }
input, select, textarea { font: inherit; letter-spacing: inherit; }
/* 600, never 700: a heading is told apart by its size and its colour, and the
   extra weight on top is the third way of saying the same thing. */
/* The Display cut is optically sized for headings — tighter spacing, finer
   joints. Spelled out in full rather than ending the list with `inherit`,
   which is only legal on its own and quietly voids the whole declaration. */
h1, h2, h3 {
  margin: 0 0 .5em; color: var(--ink); font-weight: 600; letter-spacing: -.018em;
  font-family: "Segoe UI Variable Display", "Segoe UI Variable Text", "Segoe UI",
               system-ui, -apple-system, Roboto, "Helvetica Neue", Arial, sans-serif;
}
b, strong { font-weight: 600; }
/* Counts, percentages and times line up under each other instead of jittering
   as the digits change. */
.rm-n, .bd-pct, .bd-steps b, .bd-est, .bd-act, .stat .n, .rm-prog { font-variant-numeric: tabular-nums; }
.muted { color: var(--muted); }
.tiny { font-size: 12px; }
.error { color: var(--danger); font-size: .9em; }

/* MUST come before any rule that sets `display` on an element we toggle with
   the `hidden` attribute. `[hidden]` is only `display:none` in the UA sheet,
   so a plain `#view-login { display: grid }` silently defeats it — the login
   card then stays painted over the signed-in app forever. Cost us a long hunt. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------- login */
#view-login { display: grid; place-items: center; min-height: 100dvh; padding: 16px; }
/* What the search box found, hanging under it. Name first and in bold, because
   that is what you typed; the path under it in small, because that is how you
   tell two files with the same name apart. */
#search-wrap { position: relative; display: flex; flex: 0 1 360px; min-width: 0; }
#search-wrap #search { flex: 1; min-width: 0; }
#search-hits { position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
               width: min(440px, 84vw); max-height: 60dvh; overflow-y: auto; padding: 6px;
               background: var(--card); border: 1px solid var(--border); border-radius: 12px;
               box-shadow: 0 14px 34px rgb(16 24 40 / .18); }
#search-hits button { display: block; width: 100%; padding: 7px 9px; border: 0; border-radius: 9px;
                      background: none; text-align: left; cursor: pointer; }
#search-hits button:hover, #search-hits button:focus-visible { background: var(--accent-soft); }
#search-hits b { display: block; font-size: 13px; font-weight: 600; color: var(--text);
                 overflow-wrap: anywhere; }
#search-hits span { display: block; margin-top: 1px; }
#search-hits p { margin: 6px 8px; }
.login-card {
  background: var(--card); border: 1px solid var(--border); border-radius: 16px;
  box-shadow: var(--shadow); padding: 40px 32px; width: min(360px, 100%);
  text-align: center;
}
/* The game's own icon, rounded the way the stores round it. A square logo in a
   round-cornered tool looks like a sticker somebody left on. */
.login-logo { width: 64px; height: 64px; border-radius: 16px; margin-bottom: 10px;
              box-shadow: var(--shadow); }
.login-card h1 { font-size: 22px; }
#login-code {
  width: 100%; text-align: center; letter-spacing: 2px; font-size: 18px;
  padding: 12px; margin: 16px 0 12px; border: 1px solid var(--border);
  border-radius: var(--r); text-transform: uppercase;
}
#login-code:focus { outline: 2px solid var(--accent); border-color: transparent; }

/* ------------------------------------------------------------- shell */
#topbar {
  display: flex; align-items: center; gap: 12px;
  background: var(--dark); color: #fff; padding: 10px 16px;
  position: sticky; top: 0; z-index: 20;
}
.brand { white-space: nowrap; display: inline-flex; align-items: center; gap: 8px; }
.brand-logo { width: 26px; height: 26px; border-radius: 7px; display: block; }
#search {
  flex: 1; max-width: 420px; padding: 7px 12px; border-radius: 8px;
  border: 1px solid #334155; background: #1e293b; color: #fff; min-width: 60px;
}
#search::placeholder { color: #94a3b8; }

/* The bell sits to the left of the avatar and carries a dot when there is
   something unread — news is read once, so it gets an icon and not a tab. */
.icon-btn { margin-left: auto; border: 0; background: none; color: #fff;
            font-size: 17px; line-height: 1; padding: 6px; border-radius: 8px;
            position: relative; }
.icon-btn:hover { background: #1e293b; }
.icon-btn i { position: absolute; top: 3px; right: 3px; width: 8px; height: 8px;
              border-radius: 50%; background: #f59e0b; }
/* With the bell taking the left margin, the avatar sits right beside it. */
.icon-btn + .avatar { margin-left: 4px; }

/* ------------------------------------------------------------- profile */
.avatar { margin-left: auto; width: 34px; height: 34px; flex: 0 0 34px;
          border: 0; border-radius: 50%; color: #fff; font-weight: 600;
          font-size: 13px; letter-spacing: .02em; display: inline-flex;
          align-items: center; justify-content: center;
          box-shadow: 0 0 0 2px rgb(255 255 255 / .18); }
.avatar:hover { box-shadow: 0 0 0 3px rgb(255 255 255 / .35); }
.avatar.lg { margin: 0; width: 42px; height: 42px; flex-basis: 42px; font-size: 16px;
             box-shadow: none; }

.profile-menu { position: fixed; z-index: 60; width: 244px; padding: 12px;
                background: var(--card); color: var(--text);
                border: 1px solid var(--border); border-radius: 14px;
                box-shadow: 0 12px 32px rgb(15 23 42 / .18); }
.pm-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.pm-label { font-size: 11px; font-weight: 600; letter-spacing: .04em;
            text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }
.pm-colors { display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; }
.sw { width: 100%; aspect-ratio: 1; border: 0; border-radius: 50%; padding: 0; }
.sw.on { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--text); }
.pm-item { width: 100%; margin-top: 14px; padding: 9px; border-radius: 9px;
           border: 1px solid var(--border); background: none; color: var(--text);
           font-weight: 600; text-align: left; }
.pm-item:hover { border-color: var(--accent); color: var(--accent); }
/* A statement, not a button: once the tool IS the app, there is nothing left
   to press — so it does not look pressable. */
.pm-note { margin-top: 14px; padding: 9px; }
/* on / off, read at a glance without opening the panel. */
.pm-state { float: right; font-size: 11px; font-weight: 600; text-transform: uppercase;
            letter-spacing: .04em; color: var(--muted); }
.pm-state.on { color: var(--ok, #16a34a); }
/* The one switch in the Notifications panel. */
.nf-row { display: flex; align-items: center; gap: 12px; margin: 14px 0 6px; }
.nf-row > div { flex: 1; }
.pm-out { width: 100%; margin-top: 8px; padding: 9px; border-radius: 9px;
          border: 1px solid var(--border); background: none; color: var(--danger);
          font-weight: 600; }
.pm-out:hover { background: var(--danger-soft); border-color: var(--danger-soft); }

#tabs {
  display: flex; gap: 2px; background: var(--card);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 52px; z-index: 19;
  overflow-x: auto; scrollbar-width: none;
}
.tab {
  border: 0; background: none; padding: 11px 16px; color: var(--muted);
  border-bottom: 2px solid transparent; white-space: nowrap;
}
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

main { padding: 16px; max-width: 1280px; margin: 0 auto; }
/* The translation grid is six languages wide plus a sidebar: capping it at
   1280px wasted the screen and forced horizontal scrolling on a 6-column
   table. Development gets the whole window. */
body:has(#view-dev:not([hidden])) main { max-width: none; }/* The plan carries a pane on each side now, and the calendar in the middle was
   paying for both: a quarter more room on the screens that hold the hub. */
body:has(#view-roadmap:not([hidden])) main,
body:has(#view-calendar:not([hidden])) main { max-width: 1600px; }


/* ------------------------------------------------------------ common */
.btn {
  border: 1px solid var(--border); background: var(--card); color: var(--text);
  border-radius: 8px; padding: 7px 14px;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:hover { filter: brightness(1.1); color: #fff; }
.btn.danger { color: var(--danger); }
.btn.danger:hover { border-color: var(--danger); background: var(--danger-soft); }
.btn.back { color: var(--back); }
.btn.back:hover { border-color: var(--back); background: var(--back-soft); }
.btn.ghost { background: none; border-color: #334155; color: #cbd5e1; }
.btn.ghost:hover { color: #fff; border-color: #fff; }
.btn.wide { width: 100%; }
.btn.small { padding: 3px 9px; font-size: .85em; border-radius: 6px; }

.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow); padding: 16px;
}

.chip {
  display: inline-block; padding: 2px 10px; border-radius: 999px;
  font-size: .8em; font-weight: 600; white-space: nowrap;
}
.chip.Backlog       { background: #f1f5f9; color: #64748b; }
.chip.Todo          { background: #dbeafe; color: #1e40af; }
.chip.Inprogress    { background: #fef3c7; color: #92400e; }
.chip.Inreview      { background: #ede9fe; color: #5b21b6; }
.chip.Done          { background: #dcfce7; color: #166534; }
.chip.Planned       { background: #f1f5f9; color: #64748b; }
.chip.Indev         { background: #fef3c7; color: #92400e; }
.chip.Storereview   { background: #ede9fe; color: #5b21b6; }
.chip.Live          { background: #dcfce7; color: #166534; }
.chip.admin  { background: var(--dark); color: #fff; }
.chip.editor { background: var(--accent-soft); color: var(--accent); }
.chip.viewer { background: #f1f5f9; color: #64748b; }

/* =====================================================================
   Development, Level builder, Build and Activity — put back.

   These rules were deleted wholesale by the board rebuild (9de5993: 544 lines
   out, 123 in), which took the whole middle of the stylesheet with the part it
   meant to replace. Everything below is verbatim from the commit before it,
   minus the rules the old board owned and the two bare class names the new one
   has since taken over. The Development grid had been rendering as a bare
   browser table since; the cell editor, which is positioned over its cell by
   CSS alone, had been dropping into the flow underneath it.
   ===================================================================== */
/* ------------------------------------------------------------- lists */
.toolbar { display: flex; gap: 10px; margin-bottom: 14px; flex-wrap: wrap; align-items: center; }
.toolbar select { padding: 7px 10px; border-radius: 8px; border: 1px solid var(--border); }
table.list { width: 100%; border-collapse: collapse; background: var(--card); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; box-shadow: var(--shadow); }
table.list th { text-align: left; font-size: .78em; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: 10px 12px; border-bottom: 2px solid var(--border); background: #f8fafc; }
table.list td { padding: 10px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.list tr:last-child td { border-bottom: 0; }
table.list tr.rowlink { cursor: pointer; }
table.list tr.rowlink:hover td { background: var(--accent-soft); }
.table-wrap { overflow-x: auto; }
/* An empty list is a sentence, not a missing table. Used by Team when a
   search matches nobody and by Updates before the first entry — it was being
   rendered unstyled, which read as a rendering fault rather than an answer. */
.empty { text-align: center; padding: 34px 16px; color: var(--muted); }
.empty p { margin: 4px 0; }
.empty p:first-child { color: var(--text); font-weight: 600; }
/* Says which side of the line this screen is on, once, at the top: nothing
   here is in Drive. Cheaper than a badge on every row. */
.dev-note { margin: -6px 0 14px; }
/* Development / Games / Levels. Wraps rather than scrolls: a deep path on a
   phone should cost a second line, not a hidden one. */
.crumbs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
          margin-bottom: 8px; font-size: 13px; }
.crumbs .sep { color: var(--border); }
.crumb { border: 0; background: none; padding: 0; font-size: 13px;
         color: var(--muted); }
button.crumb:hover { color: var(--accent); text-decoration: underline; }
.crumb.here { color: var(--text); font-weight: 600; }
/* The Drive side of the boundary announces itself. Same rows, different
   consequence: creating here creates in Drive. */
.dev-note.drive { color: var(--accent); }
.dev-synced { margin-top: 12px; }
/* The icon is the door to Drive, so it has to look like something to press. */
.dev-row .mime { font-size: 15px; opacity: .8; padding: 4px 6px; border-radius: 8px;
                 cursor: alias; }
.dev-row .mime:hover { opacity: 1; background: var(--accent-soft); }
.chip.cat { background: var(--accent-soft); color: var(--accent); }
/* Folder / Sheet / Doc, picked before naming — three buttons rather than a
   select, because the choice changes what gets created in Drive and deserves
   to be visible rather than folded away. */
.kinds { display: flex; gap: 8px; margin: 14px 0 4px; flex-wrap: wrap; }
.kind { border: 1px solid var(--border); background: var(--card); color: var(--text);
        border-radius: 999px; padding: 7px 14px; font-weight: 600; font-size: 13px; }
.kind:hover { border-color: var(--accent); }
.kind.on { background: var(--accent); border-color: var(--accent); color: #fff; }
/* Reading a file — a page of its own, not a dialog. A settings sheet has a
   dozen tabs and hundreds of rows: it gets the window, and the table scrolls
   sideways inside it rather than pushing the page wide. */
/* A spreadsheet is its sheets down the LEFT and one table on the right. They
   used to be a strip across the top, which wrapped onto two lines on a
   twenty-sheet file and pushed the table off the screen — and a name is found
   far quicker down a list than along a row of pills read sideways. */
.sheet-split { display: grid; grid-template-columns: 236px minmax(0, 1fr);
               gap: 16px; align-items: start; margin-top: 14px; }
.sheet-split .file-body { margin-top: 0; min-width: 0; }
/* Follows the page down: the table is the long thing here, and having to
   scroll back to the top to change sheet is the strip's problem all over. */
.sheet-rail { display: flex; flex-direction: column; gap: 8px;
              position: sticky; top: 8px; max-height: calc(100vh - 120px); }
.rail-tabs { display: flex; gap: 4px; }
.rail-tab { flex: 1; border: 1px solid var(--border); background: var(--card);
            color: var(--muted); border-radius: 8px; padding: 6px 8px;
            font-weight: 600; font-size: 12px; }
.rail-tab:hover { border-color: var(--accent); }
.rail-tab.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.rail-list { display: flex; flex-direction: column; gap: 6px;
             overflow-y: auto; min-height: 0; padding: 2px; }
.rail-empty { margin: 8px 2px; }
/* One sheet, one card — and the card is the SLOT, not the name button inside
   it: the two icons belong within that frame, and hanging them off the side
   put them outside the thing they act on and half off the rail.

   Sheets are dragged, like the tabs of a spreadsheet: the one held follows the
   pointer and the others slide out of its way. No transition on the dragged
   one — it must sit under the cursor, not chase it. */
.tab-slot { display: flex; align-items: center; cursor: grab;
            transition: transform .12s ease;
            border: 1px solid var(--border); background: var(--card);
            border-radius: 8px; overflow: hidden; }
.tab-slot:hover { border-color: var(--accent); }
.tab-slot.on { background: var(--accent); border-color: var(--accent); }
/* A sheet hidden in the spreadsheet: visibly not one of the others. */
.tab-slot.off { border-style: dashed; }
.tab-slot.dragging { transition: none; cursor: grabbing; z-index: 2;
                     position: relative; border-color: var(--accent);
                     box-shadow: 0 6px 16px rgb(15 23 42 / .18); }
/* The name: the whole left of the card, and no frame of its own — the slot is
   already the frame. */
.sheet-card { flex: 1; min-width: 0; text-align: left; cursor: inherit;
              touch-action: none; border: 0; background: none; color: var(--text);
              padding: 8px 4px 8px 10px; font-weight: 600; font-size: 13px; }
.tab-slot.on .sheet-card { color: #fff; }
.sheet-name { display: block; overflow: hidden; text-overflow: ellipsis;
              white-space: nowrap; }
/* A document's tabs nest. The indent says so — it used to be an arrow glued to
   the front of the title, which sorted and searched as part of the name. */
.tab-slot[style*="--lvl"] .sheet-card { padding-left: calc(10px + var(--lvl, 0) * 12px); }
/* Always readable, never loud: they were invisible at rest and unreadable at
   size, which is two ways of not being a button. */
.sheet-acts { display: flex; padding-right: 4px; }
.sheet-acts .ico { display: flex; border: 0; background: none; color: var(--muted);
                   padding: 5px 5px; border-radius: 6px; cursor: pointer;
                   text-decoration: none; }
.sheet-acts .ico svg { width: 15px; height: 15px; fill: none; stroke: currentColor;
                       stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.sheet-acts .ico:hover { color: var(--accent); background: var(--accent-soft); }
.tab-slot.on .sheet-acts .ico { color: rgb(255 255 255 / .8); }
.tab-slot.on .sheet-acts .ico:hover { color: #fff; background: rgb(255 255 255 / .2); }
.rail-add { border: 1px dashed var(--border); background: var(--card);
            color: var(--accent); border-radius: 8px; padding: 8px;
            font-weight: 600; font-size: 13px; }
.rail-add:hover { border-color: var(--accent); }
/* Narrow: the rail goes back over the table, since 236px of it plus a table is
   more than a phone has. */
@media (max-width: 860px) {
  .sheet-split { grid-template-columns: minmax(0, 1fr); }
  .sheet-rail { position: static; max-height: none; }
  .rail-list { flex-direction: row; flex-wrap: wrap; }
  .tab-slot { flex: 0 0 auto; }
  .sheet-acts { opacity: 1; }
}
/* The title renames the file, so it has to look like something you can click. */
.dev-head h3.editable { cursor: text; border-radius: 6px; padding: 2px 6px;
                        margin-left: -6px; }
.dev-head h3.editable:hover { background: var(--accent-soft); color: var(--accent); }
.head-input { font-size: 19px; font-weight: 600; padding: 2px 6px; min-width: 260px;
              border: 1px solid var(--accent); border-radius: 6px;
              background: var(--card); color: var(--text); font-family: inherit; }
.file-body { margin-top: 14px; }
.file-body table.list td { white-space: pre-wrap; vertical-align: top; max-width: 420px; }
/* The header row stays put while a long tab scrolls — the first column of a
   settings tab means nothing without it. */
.file-body table.list th { position: sticky; top: 0; z-index: 1; }
/* An editable tab. The gestures are a spreadsheet's, so the look is too: a
   selection tinted, its anchor outlined, and no text selection getting in the
   way of dragging a rectangle.

   Nothing here may change a cell's size. A selection is a background and an
   outline — both free — and the editor is laid over the cell rather than put
   inside it, or every click would shove the table sideways. */
table.grid { user-select: none; }
/* `table.list` clips itself with overflow:hidden to round its own corners —
   which silently made the table a scroll container, so EVERY sticky cell in it
   stuck to the table (which never scrolls inside itself) rather than to
   .table-wrap. The row numbers and the sheet's own header row therefore never
   actually stayed in view, and pinned columns could not either. The clipping
   moves to the wrapper, which is the thing that really scrolls. */
table.grid { overflow: visible; border: 0; border-radius: 0; box-shadow: none; }
.file-body .table-wrap { border: 1px solid var(--border); border-radius: var(--r);
                         background: var(--card); box-shadow: var(--shadow); }
/* A line between the columns, a shade darker than the one between the rows:
   without it a wide settings tab reads as a paragraph of numbers, and the eye
   has to count across to know which column it is in. */
table.grid td, table.grid th { cursor: cell; border-right: 1px solid #cbd5e1; }
table.grid td:last-child, table.grid th:last-child { border-right: 0; }
/* Room after the last column.
   Scrolled all the way right, the last column ended flush against the edge of
   the pane — under its border, under the vertical scrollbar, with nothing
   after it — so the column somebody had just scrolled to reach was the one
   hardest to read and to click. The margin is INSIDE what scrolls, which is
   what makes it reachable rather than merely present.

   The min-width takes the margin off again, so a narrow tab that fits does not
   grow a scrollbar over 44 empty pixels. */
.table-wrap > table.grid { width: max-content; min-width: calc(100% - 44px);
                           margin-right: 44px; }
table.grid td { position: relative; }
/* An empty cell has no line inside it, so its row collapsed to its padding and
   a freshly added row came out half the height of the ones above it. The space
   is CSS's, not the cell's: textContent stays empty and so does the file. */
table.grid td:empty::after { content: "\00a0"; }
table.grid .sel { background: var(--accent-soft); }
table.grid .anchor { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Over the cell, exactly its width, growing downward when the text needs the
   room — a translation is three lines and a one-line field hid two of them.
   Never wider: the column is the column. */
.cell-input { position: absolute; top: 0; left: 0; width: 100%; min-height: 100%;
              padding: 10px 12px; font: inherit; line-height: inherit; border: 0;
              outline: 2px solid var(--accent); outline-offset: -2px;
              background: var(--card); color: var(--text);
              resize: none; overflow: hidden; white-space: pre-wrap;
              overflow-wrap: anywhere; z-index: 6;
              box-shadow: 0 8px 20px rgb(15 23 42 / .16); }
/* The letters and the numbers. No icons, no buttons: the margins of a
   spreadsheet are just the coordinates, and they are how a whole row or column
   is taken hold of. */
table.grid .colhead, table.grid .rowhead, table.grid .corner {
  background: #f1f5f9; color: var(--muted); font-size: 11px; font-weight: 600;
  text-align: center; padding: 4px 6px; user-select: none; cursor: pointer;
}
table.grid .rowhead { position: sticky; left: 0; z-index: 2; width: 42px;
                      border-right: 1px solid var(--border); }
table.grid .axis th { position: sticky; top: 0; z-index: 3; }
/* The sheet's own header row sits under the letters, not over them. */
table.grid thead tr:nth-child(2) th { top: 25px; }
table.grid thead tr:nth-child(2) th.rowhead { z-index: 6; }
/* Above the pinned columns, which stop under it rather than over it. */
table.grid .corner { position: sticky; left: 0; z-index: 6; }
table.grid .colhead.sel, table.grid .rowhead.sel { background: var(--accent);
                                                   color: #fff; }
/* Where a dragged row, column or block will land — shown while the pointer
   is still holding it, because a move you cannot see coming is a move you
   undo. */
table.grid .drop { box-shadow: inset 0 0 0 2px var(--accent); }
table.grid .dropzone { background: var(--accent-soft);
                       box-shadow: inset 0 0 0 1px var(--accent); }
/* A selection of several cells can be picked up from anywhere inside it. */
table.grid td.sel { cursor: grab; }
table.grid .colhead, table.grid .rowhead { cursor: grab; }
/* Wins over every cursor above: while something is being carried, the hand is
   closed wherever the pointer goes. */
table.grid.carrying, table.grid.carrying td, table.grid.carrying th,
table.grid.carrying .colhead, table.grid.carrying .rowhead { cursor: grabbing; }
/* What is being carried, under the pointer — the only part of a drag that
   moves with the hand. */
.drag-ghost { position: fixed; z-index: 70; pointer-events: none;
              background: var(--dark); color: #fff; font-size: 12px;
              font-weight: 600; padding: 4px 9px; border-radius: 999px;
              box-shadow: 0 6px 18px rgb(15 23 42 / .28); }
/* The line it will land on, on the side it will land. */
table.grid .drop-before { box-shadow: inset 3px 0 0 0 var(--accent); }
table.grid .drop-after { box-shadow: inset -3px 0 0 0 var(--accent); }
table.grid tr:has(.rowhead.drop-before) td,
table.grid .rowhead.drop-before { box-shadow: inset 0 3px 0 0 var(--accent); }
table.grid tr:has(.rowhead.drop-after) td,
table.grid .rowhead.drop-after { box-shadow: inset 0 -3px 0 0 var(--accent); }
table.grid .lifted { opacity: .55; }
/* Under the table: what it holds on the left, room to add on the right. */
.grid-foot { margin-top: 8px; align-items: center; gap: 10px; flex-wrap: wrap; }
.add-rows { display: inline-flex; align-items: center; gap: 6px;
            font-size: 12px; color: var(--muted); white-space: nowrap; }
.add-rows input { width: 68px; padding: 4px 6px; border: 1px solid var(--border);
                  border-radius: 6px; font: inherit; color: var(--text); }
/* The edge of a column, dragged to widen it. Tool-side only: a width is how
   somebody reads a table here, not something Drive should be told about. */
table.grid .colhead { position: relative; }
.colgrip { position: absolute; top: 0; right: -3px; width: 7px; height: 100%;
           cursor: col-resize; z-index: 5; }
.colgrip:hover { background: var(--accent); opacity: .45; }
/* Columns pinned to the left edge, so English stays in view while German is
   read. A pinned cell needs an opaque background of its own: it is scrolled
   UNDER, and the default transparent one let the text of the column passing
   behind it show through. Always a run from the first column, so the sticky
   offsets are simply the widths before it — a pin that skipped one would leave
   a hole that scrolls. */
table.grid .frz { position: sticky; z-index: 1; background: var(--card); }
table.grid td.frz.sel { background: var(--accent-soft); }
table.grid .colhead.frz { background: #f1f5f9; z-index: 5; }
table.grid .colhead.frz.sel { background: var(--accent); }
table.grid thead tr:nth-child(2) th.frz { background: #f8fafc; z-index: 5; }
/* Where the pinning stops, so the join is a line and not a coincidence. */
table.grid .frz-edge { border-right: 2px solid var(--accent); }
.doc-text { white-space: pre-wrap; font: inherit; margin: 0; overflow-wrap: anywhere; }
/* The document in the middle with its conversations on the right. The pane
   holds its width and the text takes the rest, so widening the window widens
   the reading column rather than the margin beside it. Room at the foot for
   the floating bar, which must never sit on the last line. */
.doc-split { display: grid; grid-template-columns: minmax(0, 1fr) 296px; gap: 18px;
             align-items: start; padding-bottom: 96px; }
.doc-main { min-width: 0; }
.doc-notes { position: sticky; top: 8px; max-height: calc(100vh - 160px);
             overflow-y: auto; }
.doc-notes h4 { margin: 0 0 10px; }
@media (max-width: 1200px) {
  .doc-split { grid-template-columns: minmax(0, 1fr); }
  .doc-notes { position: static; max-height: none; }
}
/* A document, laid out for reading and writing. Four styles, four sizes —
   that is the whole scheme, and the size comes from the TYPE rather than the
   tag so the page can keep a sensible outline underneath. */
.doc { background: var(--card); border: 1px solid var(--border); border-radius: var(--r);
       box-shadow: var(--shadow); padding: 30px 38px; max-width: 96ch;
       margin: 0 auto; overflow-wrap: anywhere; }
.doc > :first-child { margin-top: 0; }
.doc > :last-child { margin-bottom: 0; }
/* The title IS the tab's name: centred, 24, bold, and editing it renames the
   tab — so the document cannot be called one thing here and another in the
   rail. */
.doc-name { font-size: 24px; font-weight: 700; text-align: center; line-height: 1.25;
            margin: 0 0 1.4em; outline: 0; }
.doc-name:empty::before { content: 'Untitled'; color: var(--muted); }
.doc-h1 { font-size: 20px; font-weight: 700; line-height: 1.3; margin: 1.5em 0 .5em; }
.doc-h2 { font-size: 18px; font-weight: 600; line-height: 1.35; margin: 1.3em 0 .45em; }
.doc-h3 { font-size: 16px; font-weight: 600; line-height: 1.4; margin: 1.1em 0 .4em; }
.doc p, .doc-p, .doc-list li { font-size: 16px; font-weight: 400; line-height: 1.6; }
.doc p { margin: 0 0 .7em; }
.doc a { color: var(--accent); }
.doc-list { margin: 0 0 .9em; padding-left: 1.2em; }
.doc-list li { margin: .15em 0; margin-left: calc(var(--lvl, 0) * 1.3em); }
/* An empty paragraph is a pause in the reading — and a line you can write on,
   so it keeps enough height to be clicked. */
.doc-gap { min-height: 1.25em; margin: 0; }
.doc-table { margin: .3em 0 1em; width: auto; min-width: 60%; }
.doc-table td, .doc-table th { padding: 7px 11px; font-size: .95em; }
/* Column lines, which the app's own lists do without: a list is read across a
   row, but a document table is a grid you type into — and a cell with no edge
   is a cell you cannot aim at, especially an empty one. */
.doc-table td + td, .doc-table th + th { border-left: 1px solid var(--border); }
/* A table whose columns have been set stops measuring itself against its own
   contents: the width is the writer's, and text that fills a cell WRAPS, which
   makes the row taller. Without this, typing a long word in one cell shoves
   every other column sideways while you are still in the middle of the word. */
.doc-table.fixed { table-layout: fixed; width: 100%; }
/* A cell holds the document's own blocks, so it inherits their spacing — which
   is written for a page, not for a box 30 pixels tall. Inside a cell the gaps
   between lines close up; the lines themselves are unchanged. */
.doc-table td > *, .doc-table th > * { margin: 0; }
.doc-table td > * + *, .doc-table th > * + * { margin-top: .25em; }
.doc-table .doc-list { padding-left: 1.1em; }
.doc-table .doc-list li { margin: 0; }
/* An empty line in a cell still has to be clickable. */
.doc-table .doc-gap { min-height: 1.2em; }
.doc-table td, .doc-table th { vertical-align: top; overflow-wrap: break-word; }
/* The grip that a column border is dragged by: it sits over the border, is not
   part of what the cell says, and only shows itself when there is a pointer
   near enough to use it. */
.doc-table th, .doc-table td { position: relative; }
.col-grip { position: absolute; top: 0; right: -3px; bottom: 0; width: 7px;
            cursor: col-resize; user-select: none; z-index: 2; }
.col-grip::after { content: ''; position: absolute; inset: 0 3px; background: var(--accent);
                   opacity: 0; transition: opacity .12s; }
.col-grip:hover::after, .doc-table.sizing .col-grip::after { opacity: .85; }
/* While a border is being dragged, nothing else on the page should be picking
   up the pointer — a drag that lands on a word selects it. */
.doc-table.sizing { user-select: none; }
.doc .table-wrap { margin-bottom: 1em; }
/* Commented words. Underlined rather than block-highlighted: a comment marks a
   passage, it does not replace the reading of it. */
mark.doc-noted { background: none; color: inherit; cursor: pointer;
                 box-shadow: inset 0 -2px 0 0 #f59e0b; border-radius: 2px; }
mark.doc-noted:hover { background: #fef3c7; }
mark.doc-noted.on { background: #fde68a; }
/* One conversation, beside the words it is about. */
.dn-thread { border: 1px solid var(--border); border-left: 3px solid #f59e0b;
             border-radius: 10px; padding: 9px 11px; margin-bottom: 8px;
             background: var(--card); cursor: pointer; }
.dn-thread:hover { border-color: var(--accent); }
.dn-thread.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.dn-quote { font-size: 12px; color: var(--muted); font-style: italic;
            margin: 0 0 7px; padding-left: 8px; border-left: 2px solid var(--border); }
.dn-quote.gone { text-decoration: line-through; }
.dn-when { margin: 7px 0 0; }
.dn-del, .dn-reply { border: 0; background: none; padding: 0; cursor: pointer;
                     font: inherit; color: var(--accent); }
.dn-del { color: var(--danger); font-size: inherit; }
.dn-reply { margin-top: 6px; font-size: 12px; font-weight: 600; }
/* The document is editable throughout, so it carries a caret and no box: no
   outline on focus, no tint, nothing that says "form field". */
.doc[contenteditable="true"] { outline: 0; cursor: text; }
.doc[contenteditable="true"] table { cursor: default; }
/* The editing bar: floating, at the foot of the window, one icon per category.
   Down there rather than over the page: a document is read top to bottom, so a
   bar pinned above the first line sits on the very part being read. */
.doc-bar { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
           z-index: 45; display: flex; align-items: center; gap: 4px;
           padding: 6px; border-radius: 12px; border: 1px solid var(--border);
           background: var(--card); box-shadow: 0 8px 28px rgb(15 23 42 / .18); }
.db-tool { position: relative; display: flex; align-items: center; }
.db-btn { border: 0; background: none; color: var(--text); cursor: pointer;
           display: flex; align-items: center; justify-content: center;
           border-radius: 8px; padding: 7px; }
.db-btn svg { width: 19px; height: 19px; }
.doc-bar svg { fill: none; stroke: currentColor; stroke-width: 2.1;
               stroke-linecap: round; stroke-linejoin: round; }
/* The bar of the colour glyph carries the colour it reports. */
.db-ink { stroke: var(--muted); }
.db-btn:hover { background: var(--accent-soft); }
/* A button that cannot be used says so, rather than doing nothing when pressed. */
.db-btn:disabled { opacity: .35; cursor: default; }
.db-btn:disabled:hover { background: none; }
.db-tool.on .db-btn { background: var(--accent); color: #fff; }
.db-tool.on .db-ink { stroke: #fff; }
.db-sep { width: 1px; height: 22px; background: var(--border); margin: 0 3px; }
/* Menus open UPWARD: there is no room below a bar that sits on the bottom
   edge, and a menu off the end of the window is a menu nobody can reach. */
.db-pop { position: absolute; bottom: calc(100% + 10px); left: 0; min-width: 210px;
          padding: 5px; border-radius: 10px; background: var(--dark); color: #fff;
          box-shadow: 0 10px 30px rgb(15 23 42 / .35); }
.db-item { display: flex; align-items: center; gap: 8px; width: 100%;
           border: 0; background: none; color: inherit; cursor: pointer;
           border-radius: 7px; padding: 7px 9px; font-size: 13px; text-align: left; }
.db-item:hover { background: rgb(255 255 255 / .12); }
.db-item em { margin-left: auto; font-style: normal; font-size: 11px;
              color: rgb(255 255 255 / .5); }
.db-name { white-space: nowrap; }
/* The tick keeps its space whether or not it is shown, so the labels do not
   shuffle sideways as the caret moves between styles. */
.db-tick { width: 13px; flex: 0 0 13px; }
.db-item.on .db-tick::before { content: '✓'; font-size: 12px; }
.db-swatches { display: grid; grid-template-columns: repeat(6, 1fr); gap: 5px;
               padding: 5px 6px 8px; }
.db-colour { width: 20px; height: 20px; padding: 0; border-radius: 50%;
             border: 1px solid rgb(255 255 255 / .25); background: var(--c);
             cursor: pointer; }
.db-colour:hover { transform: scale(1.15); }
.db-clear { border-top: 1px solid rgb(255 255 255 / .14); border-radius: 0 0 7px 7px; }
/* What the saving is doing, in the bar with the controls. Empty most of the
   time, so it takes no room until it has something to say. */
.doc-status { font-size: 12px; color: var(--muted); white-space: nowrap;
              transition: color .15s ease; }
.doc-status:not(:empty) { padding: 0 8px 0 4px; }
/* A document's tabs nest. The indent says so — it used to be an arrow glued to
   the front of the title, which sorted and searched as part of the name. */
.tab-slot[style*="--lvl"] .sheet-card { padding-left: calc(10px + var(--lvl, 0) * 12px); }
/* Always readable, never loud: they were invisible at rest and unreadable at
   size, which is two ways of not being a button. */
.sheet-acts { display: flex; padding-right: 4px; }
.sheet-acts .ico { display: flex; border: 0; background: none; color: var(--muted);
                   padding: 5px 5px; border-radius: 6px; cursor: pointer;
                   text-decoration: none; }
.sheet-acts .ico svg { width: 15px; height: 15px; fill: none; stroke: currentColor;
                       stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.sheet-acts .ico:hover { color: var(--accent); background: var(--accent-soft); }
.tab-slot.on .sheet-acts .ico { color: rgb(255 255 255 / .8); }
.tab-slot.on .sheet-acts .ico:hover { color: #fff; background: rgb(255 255 255 / .2); }
.rail-add { border: 1px dashed var(--border); background: var(--card);
            color: var(--accent); border-radius: 8px; padding: 8px;
            font-weight: 600; font-size: 13px; }
.rail-add:hover { border-color: var(--accent); }
/* Narrow: the rail goes back over the table, since 236px of it plus a table is
   more than a phone has. */
@media (max-width: 860px) {
  .sheet-split { grid-template-columns: minmax(0, 1fr); }
  .sheet-rail { position: static; max-height: none; }
  .rail-list { flex-direction: row; flex-wrap: wrap; }
  .tab-slot { flex: 0 0 auto; }
  .sheet-acts { opacity: 1; }
}
/* The title renames the file, so it has to look like something you can click. */
.dev-head h3.editable { cursor: text; border-radius: 6px; padding: 2px 6px;
                        margin-left: -6px; }
.dev-head h3.editable:hover { background: var(--accent-soft); color: var(--accent); }
.head-input { font-size: 19px; font-weight: 600; padding: 2px 6px; min-width: 260px;
              border: 1px solid var(--accent); border-radius: 6px;
              background: var(--card); color: var(--text); font-family: inherit; }
.file-body { margin-top: 14px; }
.file-body table.list td { white-space: pre-wrap; vertical-align: top; max-width: 420px; }
/* The header row stays put while a long tab scrolls — the first column of a
   settings tab means nothing without it. */
.file-body table.list th { position: sticky; top: 0; z-index: 1; }
/* Commented words. Underlined rather than block-highlighted: a comment marks a
   passage, it does not replace the reading of it. */
mark.doc-noted { background: none; color: inherit; cursor: pointer;
                 box-shadow: inset 0 -2px 0 0 #f59e0b; border-radius: 2px; }
mark.doc-noted:hover { background: #fef3c7; }
mark.doc-noted.on { background: #fde68a; }
/* One conversation, beside the words it is about. */
.dn-thread { border: 1px solid var(--border); border-left: 3px solid #f59e0b;
             border-radius: 10px; padding: 9px 11px; margin-bottom: 8px;
             background: var(--card); cursor: pointer; }
.dn-thread:hover { border-color: var(--accent); }
.dn-thread.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.dn-quote { font-size: 12px; color: var(--muted); font-style: italic;
            margin: 0 0 7px; padding-left: 8px; border-left: 2px solid var(--border); }
.dn-quote.gone { text-decoration: line-through; }
.dn-when { margin: 7px 0 0; }
.dn-del, .dn-reply { border: 0; background: none; padding: 0; cursor: pointer;
                     font: inherit; color: var(--accent); }
.dn-del { color: var(--danger); font-size: inherit; }
.dn-reply { margin-top: 6px; font-size: 12px; font-weight: 600; }
/* The document is editable throughout, so it carries a caret and no box: no
   outline on focus, no tint, nothing that says "form field". */
.doc[contenteditable="true"] { outline: 0; cursor: text; }
.doc[contenteditable="true"] table { cursor: default; }
/* The colour palette raised by the shortcut, at the words it would colour —
   the bar's own menu is at the foot of the screen, and when a shortcut is used
   the eye is on the selection. Same swatches, so it is recognisably the same
   thing in a different place. */
.doc-palette, .doc-slash { position: fixed; z-index: 60; width: 210px; padding: 5px;
               border-radius: 10px; background: var(--dark); color: #fff;
               box-shadow: 0 10px 30px rgb(15 23 42 / .35); }
/* Why a configuration did not happen, said where the settings are rather than
   in a toast that fades — the reader is mid-adjustment and needs the answer to
   still be there when they look up. */
.mw-room { margin: 2px 0 10px; padding: 7px 9px; border-radius: 8px; font-size: 12px;
           line-height: 1.45; color: #92400e; background: #fef3c7;
           border: 1px solid #fde68a; }

/* The size picker: a shape to sweep out rather than two numbers to type. */
.db-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 3px; padding: 3px; }
.db-grid button { width: 100%; aspect-ratio: 1; padding: 0; border: 0; border-radius: 2px;
                  background: rgb(255 255 255 / .16); cursor: pointer; }
.db-grid button.on { background: var(--accent); }
.db-size { margin: 2px 0 3px; text-align: center; font-size: 11px; opacity: .6; }

/* Cells are typed into, so they say which one has the caret. */
.doc-table td:focus, .doc-table th:focus { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The row Enter would take. Marked by its background rather than by the tick
   the bar's menus use: a tick says "this is what the line already is", and
   here nothing is chosen yet. */
.doc-slash .db-item.sel { background: rgb(255 255 255 / .16); }
.doc-slash .db-item em { opacity: .55; }

/* History: the two versions side by side, full screen. Full screen because the
   question is always "what is different", and that is answered by seeing both
   at once — not by remembering one while reading the other. */
.hist { position: fixed; inset: 0; z-index: 90; display: flex; flex-direction: column;
        background: var(--bg, #f8fafc); }
.hist-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px;
             border-bottom: 1px solid var(--border); background: var(--card); }
.hist-head b { margin-right: 8px; }
.hist-body { flex: 1; display: grid; grid-template-columns: 232px minmax(0, 1fr);
             min-height: 0; }
.hist-list { border-right: 1px solid var(--border); background: var(--card);
             overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 6px; }
.hist-item { display: flex; flex-direction: column; gap: 2px; text-align: left;
             border: 1px solid var(--border); background: var(--card); color: var(--text);
             border-radius: 9px; padding: 8px 10px; font-size: 13px; cursor: pointer; }
.hist-item:hover { border-color: var(--accent); }
.hist-item.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.hist-item.on .muted { color: rgb(255 255 255 / .75); }
.hist-panes { display: grid; grid-template-columns: 1fr 1fr; min-height: 0; }
.hist-pane { display: flex; flex-direction: column; min-width: 0; min-height: 0;
             border-right: 1px solid var(--border); }
.hist-pane:last-child { border-right: 0; }
.hist-label { display: flex; align-items: baseline; gap: 8px; margin: 0;
              padding: 9px 16px; font-weight: 600; font-size: 13px;
              border-bottom: 1px solid var(--border); background: var(--card); }
.hist-doc { flex: 1; overflow-y: auto; padding: 14px 16px 40px; }
/* One line per line, so the two sides stay level with each other — this is a
   comparison, not a rendering of the document. */
.hist-line { padding: 3px 8px; border-radius: 5px; border-left: 3px solid transparent;
             font-size: 13px; line-height: 1.5; white-space: pre-wrap;
             overflow-wrap: anywhere; }
.hist-h1 { font-weight: 700; font-size: 15px; margin-top: .6em; }
.hist-h2 { font-weight: 600; font-size: 14px; margin-top: .5em; }
.hist-h3 { font-weight: 600; }
.hist-li::before { content: '• '; color: var(--muted); }
.hist-table { font-family: ui-monospace, monospace; color: var(--muted);
              background: rgb(15 23 42 / .04); }
.hist-empty { color: var(--border); }
/* What differs. Green on the side that has it, amber on the side that does
   not — the colours say which way the change went without a legend. */
.hist-now .hist-line.on { background: #dcfce7; border-left-color: #16a34a; }
.hist-then .hist-line.on { background: #fef3c7; border-left-color: #d97706; }
.hist-empty-all { padding: 48px; text-align: center; }

/* The saved / not-saved states of the bar's status line. */
.doc-status.ok { color: var(--ok, #16a34a); }
.doc-status.bad { color: var(--danger); }
/* ---------------------------------------------------- level generator */

/* The set as a page: levels | board | settings. Full width, because drawing
   needs the room and a hundred levels need a rail, not a dialog. */
.gen-page { display: grid; grid-template-columns: 230px minmax(0, 1fr) 280px;
            gap: 18px; align-items: start; margin-top: 4px; }
.gen-rail-wrap { display: flex; flex-direction: column; gap: 10px; }
.gen-rail { display: flex; flex-direction: column; gap: 8px; padding: 2px;
            max-height: calc(100vh - 240px); overflow-y: auto; }
/* A level at a glance — its shape is how it is recognised, which is the whole
   reason this screen exists instead of a spreadsheet of "1-2-3". */
.gen-card { display: flex; align-items: center; gap: 10px; text-align: left;
            padding: 8px 10px; background: var(--card); border: 1px solid var(--border);
            border-radius: var(--r); }
.gen-card:hover { border-color: var(--accent); }
.gen-card.liftable { cursor: grab; user-select: none; touch-action: none; }
.gen-card.liftable:active { cursor: grabbing; }
.gen-card.drag-ghost { opacity: .35; }
.gen-card.on { border-color: var(--accent); background: var(--accent-soft);
               box-shadow: 0 0 0 1px var(--accent); }
.gen-card b { font-size: 13px; display: block; }
.gen-card-txt { display: flex; flex-direction: column; }
.gen-mini { display: grid; gap: 1px; width: 44px; flex: 0 0 44px; }
.gen-mini i { aspect-ratio: 1; background: var(--bg); border: 1px solid var(--border); }
.gen-mini i.on { background: var(--text); border-color: var(--text); }
/* The board, centre stage. Cells sized by the column count, so a 2x2 is four
   big targets and a 12x12 still fits. */
.gen-board { display: flex; justify-content: center; padding-top: 6px; }
.gen-grid { display: grid; gap: 3px; width: min(100%, 580px); }
.gen-grid .cell { aspect-ratio: 1; padding: 0; border: 1px solid var(--border);
                  border-radius: 4px; background: var(--card); }
.gen-grid .cell:hover { border-color: var(--accent); }
.gen-grid .cell.on { background: var(--text); border-color: var(--text); }
/* Safe path: the two ends teal, the traps red — the colours the game itself
   shows, so the rail reads like the level sheet everybody already knows. */
.sp-grid .cell.s { background: #14b8a6; border-color: #0f9488; cursor: grab; }
.sp-grid .cell.s:active { cursor: grabbing; }
.sp-grid .cell.s.sp-lift { opacity: .4; }
.sp-grid .cell.b { background: #ef4444; border-color: #dc2626; }
.gen-mini i.s { background: #14b8a6; border-color: #0f9488; }
.gen-mini i.b { background: #ef4444; border-color: #dc2626; }
/* Rows and columns shoulder to shoulder: they are one fact about the board. */
.mw-dims { display: flex; gap: 10px; }
.mw-dims > div { flex: 1; }
/* The settings rail. Sticky so the fields and the preview stay in reach while
   the level list scrolls. */
.gen-side { background: var(--card); border: 1px solid var(--border);
            border-radius: var(--r); padding: 14px; position: sticky; top: 108px; }
.gen-side label { display: block; font-weight: 600; margin: 12px 0 4px; }
.gen-side label:first-child { margin-top: 0; }
.gen-side input { width: 100%; padding: 8px 10px; border: 1px solid var(--border);
                  border-radius: 8px; }
.gen-side .btn.wide { margin-top: 14px; }
/* What will actually be written — never out of sight, because the encoding is
   the thing that goes wrong. */
.gen-out-label { font-size: 11px; font-weight: 600; letter-spacing: .04em;
                 text-transform: uppercase; color: var(--muted); }
.gen-out { margin: 0; padding: 10px 12px; background: var(--bg); border-radius: 8px;
           font: 12px/1.6 ui-monospace, monospace; white-space: pre; overflow-x: auto;
           max-height: 220px; overflow-y: auto; }
/* Math crossword. The board mirrors the game: tiles on a dark stage, hidden
   cells in amber — the colour the game itself uses for "to be found". */
.mw-wrap { width: min(100%, 560px); }
/* The puzzle's real size — the filled part, which is what the sheet gets —
   above the board where it is read before the grid is counted. */
.mw-size { margin: 0 0 8px; font-size: 17px; font-weight: 600; }
.mw-size .tiny { font-weight: 500; margin-left: 6px; }
.mw-grid { display: grid; gap: 4px; background: var(--dark); padding: 18px;
           border-radius: var(--r); }
.mcell { aspect-ratio: 1; padding: 0; border: 1px dashed #334155; border-radius: 6px;
         background: transparent; color: var(--text); font-weight: 600;
         font-size: clamp(11px, 2.2vw, 17px); min-width: 0; }
.mcell.filled { background: #fdf3e3; border: 1px solid #fdf3e3; color: #1e293b; }
.mcell.hid { background: #f59e0b; border-color: #f59e0b; color: #fff; }
.mcell.sel { outline: 3px solid var(--accent); outline-offset: 1px; }
.mw-keys { margin-top: 10px; gap: 6px; }
.mw-keys .btn { min-width: 38px; font-weight: 600; }
.mw-two { display: flex; gap: 10px; }
.mw-two > span { flex: 1; }
/* The bars act on release-free 'input': no Generate button, no confirm. */
.gen-side input[type=range] { padding: 0; height: 26px; border: 0;
                              accent-color: var(--accent); cursor: pointer; }
.mw-count { float: right; color: var(--accent); }
/* Difficulty, in four bands. The same colours on the card badge, on the
   counter and on the bar itself: 12 out of 20 means nothing until it is
   orange. */
.d1 { color: #16a34a; }
 .d2 { color: #ca8a04; }
.d3 { color: #ea580c; }
 .d4 { color: #dc2626; }
.gen-side input[type=range].d1 { accent-color: #16a34a; }
.gen-side input[type=range].d2 { accent-color: #eab308; }
.gen-side input[type=range].d3 { accent-color: #ea580c; }
.gen-side input[type=range].d4 { accent-color: #dc2626; }
/* Top-right of the card, over the thumbnail: the first thing to read when
   scanning the rail for a level of the right level. */
.gen-card { position: relative; }
.dbadge { position: absolute; top: 6px; right: 6px; z-index: 1;
          min-width: 20px; padding: 1px 5px; border-radius: 6px;
          font-size: 11px; font-weight: 600; font-style: normal;
          line-height: 1.5; text-align: center; color: #fff; }
.dbadge.d1 { background: #16a34a; }
 .dbadge.d2 { background: #eab308; }
.dbadge.d3 { background: #ea580c; }
 .dbadge.d4 { background: #dc2626; }
.mw-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; min-height: 26px; }
.chip.mwh { background: #fef3c7; color: #92400e; }
.chip.mwt { background: var(--accent-soft); color: var(--accent); }
.mw-warn { margin-top: 8px; color: #b45309; }
.mw-warn p { margin: 3px 0; }
.gen-mini i.hid { background: #f59e0b; border-color: #f59e0b; }
/* The tray on the rail card: what the level asks for (amber) and what it
   bluffs with (blue), readable without opening the level. */
.mw-card-tiles { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 3px;
                 max-width: 130px; }
.mw-card-tiles .tile { font-size: 9px; font-weight: 600; font-style: normal;
                       padding: 1px 4px; border-radius: 4px; line-height: 1.5; }
.tile.th { background: #fef3c7; color: #92400e; }
.tile.tt { background: var(--accent-soft); color: var(--accent); }
/* Word grid. The board IS the game's screen: plain letters on a dark stage,
   each word struck through in its own colour. No cell chrome and no gaps —
   a word search is a field of letters, and drawing boxes round them turns the
   reading into counting. */
.wg-wrap { width: min(100%, 560px); }
.wg-stage { background: var(--dark); padding: 16px; border-radius: var(--r); }
/* The grid is the square the marks are drawn over, so it holds the padding
   OUTSIDE itself: cell centres have to land where the SVG says they do. */
.wg-grid { position: relative; display: grid; gap: 0;
           grid-template-columns: repeat(var(--w), 1fr);
           aspect-ratio: var(--w) / var(--h); }
.wg-grid i { position: relative; z-index: 1; display: grid; place-items: center;
             font-style: normal; font-weight: 600; color: #fff; letter-spacing: .02em;
             font-size: clamp(12px, 2.6vw, 22px); }
/* Drawn in CELL units and stretched over the grid, so nothing in the code has
   to know how many pixels a cell is. */
.wg-marks { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0;
            overflow: visible; }
.wg-marks line { stroke-width: .78; stroke-linecap: round; opacity: .5;
                 transition: opacity .12s; }
.wg-note { margin: 10px 2px 0; }
/* One line per word: the English in the grid, and what it means underneath —
   which is the half of this game that is not a puzzle. */
.wg-list { display: flex; flex-direction: column; gap: 4px; margin: 12px 0 2px; }
.wg-chip { display: flex; align-items: center; gap: 8px; padding: 4px 4px 4px 9px;
           border-radius: 8px; background: var(--bg); font-size: 12px;
           border-left: 4px solid var(--ink); }
.wg-chip b { font-weight: 700; letter-spacing: .04em; }
.wg-chip em { flex: 1; font-style: normal; color: var(--muted); min-width: 0; }
.wg-x { border: 0; background: none; cursor: pointer; padding: 2px 4px; line-height: 1;
        border-radius: 6px; font-size: 11px; opacity: .45; }
.wg-x:hover { opacity: 1; background: var(--card); }
.wg-lang { display: flex; gap: 8px; align-items: center; }
.wg-lang select { width: 78px; }
.wg-lang .btn { flex: 1; }
.gen-side select { width: 100%; padding: 8px 10px; border: 1px solid var(--border);
                   border-radius: 8px; background: var(--card); color: var(--text); }

/* One line: white drawings need a dark stage to exist at all. */
.ol-mini { width: 44px; height: 44px; flex: 0 0 44px; background: var(--dark);
           border-radius: 6px; display: flex; align-items: center;
           justify-content: center; overflow: hidden; }
.ol-mini img { max-width: 84%; max-height: 84%; }
.ol-stage { background: var(--dark); border-radius: var(--r); padding: 28px;
            display: flex; align-items: center; justify-content: center;
            width: min(100%, 560px); min-height: 320px; }
.ol-wrap { position: relative; display: inline-block; }
.ol-wrap img { display: block; max-width: 100%; max-height: 58vh;
               min-width: 220px; image-rendering: -webkit-optimize-contrast; }
/* The overlay is the editor now — it takes the clicks. */
.ol-wrap canvas { position: absolute; inset: 0; cursor: crosshair; }
.ol-name { margin: 0 0 2px; font-weight: 600; overflow-wrap: anywhere; }
.gen-side .btn.wide + .btn.wide { margin-top: 8px; }
@media (max-width: 940px) {
  .gen-page { grid-template-columns: 1fr; }
  .gen-side { position: static; }
  .gen-rail { flex-direction: row; overflow-x: auto; max-height: none; }
  .gen-card { flex: 0 0 auto; }
}
/* ------------------------------------------------------------ builds */
/* The drop zone is the feature: the dev drags an APK out of Android Studio
   and onto the page. The button exists for everyone who does not think to. */
#b-drop { border: 2px dashed var(--border); border-radius: var(--r);
          padding: 22px; text-align: center; color: var(--muted);
          margin-bottom: 14px; transition: border-color .15s, background .15s; }
#b-drop.over { border-color: var(--accent); background: var(--accent-soft);
               color: var(--accent); }
.up-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px;
          background: var(--card); border: 1px solid var(--border);
          border-radius: 10px; margin-bottom: 8px; }
.up-row .up-name { font-weight: 600; overflow-wrap: anywhere; flex: 1; }
.up-row progress { width: 180px; height: 8px; }
.up-row.failed { border-color: #fecaca; background: var(--danger-soft); }
.up-row.failed .up-pct { color: var(--danger); font-weight: 600; }
.arch-block { margin-top: 18px; }
.arch-block summary { color: var(--muted); font-weight: 600; cursor: pointer;
                      padding: 6px 0; }
/* ---------------------------------------------------------- activity */
/* The admin's inbox: one line per thing handed back up, waiting at the top.
   The question asked of this screen is "can I say yes to this", so the answer
   sits at the end of the line it belongs to. */
.av-group { margin-bottom: 18px; }
.av-row { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px;
          background: var(--card); border: 1px solid var(--border);
          border-radius: 10px; margin-bottom: 6px; box-shadow: var(--shadow); }
.av-row.pending { border-left: 3px solid var(--accent); }
/* A question of YOURS still sitting on somebody else. Amber, not accent: the
   two lists look alike on purpose, so the edge is what says which way round
   this one is — and amber is already the hourglass's colour in a thread. */
.av-row.av-waiting { border-left-color: #d97706; }
.av-row.approved, .av-row.returned { opacity: .72; }
.av-head { display: flex; align-items: flex-start; gap: 8px; }
.av-head { align-items: center; }
.av-head .av-line { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.av-row .av-note { margin-top: 8px !important; }
.av-acts { display: flex; gap: 5px; flex: 0 0 auto; }
.av-act { width: 26px; height: 26px; padding: 0; border: 1px solid var(--border);
          border-radius: 8px; background: var(--card); font-size: 13px; font-weight: 700;
          line-height: 1; }
.av-act.ok { color: var(--ok); }
.av-act.ok:hover { border-color: var(--ok); background: var(--ok-soft); }
.av-act.no { color: var(--back); }.av-act.wait { color: #b45309; }
.av-act.wait:hover { border-color: #b45309; background: var(--warn-soft); }
.av-act.no:hover { border-color: var(--back); background: var(--back-soft); }
.av-row[data-card], .av-row[data-hub-ping] { display: block; cursor: pointer; }
.av-row[data-card]:hover, .av-row[data-hub-ping]:hover { border-color: var(--accent); }
/* Everything under the first line sits flush left; only the date steps aside,
   to the bottom-right corner, where a date lives on anything printed. */
.av-card { margin: 6px 0 0; font-size: 13px; overflow-wrap: anywhere; }
.av-part { margin: 2px 0 0; font-size: 13px; color: var(--text); overflow-wrap: anywhere; }
.av-when { margin: 6px 0 0 !important; text-align: right; }
/* One frame per message, wearing its author's colour: the thread reads as
   who-said-what at a glance, which is what "where were we" asks. */
.av-msg { margin-top: 6px; padding: 5px 8px; border-radius: 8px; font-size: 13px;
          color: var(--text); background: color-mix(in oklab, var(--c) 7%, #fff);
          border-left: 3px solid var(--c); overflow-wrap: anywhere; white-space: pre-wrap; }
.av-msg-who { display: block; font-size: 10px; font-weight: 600;
              color: color-mix(in oklab, var(--c) 72%, #101828); }
.av-more { margin: 6px 0 0; }
.av-act.say { font-size: 11px; }
.av-act.say:hover { border-color: var(--accent); background: var(--accent-soft); }
.av-main { flex: 1; min-width: 0; }
.av-main p { margin: 0; }
.av-line { overflow-wrap: anywhere; }
.av-label { color: var(--ink); font-weight: 600; }
.av-note { margin-top: 4px !important; padding: 6px 9px; border-radius: 8px;
           background: var(--bg); font-size: 13px; white-space: pre-wrap;
           overflow-wrap: anywhere; }
.av-note.reply { background: var(--back-soft); }
.av-do { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; flex-wrap: wrap;
         justify-content: flex-end; }
.av-state { font-size: 12px; font-weight: 600; }
.av-state.approved { color: var(--ok); }
.av-state.returned { color: var(--back); }
/* A number on the tab, the same way the bell carries a dot. */
.tab-dot { margin-left: 6px; padding: 0 6px; border-radius: 999px; background: var(--accent);
           color: #fff; font-size: 11px; font-weight: 600; font-style: normal;
           line-height: 17px; display: inline-block; }
/* ------------------------------------------------------------- board */
/* A desk, not a report: what there is to do, the thing being done, and what is
   being said about it. Picking a task changes the middle and the right and
   nothing navigates, so the three panes scroll on their own and the page
   itself does not scroll at all. */
.bd-top { padding: 0 2px 12px; }
.bd-top-row { display: flex; align-items: baseline; gap: 14px; }
/* The release name IS the picker. A word you can click needs no box drawn
   around it — the chevron is the whole affordance, and it only speaks up when
   the pointer is near. */
.bd-relpick { display: inline-flex; align-items: center; gap: 6px; padding: 2px 6px;
              margin-left: -6px; border: 0; border-radius: 8px; background: none;
              font-family: "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
              font-size: 20px; font-weight: 600; letter-spacing: -.018em; color: var(--ink); }
.bd-relpick i { width: 0; height: 0; margin-top: 5px; border: 4px solid transparent;
                border-top: 5px solid var(--muted); opacity: .45; }
.bd-relpick:hover { background: var(--accent-soft); color: var(--accent); }
.bd-relpick:hover i { border-top-color: var(--accent); opacity: 1; }
/* Whose desk is on screen, next to which build it is. Admin only: for
   everybody else there is one answer and their own name is not news. */
.bd-whopick { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px;
              padding: 3px 8px 3px 4px; border: 1px solid var(--border); border-radius: 999px;
              background: var(--card); color: var(--muted); font-size: 12px; font-weight: 600; }
.bd-whopick i { width: 0; height: 0; border: 4px solid transparent;
                border-top: 5px solid var(--muted); opacity: .5; }
.bd-whopick:hover { border-color: var(--accent); color: var(--accent); }
.bd-whopick:hover i { border-top-color: var(--accent); opacity: 1; }
/* Who is on the task, changed from the task. */
.bd-whos { display: inline-flex; align-items: center; gap: 3px; padding: 3px 7px;
           border: 1px solid var(--border); border-radius: 999px; background: var(--card); }
.bd-whos:hover { border-color: var(--accent); }
.bd-whos-none { font-size: 12px; font-weight: 600; color: var(--muted); }
/* A card two people share: the list says which half is which, and the half
   that is not yours is there to be read, not worked on. */
.bd-cl-body .rm-check .avatar { flex: 0 0 auto; margin-left: 2px; }
/* Whose the line is, and the control that changes it: their face, or a + when
   it is still nobody's. Either way you click the same spot. */
.rm-check > .bd-own, .rm-check > .tiny-av, .rm-check > .bd-nobody { margin-left: 2px; }
.bd-own { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
          width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%;
          background: none; }
.bd-own:hover { box-shadow: 0 0 0 2px var(--accent-soft); }
.bd-own.none { border: 1px dashed var(--border); color: var(--muted); font-size: 14px;
               line-height: 1; }
.bd-own.none:hover { border-color: var(--accent); color: var(--accent); box-shadow: none; }
.bd-nobody { display: inline-flex; align-items: center; justify-content: center;
             width: 20px; height: 20px; border: 1px dashed var(--border); border-radius: 50%;
             color: var(--muted); font-size: 11px; font-style: normal; }
/* What happened, as against what was decided: a fact with a time on it gets a
   line, not a box. */
.ac-log { display: flex; align-items: center; gap: 8px; padding: 5px 10px; margin: 0 -10px;
          border-radius: 10px; }
.ac-log:hover { background: #f7f9fd; }
.ac-log p { flex: 1; min-width: 0; margin: 0; color: var(--muted); }
.ac-log p b { color: var(--text); font-weight: 600; }
.ac-log.card p b, .ac-log.card p { color: var(--ok); }

/* ---- Inbox: the sandbox */
.ib-shelves { display: flex; align-items: flex-start; gap: 14px; overflow-x: auto;
              padding-bottom: 20px; }
.ib-shelf { flex: 0 0 320px; }
.ib-shelf h3 { margin: 0 0 8px 2px; font-size: 13px; letter-spacing: .04em;
               text-transform: uppercase; color: var(--muted); }
.ib-box { margin-bottom: 10px; padding: 8px; background: #eef1f6; border-radius: 12px; }
.ib-box-head { display: flex; align-items: center; gap: 7px; padding: 1px 4px 6px;
               cursor: pointer; }
.ib-chev { flex: 0 0 auto; font-style: normal; font-size: 10px; color: var(--muted); }
.ib-box-head b { font-size: 13px; color: var(--ink); }
.ib-box-head .tiny { margin-right: auto; }
.ib-add { width: 22px; height: 22px; padding: 0; border: 0; border-radius: 50%;
          background: var(--card); color: var(--muted); font-size: 15px; line-height: 1; }
.ib-add:hover { color: var(--accent); }
.ib-new input { width: 100%; margin-bottom: 7px; padding: 7px 9px; font: inherit;
                font-size: 13px; color: var(--text); background: var(--card);
                border: 1px solid var(--accent); border-radius: 9px; }
/* An idea reads by its drawing first: the title is usually four words and the
   sketch is the thought. */
.ib-card { padding: 8px 9px; margin-bottom: 6px; background: var(--card);
           border: 1px solid var(--border); border-radius: 10px; cursor: pointer;
           box-shadow: 0 1px 2px rgb(16 24 40 / .04); }
.ib-card:hover { border-color: var(--accent); }
.ib-thumb { display: flex; justify-content: center; margin: -2px -3px 6px;
            padding: 4px 0; overflow: hidden; border-radius: 7px; background: #fbfcfe; }
.ib-title { margin: 0; font-size: 13px; font-weight: 600; color: var(--text);
            overflow-wrap: anywhere; }
.ib-foot { display: flex; align-items: center; gap: 6px; margin-top: 6px; }
.ib-mark { padding: 0 6px; border-radius: 5px; background: var(--bg); color: var(--muted);
           font-size: 10px; font-weight: 600; }

/* An idea, opened: a screen of its own and not a dialog. Three panes, like the
   board's desk — the shelf on the left so you can walk it without going back,
   the phone in the middle because that is what you came to draw, and what the
   idea SAYS on the right: notes over the resources, seven tenths to three. */
.ib-top { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.ib-top input { flex: 0 1 460px; min-width: 0; }
.ib-saved { min-width: 46px; text-align: right; }
.ib-page { display: grid; grid-template-columns: 250px minmax(0, 1fr) 330px; gap: 14px;
           align-items: stretch; height: calc(100dvh - 168px); min-height: 420px; }
.ib-rail { overflow-y: auto; overscroll-behavior: contain; padding: 10px;
           background: #f1f3f8; border-radius: 12px; }
.ib-rail-sec + .ib-rail-sec { margin-top: 14px; }
.ib-rail-sec h5 { margin: 0 0 6px; font-size: 11px; font-weight: 600; letter-spacing: .05em;
                  text-transform: uppercase; color: var(--muted); }
.ib-rail-row { display: block; width: 100%; margin-bottom: 5px; padding: 7px 9px; border: 0;
               border-radius: 9px; background: var(--card); text-align: left; cursor: pointer;
               box-shadow: 0 1px 2px rgb(16 24 40 / .04); }
.ib-rail-row b { display: block; font-size: 13px; font-weight: 600; color: var(--text);
                 overflow-wrap: anywhere; }
.ib-rail-row span { display: block; margin-top: 1px; }
.ib-rail-row:hover { box-shadow: 0 0 0 1px var(--accent); }
.ib-rail-row.on { box-shadow: 0 0 0 2px var(--accent); }
.ib-mid { display: flex; flex-direction: column; align-items: center; min-width: 0;
          min-height: 0; }
.ib-mid .ib-tools { flex: 0 0 auto; }
.ib-notes { display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.ib-note-box { flex: 7 1 0; display: flex; flex-direction: column; min-height: 0; }
.ib-res-box { flex: 3 1 0; display: flex; flex-direction: column; min-height: 0;
              overflow-y: auto; }
.ib-notes label { flex: 0 0 auto; margin: 0 0 6px; font-size: .8em; font-weight: 600;
                  color: var(--muted); }
.ib-note-box textarea { flex: 1; min-height: 0; width: 100%; padding: 10px 12px; font: inherit;
                        color: var(--text); border: 1px solid var(--border); border-radius: 10px;
                        background: var(--card); resize: none; }
.ib-where { flex: 0 0 auto; text-transform: uppercase; letter-spacing: .04em; font-weight: 600; }
.ib-tools { display: flex; align-items: center; gap: 4px; margin: 2px 0 6px; }
.ib-tools-sp { flex: 0 0 10px; }
.ib-tool { width: 30px; height: 28px; padding: 0; border: 1px solid var(--border);
           border-radius: 8px; background: var(--card); color: var(--text); font-size: 14px; }
.ib-tool:hover { border-color: var(--accent); color: var(--accent); }
.ib-tool.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.ib-ink { width: 20px; height: 20px; padding: 0; border: 1px solid var(--border);
          border-radius: 50%; background: var(--c); }
.ib-ink.on { box-shadow: 0 0 0 2px var(--card), 0 0 0 4px color-mix(in oklab, var(--c) 45%, #fff); }
/* A phone, and as much of the window's height as there is: the aspect ratio is
   the iPhone's, so the width follows from it rather than being guessed. */
.ib-frame { position: relative; flex: 1 1 auto; min-height: 0; aspect-ratio: 393 / 852;
            margin: 0 auto;
            border: 1px solid var(--border); border-radius: 20px; background: #fbfcfe;
            overflow: hidden; }
.ib-svg { display: block; width: 100%; height: 100%; touch-action: none; }
.ib-svg.can { cursor: crosshair; }
/* What is picked wears a dashed box and four corners, like anywhere else you
   have ever moved a rectangle. The box itself must not catch the pointer, or it
   would stand between you and the shape it is drawn around. */
.ib-selbox { pointer-events: none; }
.ib-grip { cursor: nwse-resize; }
.ib-grip[data-h="ne"], .ib-grip[data-h="sw"] { cursor: nesw-resize; }
/* Typing a label happens where the label will be, not in a dialog about it. */
.ib-type { position: absolute; z-index: 5; min-width: 90px; max-width: 70%; padding: 3px 6px;
           font: inherit; font-size: 13px; color: var(--text); background: var(--card);
           border: 1px solid var(--accent); border-radius: 6px;
           box-shadow: 0 4px 14px rgb(16 24 40 / .18); }
/* On a card it is a portrait tile, the shape of what it is a picture of. */
.ib-svg.thumb { width: 52px; height: 112px; }
.ib-drop { margin-top: 8px; padding: 12px; border: 1px dashed var(--border); border-radius: 10px;
           text-align: center; font-size: 12px; color: var(--muted); cursor: pointer; }
.ib-drop:hover, .ib-drop.over { border-color: var(--accent); color: var(--accent);
                                background: var(--accent-soft); }
.bd-ship { margin: 0 0 0 auto; }
.bd-ship.late { color: var(--danger); font-weight: 600; }
.bd-thin { height: 5px; margin: 8px 0 0; border-radius: 999px; background: #e7eaf1;
           overflow: hidden; }
.bd-thin i { display: block; height: 100%; border-radius: 999px;
             background: linear-gradient(90deg, var(--accent), var(--ok));
             transition: width .35s ease; }

/* The desk fills what is left of the window rather than guessing at it: main
   takes the height under the shell, the view stretches inside it, and the desk
   takes whatever the release strip above did not. Guessing meant a page that
   scrolled by exactly the height of that strip. */
body:has(#view-board:not([hidden])) main { display: flex; flex-direction: column;
                                           height: calc(100dvh - 107px); }
#view-board { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.bd-desk { display: grid; grid-template-columns: 290px minmax(0, 1fr) 330px; gap: 14px;
           align-items: stretch; flex: 1; min-height: 340px; }
.bd-pane { overflow-y: auto; overscroll-behavior: contain; }
.bd-queue { background: #f1f3f8; border-radius: 12px; padding: 10px; }
.bd-work { background: var(--card); border: 1px solid var(--border); border-radius: 12px;
           padding: 16px 18px; box-shadow: var(--shadow); }
.bd-talk { background: var(--card); border: 1px solid var(--border); border-radius: 12px;
           padding: 14px 16px; box-shadow: var(--shadow); }
.bd-empty { margin-top: 40px; text-align: center; }

/* ---- the queue */
/* Four icons instead of four headings stacked down a narrow pane: the pane
   was spending more of itself saying what the lists were than listing
   anything, and the one that mattered on a given morning was never the one at
   the top. Each carries its count and its own colour. */
.bq-tabs { position: sticky; top: -10px; z-index: 2; display: flex; gap: 4px;
           margin: 0 0 9px; padding: 4px 0 6px;
           background: linear-gradient(#f1f3f8 78%, #f1f3f800); }
.bq-tab { flex: 1; display: inline-flex; align-items: center; justify-content: center;
          gap: 5px; padding: 7px 0; border: 1px solid transparent; border-radius: 9px;
          background: none; color: var(--muted); font-size: 12px; font-weight: 600;
          font-variant-numeric: tabular-nums; line-height: 1; }
.bq-tab:hover { background: #e9edf6; color: var(--text); }
.bq-tab.on { background: var(--card); border-color: var(--border); color: var(--text);
             box-shadow: var(--shadow); }
.bq-tab.doing.on { color: var(--accent); }
.bq-tab.review.on { color: #b45309; }
.bq-tab.done.on { color: var(--ok); }
.bd-none { margin: 2px 6px; }
.bd-row { display: flex; align-items: flex-start; gap: 8px; width: 100%; text-align: left; color: var(--text);
          margin-bottom: 5px; padding: 8px 10px; border: 1px solid transparent;
          border-radius: 9px; background: var(--card); box-shadow: var(--shadow); }
.bd-row:hover { border-color: #cfd6e4; }
.bd-row.on { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.bd-row.waiting { border-left: 3px solid #d97706; background: var(--warn-soft); }
.bd-row.done { opacity: .65; }
/* Sent up, and out of your hands until somebody answers. Still there, still
   findable, no longer competing with the work you can actually get on with. */
.bd-row.review { background: #fafbfd; }
.bd-row.review .bd-row-title { color: var(--muted); }
.bd-row.review .bd-row-tags, .bd-row.review .bd-row-sub,
.bd-row.review .bd-row-who { opacity: .5; }
.bd-row.done .bd-row-title { text-decoration: line-through; color: var(--muted); }
.bd-row-main { flex: 1; min-width: 0; }
.bd-row-tags { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 4px; }
/* What is in flight and what is waiting on somebody else, in the colours those
   two states wear everywhere else in the tool. */
.bd-mark { display: inline-block; vertical-align: -1px; line-height: 0; }
.bd-row-title .bd-mark { margin-right: 5px; }
.bd-mark.doing { color: var(--accent); }
.bd-mark.review { color: #b45309; }
.bd-mark.back { color: var(--back); }
.bd-row-title { display: block; overflow-wrap: anywhere; }
.bd-row-sub { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; margin-top: 4px; }
.bd-row-sub:empty { display: none; }
.bd-row-who { display: flex; flex: 0 0 auto; }
.bd-row-who .tiny-av { width: 20px; height: 20px; flex-basis: 20px; font-size: 9px;
                       margin-left: -6px; border: 2px solid var(--card); }
.bd-row-who .tiny-av:first-child { margin-left: 0; }

/* ---- the task */
.bd-work-head { display: flex; align-items: center; gap: 8px; }
.bd-work-head .rm-big { flex: 1; font-size: 20px; }
/* The one flag the doer flips, left of the title where urgency used to sit:
   ⏳ waiting — a resource is missing, an answer has not arrived, the task
   cannot move. Amber, the colour everything waiting already wears here. */
.bd-wait { flex: 0 0 auto; border: 1px solid transparent; background: none; border-radius: 8px;
           font-size: 16px; line-height: 1; padding: 5px 7px; cursor: pointer;
           opacity: .4; filter: grayscale(1); }
.bd-wait:hover { opacity: .9; filter: none; border-color: var(--border); }
.bd-wait.on { opacity: 1; filter: none; background: var(--warn-soft); border-color: #d97706; }
span.bd-wait.on { cursor: default; }
/* The one button an agent presses that changes what happens next, so it sits
   in the corner of the task rather than at the bottom of a list. */
.bd-review-btn { display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
                 padding: 6px 13px; border: 0; border-radius: 9px;
                 background: var(--accent); color: #fff; font-size: 13px; font-weight: 600;
                 box-shadow: 0 1px 2px rgb(47 107 237 / .35); }
.bd-review-btn:hover { filter: brightness(1.07); }
.bd-review-btn.waiting { background: var(--accent-soft); color: var(--accent);
                         box-shadow: inset 0 0 0 1px #cfdefb; cursor: default; }
/* Came back: the same button, wearing the colour it came back under. */
.bd-review-btn.back { background: var(--back); box-shadow: 0 1px 2px rgb(168 85 247 / .35); }
.bd-facts { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 10px 0 2px; }
.bd-facts-sp { flex: 1; }
.bd-est { width: 66px; padding: 6px 8px; font: inherit; font-size: 13px; text-align: center;
          color: var(--text); background: var(--card); border: 1px solid var(--border);
          border-radius: 8px; }
.bd-est::placeholder { color: var(--muted); }
.bd-est:hover { border-color: var(--accent); }
.bd-pill { padding: 4px 10px; border-radius: 999px; border: 1px solid var(--border);
           background: var(--card); font-size: 12px; font-weight: 600; color: var(--muted); }
.bd-pill.doing { background: var(--accent-soft); border-color: #cfdefb; color: var(--accent); }
.bd-pill.done { background: var(--ok-soft); border-color: #c7ecdb; color: var(--ok); }
.bd-mini { width: 116px; padding: 4px 8px; border: 1px solid var(--border);
           border-radius: 8px; font-size: 12px; color: var(--muted); }
.bd-mini:focus { color: var(--text); outline: none; border-color: var(--accent); }
.bd-people { display: flex; flex-wrap: wrap; gap: 4px; margin: 10px 0 4px; }
.bd-agent { padding: 2px; border: 0; background: none; border-radius: 50%; line-height: 0; }
.bd-agent .tiny-av { opacity: .32; }
.bd-agent.on .tiny-av { opacity: 1; box-shadow: 0 0 0 2px var(--card), 0 0 0 4px var(--accent); }
.bd-agent:hover .tiny-av { opacity: .75; }
.bd-tagrow { margin: 8px 0 2px; }
/* A section name needs room under it, or the title reads as part of the first
   line of what it names. */
.bd-work label, .bd-talk label { display: block; margin: 22px 0 9px; font-size: .8em;
                                 font-weight: 600; color: var(--muted); }
/* No resize handle: the box is already the size of what is in it, and the
   handle was the only thing suggesting anybody should have to drag it. */
.bd-work textarea, .bd-talk textarea { width: 100%; padding: 8px 10px; font: inherit;
                                       border: 1px solid var(--border); border-radius: 8px;
                                       resize: none; overflow: hidden; min-height: 64px; }
.bd-sec { margin-top: 4px; }
/* The section's name on the left, what it can be given on the right. */
/* The name and the button that fills the section are on one line, and on the
   same line: the label carried its own top margin into the flex row, which
   pushed the words half a line below the + they sit beside. The spacing is the
   row's now, so both centre on it. */
.bd-sec-head { display: flex; align-items: center; justify-content: space-between;
               gap: 10px; margin: 22px 0 9px; }
.bd-sec-head label { flex: 0 0 auto; margin: 0; }
.bd-sec-head .bd-add-list { flex: 1; max-width: 280px; margin-left: auto; }
.bd-sec-head .bd-cl-new { margin-left: auto; }
.bd-sec-head .bd-add-list:not([hidden]) + .bd-cl-new { margin-left: 0; }
/* ---- the description: read first, edited on purpose */
.bd-desc { --lh: 1.5em; }
.bd-desc-read, .bd-desc .bd-desc-edit { display: block; width: 100%; padding: 8px 10px;
  font: inherit; line-height: 1.5; border: 1px solid var(--border); border-radius: 8px;
  background: var(--card); min-height: calc(5 * var(--lh) + 18px); }
.bd-desc-read { white-space: pre-wrap; overflow-wrap: anywhere; }
.bd-desc-read.empty { color: var(--muted); }
/* A caret and a hover edge are a promise that clicking will do something, so
   they are only made where there is an editor behind the text. */
.bd-desc:has(.bd-desc-edit) .bd-desc-read { cursor: text; }
.bd-desc:has(.bd-desc-edit) .bd-desc-read:hover { border-color: #cfd6e4; }
.bd-desc .bd-desc-edit:focus { border-color: var(--accent); outline: none; }
/* Ten lines and no further, with the last of them fading rather than cut in
   half: the fade is what says there is more, so the button underneath is a
   confirmation instead of a surprise. */
.bd-desc.long:not(.open) .bd-desc-read { overflow: hidden;
  max-height: calc(10 * var(--lh) + 18px);
  -webkit-mask-image: linear-gradient(#000 76%, transparent);
          mask-image: linear-gradient(#000 76%, transparent); }
.bd-desc.long:not(.open) .bd-desc-edit { overflow: auto;
  max-height: calc(10 * var(--lh) + 18px); }
.bd-desc-more { margin-top: 5px; padding: 3px 8px; border: 0; border-radius: 6px;
  background: none; color: var(--accent); font-size: 12px; font-weight: 600; }
.bd-desc-more:hover { background: var(--accent-soft); }
/* ---- checklists: the names on one line, the steps across the pane */
.bd-cl-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
/* A minimum width, so a two-letter name still reads as a tab and the row keeps
   its rhythm. The name takes the left, the count the right. */
.bd-tab { position: relative; display: flex; align-items: center; gap: 8px;
          justify-content: space-between; flex: 1 1 138px;
          padding: 6px 11px 9px; border: 1px solid var(--border); border-radius: 9px;
          background: var(--card); color: var(--muted); font-size: 13px; overflow: hidden; }
.bd-tab:hover { border-color: #cfd6e4; color: var(--text); }
.bd-tab.on { border-color: var(--accent); color: var(--text);
             box-shadow: 0 0 0 3px var(--accent-soft); }
.bd-tab-name { font-weight: 600; }
/* Each count in the colour of what it counts, and each in a frame of its own so
   neither ends up leaning on the edge of the tab. */
.bd-tab-counts { display: flex; align-items: center; gap: 5px; flex: 0 0 auto; }
.bd-chip { display: inline-flex; align-items: center; gap: 4px; padding: 1px 7px;
           border-radius: 999px; font-size: 11px; font-weight: 600; line-height: 17px;
           font-variant-numeric: tabular-nums; }
.bd-chip.doing { background: var(--accent-soft); color: var(--accent); }
.bd-chip.review { background: var(--warn-soft); color: #b45309; }
.bd-chip.back { background: var(--back-soft); color: var(--back); }
/* Round, and nothing else: the word "checklist" is written above it twice. */
.bd-cl-new { display: grid; place-items: center; width: 30px; height: 30px; padding: 0;
             border: 1px dashed #cfd6e4; border-radius: 50%; background: none;
             color: var(--muted); font-size: 17px; line-height: 1; }
.bd-cl-new:hover { border-style: solid; border-color: var(--accent);
                   background: var(--accent-soft); color: var(--accent); }
.bd-cl-new[hidden], .bd-add-list[hidden] { display: none; }
.bd-tab-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
              display: flex; background: #e7eaf1; }
/* Three segments rather than one fill: what is settled, what is with somebody
   else, what came back. A single colour had to pick which half of a list it
   was going to tell you about. */
.bd-tab-bar .seg { height: 100%; }
.bd-tab-bar .seg.done { background: var(--accent); }
.bd-tab-bar .seg.wait { background: #d97706; }
.bd-cl-body { margin: 9px 2px 0; }
/* The steps are the work, so they are the biggest text in the pane and each
   one takes a line it can be read on. The fill on hover is the row saying it
   is a row. */
/* A step is a <label>, and every label inside this pane is a section heading
   until told otherwise — which is what had been shrinking the work itself to
   12px of grey. It is the biggest text in the pane, in the ink the pane
   writes in, on a line of its own. */
.bd-cl-body .rm-check { display: flex; align-items: center; gap: 11px; margin: 0;
  padding: 7px 9px; border-radius: 8px; font-size: 16px; font-weight: 400; color: var(--text); }
.bd-cl-body .rm-check:hover { background: var(--accent-soft); }
/* An open hand over the step itself. The label stretches to where the buttons
   begin, so the hand covers exactly the part that means "pick this up" and
   stops where the part that means "press this" starts. */
.bd-cl-body .rm-check { cursor: default; }
.bd-cl-body .rm-check > span { cursor: pointer; user-select: text; }
/* The handle the drag lives on: quiet until the row is under the mouse, and
   the one place on the row where the cursor promises a grab. */
.bd-grip { flex: 0 0 auto; margin-right: -2px; color: var(--muted); font-size: 11px;
           font-style: normal; letter-spacing: -1px; cursor: grab; opacity: 0;
           user-select: none; }
.rm-check:hover .bd-grip { opacity: .7; }
.bd-grip:active { cursor: grabbing; }
.bd-tab.on .bd-tab-name { cursor: pointer; }
.bd-rename { width: 100%; min-width: 90px; padding: 1px 5px; font: inherit; color: var(--text);
             border: 1px solid var(--accent); border-radius: 6px; background: var(--card); }
.bd-tab-name .bd-rename { font-size: inherit; }
.bd-cl-body .rm-check.bd-lifted { opacity: .45; }
.bd-cl-body .rm-check button { cursor: pointer; }
.bd-cl-body .rm-check input[type=checkbox] { width: 16px; height: 16px; flex: 0 0 16px; }
/* The two things you do to a step, on the right of the step: ask somebody
   about it, or hand it up. Quiet until the row is under the mouse, but never
   invisible — a button you have to hunt for is a button nobody presses. */
/* No ring around them, in any state: the row is already saying it is under the
   mouse, and a circle drawn round an icon on the way past is the screen
   answering a question nobody asked. Colour is enough. */
.bd-ask { display: grid; place-items: center; width: 26px; height: 26px; padding: 0;
          border: 0; border-radius: 50%; background: none; outline: none;
          color: var(--muted); font-size: 14px; font-weight: 600; line-height: 1;
          opacity: .45; }
.rm-check:hover .bd-ask { opacity: 1; }
.bd-ask:hover { color: var(--accent); }
/* The one that came back is red at rest and full strength on hover: it is not
   a quiet action, it is the one thing on the row asking to be done again. */
.bd-ask.back { color: var(--back); opacity: 1; }
.bd-ask.back:hover { color: var(--back); filter: brightness(.9); }
.bd-ask.send { color: var(--accent); }

/* ---- the note, taken where it was asked for */
.np { position: fixed; z-index: 110; width: min(304px, calc(100vw - 20px));
      padding: 11px 12px 10px; background: var(--card); border: 1px solid #cfdefb;
      border-radius: 12px; box-shadow: 0 12px 30px rgb(16 24 40 / .16); }
.np-title { margin: 0 0 8px; font-size: 12px; font-weight: 600; color: var(--muted); }
.np-people { display: flex; flex-direction: column; gap: 2px; max-height: min(202px, 30dvh);
             overflow: auto; margin-bottom: 8px; }
.np-who { display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 6px;
          border: 0; border-radius: 8px; background: none; color: var(--text);
          text-align: left; font-size: 13px; }
.np-who:hover { background: var(--accent-soft); color: var(--accent); }
.np-who.on { background: var(--accent); color: #fff; }
.np-note { width: 100%; padding: 7px 9px; font: inherit; line-height: 1.45;
           border: 1px solid var(--border); border-radius: 8px; resize: none; }
.np-note:focus { border-color: var(--accent); outline: none; }
.np-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 8px; }
.bd-add-item input, .bd-add-list input { width: 100%; padding: 6px 8px; background: none;
  border: 1px solid transparent; border-radius: 6px; color: var(--muted); }
.bd-add-item input:hover, .bd-add-list input:hover { border-color: var(--border); }
.bd-add-item input:focus, .bd-add-list input:focus { border-color: var(--accent);
  background: var(--card); color: var(--text); outline: none; }
/* The frame IS the button. It was already the size of the target as far as
   anybody dropping something was concerned, and the two buttons inside it were
   asking which half of the same act you meant. */
.bd-drop { display: block; width: 100%; margin-top: 8px; padding: 16px 12px;
           border: 2px dashed var(--border); border-radius: 10px; background: none;
           text-align: center; color: var(--muted); font: inherit; font-size: 13px; }
.bd-drop:hover { border-color: #cfd6e4; color: var(--text); }
.bd-drop.over { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
/* A resource reads like a step, because it is treated like one: the row fills
   under the mouse, the name opens it, the @ asks somebody about it, and the
   right-click removes it. */
.bd-file { display: flex; align-items: center; gap: 10px; padding: 6px 9px;
           border-radius: 8px; border-bottom: 1px solid var(--border); }
.bd-file:hover { background: var(--accent-soft); }
.bd-file:last-of-type { border-bottom: 0; }
.bd-file a, .bd-file-name { flex: 1; min-width: 0; padding: 0; border: 0; background: none;
  color: var(--accent); text-align: left; font: inherit; overflow-wrap: anywhere; }
.bd-file-name:hover { text-decoration: underline; }
.bd-file-kind { opacity: .7; flex: 0 0 auto; }
.bd-file.on-focus { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.bd-file.on-focus.pending { background: var(--warn-soft); box-shadow: inset 2px 0 0 #d97706; }
.bd-file.on-focus.returned { background: var(--back-soft); box-shadow: inset 2px 0 0 var(--back); }
.bd-file.on-focus.approved { background: var(--ok-soft); box-shadow: inset 2px 0 0 var(--ok); }
/* Looking at it: the picture at the size the dialog can give it, the file's
   own name above, and the two things anybody opens one for underneath. */
.fp-shot { display: grid; place-items: center; max-height: 52dvh; overflow: auto;
           margin: 10px 0 4px; padding: 10px; background: #f4f6fb; border-radius: 10px; }
.fp-shot img { max-width: 100%; max-height: 48dvh; border-radius: 6px; }

/* ---- work handed back up for a look */
.bd-send { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.bd-up { padding: 2px 5px; border: 0; background: none; color: var(--muted);
         font-size: 13px; line-height: 1; border-radius: 6px; opacity: .5; }
.rm-check:hover .bd-up { opacity: 1; }
.bd-up:hover { background: var(--accent-soft); color: var(--accent); }
.bd-review { display: flex; align-items: flex-start; gap: 9px; padding: 6px 0;
             border-bottom: 1px solid var(--border); }
.bd-review > div { flex: 1; min-width: 0; }
.bd-review p { margin: 0; }
.bd-rv-state { flex: 0 0 auto; padding: 2px 8px; border-radius: 6px; font-size: 11px;
               font-weight: 600; background: var(--accent-soft); color: var(--accent); }
.bd-review.approved .bd-rv-state { background: var(--ok-soft); color: var(--ok); }
.bd-review.returned .bd-rv-state { background: var(--back-soft); color: var(--back); }

/* ---- somebody needs you, on a card that is not yours */
/* A circle in the corner with a number in it, and nothing else until it is
   asked. A panel across the middle of the screen is a panel in the way of the
   work it is interrupting. */
.pg { position: fixed; left: 20px; bottom: 20px; z-index: 40;
      display: flex; align-items: flex-end; gap: 10px; }
.pg-dot { flex: 0 0 auto; width: 42px; height: 42px; border: 0; border-radius: 50%;
          background: var(--accent); color: #fff; font-size: 15px; font-weight: 600;
          font-variant-numeric: tabular-nums;
          box-shadow: 0 6px 18px rgb(47 107 237 / .35); }
.pg-dot:hover { filter: brightness(1.07); }
.pg.open .pg-dot { background: var(--card); color: var(--accent);
                   box-shadow: 0 0 0 1px #cfdefb, 0 6px 18px rgb(16 24 40 / .12); }
.pg-panel { width: min(430px, calc(100vw - 100px)); padding: 6px;
            background: var(--card); border: 1px solid #cfdefb; border-radius: 14px;
            box-shadow: 0 12px 30px rgb(16 24 40 / .18);
            animation: pg-in .18s ease-out; }
@keyframes pg-in { from { opacity: 0; transform: translateY(8px); }
                   to   { opacity: 1; transform: translateY(0); } }
.pg-one { display: flex; align-items: center; gap: 9px; padding: 7px 8px;
          border-radius: 10px; }
.pg-one:hover { background: var(--accent-soft); }
.pg-one + .pg-one { border-top: 1px solid var(--border); }
.pg-main { flex: 1; min-width: 0; }
.pg-main p { margin: 0; }
/* One line each, cut rather than wrapped: a list that grows to fit a paragraph
   stops being a list. */
.pg-card, .pg-said { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pg-card { color: var(--ink); font-weight: 600; font-size: 13px; }
.pg-said { font-size: 12px; color: var(--muted); }
.bd-ping-proj { color: var(--muted); font-weight: 400; }
.pg-foot { display: flex; align-items: center; justify-content: flex-end; gap: 4px;
           padding: 4px 8px 2px; }
.pg-foot .tiny { margin-right: auto; }
.pg-arrow { width: 26px; height: 26px; padding: 0; border: 1px solid var(--border);
            border-radius: 8px; background: var(--card); color: var(--text);
            font-size: 14px; line-height: 1; }
.pg-arrow:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.pg-arrow:disabled { opacity: .35; cursor: default; }

/* ---- what is being said */
.bd-say { margin-top: 10px; }
.bd-say .btn { margin-top: 6px; }
/* The names turn up when the "@" does. */
.mention-pop { position: fixed; z-index: 80; padding: 4px; background: var(--card);
               border: 1px solid var(--border); border-radius: 10px;
               box-shadow: 0 10px 26px rgb(16 24 40 / .16); }
.mention-pop button { display: flex; align-items: center; gap: 8px; width: 100%;
                      padding: 5px 7px; border: 0; border-radius: 7px; background: none;
                      color: var(--text); text-align: left; font-size: 13px; }
.mention-pop button.on, .mention-pop button:hover { background: var(--accent-soft);
                                                    color: var(--accent); }
.bd-said { white-space: pre-wrap; overflow-wrap: anywhere; margin-top: 2px !important; }
.bd-at-said { color: var(--accent); }
/* ---- activity: one thread per thing worth answering */
/* A review and a mention are the two things somebody can answer, and each keeps
   its own conversation. One column holding four reviews and three questions is
   a column read back by nobody. */
.ac-thread { padding: 9px 10px; margin: 0 -10px; border-radius: 10px;
             border-bottom: 1px solid var(--border); }
.ac-thread:last-of-type { border-bottom: 0; }
.ac-thread[data-goto] { cursor: pointer; }
.ac-thread:hover { background: #f7f9fd; }
.ac-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 3px; }
.ac-what { font-size: 13px; font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.ac-thread .tiny.muted { margin: 0; }
.ac-state { flex: 0 0 auto; padding: 1px 7px; border-radius: 6px; font-size: 10px;
            font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
            background: var(--warn-soft); color: #b45309; }
.ac-state.approved { background: var(--ok-soft); color: var(--ok); }/* An answer is waited on: the one chip that is allowed to be loud. */
.ac-state.reply { background: var(--danger-soft); color: var(--danger); }
.ac-state.returned { background: var(--back-soft); color: var(--back); }
.ac-state.mention { background: var(--accent-soft); color: var(--accent); }
.ac-said { display: flex; align-items: flex-start; gap: 8px; margin-top: 7px; }
.ac-said > div { flex: 1; min-width: 0; }
.ac-said p { margin: 0; }
.ac-said.reply { padding-left: 9px; border-left: 2px solid var(--border); }
.ac-reply textarea { width: 100%; margin-top: 6px; padding: 5px 8px; font: inherit;
  font-size: 13px; line-height: 1.45; color: var(--text); background: none;
  border: 1px solid transparent; border-radius: 8px; resize: none; overflow: hidden; }
.ac-reply textarea:hover { border-color: var(--border); }
.ac-reply textarea:focus { border-color: var(--accent); background: var(--card); outline: none; }
/* Two tabs, because a review and a question are not answered the same way: one
   with a decision, the other with a sentence. They are also the pane's heading
   — the word "Activity" was already sitting there as a label, and one of the
   two had to become clickable anyway. */
.ac-tabs { display: flex; gap: 2px; margin: 0 -6px 9px; }
.ac-tab { padding: 4px 7px; border: 0; border-radius: 7px; background: none;
          font: inherit; font-size: .8em; font-weight: 600; color: var(--muted); }
.ac-tab:hover { color: var(--text); }
.ac-tab.on { color: var(--accent); background: var(--accent-soft); }
.ac-tab b { font-weight: 700; }
/* Somebody is waiting on YOU on this card — the one number on the tab worth
   a colour, since the mention circle in the corner never carries these. */
.ac-tab .ac-you { padding: 0 6px; border-radius: 999px; background: var(--accent);
                  color: #fff; font-size: 11px; }
.ac-tab.on .ac-you { background: var(--accent); }
/* The decision, in the corner of the thing it decides. */
.ac-do { display: flex; gap: 6px; margin-left: auto; }
/* The feed: one thing after another, no boxes. Drawing a frame around each
   sentence somebody said turns five sentences into five files. */
.ac-feed .ac-thread { border-bottom: 0; padding-top: 4px; padding-bottom: 4px; }
.ac-feed .ac-thread + .ac-thread { margin-top: 4px; }
/* Whose move it is, said in words. A question with nobody holding it is a
   question that dies quietly, and "who spoke last" is not the same thing. */
.ac-turn { margin: 4px 0 0 32px; font-size: 12px; font-weight: 600; color: #b45309; }
.pg-turn { margin: 1px 0 2px; font-size: 11px; font-weight: 600; color: var(--accent); }
.pg-turn.wait { color: #b45309; }
.pg-do { display: flex; flex-direction: column; gap: 4px; flex: 0 0 auto; align-items: stretch; }
.pg-do .btn { white-space: nowrap; }
.pg-one.back { background: var(--accent-soft); }
/* The thread being read, and the step it is about: both stay marked until the
   subject changes. A flash asks you to remember which row it was by the time
   you have finished reading the question. */
/* Four things, four colours: blue for being called for by name, amber for work
   waiting on somebody, red for work that came back, green for work that is
   finished with. The step it points at is highlighted in the same colour, or
   the two ends of one subject look like two subjects. */
.ac-thread.on, .bd-cl-body .rm-check.on-focus {
  background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.ac-thread.pending.on, .bd-cl-body .rm-check.on-focus.pending {
  background: var(--warn-soft); box-shadow: inset 2px 0 0 #d97706; }
.ac-thread.returned.on, .bd-cl-body .rm-check.on-focus.returned {
  background: var(--back-soft); box-shadow: inset 2px 0 0 var(--back); }
.ac-thread.approved.on, .bd-cl-body .rm-check.on-focus.approved {
  background: var(--ok-soft); box-shadow: inset 2px 0 0 var(--ok); }

@media (max-width: 1100px) {
  .bd-desk { grid-template-columns: 1fr; height: auto; }
  .bd-pane { overflow: visible; }
  .bq-tabs { position: static; background: none; }
}

/* ----------------------------------------------------------- roadmap */
/* A board, read left to right: what is not promised to a build yet, then one
   list per release. The vertical order inside a list IS the priority, so the
   lists are narrow and the cards are small — the point is seeing the top of
   three releases at once, not reading one card in comfort. */
.rm-board { display: flex; align-items: flex-start; gap: 12px; overflow-x: auto;
            padding-bottom: 10px; }
.rm-col { flex: 0 0 282px; display: flex; flex-direction: column;
          max-height: calc(100dvh - 215px);
          background: #f1f5f9; border-radius: 12px; padding: 10px 10px 8px; }
.rm-col.archived { opacity: .55; }
.rm-col-head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 8px; }
.rm-col-head b { flex: 1; overflow-wrap: anywhere; }
.rm-n { color: var(--muted); font-size: 12px; font-weight: 600; }
.rm-menu { border: 0; background: none; color: var(--muted); font-size: 16px;
           line-height: 1; padding: 2px 6px; border-radius: 6px; }
.rm-menu:hover { background: var(--border); color: var(--text); }
.rm-rename { flex: 1; min-width: 0; font: inherit; font-weight: 600;
             border: 1px solid var(--accent); border-radius: 6px; padding: 2px 6px; }
/* When a release ships and how far it has got, on every column header: the
   roadmap is where releases are decided, so it is where they are all seen. */
.rm-when { margin: -4px 4px 6px; color: var(--muted); }
.rm-when.late { color: var(--danger); font-weight: 600; }
.rm-fill { height: 4px; margin: 0 4px 8px; border-radius: 2px; background: #e2e8f0; }
.rm-fill i { display: block; height: 100%; border-radius: 2px;
             background: linear-gradient(90deg, var(--accent), var(--ok)); }
.rm-when-inline { flex: 0 0 auto; min-width: 150px; }
.rm-when-inline .rm-when { margin: 0; }
.rm-when-inline .rm-fill { margin: 3px 0 0; }
.rm-newcol-date { width: 100%; padding: 6px 8px; border: 1px solid var(--border);
                  border-radius: 8px; margin-bottom: 2px; }
.rm-newcol-form label { display: block; margin: 6px 0 2px; }
.rm-col-body { flex: 1 1 auto; overflow-y: auto; display: flex;
               flex-direction: column; gap: 8px; }
/* The empty space under the last card belongs to the list, so a card can be
   let go anywhere below it and not only exactly on top of something. */
.rm-normal { flex: 1 1 auto; min-height: 44px; display: flex;
             flex-direction: column; gap: 6px; }

/* ---- a card: what it is, whose it is, and nothing else */
.rm-card { display: flex; gap: 8px; align-items: flex-start; background: var(--card);
           border: 1px solid var(--border); border-radius: 8px; padding: 7px 9px;
           cursor: pointer; box-shadow: var(--shadow); }
.rm-card:hover { border-color: var(--accent); }
.rm-card.rm-dragging { opacity: .35; }
.rm-card.done .rm-title { color: var(--muted); text-decoration: line-through; }
.rm-card-main { flex: 1; min-width: 0; }
.rm-title { overflow-wrap: anywhere; }
.rm-card.waiting { background: var(--warn-soft); border-color: #d97706; }
.rm-meta { display: flex; align-items: center; gap: 6px; margin-top: 5px; flex-wrap: wrap; }
/**
 * Chips — tags, and everything shaped like one.
 *
 * One recipe, given a hue in --c: a pastel fill, a deeper shade of the SAME
 * hue for the letters, and a hairline between the two. The contrast lives
 * inside the chip rather than between the chip and the page, which is what
 * lets eleven of them sit in a row without the screen turning into a paint
 * chart. A saturated fill with white text reads as a warning label; this reads
 * as a category, which is what a tag is.
 */
.rm-tag, .rm-proj-tag, .bd-rel-tag, .bd-due, .bd-est, .bd-act {
  --c: #6b7488;
  display: inline-block; padding: 2px 8px; border: 0; border-radius: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: .01em; line-height: 16px;
  white-space: nowrap;
  background: color-mix(in oklab, var(--c) 13%, #fff);
  color: color-mix(in oklab, var(--c) 74%, #101828);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c) 18%, #fff);
}
.rm-tag { font-size: 10px; letter-spacing: .03em; text-transform: uppercase; }
.rm-tag.mini { padding: 1px 6px; font-size: 9px; line-height: 14px; }
.rm-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 5px; }
.rm-block > .rm-tags, .rm-mini > .rm-tags { margin: 0 3px 6px; }
.rm-proj-tag { --c: var(--accent); }
.bd-due { --c: #b45309; }
.bd-due.late { --c: #d92d20; }
.bd-act { --c: #12996a; }
.rm-ticks { font-size: 11px; font-weight: 600; color: var(--muted); }
.rm-meta .tiny-av { margin-left: auto; }
/* A dot, not a chip: three words of status on every card is more ink than the
   titles they sit under, and it is one click either way. */
.rm-dot { flex: 0 0 14px; width: 14px; height: 14px; margin-top: 3px; padding: 0;
          border-radius: 50%; border: 2px solid #cbd5e1; background: none;
          position: relative; }
.rm-dot.doing { border-color: var(--accent); background: var(--accent); }
.rm-dot.done { border-color: var(--ok); background: var(--ok); }
.rm-dot.done::after { content: "✓"; position: absolute; inset: 0; color: #fff;
                      font-size: 9px; line-height: 10px; text-align: center; }

/* The star a release build wears: its own last column, big enough to be
   read from across the room. */
.b-star-cell { width: 52px; text-align: right; padding-right: 14px; }
.b-star { color: #f59e0b; font-size: 30px; line-height: 1;
          text-shadow: 0 1px 2px rgb(245 158 11 / .35); }
.rel-row { padding: 4px 0; }

/* ---- comments on cells */
/* The mark a commented cell wears: a corner fold, quiet but findable. */
td.noted, th.noted { position: relative; }
td.noted::after, th.noted::after { content: ''; position: absolute; top: 0; right: 0;
  border: 5px solid transparent; border-top-color: var(--accent); border-right-color: var(--accent); }
.cn-flash { outline: 2px solid var(--accent); outline-offset: -2px; }
.cnp { position: fixed; z-index: 61; width: 340px; max-width: calc(100vw - 24px);
  padding: 12px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--card); box-shadow: 0 10px 30px rgb(15 23 42 / .2); }
.cnp-threads { display: flex; flex-direction: column; gap: 10px; max-height: 40vh;
  overflow: auto; margin-bottom: 8px; }
.cn-thread { display: flex; flex-direction: column; gap: 6px; }
.av-where { margin: 6px 0 0; }
.av-where + .av-card { margin-top: 0; }
.cn-at { margin: 0; }
.cnp .np-note { width: 100%; }
.cn-target { margin: 0 0 4px; }

/* ---- how a folder prints */
.dev-gear { padding: 6px 10px; font-size: 15px; line-height: 1; }
.pr-on { font-weight: 600; margin-bottom: 4px; }
.pr-lb { display: block; margin: 12px 0 4px; font-size: 12px; font-weight: 600;
         letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
#pr-fm { width: 100%; font-family: ui-monospace, "Cascadia Mono", Menlo, monospace;
         font-size: 13px; }
#pr-ig { width: 100%; font-family: ui-monospace, "Cascadia Mono", Menlo, monospace;
         font-size: 13px; }
/* The format and what it makes, side by side: the whole point is reading the
   second while typing the first. */
.pr-eg { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px;
         align-items: start; }
.pr-eg-t { border-collapse: collapse; margin-top: 4px; font-size: 11px; width: 100%; }
.pr-eg-t th, .pr-eg-t td { border: 1px solid var(--border); padding: 2px 5px;
                           text-align: left; white-space: nowrap; }
.pr-eg-t th { background: var(--soft); font-weight: 600; }
.pr-out { margin: 0; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
          background: var(--soft); font-family: ui-monospace, "Cascadia Mono", Menlo, monospace;
          font-size: 12px; line-height: 1.45; white-space: pre; overflow: auto;
          max-height: 220px; }
.pr-out.bad { border-color: var(--danger); color: var(--danger); background: var(--danger-soft);
              white-space: pre-wrap; }

/* ---- a project, seen from inside one release */
.rm-block { border: 1px solid #cbd5e1; border-radius: 10px;
            background: rgb(226 232 240 / .6); padding: 6px; }
.rm-block.rm-dragging { opacity: .35; }
.rm-block-head { display: flex; align-items: center; gap: 6px;
                 padding: 2px 3px 6px; cursor: pointer; }
.rm-block-title { flex: 1; font-weight: 600; font-size: 13px; overflow-wrap: anywhere; }
.rm-prog { font-size: 11px; font-weight: 600; color: var(--muted); }
.rm-members { display: flex; }
.rm-members .tiny-av { width: 18px; height: 18px; flex: 0 0 18px; font-size: 9px;
                       margin-left: -5px; border: 2px solid var(--card); }
.rm-more { align-self: center; margin-left: 3px; font-size: 10px; color: var(--muted); }
.rm-prog-bar { height: 3px; margin: 0 3px 6px; border-radius: 2px; background: #cbd5e1; }
.rm-prog-bar i { display: block; height: 100%; border-radius: 2px; background: var(--ok); }
.rm-prog-bar.big { height: 5px; margin: 8px 0 4px; }
.rm-block-body { display: flex; flex-direction: column; gap: 6px; min-height: 30px; }
.rm-block-body .rm-empty { margin: 2px 4px; }
/* The "nothing here yet" line goes the moment a card is dragged in, so the
   block never reads as empty while holding one. */
.rm-block-body:has(.rm-card) .rm-empty { display: none; }

/* ---- the two strips a column can grow */
.rm-urgent { display: flex; flex-direction: column; gap: 6px; padding: 6px;
             border: 1px solid #fecaca; border-radius: 10px;
             background: var(--danger-soft); }
.rm-urgent h5 { margin: 0 0 1px; font-size: 11px; letter-spacing: .05em;
                text-transform: uppercase; color: var(--danger); }
/* The switch that turns a column into its own archive: a tick in a ring,
   quiet until it is on and then unmistakably blue. */
.rm-seen { flex: 0 0 auto; width: 18px; height: 18px; padding: 0; border-radius: 50%;
           border: 1.5px solid #cbd5e1; background: none; color: var(--muted);
           font-size: 10px; line-height: 1; cursor: pointer; }
.rm-seen:hover { border-color: var(--accent); color: var(--accent); }
.rm-seen.on { border-color: var(--accent); background: var(--accent); color: #fff; }
.rm-done { border-top: 1px solid var(--border); padding-top: 6px; }
.rm-done summary { padding: 2px; font-size: 12px; font-weight: 600;
                   color: var(--muted); cursor: pointer; }
.rm-done-list { display: flex; flex-direction: column; gap: 6px;
                padding-top: 6px; min-height: 20px; }

/* ---- adding, everywhere, in one gesture */
/* Two buttons and not one: a task and a project are different things to start,
   and a menu asking which would put a decision in front of a title. */
.rm-adds { display: flex; gap: 4px; margin-top: 6px; }
.rm-add, .rm-newcol-open { flex: 1; padding: 7px 9px; border: 0; background: none;
                           color: var(--muted); font-weight: 600; text-align: left;
                           border-radius: 8px; white-space: nowrap; }
.rm-newcol-open { width: 100%; margin-top: 6px; }
.rm-add:hover, .rm-newcol-open:hover { background: #e2e8f0; color: var(--text); }
.rm-add[data-kind=project] { flex: 0 0 auto; }
/* Quiet until wanted: a project block is read for its tasks, not for its
   buttons, so the way to add one sits at the bottom in the smallest voice. */
.rm-block-plus { width: 100%; padding: 4px 6px; margin-top: 4px; border: 0;
                 background: none; color: var(--muted); font-size: 12px;
                 text-align: left; border-radius: 6px; }
.rm-block-plus:hover { background: rgb(255 255 255 / .7); color: var(--text); }
.rm-block-add { margin-top: 4px; }
.rm-block-add input { width: 100%; padding: 6px 8px; border: 1px solid var(--accent);
                      border-radius: 8px; }
.rm-compose { margin-top: 6px; }
.rm-compose textarea, .rm-newcol-form input {
  width: 100%; padding: 7px 9px; border: 1px solid var(--accent); border-radius: 8px;
  resize: none; box-shadow: var(--shadow); }
.rm-compose-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.rm-x { padding: 2px 6px; border: 0; background: none; color: var(--muted);
        font-size: 15px; line-height: 1; border-radius: 6px; }
.rm-x:hover { background: var(--border); color: var(--text); }
.rm-newcol.col { flex: 0 0 240px; }
.rm-newcol.row { max-width: 240px; margin-top: 4px; }
.rm-arch { display: flex; align-items: center; gap: 5px; }

/* ---- the same board, laid on its side */
/* One strip per release, read left to right, so the whole plan can be compared
   down the page. Deliberately not the same picture as the lists: a project is
   one card here. Lists are for working in, this is for seeing what is where. */
/* ---- Global: the plan itself, in time and in numbers */
.rm-global { max-width: 1000px; padding-bottom: 28px; }
.rm-loose { margin: 10px 0 0; }

/* A month, because the question a release date raises is which days the build
   owns — and that is a shape, not a number. */
.rm-cal { padding: 12px 14px 14px; background: var(--card); border: 1px solid var(--border);
          border-radius: 12px; box-shadow: var(--shadow); }
.rm-cal-head { display: flex; align-items: center; justify-content: center; gap: 14px;
               margin-bottom: 9px; }
.rm-cal-head b { font-size: 15px; color: var(--ink); }
.rm-cal-go { width: 26px; height: 26px; padding: 0; border: 1px solid var(--border);
             border-radius: 7px; background: var(--card); color: var(--muted);
             font-size: 15px; line-height: 1; }
.rm-cal-go:hover { border-color: var(--accent); color: var(--accent); }
.rm-cal-week, .rm-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.rm-cal-week span { padding: 2px 0; text-align: center; font-size: 10px; font-weight: 600;
                    letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.rm-cal-day, .rm-cal-pad { min-height: 58px; }
.rm-cal-day { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
              padding: 4px 6px; border: 1px solid var(--border); border-radius: 8px;
              font-style: normal; }
.rm-cal-day b { font-size: 12px; font-weight: 600; color: var(--muted); }
/* Covered by a release: its colour, softly. The day it ships wears the number,
   in the same colour the release row carries underneath. */
.rm-cal-day.held { background: color-mix(in oklab, var(--c) 10%, #fff);
                   border-color: color-mix(in oklab, var(--c) 22%, #fff); }
.rm-cal-day.held b { color: color-mix(in oklab, var(--c) 74%, #101828); }
.rm-cal-day.ships { background: color-mix(in oklab, var(--c) 18%, #fff);
                    border-color: color-mix(in oklab, var(--c) 45%, #fff); }
.rm-cal-day em { font-style: normal; font-size: 10px; font-weight: 700;
                 color: color-mix(in oklab, var(--c) 78%, #101828); }
.rm-cal-day.today { box-shadow: inset 0 0 0 2px var(--text); }
/* Who is around that day, in the corner of it — and the day itself is the
   button: saying "I am free on the 19th" should cost one click on the 19th.
   A day you are on keeps a thin ring, which the release colour underneath can
   still be read through. */
.rm-cal-day { position: relative; }
.rm-cal-day.can { cursor: pointer; }
.rm-cal-day.can:hover { border-color: var(--accent); }
.rm-cal-day.mine { box-shadow: inset 0 0 0 1px var(--accent); }
.rm-cal-day.today.mine { box-shadow: inset 0 0 0 2px var(--text); }
/* One dot per person, in their own colour. Four faces in the corner of a small
   square is a crowd; a month of them is a mess. The names are one hover away —
   the question on a calendar is usually "is anybody free", and only sometimes
   "who". */
.rm-cal-free { position: absolute; top: 4px; right: 5px; display: flex; gap: 3px; }
.rm-cal-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--c);
              box-shadow: 0 0 0 1.5px var(--card); }
.rm-cal-who { display: none; position: absolute; top: 14px; right: -2px; z-index: 30;
              min-width: 132px; padding: 7px 9px; background: var(--card);
              border: 1px solid var(--border); border-radius: 10px;
              box-shadow: 0 10px 26px rgb(16 24 40 / .16); font-style: normal;
              text-align: left; }
.rm-cal-free:hover .rm-cal-who { display: block; }
.rm-cal-who-row { display: flex; align-items: center; gap: 6px; padding: 2px 0;
                  font-size: 12px; color: var(--text); white-space: nowrap; }

/* One release, as a row: what it is, when it goes out, how full it is, and
   whose hands it is in. */
.rm-rel { margin-top: 8px; padding: 10px 12px 12px; background: var(--card);
          border: 1px solid var(--border); border-left: 3px solid var(--c);
          border-radius: 10px; box-shadow: var(--shadow); }
.rm-rel.archived { opacity: .72; }
.rm-rel-top { display: flex; align-items: center; gap: 8px; }
.rm-rel-num { font-size: 15px; color: var(--ink); }
.rm-rel-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; font-size: 13px; color: var(--muted); }
.rm-rel-chip { flex: 0 0 auto; padding: 1px 7px; border-radius: 6px; background: var(--bg);
               color: var(--muted); font-size: 10px; font-weight: 600; letter-spacing: .03em;
               text-transform: uppercase; }
.rm-rel-chip.on { background: var(--accent-soft); color: var(--accent); }
.rm-rel-chip.ok { background: var(--ok-soft); color: var(--ok); }
.rm-rel-chip.off { background: var(--danger-soft); color: var(--danger); }
.rm-rel-when { margin: 4px 0 0; color: var(--muted); }
.rm-rel-when.late { color: var(--danger); }
.rm-rel .rm-fill { margin: 8px 0 5px; }
.rm-rel-nums { margin: 0; }
.rm-rel-who { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 8px; }
.rm-who { display: flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 600;
          color: var(--muted); }

/* Planning one: the number, what it is for, the day it goes out. */
.rm-newrel { margin: 2px 0 4px; padding: 10px 12px; background: var(--card);
             border: 1px solid var(--border); border-radius: 10px; }
.rm-newrel-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.rm-newrel input[type="text"] { padding: 6px 9px; font: inherit; font-size: 13px;
                                color: var(--text); background: var(--card);
                                border: 1px solid var(--border); border-radius: 8px; }
.rm-nr-num { width: 110px; }
.rm-nr-name { flex: 1; min-width: 170px; }
.rm-archive { margin-top: 16px; }
.rm-archive > summary { font-size: .8em; font-weight: 600; color: var(--muted); cursor: pointer; }

.rm-agent .rm-col-head .avatar { flex: 0 0 auto; }
.rm-agent .rm-empty { margin: 6px 2px; }
/* The shared shelf: the same cards as the columns beside it, gathered once. */
.rm-shared { background: #eaf0fb; }
.rm-shared .rm-col-head b { color: var(--accent); }

/* One build fills the screen, and the arrows walk the plan in the order it
   ships. Six columns of releases answered a question Global answers better. */
.rm-pick { display: flex; align-items: center; gap: 10px; margin: 0 0 10px;
           padding: 8px 12px; background: var(--card); border: 1px solid var(--border);
           border-radius: 10px; box-shadow: var(--shadow); }
.rm-pick-mid { flex: 1; min-width: 0; text-align: center; }
.rm-pick-mid b { display: block; font-size: 16px; color: var(--ink); }
.rm-pick-sub { display: block; margin-top: 1px; font-size: 12px; font-weight: 400;
               color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rm-pick-go { width: 26px; height: 26px; padding: 0; border: 1px solid var(--border);
              border-radius: 7px; background: var(--card); color: var(--muted);
              font-size: 15px; line-height: 1; }
.rm-pick-go:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.rm-pick-go:disabled { opacity: .35; cursor: not-allowed; }

/* A task written in somebody's column is theirs, in the build on screen. */
.rm-ac-open { width: 100%; margin-top: 6px; padding: 7px 9px; border: 0; border-radius: 8px;
              background: none; color: var(--muted); text-align: left; font-size: 13px; }
.bd-queue .bd-row.rm-dragging { opacity: .35; }
.rm-ac-open:hover { background: #e2e8f0; color: var(--text); }
.rm-ac-add { margin-top: 6px; }
.rm-ac-add textarea { width: 100%; padding: 7px 9px; font: inherit; font-size: 13px;
                      color: var(--text); border: 1px solid var(--accent);
                      border-radius: 8px; resize: none; }
/* The shared shelf is the one column that is nobody's in particular, so it is
   the one that asks. Faces only: the tooltip is the name, and a row of names
   would be taller than the box they belong to. */
.rm-ac-who { display: flex; flex-wrap: wrap; gap: 1px; margin-top: 6px; }
.rm-ac-hint { margin: 4px 0 0; }

/* Work that outlives one build, and so has no column anywhere. */
.rm-proj { margin-top: 6px; padding: 9px 12px; background: var(--card);
           border: 1px solid var(--border); border-radius: 10px; cursor: pointer; }
.rm-proj:hover { border-color: var(--accent); }
.rm-proj-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.rm-proj .rm-fill { margin: 7px 0 0; }
.rm-proj-rels { margin: 5px 0 0; }
.rm-rel { cursor: pointer; }
/* Now, next or later: a decision, so it is a control and not a deduction. */
.rm-hz { display: inline-flex; gap: 2px; padding: 2px; background: #f1f5f9; border-radius: 9px; }
.rm-hz button { padding: 4px 10px; border: 0; border-radius: 7px; background: none;
                color: var(--muted); font: inherit; font-size: 12px; font-weight: 600; }
.rm-hz button:hover { color: var(--text); }
.rm-hz button.on { background: var(--card); color: color-mix(in oklab, var(--c) 78%, #101828);
                   box-shadow: 0 1px 2px rgb(16 24 40 / .1), inset 0 0 0 1px color-mix(in oklab, var(--c) 35%, #fff); }
.modal .rm-hz { margin-top: 2px; }
/* The same word on the row, in the same colour as its edge. */
.rm-rel-chip.hz { background: color-mix(in oklab, var(--c) 13%, #fff);
                  color: color-mix(in oklab, var(--c) 74%, #101828); }

/* The plan with its two companions: the news on the left, what waits on you on
   the right. The middle keeps most of the width — it is the reason the screen
   exists — and the panes scroll on their own. */
.hub { display: grid; grid-template-columns: 230px minmax(0, 1fr) 300px; gap: 14px;
       align-items: start; }
.hub-pane { position: sticky; top: 10px; max-height: calc(100dvh - 140px); overflow-y: auto;
            overscroll-behavior: contain; padding: 12px; background: var(--card);
            border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); }
.hub-pane h4 { margin: 0 0 8px; font-size: 11px; font-weight: 600; letter-spacing: .05em;
               text-transform: uppercase; color: var(--muted); }
.hub-line { display: flex; align-items: flex-start; gap: 9px; width: 100%; padding: 7px 6px;
            border: 0; background: none; text-align: left; border-radius: 9px; }
.hub-line + .hub-line { border-top: 1px solid var(--border); }
.hub-line > div { flex: 1; min-width: 0; }
.hub-go { cursor: pointer; }
.hub-go:hover { background: var(--accent-soft); }
.hub-emoji { flex: 0 0 auto; font-size: 17px; line-height: 1.2; }
.hub-what { margin: 0; font-size: 13px; color: var(--text); overflow-wrap: anywhere; }
.hub-snip { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.hub-said { color: var(--text); }
.hub-tabs { margin: -2px 0 8px; }
.hub-pane .av-row .av-do { flex-wrap: wrap; }
@media (max-width: 1100px) {
  .hub { grid-template-columns: 1fr; }
  .hub-pane { position: static; max-height: none; }
}

.rm-views { display: flex; gap: 2px; padding: 2px; background: #f1f5f9;
            border-radius: 8px; }
.rm-views button { border: 0; background: none; color: var(--muted);
                   font-size: 13px; font-weight: 600; padding: 4px 12px;
                   border-radius: 6px; }
.rm-views button.on { background: var(--card); color: var(--text);
                      box-shadow: var(--shadow); }
.rm-rows { display: flex; flex-direction: column; gap: 10px; }
.rm-row { background: #f1f5f9; border-radius: 12px; padding: 10px 12px; }
.rm-row.archived { opacity: .55; }
.rm-row-head { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }
.rm-row-head b { font-size: 15px; }
.rm-row-head .rm-n { font-weight: 600; }
.rm-row-head .rm-adds { margin: 0 0 0 auto; }
.rm-strip { display: flex; align-items: stretch; gap: 8px; overflow-x: auto;
            min-height: 52px; padding-bottom: 2px; }
.rm-strip > .rm-card, .rm-strip > .rm-mini { flex: 0 0 210px; }
.rm-strip .rm-empty { align-self: center; margin: 0; }
.rm-strip:has(.rm-card, .rm-mini) .rm-empty { display: none; }
.rm-mini { display: flex; flex-direction: column; gap: 5px; padding: 8px 10px;
           background: rgb(226 232 240 / .75); border: 1px solid #cbd5e1;
           border-radius: 8px; cursor: pointer; }
.rm-mini:hover { border-color: var(--accent); }
.rm-mini.rm-dragging { opacity: .35; }
.rm-mini-top { display: flex; align-items: flex-start; gap: 6px; }
.rm-mini-title { flex: 1; font-weight: 600; font-size: 13px; overflow-wrap: anywhere; }
.rm-mini-foot { display: flex; align-items: center; gap: 6px; }
.rm-mini .rm-prog { font-weight: 600; }
.rm-mini .rm-prog-bar { margin: 0; }
.rm-ghost { display: none; }
/* Where the urgencies stop and the order begins. */
.rm-sep { flex: 0 0 1px; align-self: stretch; background: #cbd5e1; margin: 2px 2px; }

/* ---- the two detail views */
.rm-big { width: 100%; margin: -4px 0 4px; padding: 6px 8px; font-size: 17px;
          font-weight: 600; background: none; border: 1px solid transparent;
          border-radius: 8px; }
.rm-big:hover { border-color: var(--border); }
.rm-big:focus { border-color: var(--accent); background: var(--card); outline: none; }
.rm-check { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.rm-check span { flex: 1; }
.rm-check.is-done span { color: var(--muted); text-decoration: line-through; }
.rm-groups { padding: 4px 10px 8px; border: 1px solid var(--border); border-radius: 10px; }
.rm-group + .rm-group { margin-top: 8px; border-top: 1px solid var(--border); }
.rm-group h5 { margin: 10px 0 4px; font-size: 11px; letter-spacing: .05em;
               text-transform: uppercase; color: var(--muted); }
.rm-row { display: flex; align-items: center; gap: 8px; padding: 5px 4px;
          border-radius: 6px; cursor: pointer; }
.rm-row:hover { background: var(--accent-soft); }
.rm-row-title { flex: 1; overflow-wrap: anywhere; }
.rm-row.is-done .rm-row-title { color: var(--muted); text-decoration: line-through; }
/* A task under a heading still belongs to somebody, so the name is asked on the
   same line as the title rather than in a second step. */
.rm-add-task { display: flex; align-items: center; gap: 6px; }
.rm-add-task select { flex: 0 0 auto; padding: 3px 4px; font-size: 12px; color: var(--muted);
                      background: none; border: 1px dashed var(--border); border-radius: 6px; }
.rm-add-task input { flex: 1; min-width: 0; padding: 5px 4px; background: none;
                     border: 1px solid transparent; border-radius: 6px;
                     color: var(--muted); }
.rm-add-task input:hover { border-color: var(--border); }
.rm-add-task input:focus { border-color: var(--accent); background: var(--card);
                           color: var(--text); outline: none; }
.rm-members.big .tiny-av { width: 26px; height: 26px; flex: 0 0 26px; font-size: 11px; }

/* ---- tags: picking them, and choosing what colour they are */
/* Off is a plain outline with a dot of the hue, so you can see the colour you
   are about to put on the card; on is the chip itself. Two clearly different
   things, neither of them shouting. */
.rm-tag-pick { display: flex; flex-wrap: wrap; gap: 5px; }
.rm-tag.pick { display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
               padding: 4px 10px; font-size: 10px; line-height: 18px; }
.rm-tag.pick i { display: none; width: 7px; height: 7px; border-radius: 50%;
                 background: var(--c); }
.rm-tag.pick:not(.on) { background: var(--card); color: var(--muted);
                        box-shadow: inset 0 0 0 1px var(--border); }
.rm-tag.pick:not(.on) i { display: block; }
.rm-tag.pick:not(.on):hover { color: var(--text);
                              box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--c) 40%, #fff); }
.rm-tag.pick.on { box-shadow: inset 0 0 0 1.5px color-mix(in oklab, var(--c) 32%, #fff); }
.rm-tag.pick:disabled { cursor: default; }
.rm-tags-mgr { max-height: 46dvh; overflow: auto; }
.rm-tag-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; flex-wrap: wrap; }
.rm-tag-row .rm-tag { flex: 0 0 auto; padding: 3px 9px; font-size: 11px; cursor: pointer; }
.modal .rm-tag-row input.rm-tag-name { flex: 1; min-width: 90px; width: auto; padding: 4px 6px;
                                       background: none; border-color: transparent; }
.modal .rm-tag-row input.rm-tag-name:hover { border-color: var(--border); }
.modal .rm-tag-row input.rm-tag-name:focus { border-color: var(--accent);
                                             background: var(--card); outline: none; }
.rm-tag-n { min-width: 14px; text-align: right; }
.rm-pal { flex: 0 0 100%; display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 2px 10px; }
.rm-pal .sw { width: 26px; height: 26px; flex: 0 0 26px; aspect-ratio: auto; }
.rm-tag-add input { margin-top: 8px; }
.avatar.tiny-av { width: 22px; height: 22px; flex: 0 0 22px; font-size: 10px;
                  margin: 0; box-shadow: none; }
.modal .actions .left { margin-right: auto; }

/* ----------------------------------------------------------- updates */
/* Behind the bell now, so the entries are a scroll inside a modal rather than
   a page of cards. */
.up-scroll { max-height: 62dvh; overflow: auto; }
.up-scroll .update + .update { border-top: 1px solid var(--border);
                               padding-top: 14px; }
.update { margin-bottom: 12px; }
.up-head { display: flex; align-items: baseline; justify-content: space-between;
           gap: 12px; flex-wrap: wrap; }
.up-head h3 { margin: 0; font-size: 17px; }
.up-summary { margin: 8px 0 0; color: var(--muted); max-width: 68ch; }
.up-changes { margin: 12px 0 0; padding-left: 20px; }
.up-changes li { margin: 4px 0; max-width: 68ch; }

/* -------------------------------------------------------------- team */
.code-reveal {
  background: var(--dark); color: #fff; font-family: ui-monospace, monospace;
  font-size: 20px; letter-spacing: 2px; padding: 14px; border-radius: var(--r);
  text-align: center; margin: 10px 0; user-select: all;
}
.session-row { display: flex; align-items: center; gap: 10px; font-size: .88em; color: var(--muted); padding: 4px 0; }

/* ------------------------------------------------------- link picker */
/* The shape of the tool, laid out and visible. A pair of dropdowns asked you
   to know the name of the thing you were looking for before it would show you
   anything, which is the one situation in which you are looking for it. */
/* One level at a time: the trail says where you are, the list says what is
   here, and the line underneath says what pressing Link will do. */
.lk-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 1px; margin: 12px 0 7px; }
.lk-crumb { padding: 3px 7px; border: 0; border-radius: 6px; background: none;
            color: var(--accent); font-size: 12px; font-weight: 600; }
.lk-crumb:hover { background: var(--accent-soft); }
.lk-crumb.here { color: var(--text); }
.lk-sep { color: var(--muted); font-size: 12px; }
.lk-list { max-height: 40dvh; overflow: auto; padding: 5px;
           border: 1px solid var(--border); border-radius: 10px; }
.lk-row { display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 9px;
          border: 0; border-radius: 8px; background: none; color: var(--text);
          text-align: left; font-size: 13px; }
.lk-row:hover { background: var(--accent-soft); color: var(--accent); }
.lk-row.on { background: var(--accent); color: #fff; }
.lk-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lk-in { flex: 0 0 auto; color: var(--muted); font-size: 11px; font-weight: 600;
         font-style: normal; font-variant-numeric: tabular-nums; }
.lk-row:hover .lk-in, .lk-row.on .lk-in { color: inherit; }
.lk-none { margin: 6px 9px; }
.lk-picked { margin: 9px 3px 0; color: var(--muted); }
.lk-picked b { color: var(--text); font-weight: 600; }

/* -------------------------------------------------------- date field */
/* The browser's date popup renders in its own chrome, where CSS cannot reach
   it — so the value lives in a hidden input and the face is ours. */
.dt { display: inline-block; }
.dt-open { display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px;
           border: 1px solid var(--border); border-radius: 8px; background: var(--card);
           color: var(--text); font-size: 13px; }
.dt-open svg { color: var(--muted); flex: 0 0 auto; }
.dt-open:hover:not(:disabled) { border-color: var(--accent); }
.dt-open:hover:not(:disabled) svg { color: var(--accent); }
.dt-open.empty { color: var(--muted); }
.dt-open:disabled { opacity: .6; cursor: default; }
.dt-panel { position: fixed; z-index: 80; width: 268px; padding: 12px;
            background: var(--card); border: 1px solid var(--border);
            border-radius: 14px; box-shadow: 0 12px 32px rgb(16 24 40 / .16); }
.dt-head { display: flex; align-items: center; gap: 6px; margin-bottom: 10px; }
.dt-head b { flex: 1; text-align: center; font-weight: 600; color: var(--ink); }
.dt-step { width: 26px; height: 26px; border: 0; border-radius: 7px; background: none;
           color: var(--muted); font-size: 17px; line-height: 1; }
.dt-step:hover { background: var(--accent-soft); color: var(--accent); }
.dt-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
/* The hour, under the days: one row, because a deadline has exactly one. */
.dt-when { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.dt-when input { flex: 1; padding: 5px 8px; font: inherit; font-size: 13px; color: var(--text);
                 border: 1px solid var(--border); border-radius: 8px; background: var(--card); }
.dt-when input:hover { border-color: var(--accent); }
.dt-wd { padding-bottom: 4px; font-size: 11px; font-weight: 600; color: var(--muted);
         text-align: center; }
.dt-day { height: 32px; border: 0; border-radius: 8px; background: none; color: var(--text);
          font-size: 13px; font-variant-numeric: tabular-nums; }
.dt-day:hover { background: var(--accent-soft); color: var(--accent); }
.dt-day.out { color: #b6bdcc; }
.dt-day.today { box-shadow: inset 0 0 0 1px var(--accent); color: var(--accent); }
.dt-day.on { background: var(--accent); color: #fff; box-shadow: none; }
.dt-foot { display: flex; justify-content: space-between; margin-top: 10px;
           padding-top: 9px; border-top: 1px solid var(--border); }
.dt-act { border: 0; background: none; color: var(--muted); font-size: 12px;
          font-weight: 600; padding: 3px 7px; border-radius: 6px; }
.dt-act:hover { background: var(--accent-soft); color: var(--accent); }

/* ------------------------------------------------------------- modal */
#modal-host:not(:empty) {
  position: fixed; inset: 0; background: rgb(15 23 42 / .45);
  display: grid; place-items: center; z-index: 50; padding: 16px;
}
.modal {
  background: var(--card); border-radius: 14px; box-shadow: 0 20px 60px rgb(0 0 0 / .3);
  width: min(560px, 100%); max-height: 90dvh; overflow: auto; padding: 22px;
}
.modal h2 { font-size: 18px; }
.modal .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.modal label { display: block; font-size: .8em; font-weight: 600; color: var(--muted); margin: 10px 0 4px; }
/* Every input, not only the ones that spell out type="text": an input written
   without it fell through to the browser's default and looked nothing like the
   rest of the tool — square, and about a third of the width it was given. */
.modal input:not([type=checkbox]):not([type=radio]),
.modal select, .modal textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px;
}

/* Pasting a link is the one thing this modal is for, so the field takes the
   width and the button sits on it rather than beside it. */
.import-row { display: flex; align-items: center; gap: 8px; margin: 14px 0 12px;
              padding: 5px 5px 5px 6px; border: 1px solid var(--border);
              border-radius: 14px; background: var(--card);
              transition: border-color .15s, box-shadow .15s; }
.import-row:focus-within { border-color: var(--accent);
                           box-shadow: 0 0 0 4px var(--accent-soft); }
/* [type=text] is carried for weight, not for meaning: the generic rule above
   is one attribute selector heavier and would otherwise put a second border
   inside this one. */
.modal .import-row input[type=text] { flex: 1; min-width: 0; border: 0;
                                      border-radius: 10px; padding: 11px 12px;
                                      font-size: 15px; background: none; }
.modal .import-row input[type=text]:focus { outline: none; }
.import-row .btn { border-radius: 10px; padding: 11px 20px; font-size: 15px; }
.modal textarea { min-height: 70px; resize: vertical; }
.modal .actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }
.modal .actions .left { margin-right: auto; }
.checkline { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-weight: 600; }
.checkline input { width: 18px; height: 18px; }

.ref-line { display: flex; gap: 8px; align-items: center; padding: 3px 0; }
.ref-line a { color: var(--accent); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 300px; }

/* ------------------------------------------------------------- toast */
#toast {
  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
  background: var(--dark); color: #fff; padding: 10px 20px; border-radius: 999px;
  box-shadow: 0 8px 24px rgb(0 0 0 / .25); z-index: 60; max-width: 90vw;
}

/* ------------------------------------------------------------ search */
.search-results { position: absolute; top: 50px; left: 0; right: 0; margin: 0 auto; max-width: 640px; z-index: 40; }
.search-results .card { max-height: 60dvh; overflow: auto; }
.sr-item { padding: 8px 10px; border-radius: 8px; cursor: pointer; display: flex; gap: 10px; align-items: center; }
.sr-item:hover { background: var(--accent-soft); }
.sr-kind { font-size: .72em; font-weight: 600; text-transform: uppercase; color: var(--muted); width: 52px; flex-shrink: 0; }

@media (max-width: 640px) {
  #whoami { display: none; }
  main { padding: 10px; }
  .modal .grid { grid-template-columns: 1fr; }
}

/* ------------------------------------------- Development > Translations */
.dev-head { display: flex; align-items: baseline; gap: 18px; flex-wrap: wrap; margin-bottom: 12px; }
.subtabs { display: flex; gap: 6px; }
.subtab { border: 1px solid var(--border); background: var(--card); border-radius: 999px;
          padding: 5px 14px; font-weight: 600; font-size: 13px; }
.subtab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.chip.pending { background: #FEF3C7; color: #92400E; }
.tiny { font-size: 11px; }

.tr-bar { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; flex-wrap: nowrap; }
/* .row wraps by default; here the zone name must share the line with the
   sync buttons rather than pushing them onto a second row. */
.grow { flex: 1; }

.tr-layout { display: grid; grid-template-columns: 200px minmax(0, 1fr) 320px; gap: 14px; align-items: start; }
.tr-screens { display: flex; flex-direction: column; gap: 2px;
              max-height: 70vh; overflow: auto; }
.tr-screen { display: flex; justify-content: space-between; align-items: center; gap: 8px;
             padding: 7px 10px; border: 0; background: transparent; border-radius: 8px;
             text-align: left; font-size: 13px; }
.tr-screen:hover { background: #F1F5F9; }
.tr-screen.active { background: var(--accent); color: #fff; font-weight: 600; }
.tr-screen em { font-style: normal; font-size: 11px; background: #FEE2E2; color: #991B1B;
                border-radius: 999px; padding: 1px 6px; }
.tr-screen.active em { background: rgba(255,255,255,.25); color: #fff; }

.tr-table-wrap { background: var(--card); border: 1px solid var(--border);
                 border-radius: var(--r); overflow: auto; max-height: 70vh; }
.tr-table { border-collapse: collapse; width: 100%; }
.tr-table th { position: sticky; top: 0; z-index: 1; background: #F8FAFC;
               font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
               color: var(--muted); text-align: center; padding: 8px;
               border-bottom: 1px solid var(--border); }
.tr-table td { border-bottom: 1px solid var(--border); padding: 0; vertical-align: top; }
.tr-table td.missing { background: #FEF2F2; }
/* Translations are the content, not metadata: they get the full text colour
   rather than inheriting the muted tone the surrounding chrome uses. */
.tr-table textarea { width: 100%; min-width: 150px; border: 0; background: transparent;
                     padding: 8px; font: inherit; color: var(--text);
                     /* no resize grip: the box already grows with its content,
                        and the corner handle read as a broken cell border */
                     resize: none; field-sizing: content; }
.tr-table textarea:focus { outline: 2px solid var(--accent); outline-offset: -2px; background: #fff; }
/* readonly is now the default state (editing needs a double-click), so it can
   no longer mean "greyed out" — that rule was quietly muting every cell. */
.link-btn { border: 0; background: transparent; font-size: 12px; color: var(--muted); padding: 8px; }

@media (max-width: 800px) {
  .tr-layout { grid-template-columns: 1fr; }
  .tr-screens { flex-direction: row; overflow-x: auto; max-height: none; }
}
.tr-table tr.removed td { opacity: .55; background: #FFF7ED; }
.tr-table tr.removed textarea { text-decoration: line-through; }

/* ---- translations: sheet header, gutter, comment markers ---- */
.tr-sheet { min-width: 0; }
.tr-sheet-head {
  display: flex; align-items: center; gap: 6px; margin-bottom: 8px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r); padding: 8px 12px;
}
.icon-btn { border: 1px solid transparent; background: none; border-radius: 8px;
            padding: 4px 8px; color: var(--muted); font-size: 13px; }
.icon-btn:hover { border-color: var(--border); color: var(--accent); }
.icon-btn.has-comment { color: #92400e; background: #fef9c3; }

/* the active sheet is a white card, not a blue block */
.tr-screen { position: relative; display: flex; align-items: center; gap: 6px;
             padding: 7px 10px; border: 1px solid transparent; border-radius: 8px;
             background: transparent; font-size: 13px; cursor: pointer; }
.tr-screen .nm { flex: 1; text-align: left; }
.tr-screen:hover { background: #f1f5f9; }
.tr-screen.active { background: var(--card); border-color: var(--border);
                    box-shadow: var(--shadow); font-weight: 600; color: var(--text); }
.tr-screen .cm { font-style: normal; font-size: 11px; color: #92400e;
                 background: #fef9c3; border-radius: 999px; padding: 1px 6px; }
.tr-screen .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--danger); }
.tr-screen .dl { opacity: 0; border: 0; background: none; color: var(--muted);
                 padding: 0 2px; font-size: 13px; }
.tr-screen:hover .dl, .tr-screen.active .dl { opacity: 1; }
.tr-screen .dl:hover { color: var(--accent); }

.tr-table th.gutter, .tr-table td.gutter { width: 44px; padding: 0; text-align: center; }
.tr-table td.gutter { position: relative; }
.row-c, .cell-c { opacity: 0; border: 0; background: none; font-size: 11px; padding: 2px; }
.row-c.has-comment { opacity: 1; }
.tr-table tr:hover .row-c, .tr-table td:hover .cell-c { opacity: .8; }
.row-c:hover, .cell-c:hover { opacity: 1; }
.cell-c { position: absolute; top: 2px; right: 2px; }
.tr-table td { position: relative; }
.tr-table th { position: sticky; }
.col-c { opacity: 0; border: 0; background: none; font-size: 11px; padding: 2px; }
.tr-table th:hover .col-c { opacity: .8; }

/* A commented cell gets a corner marker, not a filled background: one row
   comment plus one column comment already tints most of the grid, and yellow
   everywhere means yellow nowhere. Same convention as Sheets. */
.tr-table td.commented::after {
  content: ''; position: absolute; top: 0; right: 0;
  border: 5px solid transparent; border-top-color: #d97706; border-right-color: #d97706;
}
.tr-table th.commented { box-shadow: inset 0 -2px 0 #d97706; }

/* Add N rows at the end: the count sits in the control rather than behind a
   prompt, so building a level table is type-a-number-and-go. */
.add-end-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.add-end-row .row-add-end { width: auto; margin-top: 0; padding: 6px 14px; }
.add-end-row input { width: 68px; padding: 6px 8px; text-align: center;
                     border: 1px solid var(--border); border-radius: 8px; font: inherit; }

.row-add-end { width: 100%; border: 1px dashed var(--border); background: none;
               color: var(--muted); padding: 9px; border-radius: 0 0 var(--r) var(--r); }
.row-add-end:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }

/* ---- comment window ---- */
.modal header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.modal header .icon-btn { margin-left: auto; }
.cm-list { max-height: 40vh; overflow: auto; margin-bottom: 12px; }
.cm-item { border-left: 3px solid var(--accent); background: #fefce8;
           padding: 8px 10px; border-radius: 0 8px 8px 0; margin-bottom: 8px; }
.cm-item.done { border-left-color: var(--border); background: #f8fafc; opacity: .65; }
.cm-meta { display: flex; align-items: center; gap: 8px; font-size: 12px; margin-bottom: 3px; }
.cm-body { white-space: pre-wrap; }
.cm-new textarea { width: 100%; padding: 9px; border: 1px solid var(--border);
                   border-radius: 8px; resize: vertical; }
.cm-people { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 10px; }
.chip.mention { background: var(--accent-soft); color: var(--accent);
                border: 1px solid transparent; cursor: pointer; }
.chip.mention:hover { border-color: var(--accent); }

.zone-title { margin: 0; font-size: 17px; }

.link-back { border: 0; background: none; color: var(--muted);
             padding: 0 0 6px; font-size: 13px; display: block; }
.link-back:hover { color: var(--accent); }

/* ---- row context menu ---- */
.ctx-menu { position: fixed; z-index: 100; background: var(--card);
            border: 1px solid var(--border); border-radius: 10px;
            box-shadow: 0 8px 24px rgb(15 23 42 / .16); padding: 4px; min-width: 190px; }
.ctx-menu button { display: block; width: 100%; text-align: left; border: 0; color: var(--text);
                   background: none; padding: 8px 12px; border-radius: 7px; font-size: 13px; }
.ctx-menu button:hover { background: var(--accent-soft); color: var(--accent); }
.ctx-menu button.danger:hover { background: var(--danger-soft); color: var(--danger); }
/* There, and not this moment's action: greyed rather than missing, so the menu
   is the same menu every time and the reason is in its tooltip. */
.ctx-menu button.off { color: var(--muted); opacity: .5; cursor: not-allowed; }
.ctx-menu button.off:hover { background: none; color: var(--muted); }

.tr-table tr.to-delete td { background: #fff1f2; }
.tr-table tr.to-delete textarea { text-decoration: line-through; color: var(--danger); }

/* ---- cell selection & row drag ---- */
.tr-table td.cell { cursor: cell; user-select: none; }
.tr-table td.cell.sel { background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent); }
.tr-table td.cell.sel.commented { background: #fef9c3; }
.tr-table td.cell textarea { cursor: inherit; }
.tr-table td.cell textarea:not([readonly]) { cursor: text; background: #fff;
                                             outline: 2px solid var(--accent); outline-offset: -2px; }
.tr-table th[data-selcol], .tr-table td[data-selrow] { cursor: pointer; user-select: none; }
.tr-table td.gutter { text-align: center; white-space: nowrap; }
.drag-handle { cursor: grab; color: #cbd5e1; font-size: 13px; padding: 0 2px; }
.drag-handle:active { cursor: grabbing; }
.tr-table tr:hover .drag-handle { color: var(--muted); }
.dotc { font-size: 10px; }
.tr-table tr.dragging { opacity: .4; }
.tr-table tr.drop-above td { box-shadow: inset 0 2px 0 var(--accent); }
.tr-table tr.drop-below td { box-shadow: inset 0 -2px 0 var(--accent); }

/* ---- activity side panel ---- */
.tr-activity { background: var(--card); border: 1px solid var(--border);
               border-radius: var(--r); display: flex; flex-direction: column;
               max-height: 70vh; overflow: hidden; }
.tr-activity header { padding: 10px 14px; border-bottom: 1px solid var(--border); }
.tp-body { overflow: auto; padding: 10px 14px 18px; }
.tp-body h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
              color: var(--muted); margin: 16px 0 6px; }
.tp-item { border-left: 3px solid var(--accent); background: #fefce8; padding: 8px 10px;
           border-radius: 0 8px 8px 0; margin-bottom: 8px; cursor: pointer; font-size: 13px; }
.tp-item:hover { filter: brightness(.98); }
.tp-item.done { border-left-color: var(--border); background: #f8fafc; opacity: .65; }
.tp-item.err { border-left-color: var(--danger); background: var(--danger-soft); }
.tp-meta { font-size: 11px; color: var(--muted); margin-bottom: 2px; }

.tr-screen.archived { opacity: .5; }
.tr-screen.archived .nm::after { content: ' · archived'; font-size: 10px; color: var(--muted); }
/* Its tab is gone from the Sheet. Struck through rather than removed: the
   same treatment as a deleted row, and the comments are still attached. */
.tr-screen.removed .nm { text-decoration: line-through; color: var(--danger); }
.tr-screen.removed .nm::after { content: ' · deleted in Drive'; font-size: 10px;
                                text-decoration: none; color: var(--muted); }
/* Deleted here, still in the Sheet until the next push — and reversible. */
.tr-screen.to-delete .nm { text-decoration: line-through; color: var(--danger); }
.tr-screen.to-delete .nm::after { content: ' · push to delete'; font-size: 10px;
                                  text-decoration: none; color: var(--muted); }
.tr-screen.to-delete { cursor: default; }
.arch-toggle { padding: 4px 10px; color: var(--muted); }

@media (max-width: 1200px) {
  /* Below this the three columns squeeze the grid; activity drops underneath. */
  .tr-layout { grid-template-columns: 200px minmax(0, 1fr); }
  .tr-activity { grid-column: 1 / -1; max-height: 40vh; }
}

.tr-activity header { display: flex; align-items: center; gap: 8px; }
.tp-filters { display: flex; gap: 2px; }
.tp-f { border: 1px solid transparent; background: none; border-radius: 7px;
        padding: 3px 7px; font-size: 13px; filter: grayscale(1); opacity: .45; }
.tp-f.on { filter: none; opacity: 1; background: var(--accent-soft); border-color: var(--accent); }
.tp-f:hover { opacity: 1; }

/* A comment clicked in Activity flashes the cells it points at. */
.tr-table td.cell-flash { background: #fef08a; box-shadow: inset 0 0 0 2px #d97706; }

/* ---- flags ---- */
.chip.new     { background: #dcfce7; color: #166534; }
.chip.updated { background: #ffedd5; color: #9a3412; }
.tr-table td.flag-new     { box-shadow: inset 3px 0 0 #16a34a; }
.tr-table td.flag-updated { box-shadow: inset 3px 0 0 #ea580c; }
.tp-item.flag { border-left-color: #16a34a; }
.tp-actions { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.tp-actions label { display: flex; align-items: center; gap: 5px; color: var(--muted); }
.tp-actions .btn.small:disabled { opacity: .45; cursor: not-allowed; }

/* ---- Development: one row per thing, at every level ----
   Cards were fine for a single zone and became a scatter of small boxes in a
   wide empty screen as soon as there were several. A row uses the width it is
   given: name, what it holds, what needs attention, in that order. */
.dev-head { align-items: center; margin: 4px 0 12px; }
.dev-head h3 { margin: 0; font-size: 20px; }
.dev-group { margin: 20px 0 7px; font-size: 11px; font-weight: 600; letter-spacing: .05em;
             text-transform: uppercase; color: var(--muted); }
.dev-rows { display: flex; flex-direction: column; gap: 6px; }

/* Categories separate the listing rather than repeating themselves down a
   column of rows: the brackets are read once, become this heading, and are
   never printed again. */
.dev-group + .dev-group { margin-top: 22px; }
.dev-group-head { margin: 0 0 8px; font-size: 12px; font-weight: 600;
                  letter-spacing: .05em; text-transform: uppercase;
                  color: var(--muted); display: flex; align-items: baseline;
                  gap: 8px; padding-bottom: 6px;
                  border-bottom: 1px solid var(--border); }
.dev-group-head .tiny { font-weight: 600; letter-spacing: 0; }

.dev-row { display: flex; align-items: center; gap: 12px; width: 100%;
           padding: 14px 16px; text-align: left; font: inherit; cursor: pointer;
           background: var(--card); border: 1px solid var(--border);
           border-radius: var(--r); }
.dev-row:hover { border-color: var(--accent); background: var(--accent-soft); }
.dev-row .nm { font-size: 15px; }
.dev-row .chev { color: var(--muted); font-size: 18px; line-height: 1; }
.dev-row.archived { opacity: .55; }
.add-row { display: flex; gap: 6px; margin-top: 6px; }
.add-row .row-add-end { margin-top: 0; }
@media (max-width: 720px) {
  .dev-row { flex-wrap: wrap; gap: 4px 10px; }
  .dev-row .grow { display: none; }
}

/* Game categories. A group is a drop target, so it needs a body a hand can
   aim at even when empty — hence the outline on an empty list rather than a
   heading alone, which would be a 20px-tall thing to hit. */
.cat-group { margin: 0 0 14px; border-radius: var(--r); padding: 2px;
             border: 1px dashed transparent; transition: background .12s, border-color .12s; }
.cat-group.drop-in { border-color: var(--accent); background: var(--accent-soft); }
.cat-head { display: flex; align-items: baseline; justify-content: space-between;
            gap: 10px; padding: 4px 6px 8px; font-weight: 600; font-size: 13px;
            letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.cat-group .dev-rows:empty { min-height: 34px; border-radius: var(--r);
                             border: 1px dashed var(--border); }
.dev-row.dragging { opacity: .4; }
.dev-row[draggable=true] { cursor: grab; }

/* The section's Drive root, in the import modal. Set apart because it is a
   setting, not the thing you came here to do. */
.sec-root { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.sec-root label { display: block; font-weight: 600; margin-bottom: 2px; }
.sec-root .import-row { margin: 8px 0 4px; }

/* A list of links, not a stack of boxes: the border was drawing a frame around
   every line and made three downloads look like a form. */
.dl-list { display: flex; flex-direction: column; margin-top: 10px;
           max-height: 260px; overflow: auto; }
.dl-folder { display: flex; align-items: center; gap: 10px;
             padding: 10px 8px; font: inherit; color: inherit; text-decoration: none;
             border-bottom: 1px solid var(--border); border-radius: 6px; }
.dl-folder:last-child { border-bottom: 0; }
.dl-folder:hover { background: var(--accent-soft); color: var(--accent); }
.dl-folder .dl-go { color: var(--muted); font-size: 15px; line-height: 1; }
.dl-folder:hover .dl-go { color: var(--accent); }

/* Syncing a whole section: what it is on now, and what it has been through. */
.sync-bar { height: 4px; border-radius: 3px; background: var(--border); margin: 10px 0 4px; overflow: hidden; }
.sync-fill { height: 100%; width: 0; background: var(--accent); transition: width .3s ease; }
.sync-line { display: flex; align-items: center; gap: 10px; padding: 7px 2px;
             border-bottom: 1px solid var(--border); font-size: 13px; }
.sync-line:last-child { border-bottom: 0; }
.sync-line.err .nm { color: var(--danger); }

/* ---- Closed folds away ----
   Same type as the section headings it sits among, so the two read as
   siblings and only the chevron says one of them opens. */
.tp-fold { display: flex; align-items: center; justify-content: space-between;
           width: 100%; border: 0; background: none; padding: 0;
           font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
           color: var(--muted); margin: 16px 0 6px; cursor: pointer; }
.tp-fold:hover { color: var(--text); }
.tp-fold .chev { font-size: 14px; line-height: 1; }

/* ---- reordering the sheet list ---- */
.tr-screen[draggable="true"] { cursor: grab; }
.tr-screen.dragging { opacity: .4; }
.tr-screen.drop-above { box-shadow: inset 0 2px 0 var(--accent); }
.tr-screen.drop-below { box-shadow: inset 0 -2px 0 var(--accent); }
.tr-screen-add { width: 100%; margin-top: 6px; border: 1px dashed var(--border);
                 background: none; color: var(--muted); border-radius: 8px;
                 padding: 6px 8px; font-size: 12px; text-align: left; cursor: pointer; }
.tr-screen-add:hover { color: var(--accent); border-color: var(--accent);
                       background: var(--accent-soft); }
