/* ════════════════════════════════════════════════════════════════════
   THE VMEM BOARD SHELL, ported from the Claude Design export

   Source: design-source/VMEM Board.dc.html
   Pinned by: tests/dc-vmem-board-fidelity.test.mjs

   EVERY DECLARATION IS COPIED FROM THE DESIGN. Nothing here is invented,
   rounded, or close enough.

   WHAT THIS FILE IS, AND WHAT IT IS NOT. The design does not draw 26 bespoke
   sections; it draws a LANGUAGE: a two-column shell with a sticky rail, a KPI
   deck, a two-up grid, a table treatment and a skeleton. That language is what
   ports, and it then applies across every section the board already has. The
   sections keep their ids and their data: renaming them would break every
   reader in js/vmem-board.js for a change nobody can see.

   SELECTORS ARE NAMESPACED `vb-`, DECLARATIONS ARE NOT. The design calls its
   pieces `.shell`, `.rail`, `.tbl`, `.skel` and `.grid2`, which are names
   generic enough to collide with anything. The prefix keeps them apart while
   the declarations stay byte-identical, which is what the fidelity test
   compares.

   SHARED FOUNDATION NOT REPEATED HERE: .cin, .mono, .lift, the house keyframes
   and the .ijb buttons live in css/dc-base.css; the overlay shell is
   css/dc-overlay.css; the journey's own language is css/dc-vmem-journey.css.
   ════════════════════════════════════════════════════════════════════ */

/* ── the two-column shell ─────────────────────────────────────────
   262px is the design's own rail width. The rail is sticky at 90px, which
   clears the 70px header plus its shadow. */
.vb-shell {
  display: grid;
  grid-template-columns: 262px minmax(0, 1fr);
  gap: 20px;
  max-width: 1560px;
  margin: 0 auto;
  padding: 20px;
  align-items: start;
}
.vb-rail {
  position: sticky;
  top: 90px;
  /* THE DESIGN'S RAIL HAS NO WIDTH: it fills the 262px track. The board's own
     .vmem-rail sets width:240px from when the shell was a flex row, which left
     a 22px dead gap inside the track once the shell became a grid. Measured,
     not guessed: rail 240 in a 262 column. `auto` restores the design's own
     behaviour rather than inventing a number, which is why it is not in the
     copied-constants list: there is no declaration to copy, the design simply
     does not set one. */
  width: auto;
}

/* ── rail items ───────────────────────────────────────────────────
   `!important` is the design's own: the active state has to beat the inline
   base style each rail button carries. */
.vb-railitem[data-active="true"] {
  background: rgba(212,175,55,.16) !important;
  border-color: rgba(240,200,50,.34) !important;
  color: #f2f7ee !important;
}
.vb-railitem:hover { background: rgba(255,255,255,.07); color: #f2f7ee; }
.vb-railitem[data-active="true"] .vb-railmark { opacity: 1 !important; }

/* ── the KPI deck ─────────────────────────────────────────────────
   Four across, two at 1180, one at 640. A count is useless if it wraps to
   two characters per line on a phone. */
.vb-kdeck { display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: 12px; }

/* ── a two-up grid, for card sections ─────────────────────────── */
.vb-grid2 { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 13px; }

/* ── skeletons ────────────────────────────────────────────────────
   Uses the house `shim` keyframe from css/dc-base.css rather than declaring a
   second one that would drift from it. */
.vb-skel {
  background: linear-gradient(90deg, rgba(255,255,255,.06) 25%, rgba(255,255,255,.14) 37%, rgba(255,255,255,.06) 63%);
  background-size: 200% 100%;
  animation: shim 1.5s linear infinite;
  border-radius: 8px;
}

/* ── tables ───────────────────────────────────────────────────────
   Density is the constraint, not the enemy. The wrapper scrolls sideways so
   the PAGE never does, which is the portal-wide rule. */
.vb-tblwrap {
  overflow-x: auto;
  border: 1px solid rgba(255,255,255,.21);
  border-radius: 16px;
  background: rgba(255,255,255,.05);
}
table.vb-tbl { width: 100%; min-width: 760px; border-collapse: separate; border-spacing: 0; }
table.vb-tbl thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 12px 14px;
  text-align: left;
  white-space: nowrap;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 9.5px;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: rgba(242,247,238,.62);
  background: #141d10;
  border-bottom: 1px solid rgba(255,255,255,.21);
}
table.vb-tbl tbody td {
  padding: 13px 14px;
  font-size: 13px;
  line-height: 1.5;
  color: rgba(242,247,238,.88);
  border-bottom: 1px solid rgba(255,255,255,.08);
  vertical-align: top;
}
table.vb-tbl tbody tr:nth-child(even) td { background: rgba(255,255,255,.028); }
table.vb-tbl tbody tr { transition: background .25s ease; }
table.vb-tbl tbody tr:hover td { background: rgba(240,200,50,.09); }
table.vb-tbl tbody tr:last-child td { border-bottom: none; }
table.vb-tbl td.num,
table.vb-tbl th.num { font-family: 'IBM Plex Mono', monospace; white-space: nowrap; }

/* ── the responsive ladder, in the design's own order ─────────────
   The 1020 breakpoint is the interesting one: the rail stops being a column
   and becomes a horizontal scroller, so 26 sections stay reachable on a
   tablet without a hamburger nobody finds. */
@media (max-width: 1180px) {
  .vb-kdeck { grid-template-columns: repeat(2, minmax(0,1fr)) }
}
@media (max-width: 1020px) {
  .vb-shell { grid-template-columns: 1fr; gap: 14px }
  .vb-rail { position: static }
  .vb-railscroll {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    gap: 12px;
    overflow-x: auto;
    padding-bottom: 6px;
  }
  .vb-railband { min-width: 180px; flex: 0 0 auto }
  .vb-grid2 { grid-template-columns: 1fr }
}
@media (max-width: 640px) {
  .vb-kdeck { grid-template-columns: 1fr }
  .vb-hidexs { display: none !important }
  .vb-rowstack { flex-direction: column !important; align-items: stretch !important }
}

/* ════════════════════════════════════════════════════════════════════
   THE KEYFRAMES THIS PORT REFERENCES

   `.vb-skel` above says `animation: shim 1.5s linear infinite`, copied from the
   design. It was BROKEN in service: @keyframes shim is declared in 29 of this
   portal's stylesheets and volunteer-board.html links none of them, so the
   skeleton rows showed a static gradient and never swept. Found by listing the
   page's own <link>s rather than assuming the house keyframe was global.

   Declaring it here is also the more faithful fix. css/dc-base.css runs shim on
   PIXELS (-380px to 380px); this design runs it on percentages, which is what a
   1560px canvas needs. The values below are the design's own.
   ════════════════════════════════════════════════════════════════════ */
@keyframes shim { 0% { background-position:200% 0 } 100% { background-position:-200% 0 } }
@keyframes vbHdrDrop { from { transform:translateY(-100%); opacity:0 } to { transform:none; opacity:1 } }
@keyframes vbSbIn    { from { transform:translateX(-18px); opacity:0 } to { transform:none; opacity:1 } }
@keyframes vbWgIn    { from { transform:translateY(14px); opacity:0 } to { transform:none; opacity:1 } }
@keyframes vbCvIn    { from { transform:translateY(10px); opacity:0 } to { transform:none; opacity:1 } }
@keyframes vbScan    { 0% { background-position:200% 0 } 100% { background-position:-200% 0 } }

/* ════════════════════════════════════════════════════════════════════
   THE CHROME: header, rail card and section head

   The design holds these as inline styles, because a mock has no reason to
   name them. Every declaration is lifted verbatim from
   design-source/VMEM Board.dc.html, only given a name.
   ════════════════════════════════════════════════════════════════════ */

/* ── Header ─────────────────────────────────────────────────────────── */
.vb-hdr {
  height:70px; position:sticky; top:0; z-index:60;
  display:flex; align-items:center; justify-content:space-between; gap:16px; padding:0 20px;
  background:linear-gradient(160deg, rgba(255,255,255,.12), rgba(255,255,255,.03) 42%, rgba(255,255,255,.10));
  border-bottom:1px solid rgba(255,255,255,.21);
  backdrop-filter:blur(16px) saturate(1.3); -webkit-backdrop-filter:blur(16px) saturate(1.3);
  animation:vbHdrDrop .5s cubic-bezier(.16,1,.3,1) both;
}
.vb-backbtn {
  display:flex; align-items:center; gap:7px; min-height:44px; padding:0 12px;
  border:1px solid rgba(255,255,255,.21); border-radius:10px; font-size:13px; cursor:pointer;
  color:#f2f7ee; background:transparent; text-decoration:none;
}
.vb-backbtn i { font-size:12px; color:#f0c832; }
.vb-logo {
  width:42px; height:42px; border-radius:50%; background:#fff; border:1.5px solid #f0c832;
  overflow:hidden; flex:0 0 auto; display:flex; align-items:center; justify-content:center; cursor:pointer;
}
.vb-logo img { width:100%; height:100%; object-fit:contain; }
.vb-logo-fallback { background:transparent; border-color:rgba(255,255,255,.21); }
.vb-rolepill {
  display:flex; align-items:center; gap:8px; padding:8px 13px;
  border:1px solid rgba(240,200,50,.34); border-radius:999px; background:rgba(212,175,55,.12);
  font-size:10.5px; letter-spacing:1.6px; text-transform:uppercase;
}
.vb-rolepill i { color:#f0c832; }
.vb-boardlinks { display:flex; gap:8px; }
.vb-boardlinks a {
  display:flex; align-items:center; gap:7px; min-height:44px; padding:0 12px;
  border:1px solid rgba(255,255,255,.21); border-radius:11px; background:rgba(255,255,255,.05);
  font-size:10.5px; letter-spacing:1.4px; text-transform:uppercase; color:#f0c832; text-decoration:none;
}
.vb-boardlinks a:hover { color:#f2f7ee; }
.vb-boardlinks a i { font-size:11px; }
.vb-userchip {
  display:flex; align-items:center; gap:9px; padding:6px 12px 6px 6px;
  border:1px solid rgba(255,255,255,.21); border-radius:999px;
}
.vb-av {
  width:32px; height:32px; border-radius:50%; display:flex; align-items:center; justify-content:center;
  background:linear-gradient(135deg,#f0c832,#f7d94e); color:#0c1109; font-size:12px; font-weight:600;
  overflow:hidden; flex:0 0 auto;
}
.vb-av img { width:100%; height:100%; object-fit:cover; border-radius:50%; }
.vb-whoname { font-size:13px; line-height:1.2; }
.vb-whorole { font-size:9px; letter-spacing:1.4px; text-transform:uppercase; color:rgba(242,247,238,.62); }
.vb-iconbtn {
  width:44px; height:44px; border:1px solid rgba(255,255,255,.21); border-radius:10px;
  background:rgba(255,255,255,.05); color:#f2f7ee; cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
}
.vb-iconbtn i { font-size:13px; }

/* ── The rail card ──────────────────────────────────────────────────── */
.vb-railcard {
  border:1px solid rgba(255,255,255,.21); border-radius:16px; background:rgba(255,255,255,.05);
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px); padding:14px 12px;
}
.vb-railhead {
  display:flex; align-items:center; gap:10px; padding:0 4px 12px;
  border-bottom:1px solid rgba(255,255,255,.12);
}
.vb-railheadicon {
  width:34px; height:34px; flex:0 0 auto; border-radius:50%;
  border:1px solid rgba(240,200,50,.34); background:rgba(212,175,55,.14);
  display:flex; align-items:center; justify-content:center;
}
.vb-railheadicon i { font-size:12px; color:#f0c832; }
.vb-railheadtitle { font-size:14px; font-weight:600; line-height:1.2; }
.vb-railheadsub {
  margin-top:2px; font-size:9px; letter-spacing:1.4px; text-transform:uppercase;
  color:rgba(242,247,238,.62);
}
.vb-rail .vb-railscroll { display:flex; flex-direction:column; gap:14px; margin-top:13px; }
/* The design sets this inline on every band. */
.vb-railband { min-width:200px; }

/* ── The section head, on every slab ────────────────────────────────── */
.vb-shell .vmem-slab-hd {
  margin-top:14px; position:relative; overflow:hidden;
  border:1px solid rgba(240,200,50,.34); border-radius:16px; padding:20px 22px;
  background:linear-gradient(160deg, rgba(212,175,55,.14), rgba(255,255,255,.03) 46%, rgba(255,255,255,.10));
  animation:vbWgIn .5s cubic-bezier(.16,1,.3,1) .04s both;
  /* BLOCK, not the design's space-between row. The design's row exists to put
     a "Preview state" select beside the title, which is a mock affordance and
     has no counterpart on a live board. Every one of this board's 25 section
     heads holds exactly three stacked children, eyebrow, title, blurb, and no
     right-hand control, so a row laid all three side by side. Measured on the
     render, not guessed. This is the design's own left column, exactly. */
  display:block;
}
.vb-shell .vmem-slab-hd::before {
  content:''; position:absolute; left:0; right:0; top:0; height:1.5px;
  background:linear-gradient(90deg,transparent,#f0c832,transparent);
  background-size:200% 100%; animation:vbScan 5.5s linear infinite;
}
/* The CHILD combinator, not a descendant. slab-discipline reuses
   .vmem-slab-hd-title as a 14px heading inside a glass card, and a descendant
   selector restyled that card heading as a 25px Cinzel section title. */
.vb-shell .vmem-slab-hd > .vmem-slab-hd-title {
  font-family:'Cinzel', serif; margin:9px 0 0; font-size:25px; line-height:1.2; font-weight:700;
  text-wrap:pretty; display:flex; align-items:center; gap:10px;
}
.vb-shell .vmem-slab-hd > .vmem-slab-hd-title i { font-size:19px; color:#f0c832; }
.vb-shell .vmem-slab-hd > .vmem-slab-hd-sub {
  margin:11px 0 0; max-width:66ch; font-size:14px; line-height:1.62;
  color:rgba(242,247,238,.88); text-wrap:pretty;
}
.vb-shell .vmem-slab > .vmem-slab-hd:first-child { margin-top:0; }
/* The eyebrow the design draws from {{ section.eyebrow }}; this board fills it
   with the rail band the slab sits in. */
.vb-eyebrow { font-size:10px; letter-spacing:2.6px; text-transform:uppercase; color:#f0c832; }

/* ════════════════════════════════════════════════════════════════════
   BEYOND THE DESIGN: what a 27-section board needs and the mock did not

   Everything above is copied. What follows is written in the same language for
   cases the design file never faced, and each is here because it was MEASURED
   failing on this board, not because it seemed likely.
   ════════════════════════════════════════════════════════════════════ */

/* A grid item defaults to min-width:auto, so its content can push it wider
   than its track. Below 1020 the bands become a row and the widest one takes
   the whole PAGE sideways. Both tracks get min-width:0 so the strip scrolls
   inside itself, which is what its own overflow-x is for. */
.vb-shell > .vmem-rail,
.vb-shell > .vmem-canvas { min-width: 0; }

@media (max-width: 640px) {
  /* The design stacks each band as a 180px column inside the strip. That is
     right for four bands of about four; this board has 27 items in seven
     bands. On a phone the strip compacts to one nowrap line: band label
     inline, items as chips. Same chips, same sizes, same colours, one axis. */
  .vmem-rail.vb-rail .vb-railband {
    min-width: 0; max-height: none; overflow: visible;
    display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
  }
  .vmem-rail.vb-rail .vmem-rail-section-lbl {
    display: block; padding: 0 8px 0 0; margin: 0; flex: 0 0 auto;
    border-right: 1px solid rgba(255,255,255,.12);
  }
  .vb-shell .vmem-slab-hd > .vmem-slab-hd-title { font-size: 21px; }
}

/* ── The banner ─────────────────────────────────────────────────────────
   Copied from the design. It sits above the section head and carries a state
   the whole board is operating under, not a per-section message. The design
   drives it from board state; this board drives it from the SAME live state it
   already watches, so nothing here is a condition invented to fill the strip.
   Empty means absent, so the JS only has to set innerHTML. */
.vb-banner {
  display:flex; align-items:flex-start; gap:13px; padding:15px 17px;
  border:1px solid rgba(255,255,255,.21); border-radius:15px;
  background:rgba(255,255,255,.05);
  animation:vbWgIn .5s cubic-bezier(.16,1,.3,1) both;
}
.vb-banner:empty { display:none; }
.vb-banner > i { margin-top:3px; font-size:13px; }
.vb-banner-body { flex:1 1 320px; min-width:0; }
.vb-banner-eyebrow { font-size:9.5px; letter-spacing:2.2px; text-transform:uppercase; }
.vb-banner-text { margin-top:7px; font-size:13.5px; line-height:1.55; color:rgba(242,247,238,.88); text-wrap:pretty; }
.vb-banner-act {
  flex:0 0 auto; min-height:44px; padding:0 15px;
  border:1px solid rgba(255,255,255,.21); border-radius:11px;
  background:rgba(255,255,255,.06); color:#f2f7ee; font-size:13px; cursor:pointer;
}
/* The four tones the design paints, and no others. */
.vb-banner[data-tone="amber"] { border-color:rgba(245,158,11,.45); background:rgba(245,158,11,.09); }
.vb-banner[data-tone="amber"] > i, .vb-banner[data-tone="amber"] .vb-banner-eyebrow { color:#f59e0b; }
.vb-banner[data-tone="gold"]  { border-color:rgba(240,200,50,.34); background:rgba(212,175,55,.12); }
.vb-banner[data-tone="gold"] > i, .vb-banner[data-tone="gold"] .vb-banner-eyebrow { color:#f0c832; }
.vb-banner[data-tone="red"]   { border-color:rgba(255,72,72,.45); background:rgba(255,72,72,.10); }
.vb-banner[data-tone="red"] > i, .vb-banner[data-tone="red"] .vb-banner-eyebrow { color:#ff4848; }
.vb-banner[data-tone="blue"]  { border-color:rgba(85,136,255,.40); background:rgba(85,136,255,.08); }
.vb-banner[data-tone="blue"] > i, .vb-banner[data-tone="blue"] .vb-banner-eyebrow { color:#5588ff; }
@media (max-width:640px) {
  .vb-banner { flex-direction:column; }
  .vb-banner-act { width:100%; }
}