/* ── The project hubs — Future City and ExploraVision ───────────────────────
   A modal, not a screen (see js/11o-projects.js for why), so everything here
   sits inside `.vgm-panel` and inherits its scrolling. Nothing is `position:
   fixed`: the 2026-09-03 tool-rail bug was a fixed overlay that reported
   healthy in every computed property while being unrendered, and a shared
   board twelve children read has no need of one.

   Scoped under `.bs` like the rest of the app's later sheets. */

.bs .vhp-block { margin: 0 0 22px; }
.bs .vhp-h {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}

/* ── what the competition wants ── */
.bs .vhp-deliv {
  padding: 10px 12px;
  margin-bottom: 8px;
  border: 1px solid var(--bs-rule, #ddd8d0);
  border-radius: 8px;
  background: var(--card-hi);
}
.bs .vhp-deliv-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}
.bs .vhp-deliv-title { font-weight: 700; color: var(--bs-ink); }
.bs .vhp-deliv-detail { margin-top: 4px; font-size: 14px; color: var(--bs-ink-3); }
.bs .vhp-deliv-owner { margin-top: 4px; font-size: 13px; color: var(--bs-ink-3); }
/* The points ARE the hierarchy on this board — 72 against 10 is the whole
   argument about where the work should go, so they are read at a glance. */
.bs .vhp-pts {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-size: 12px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--gold-bg);
  color: var(--bs-blue);
}
.bs .vhp-pts.is-budget { background: #f1efe6; color: #7a6a2f; }
.bs .vhp-due { font-size: 12px; font-weight: 600; color: #9a5b2f; }

/* ── who is on this ── */
.bs .vhp-member {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  padding: 5px 0;
  border-bottom: 1px solid var(--bs-stock-2);
}
.bs .vhp-member-name { font-weight: 600; min-width: 9rem; }
.bs .vhp-role {
  font-size: 12px;
  padding: 1px 7px;
  border-radius: 999px;
  background: #eceee9;
  color: #4a5545;
}
/* Said out loud rather than left blank: an empty slot on a shared board that
   everyone can see is the nudge, and Mark's cohort all read each other's. */
.bs .vhp-role.is-none { background: transparent; color: #a39a8d; font-style: italic; }

/* ── picking your two jobs ── */
.bs .vhp-ask {
  padding: 12px;
  border: 1px solid var(--bs-blue);
  border-radius: 10px;
  background: var(--gold-bg);
}
.bs .vhp-roles { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.bs .vhp-role-pick {
  padding: 7px 12px;
  font-size: 14px;
  border: 1px solid var(--bs-rule, #ccc6bc);
  border-radius: 999px;
  background: #fff;
  color: var(--bs-ink);
  cursor: pointer;
}
.bs .vhp-role-pick.is-on {
  background: var(--bs-blue);
  border-color: var(--bs-blue);
  color: #fff;
  font-weight: 600;
}
.bs .vhp-role-pick:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }

/* ── the composer and the board ── */
.bs .vhp-compose { display: grid; gap: 8px; margin-bottom: 14px; }
.bs .vhp-kind, .bs .vhp-body {
  padding: 7px 9px;
  font: inherit;
  color: var(--bs-ink);
  background: var(--card-hi);
  border: 1px solid var(--bs-rule, #d5d0c8);
  border-radius: 6px;
}
.bs .vhp-body { min-height: 76px; resize: vertical; }

.bs .vhp-note {
  padding: 9px 11px;
  margin-bottom: 8px;
  border-left: 3px solid var(--bs-rule, #ddd8d0);
  background: var(--bs-sheet);
  border-radius: 0 8px 8px 0;
}
.bs .vhp-note-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.bs .vhp-note-title { font-weight: 600; }
.bs .vhp-note-by { font-size: 12px; color: var(--bs-ink-3); margin-left: auto; }
.bs .vhp-note-body { margin-top: 4px; font-size: 14px; white-space: pre-wrap; }
/* Anywhere-long URLs from twelve children: break rather than widen the modal,
   which on a phone is how a shared board starts scrolling sideways. */
/* 24px MINIMUM, WCAG 2.2 SC 2.5.8 — and this one was invisible until the app
   was opened at the size the Vanguard twelve actually use. At 320px a pasted
   URL wraps to two lines and the box is 42px, so it never fell under the
   floor; on an iPad it fits on ONE line and measures 305x21. Every iPad size
   showed it: 744, 820, 834, 1024 portrait and 1180 landscape.
   `inline-flex` matches the room's other three grown controls, and was
   measured in the browser first to confirm it does not change how a long URL
   wraps — the 110-character CAL FIRE link is still 42px either way. */
.bs .vhp-note-link {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-top: 4px;
  font-size: 13px;
  color: var(--bs-blue);
  overflow-wrap: anywhere;
}
.bs .vhp-kind-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 4px;
  background: #e9e6df;
  color: #5c564d;
}
.bs .vhp-kind-tag.is-template { background: #1f6feb; color: #fff; }
/* A TEMPLATE BODY IS A DRAWING, NOT A PARAGRAPH. The ID card and the flyer are
   fixed-width ASCII layouts about 70 columns across. Under the `pre-wrap` every
   other kind uses they re-wrap at the container edge and the boxes come apart —
   and they come apart WORST on the device the twelve actually use, because an
   iPad modal is narrower than the desktop one this was written on. So: `pre`,
   a monospace face, and the overflow scrolls INSIDE this block. The modal never
   scrolls sideways. */
.bs .vhp-note.k-template .vhp-note-body {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-size: 12px;
  line-height: 1.45;
  white-space: pre;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 4px;
}
.bs .vhp-kind-tag.is-source { background: #e6eef0; color: #2f5c66; }
.bs .vhp-kind-tag.is-draft { background: #f0e9f2; color: #5b3a66; }

/* ── the roster, which is the coach's alone ────────────────────────────────
   Every control here clears the 24px tap floor (WCAG 2.2 SC 2.5.8) by
   construction rather than by repair — see tests/test_project_hub_tap_targets.py
   for why the room's newest controls are held to the bar that the old ones
   failed. `inline-flex` because a min-height on an inline box is inert. */
.bs .vhp-roster { margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--bs-rule, #ddd8d0); }
.bs .vhp-roster-row { gap: 8px; }
.bs .vhp-roster-standing, .bs .vhp-roster-pick {
  min-height: 24px;
  padding: 2px 6px;
  font: inherit;
  font-size: 13px;
  color: var(--bs-ink);
  background: var(--card-hi);
  border: 1px solid var(--bs-rule, #d5d0c8);
  border-radius: 6px;
}
.bs .vhp-roster-pick { max-width: 16rem; }
.bs .vhp-roster-off, .bs .vhp-roster-go {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 2px 10px;
  font-size: 12px;
  font-weight: 600;
  border-radius: 999px;
  cursor: pointer;
}
.bs .vhp-roster-off {
  border: 1px solid var(--bs-rule, #ccc6bc);
  background: transparent;
  color: #8a4a3a;
}
.bs .vhp-roster-go {
  border: 1px solid var(--bs-blue);
  background: var(--bs-blue);
  color: #fff;
}
.bs .vhp-roster-off:focus-visible, .bs .vhp-roster-go:focus-visible,
.bs .vhp-roster-standing:focus-visible, .bs .vhp-roster-pick:focus-visible {
  outline: 2px solid var(--bs-blue);
  outline-offset: 2px;
}
.bs .vhp-roster-add { margin-top: 10px; flex-wrap: wrap; gap: 8px; }
.bs .vhp-roster-scope { margin: 6px 0 0; font-size: 12px; color: var(--bs-ink-3); }
/* A cap reads as a budget, and a spent one reads as spent. */
.bs .vhp-cap {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-size: 11px;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--gold-bg);
  color: var(--bs-blue);
}
.bs .vhp-cap.is-full { background: #f6ece6; color: #8a4a3a; }

/* ── COLLABORATION ─────────────────────────────────────────────────────────
   Mark, 2026-09-04: *"more collab ability between users for different doc
   types"*, against a reference of "a modernized google workspace like
   notebookLM". Two things were borrowed from that reference and the rest was
   deliberately not:

   BORROWED — sources come FIRST. NotebookLM's whole organising idea is that
   the sources are the ground truth and everything else is derived from them,
   so the board groups by `kind` with sources at the top rather than showing
   one flat reverse-chronological list. And a doc is a place a conversation
   happens, not a file that sits there.

   NOT BORROWED — the look. This modal lives inside an app with a warm paper
   palette, a blue accent and a pill vocabulary already established across
   thirty-three other stylesheets, so nothing here introduces a font, a
   palette or a shadow language of its own. A room that looked like a
   different product from the screen the child arrived through would read as
   broken, not as modern.

   STILL NOTHING `position: fixed`, for the reason at the top of this file. */

/* ── claim chips: who is actually working on this ── */
.bs .vhp-claims {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
}
/* A chip is a person, so it reads name-first and the job is secondary — the
   opposite emphasis to `.vhp-role`, which hangs off a name already given. */
.bs .vhp-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  max-width: 100%;
  padding: 3px 9px;
  font-size: 12px;
  border: 1px solid var(--bs-stock-2);
  border-radius: 999px;
  background: var(--card-hi);
}
.bs .vhp-chip-name { font-weight: 600; color: var(--bs-ink); }
.bs .vhp-chip-role { color: var(--bs-ink-3); }
.bs .vhp-chip-date {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-size: 11px;
  color: #9a5b2f;
}
/* Mine is filled, so a child finds themselves on a board of twelve without
   reading twelve names. */
.bs .vhp-chip.is-me {
  border-color: var(--bs-blue);
  background: var(--gold-bg);
}
.bs .vhp-chip.is-me .vhp-chip-name { color: var(--bs-blue); }

/* Said out loud, because "nobody" is the state a teacher needs to see and a
   student is most able to fix. */
.bs .vhp-claims-none {
  font-size: 12px;
  font-style: italic;
  color: #a39a8d;
}

.bs .vhp-claim-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 9px;
  padding-top: 9px;
  border-top: 1px solid var(--bs-stock-2);
}
.bs .vhp-claim-role, .bs .vhp-claim-when {
  padding: 5px 8px;
  font: inherit;
  font-size: 13px;
  color: var(--bs-ink);
  background: var(--card-hi);
  border: 1px solid var(--bs-rule, #d5d0c8);
  border-radius: 6px;
}
.bs .vhp-claim-hint {
  font-size: 12px;
  color: var(--bs-ink-3);
}

/* ── the pinned shelf ── */
.bs .vhp-note.is-pinned {
  border-left-color: var(--bs-blue);
  background: var(--gold-bg);
}
/* 24px MINIMUM, WCAG 2.2 SC 2.5.8, and this one was measured at 36x16 in a
   browser at 320px on 2026-09-05. It is the control that curates the board,
   pressed by eleven- to fourteen-year-olds on phones, and its effect —
   reordering the docs — is the hardest in the room to notice going wrong.
   `inline-flex` because a min-height on an inline box is inert. */
.bs .vhp-pin {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-left: 6px;
  padding: 1px 9px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  background: transparent;
  border: 1px solid var(--bs-rule, #ddd8d0);
  border-radius: 999px;
  cursor: pointer;
}
.bs .vhp-pin.is-on {
  color: #fff;
  background: var(--bs-blue);
  border-color: var(--bs-blue);
}
.bs .vhp-pin-by {
  font-size: 11px;
  font-style: italic;
  color: var(--bs-ink-3);
}

/* ── reactions ── */
.bs .vhp-reacts {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 7px;
}
.bs .vhp-react {
  padding: 2px 8px;
  font-size: 13px;
  line-height: 1.5;
  background: var(--card-hi);
  border: 1px solid var(--bs-stock-2);
  border-radius: 999px;
  cursor: pointer;
  /* Transform and opacity only — the compositor-friendly pair. A reaction is
     pressed with a thumb on a phone, so it has to answer instantly. */
  transition: transform 120ms ease, background-color 120ms ease;
}
.bs .vhp-react:hover { transform: translateY(-1px); }
.bs .vhp-react:active { transform: translateY(0) scale(0.96); }
.bs .vhp-react.is-mine {
  background: var(--gold-bg);
  border-color: var(--bs-blue);
  font-weight: 700;
}
.bs .vhp-react-n {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-size: 11px;
  margin-left: 3px;
  color: var(--bs-ink-3);
}
.bs .vhp-react:focus-visible,
.bs .vhp-pin:focus-visible {
  outline: 2px solid var(--bs-blue);
  outline-offset: 2px;
}

/* ── the thread ── */
.bs .vhp-thread {
  margin: 8px 0 0 2px;
  padding-left: 10px;
  border-left: 2px solid var(--bs-stock-2);
}
.bs .vhp-comment { padding: 4px 0; font-size: 13px; }
.bs .vhp-comment-by { font-weight: 600; margin-right: 6px; }
.bs .vhp-comment-body { white-space: pre-wrap; overflow-wrap: anywhere; }
/* The reply box is revealed rather than always present: twelve docs each
   carrying an open textarea is a wall of empty boxes, which is how a board
   stops looking like anyone is using it. */
.bs .vhp-reply { display: flex; gap: 6px; margin-top: 6px; }
.bs .vhp-reply-in {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 8px;
  font: inherit;
  font-size: 13px;
  color: var(--bs-ink);
  background: var(--card-hi);
  border: 1px solid var(--bs-rule, #d5d0c8);
  border-radius: 6px;
}
/* Two quiet text buttons that must look identical but are NOT the same
   control: the reply toggle reveals a field, and the claim-off button makes a
   write. They shared a class until the UI harness clicked the wrong one —
   `querySelector` found the claim bar's, which renders first — so they are
   grouped for style and separate for identity. */
/* Grouped for style, separate for identity — the shared class that once made
   "Take my name off" answer to a reply press is why these are two selectors
   and not one.
   Both measured 18px tall at 320px on 2026-09-05, under the 24px floor. They
   are borderless text, so the padding that lifts them is invisible; an
   `::after` hit-area overlay was rejected because the reply toggle sits 7px
   under the reaction row and an overlay tall enough to reach 24px would take
   presses meant for a thumbs-up. */
.bs .vhp-reply-toggle,
.bs .vhp-claim-off {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 2px 8px;
  font-size: 12px;
  color: var(--bs-blue);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.bs .vhp-claim-off { color: #9a5b2f; }

/* ── grouping by kind, sources first ── */
.bs .vhp-group { margin-bottom: 14px; }
.bs .vhp-group-h {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}
.bs .vhp-group-n {
  font-family: var(--bs-mono, ui-monospace, monospace);
  font-weight: 400;
  margin-left: 5px;
  color: #a39a8d;
}

/* ── the teacher panel ── */
.bs .vhp-teach {
  padding: 12px;
  margin: 0 0 20px;
  border: 1px solid #c9b8d6;
  border-radius: 10px;
  background: #f7f2fa;
}
.bs .vhp-teach-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  padding: 4px 0;
  border-bottom: 1px solid #ece2f2;
  font-size: 13px;
}
.bs .vhp-teach-row:last-child { border-bottom: 0; }
.bs .vhp-teach-name { font-weight: 600; min-width: 9rem; }
.bs .vhp-teach-flag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 4px;
  background: #f3e2c8;
  color: #7a5320;
}

/* ── PROVENANCE: WHICH DOC CAME FROM WHICH ─────────────────────────────────
   NotebookLM's actual organising idea, which the board had arranged for and
   not built: the sources are ground truth and the derived writing CITES them.
   Grouping put the material in the right order; this says which source a
   given draft rests on, and which drafts a given source fed.

   TWO STRIPS, ONE INDENTED, and the indent is the whole reading. `from` is a
   claim the doc makes about itself and sits flush with its body; `used by` is
   what the rest of the board did with it and sits inset behind a rule, so a
   child scanning a source sees its own words first and the consequences
   second. Rendered as one line each because the server sends one hop and
   never a tree.

   Border-left rather than a heading: the thread above already establishes
   "inset behind a rule" as this room's word for derived material, and a
   fourth heading level inside a doc row is more furniture than a two-item
   list can carry. */
.bs .vhp-prov {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 7px;
  font-size: 12px;
}
.bs .vhp-prov.is-back {
  margin-left: 2px;
  padding-left: 10px;
  border-left: 2px solid var(--bs-stock-2);
}
.bs .vhp-prov-k {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}
.bs .vhp-prov-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 2px 4px 2px 6px;
  background: var(--card-hi);
  border: 1px solid var(--bs-stock-2);
  border-radius: 999px;
}
/* The title is the only part that can be long — twelve children paste links
   and write sentences — so it is the only part allowed to shrink, and it
   ellipsises rather than pushing the × off the chip. */
.bs .vhp-prov-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* 24px floor, WCAG 2.2 SC 2.5.8, same as the pin and the reply toggle. A "×"
   is the smallest label in the room and would otherwise be the smallest
   target in it. */
.bs .vhp-cite-off {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  color: #9a5b2f;
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}
.bs .vhp-cite-off:hover { background: #f6ece4; }
/* Grouped with the reply toggle for style below, but NEVER sharing its class:
   the 2026-09-04 bug where "Take my name off" answered a reply press came
   from exactly that saving. */
.bs .vhp-cite-toggle {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  margin-top: 4px;
  padding: 2px 8px;
  font-size: 12px;
  color: var(--bs-blue);
  background: transparent;
  border: 0;
  cursor: pointer;
}
.bs .vhp-cite {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-top: 6px;
}
.bs .vhp-cite-pick {
  flex: 1 1 12rem;
  min-width: 0;
  max-width: 100%;
  padding: 4px 6px;
  font: inherit;
  font-size: 12px;
  background: var(--card-hi);
  border: 1px solid var(--bs-rule, #ddd8d0);
  border-radius: 6px;
}
.bs .vhp-cite-off:focus-visible,
.bs .vhp-cite-toggle:focus-visible,
.bs .vhp-cite-pick:focus-visible {
  outline: 2px solid var(--bs-blue);
  outline-offset: 2px;
}
