privateboard 0.1.31 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/public/index.html CHANGED
@@ -713,6 +713,7 @@
713
713
  padding: 0;
714
714
  background: transparent;
715
715
  border: none;
716
+ opacity: 0;
716
717
  pointer-events: none;
717
718
  -webkit-app-region: drag;
718
719
  z-index: 50;
@@ -1031,7 +1032,6 @@
1031
1032
  padding: 6px 6px 8px;
1032
1033
  flex-shrink: 0;
1033
1034
  background: var(--panel);
1034
- border-bottom: 0.5px solid var(--line);
1035
1035
  }
1036
1036
  .sidebar-tab {
1037
1037
  flex: 1;
@@ -1067,13 +1067,6 @@
1067
1067
  font-weight: 600;
1068
1068
  }
1069
1069
  .sidebar-tab.active .ico { opacity: 1; }
1070
- .sidebar-tab .count {
1071
- font-size: 10px;
1072
- color: var(--text-faint);
1073
- font-weight: 400;
1074
- margin-left: 1px;
1075
- }
1076
- .sidebar-tab.active .count { color: var(--text-dim); }
1077
1070
 
1078
1071
  /* Sidebar tab panels */
1079
1072
  .sidebar-panel {
@@ -1115,13 +1108,6 @@
1115
1108
  height: 1px;
1116
1109
  background: var(--line-bright);
1117
1110
  }
1118
- .agents-section-header .badge {
1119
- background: var(--bg);
1120
- border: 0.5px solid var(--line-bright);
1121
- padding: 0 5px;
1122
- font-size: 8px;
1123
- color: var(--text-dim);
1124
- }
1125
1111
 
1126
1112
  /* Shell wraps the agent-row link + delete button as siblings — same
1127
1113
  pattern as session-row-shell · the X click never gets absorbed by
@@ -1150,7 +1136,6 @@
1150
1136
  }
1151
1137
  .agent-row:hover { background: var(--panel-2); }
1152
1138
  .agent-row.active { background: var(--panel-3); }
1153
- .agent-row.active .agent-row-title { color: var(--lime); }
1154
1139
 
1155
1140
  /* The inline X delete button on custom agent rows is gone · Delete
1156
1141
  now lives in the agent profile's ⋯ overflow menu (see
@@ -1159,18 +1144,16 @@
1159
1144
  .agent-row .agent-row-av {
1160
1145
  width: 32px;
1161
1146
  height: 32px;
1162
- background: var(--bg);
1163
- border: 0.5px solid var(--line-bright);
1164
1147
  image-rendering: pixelated;
1165
1148
  image-rendering: crisp-edges;
1166
1149
  flex-shrink: 0;
1167
1150
  }
1168
1151
  .agent-row .agent-row-content { min-width: 0; }
1169
- /* Top-line · matches `.row-top-line` in the rooms list so the
1170
- agent's name + time read with the same baseline/gap rhythm. The
1171
- trailing pin button sits AFTER the time and is hover-revealed,
1172
- so we don't need `space-between` to push it right `flex: 1` on
1173
- the title handles that. */
1152
+ /* Top-line · flex container so the agent name + trailing
1153
+ hover-revealed pin button (and chair-badge / pb-row tag in
1154
+ their variants) share one baseline. `flex: 1` on the title
1155
+ pushes the trailing button to the right edge without needing
1156
+ `space-between`. */
1174
1157
  .agent-row .agent-row-top-line {
1175
1158
  display: flex;
1176
1159
  align-items: baseline;
@@ -1256,7 +1239,6 @@
1256
1239
  .agent-row.is-chair:hover { background: var(--panel-2); }
1257
1240
  .agent-row.is-chair.active { background: var(--panel-3); }
1258
1241
  .agent-row.is-chair .agent-row-av.chair-av {
1259
- border: 0.5px solid var(--line-bright);
1260
1242
  position: relative;
1261
1243
  }
1262
1244
  .agent-row.is-chair .agent-row-av.chair-av::after {
@@ -1323,8 +1305,6 @@
1323
1305
  }
1324
1306
  .agent-row .agent-row-chair-role { color: var(--text-soft); font-weight: 600; }
1325
1307
  .agent-row .agent-row-chair-note { color: var(--text-faint); }
1326
- /* Hide the Chair section's count badge — there's only ever one. */
1327
- .agents-section-header.chair .badge { display: none; }
1328
1308
 
1329
1309
  /* "Building" section · in-flight Full persona build placeholder.
1330
1310
  Lime accent on the header to distinguish from inert pinned /
@@ -1336,10 +1316,6 @@
1336
1316
  background: var(--lime-dim, var(--line));
1337
1317
  opacity: 0.4;
1338
1318
  }
1339
- .agents-section-header.building .badge {
1340
- color: var(--lime);
1341
- border-color: var(--lime-dim, var(--line));
1342
- }
1343
1319
  /* Building row · same dimensions as a normal agent row but the
1344
1320
  avatar slot is a pulsing CRT-style frame instead of a face,
1345
1321
  and the row carries a small "BUILD" / "READY" tag chip.
@@ -1428,8 +1404,12 @@
1428
1404
 
1429
1405
  /* Refined "+ New" — list-item style, sentence case, subtle */
1430
1406
  .new-btn {
1431
- margin: 4px 6px 2px;
1432
- padding: 7px 10px;
1407
+ /* Horizontal margin = 6 (session-row-shell margin) + 4
1408
+ (sessions-scroll padding) so the hover background spans the
1409
+ exact same width as the room rows below — without it, the
1410
+ nav buttons hovered 8px wider than the list rows. */
1411
+ margin: 4px 10px 2px;
1412
+ padding: 5px 10px;
1433
1413
  background: transparent;
1434
1414
  color: var(--text);
1435
1415
  border: none;
@@ -1506,10 +1486,7 @@
1506
1486
  .new-btn.nav-search::before {
1507
1487
  --icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><path d='m21 21-4.3-4.3'/></svg>");
1508
1488
  }
1509
- /* All Reports / All Notes / Search nav-button shape · the count
1510
- badge floats to the right of the label, so the link's flex
1511
- layout grows to fill the row. `.nav-label` flex-grows,
1512
- `.nav-count` is auto-sized and hugs the trailing edge. */
1489
+ /* All Reports / All Notes / Search nav-button shape · label-only. */
1513
1490
  .new-btn.nav-notes,
1514
1491
  .new-btn.nav-reports,
1515
1492
  .new-btn.nav-search {
@@ -1521,49 +1498,20 @@
1521
1498
  flex: 1;
1522
1499
  min-width: 0;
1523
1500
  }
1524
- /* Count badge · subtle mono micro-tag. Uses --text-faint on its own
1525
- and inherits the link's color cascade on hover/active so the badge
1526
- tracks the label's lime shift. Hidden until at least one item
1527
- exists (the `hidden` attr is removed once count > 0). Same visual
1528
- vocabulary as the section-header .badge below — both render as
1529
- mono pills so the sidebar reads as one consistent counting
1530
- system. */
1531
- .new-btn.nav-notes .nav-count,
1532
- .new-btn.nav-reports .nav-count,
1533
- .new-btn.nav-search .nav-count {
1534
- font-family: var(--mono);
1535
- font-size: 10px;
1536
- font-weight: 600;
1537
- letter-spacing: 0.04em;
1538
- color: var(--text-faint);
1539
- padding: 1px 6px;
1540
- background: var(--panel-3);
1541
- border-radius: 3px;
1542
- flex-shrink: 0;
1543
- transition: color 0.12s, background 0.12s;
1544
- }
1545
- .new-btn.nav-notes:hover .nav-count,
1546
- .new-btn.nav-notes.active .nav-count,
1547
- .new-btn.nav-reports:hover .nav-count,
1548
- .new-btn.nav-reports.active .nav-count,
1549
- .new-btn.nav-search:hover .nav-count,
1550
- .new-btn.nav-search.active .nav-count {
1551
- color: var(--lime);
1552
- background: rgba(190, 242, 100, 0.08);
1553
- }
1554
1501
  .new-btn:hover {
1555
1502
  background: var(--panel-2);
1556
- color: var(--lime);
1503
+ color: var(--text);
1557
1504
  }
1558
1505
  /* The ::before icon inherits via currentColor — no explicit
1559
1506
  hover override needed; setting the parent's color cascades. */
1560
- /* Active state · matches session-row-shell.active treatment so the
1561
- sidebar reads consistently when the composer is the current view. */
1507
+ /* Active state · same panel as session-row-shell.active, but text
1508
+ stays at the default `--text` (no accent shift) selection
1509
+ reads as a settled state via the background alone. */
1562
1510
  .new-btn.active {
1563
1511
  background: var(--panel-3);
1564
- color: var(--lime);
1512
+ color: var(--text);
1565
1513
  }
1566
- .new-btn.active::before { color: var(--lime); }
1514
+ .new-btn.active::before { color: var(--text); }
1567
1515
 
1568
1516
  .sessions-scroll {
1569
1517
  flex: 1;
@@ -1596,11 +1544,11 @@
1596
1544
  set this to 16px and made the section badges drift 4px
1597
1545
  further left than the nav badges. */
1598
1546
  padding: 8px 12px 4px 10px;
1599
- font-size: 9px;
1547
+ font-size: 10px;
1600
1548
  color: var(--text-dim);
1601
1549
  text-transform: uppercase;
1602
1550
  letter-spacing: 0.14em;
1603
- font-weight: 700;
1551
+ font-weight: 600;
1604
1552
  /* Sticky · the LIVE / PAUSED / ADJOURNED dividers stay pinned
1605
1553
  to the top of the scroll viewport as the user scrolls down
1606
1554
  a long room list. Parent `.sessions-scroll` is the scroll
@@ -1624,7 +1572,6 @@
1624
1572
  }
1625
1573
  .section-header.live { color: var(--lime); }
1626
1574
  .section-header.draft { color: var(--amber); }
1627
- .section-header.paused { color: var(--amber); }
1628
1575
  .section-header.adjourned { color: var(--text-dim); }
1629
1576
  .section-header.pinned { color: var(--text-soft); }
1630
1577
  .row-status {
@@ -1636,33 +1583,7 @@
1636
1583
  margin-right: 6px;
1637
1584
  }
1638
1585
  .row-status.paused { color: var(--amber); }
1639
- .section-header .line { flex: 1; border-top: 0.5px dashed var(--line-bright); }
1640
- /* Section badge · same visual vocabulary as .new-btn.nav-* .nav-count
1641
- above so the sidebar reads as one consistent counting system.
1642
- Mono pill, panel-3 bg, faint text, rounded — overrides the
1643
- .section-header parent's sans uppercase letter-spacing because a
1644
- count is a number, not a label. The .live variant still takes its
1645
- pulsing dot via ::before. */
1646
- .section-header .badge {
1647
- font-family: var(--mono);
1648
- font-size: 10px;
1649
- font-weight: 600;
1650
- letter-spacing: 0.04em;
1651
- text-transform: none;
1652
- color: var(--text-faint);
1653
- padding: 1px 6px;
1654
- background: var(--panel-3);
1655
- border-radius: 3px;
1656
- flex-shrink: 0;
1657
- }
1658
- .section-header.live .badge {
1659
- color: var(--lime);
1660
- background: rgba(190, 242, 100, 0.08);
1661
- }
1662
- .section-header.live .badge::before {
1663
- content: "● ";
1664
- animation: pulse 1.5s infinite;
1665
- }
1586
+ .section-header .line { flex: 1; }
1666
1587
  .pin-glyph {
1667
1588
  width: 10px;
1668
1589
  height: 10px;
@@ -1677,7 +1598,6 @@
1677
1598
  reserve 22px and shrink the title). The row-shell ancestor
1678
1599
  needs `position: relative` for this to anchor correctly. */
1679
1600
  .agent-row-shell, .session-row-shell { position: relative; }
1680
- .row-top-line .pin-toggle,
1681
1601
  .agent-row-top-line .pin-toggle {
1682
1602
  position: absolute;
1683
1603
  top: 8px;
@@ -1695,16 +1615,13 @@
1695
1615
  opacity: 0;
1696
1616
  transition: opacity 0.12s, color 0.12s;
1697
1617
  }
1698
- .row-top-line .pin-toggle svg,
1699
1618
  .agent-row-top-line .pin-toggle svg {
1700
1619
  width: 11px;
1701
1620
  height: 11px;
1702
1621
  fill: currentColor;
1703
1622
  }
1704
- .session-row:hover .pin-toggle,
1705
1623
  .agent-row:hover .pin-toggle { opacity: 0.55; }
1706
1624
  .pin-toggle:hover { opacity: 1 !important; color: var(--lime); }
1707
- .session-row.pinned .pin-toggle,
1708
1625
  .agent-row.pinned .pin-toggle {
1709
1626
  opacity: 1;
1710
1627
  color: var(--lime);
@@ -1718,21 +1635,20 @@
1718
1635
  X button all clip cleanly to rounded corners. */
1719
1636
  .session-row-shell {
1720
1637
  position: relative;
1721
- margin: 1px 6px;
1638
+ margin: 2px 6px;
1722
1639
  border-radius: 4px;
1723
1640
  overflow: hidden;
1724
1641
  }
1725
1642
  .session-row-shell.active .session-row { background: var(--panel-3); }
1726
- .session-row-shell.active .row-title { color: var(--lime); }
1727
1643
 
1728
1644
  .session-row {
1729
1645
  display: block;
1730
- /* Padding kept in sync with .agent-row so the agents-tab and
1731
- rooms-tab lists share the same horizontal rhythm in the sidebar.
1732
- The empty `.row-cast` placeholder slot was removed the title
1733
- now starts at the row's left padding, matching where the agent
1734
- avatar starts in the agents tab. */
1735
- padding: 8px 10px;
1646
+ /* Horizontal padding kept in sync with .agent-row so the agents-tab
1647
+ and rooms-tab lists share the same horizontal rhythm in the
1648
+ sidebar. Vertical padding 5px single-line title with the
1649
+ subtitle row removed; tight hitbox matching the .new-btn nav
1650
+ buttons above. */
1651
+ padding: 5px 10px;
1736
1652
  text-decoration: none;
1737
1653
  color: var(--text);
1738
1654
  cursor: pointer;
@@ -1741,35 +1657,42 @@
1741
1657
  .session-row:hover { background: var(--panel-2); }
1742
1658
 
1743
1659
  /* Per-row delete button · sibling of the anchor, so its click never
1744
- gets routed to the link. Hover-revealed in the top-right band. */
1660
+ gets routed to the link. Hover-revealed in the top-right band.
1661
+ Icon-only Lucide X painted via `mask-image + currentColor` —
1662
+ same recipe as the `.new-btn::before` nav glyphs, no border or
1663
+ chip chrome. Vertical centring via the symmetric top/bottom +
1664
+ auto margin trick so the glyph stays mid-row regardless of row
1665
+ padding. */
1745
1666
  .row-delete {
1746
1667
  position: absolute;
1747
- top: 8px;
1668
+ top: 0;
1669
+ bottom: 0;
1748
1670
  right: 10px;
1749
- width: 20px; height: 20px;
1750
- background: var(--panel-2);
1751
- border: 0.5px solid var(--line-bright);
1671
+ margin: auto 0;
1672
+ width: 16px;
1673
+ height: 16px;
1674
+ border: 0;
1675
+ padding: 0;
1676
+ background-color: currentColor;
1752
1677
  color: var(--text-faint);
1753
- font-family: var(--mono);
1754
- font-size: 10px;
1755
- line-height: 1;
1756
1678
  cursor: pointer;
1757
1679
  display: none;
1758
- align-items: center;
1759
- justify-content: center;
1760
- padding: 0;
1761
- transition: all 0.12s;
1680
+ transition: color 0.12s;
1762
1681
  z-index: 2;
1682
+ -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 6 6 18'/><path d='m6 6 12 12'/></svg>");
1683
+ mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M18 6 6 18'/><path d='m6 6 12 12'/></svg>");
1684
+ -webkit-mask-repeat: no-repeat;
1685
+ mask-repeat: no-repeat;
1686
+ -webkit-mask-position: center;
1687
+ mask-position: center;
1688
+ -webkit-mask-size: 16px 16px;
1689
+ mask-size: 16px 16px;
1763
1690
  }
1764
- .session-row-shell:hover .row-delete { display: flex; }
1765
- /* Subtitle reserves right padding so its ellipsis stops before
1691
+ .session-row-shell:hover .row-delete { display: block; }
1692
+ /* Row title reserves right padding so its ellipsis stops before
1766
1693
  the absolutely-positioned X delete button on hover. */
1767
- .session-row-shell .row-subtitle { padding-right: 26px; }
1768
- .row-delete:hover {
1769
- border-color: var(--red);
1770
- color: var(--red);
1771
- background: var(--bg);
1772
- }
1694
+ .session-row-shell:hover .row-title { padding-right: 26px; }
1695
+ .row-delete:hover { color: var(--red); }
1773
1696
  .session-row-shell.removing { opacity: 0; transition: opacity 0.16s; }
1774
1697
 
1775
1698
  /* Empty state for the Adjourned section */
@@ -1833,40 +1756,17 @@
1833
1756
  flex-shrink: 0;
1834
1757
  }
1835
1758
 
1836
- .row-content { min-width: 0; overflow: hidden; }
1837
- .row-top-line {
1838
- display: flex;
1839
- align-items: baseline;
1840
- gap: 4px;
1841
- margin-bottom: 1px;
1842
- }
1843
1759
  .row-title {
1760
+ display: block;
1844
1761
  font-size: 14px;
1845
1762
  font-weight: 400;
1846
1763
  color: var(--text);
1847
1764
  overflow: hidden;
1848
1765
  text-overflow: ellipsis;
1849
1766
  white-space: nowrap;
1850
- flex: 1;
1851
- min-width: 0;
1852
1767
  font-family: var(--sans);
1853
1768
  letter-spacing: -0.005em;
1854
1769
  }
1855
- .row-subtitle {
1856
- font-size: 12px;
1857
- color: var(--text-dim);
1858
- overflow: hidden;
1859
- text-overflow: ellipsis;
1860
- white-space: nowrap;
1861
- font-family: var(--sans);
1862
- letter-spacing: -0.003em;
1863
- }
1864
- .row-subtitle .report-style {
1865
- color: var(--lime);
1866
- font-weight: 700;
1867
- margin-right: 3px;
1868
- }
1869
- .row-subtitle .sep { color: var(--text-faint); margin: 0 3px; }
1870
1770
 
1871
1771
  /* Bottom-of-list pagination hint (Slack-style) — appears
1872
1772
  briefly when the user scrolls to the bottom of the list. */
@@ -10719,7 +10619,15 @@
10719
10619
  margin: 0 auto;
10720
10620
  border: 0.5px solid var(--line-strong);
10721
10621
  border-radius: 12px;
10722
- background: var(--panel-2);
10622
+ /* Frosted glass · same recipe as `.input-bar` directly below so the
10623
+ two conjoined cards share one continuous surface treatment. The
10624
+ chat stream behind softens through the haze; the queue chrome
10625
+ stays legible. `.queue-head` / `.queue-list` rows below are
10626
+ `background: transparent` already, so the outer glass dominates. */
10627
+ background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
10628
+ background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
10629
+ backdrop-filter: blur(24px) saturate(180%);
10630
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
10723
10631
  box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
10724
10632
  position: relative;
10725
10633
  /* `overflow: visible` (was `hidden`) so hover tooltips on header
@@ -10771,7 +10679,14 @@
10771
10679
  border-bottom: none;
10772
10680
  border-top-left-radius: 12px;
10773
10681
  border-top-right-radius: 12px;
10774
- background: var(--panel-2);
10682
+ /* Frosted glass · mirror `.speaking-queue` so the paused-state
10683
+ chrome reads identically to the live queue strip — same haze,
10684
+ same conjoin with `.input-bar` underneath. Without this the
10685
+ strip pops as a solid card the moment the user hits Pause. */
10686
+ background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
10687
+ background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
10688
+ backdrop-filter: blur(24px) saturate(180%);
10689
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
10775
10690
  box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
10776
10691
  overflow: hidden;
10777
10692
  }
@@ -10836,7 +10751,15 @@
10836
10751
  border-bottom: none;
10837
10752
  border-top-left-radius: 12px;
10838
10753
  border-top-right-radius: 12px;
10839
- background: var(--panel-2);
10754
+ /* Frosted glass · third sibling in the queue/paused/adjourned trio
10755
+ that shares this slot above the input-bar. Same recipe as
10756
+ `.speaking-queue` / `.paused-strip` so the live → paused →
10757
+ adjourned chrome transitions read as a state-of-the-same-surface
10758
+ rather than three different cards. */
10759
+ background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
10760
+ background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
10761
+ backdrop-filter: blur(24px) saturate(180%);
10762
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
10840
10763
  box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
10841
10764
  /* `overflow: visible` (was `hidden`) so hover tooltips on header
10842
10765
  controls — the mirrored `.rt-sub-restore::after` for the
@@ -16107,7 +16030,10 @@
16107
16030
  <!-- voice-recorder · meeting capture. Lazy module · only allocates
16108
16031
  anything on first start(). Loaded BEFORE app.js so the global
16109
16032
  `window.BoardroomRecorder` exists by the time app.js init() wires
16110
- up the onStateChange subscription. -->
16033
+ up the onStateChange subscription. mp4-muxer vendored in
16034
+ /vendor/ provides the H.264+AAC mp4 container · classic-script
16035
+ wrapper emits `window.Mp4Muxer`. Must load before voice-recorder. -->
16036
+ <script src="vendor/mp4-muxer.min.js" defer></script>
16111
16037
  <script src="voice-recorder.js" defer></script>
16112
16038
  <script src="app.js" defer></script>
16113
16039
  <script>
@@ -16271,12 +16197,10 @@
16271
16197
  <a href="#" class="sidebar-tab active" data-sidebar-tab="rooms" role="tab" aria-selected="true">
16272
16198
  <i class="ic ic-rooms ico"></i>
16273
16199
  <span data-i18n="sidebar_tab_rooms">Rooms</span>
16274
- <span class="count" data-sidebar-tab-count="rooms">0</span>
16275
16200
  </a>
16276
16201
  <a href="#" class="sidebar-tab" data-sidebar-tab="agents" role="tab" aria-selected="false">
16277
16202
  <i class="ic ic-agents ico"></i>
16278
16203
  <span data-i18n="sidebar_tab_agents">Agents</span>
16279
- <span class="count" data-sidebar-tab-count="agents">0</span>
16280
16204
  </a>
16281
16205
  </div>
16282
16206
 
@@ -16289,7 +16213,6 @@
16289
16213
  did <X> come up?"). Same .new-btn chrome as siblings. -->
16290
16214
  <a href="#" class="new-btn nav-search" data-search-trigger>
16291
16215
  <span class="nav-label">Search</span>
16292
- <span class="nav-count" data-search-count hidden></span>
16293
16216
  </a>
16294
16217
  <!-- "All Reports" cross-room index · opens a Perplexity-Discover-
16295
16218
  style grid of every brief any room has produced. Sits below
@@ -16298,17 +16221,14 @@
16298
16221
  elsewhere in the sidebar. -->
16299
16222
  <a href="#" class="new-btn nav-reports" data-reports-trigger>
16300
16223
  <span class="nav-label" data-i18n="sidebar_all_reports">All Reports</span>
16301
- <span class="nav-count" data-reports-count hidden></span>
16302
16224
  </a>
16303
16225
  <!-- "All Notes" · chairman's-notes index. Sits directly below
16304
16226
  All Reports because both are cross-cutting aggregation views
16305
16227
  (read-only collections that span every room), distinct from
16306
16228
  the room list below. Bookmark glyph differentiates from
16307
- Reports' FileText glyph. The data-notes-count badge updates
16308
- live as new notes are saved or deleted. -->
16229
+ Reports' FileText glyph. -->
16309
16230
  <a href="#" class="new-btn nav-notes" data-notes-trigger>
16310
16231
  <span class="nav-label" data-i18n="sidebar_all_notes">All Notes</span>
16311
- <span class="nav-count" data-notes-count hidden></span>
16312
16232
  </a>
16313
16233
 
16314
16234
  <div class="sessions-scroll">
@@ -300,6 +300,14 @@
300
300
  height: 100% !important;
301
301
  display: block;
302
302
  }
303
+ /* Director nameplates pile up on this 180 px banner · directors sit
304
+ on a tight arc and their plate width plus mono caps cause adjacent
305
+ names to overlap into an unreadable blob. Hide them on this surface
306
+ and let the "speaking" pulse + rotating speaker badge carry the
307
+ carousel motion instead. Chair plate stays · single seat, no collision.
308
+ Mirrors `.hero-3d .rt-name-director { display: none !important }`
309
+ on the marketing homepage. */
310
+ .onb-voice-banner-stage .rt-name-director { display: none !important; }
303
311
  /* 3D round-table poster · static fallback when WebGL/motion is
304
312
  unavailable. Matches the marketing homepage hero scene so the
305
313
  visual is consistent across surfaces. <picture> chooses WebP /
package/public/themes.css CHANGED
@@ -71,6 +71,19 @@
71
71
  /* --sans defaults to the human face — chat input, topic question,
72
72
  replies, and live notes use it unless an agent override applies. */
73
73
  --sans: var(--font-human);
74
+
75
+ /* ─── Radius scale ───
76
+ One axis-of-truth for corner softness. The earlier codebase had
77
+ border-radius values inlined as 4 / 6 / 8 / 12 — bumping the look
78
+ meant find-and-replacing per call-site. These tokens are the new
79
+ single dial: turn one knob, the whole product rounds in lockstep.
80
+ Theme-agnostic (radius doesn't change between dark / light), so
81
+ they live on plain `:root` rather than per-theme. */
82
+ --radius-sm: 6px; /* small chrome · sidebar rows, hover pills */
83
+ --radius-md: 8px; /* buttons, inputs, badges with text */
84
+ --radius-lg: 12px; /* cards, modal panels, search card */
85
+ --radius-xl: 16px; /* large surfaces (main raised panel) */
86
+ --radius-pill: 9999px; /* pure pill / circle */
74
87
  }
75
88
 
76
89
  /* ─── DARK · regent · warm gold on near-black ───
@@ -79,14 +92,26 @@
79
92
  primary keeps working — the actual hue is gold (#C9A46B).
80
93
  See `docs/theme-system.md` for the full token reference. */
81
94
  :root[data-theme="dark"] {
95
+ /* ─── Surface ladder · structural panels ───
96
+ Used for solid raised cards, sub-cards, and the chrome shell.
97
+ For *transient* states (hover / active / pressed) prefer the
98
+ alpha overlay tokens below — they layer cleanly over vibrancy
99
+ and stay theme-agnostic at the call site. */
82
100
  --bg: #0A0A0A;
83
101
  --panel: #131312;
84
- --panel-2: #1A1A18;
85
- --panel-3: #21211E;
86
- --hi: #2A2A26;
102
+ --panel-2: #21211F;
103
+ --panel-3: #2A2A27;
87
104
  --strip-bg: color-mix(in srgb, var(--lime) 6%, var(--bg)); /* topbar / bottombar — very subtle brand tint */
88
105
  --accent-line: color-mix(in srgb, var(--lime) 22%, var(--bg)); /* brand-tinted hairlines for composer chrome */
89
106
 
107
+ /* ─── Alpha overlay system · macOS / Linear-style ───
108
+ Semantic interaction layers. The values resolve to white-alpha
109
+ on dark themes and black-alpha on light, so a single rule
110
+ (`background: var(--surface-hover)`) reads correctly in both. */
111
+ --surface-hover: rgba(255, 255, 255, 0.10);
112
+ --surface-active: rgba(255, 255, 255, 0.15);
113
+ --surface-strong: rgba(255, 255, 255, 0.20);
114
+
90
115
  --line: #26241F;
91
116
  --line-bright: #3A3934;
92
117
  --line-strong: #5A5852; /* raised from #4D4B45 so input frames separate from line-bright */
@@ -95,6 +120,7 @@
95
120
  --text-soft: #8E8B83;
96
121
  --text-dim: #7E7B70; /* was #5C5A52; raised to ~4.3:1 vs bg, near WCAG AA */
97
122
  --text-faint: #5A5848; /* was #5C5A4D; clearly fainter than dim, OK for hints */
123
+ --text-muted: rgba(255, 255, 255, 0.60); /* alpha-derived · pairs with surface overlays */
98
124
 
99
125
  --lime: #C9A46B;
100
126
  --lime-deep: #9A7B40;
@@ -102,7 +128,6 @@
102
128
  --amber: #A57843;
103
129
  --amber-dim: #5E441F;
104
130
  --red: #B5706A;
105
- --red-dim: #6B4540;
106
131
  --cyan: #6A9B97;
107
132
  --magenta: #8E7A8E;
108
133
  }
@@ -127,10 +152,16 @@
127
152
  --panel: #FAFAFA; /* was #F9F9F9 — micro-adjust for ladder spacing */
128
153
  --panel-2: #EEEEEE; /* was #F1F1F1 — clearer 1-step pop vs panel */
129
154
  --panel-3: #E4E4E4; /* was #ECECEC — clearer 2-step */
130
- --hi: #D4D4D4; /* was #E5E5E5 — visible hover surface */
131
155
  --strip-bg: color-mix(in srgb, var(--lime) 6%, var(--bg)); /* topbar / bottombar — very subtle brand tint */
132
156
  --accent-line: color-mix(in srgb, var(--lime) 22%, var(--bg)); /* brand-tinted hairlines for composer chrome */
133
157
 
158
+ /* ─── Alpha overlay system (light theme · black-on-white) ───
159
+ Mirrors the dark-theme set but inverted polarity, so the same
160
+ `var(--surface-hover)` rule looks correct in both modes. */
161
+ --surface-hover: rgba(0, 0, 0, 0.05);
162
+ --surface-active: rgba(0, 0, 0, 0.08);
163
+ --surface-strong: rgba(0, 0, 0, 0.12);
164
+
134
165
  --line: #E5E5E5; /* was #ECECEC — actually visible on white (1.10:1) */
135
166
  --line-bright: #C8C8CD; /* was #D4D4D8 — clearer everyday border */
136
167
  --line-strong: #A6A6AC; /* was #CCCCCC — structural divider / input frame */
@@ -139,6 +170,7 @@
139
170
  --text-soft: #5D5D5D; /* secondary — 7.0:1 (AAA for text > 18pt) */
140
171
  --text-dim: #717181; /* was #8E8EA0; lifted to 4.7:1 (AA) for readable */
141
172
  --text-faint: #9A9A9A; /* hint level ~2.8:1 — for disabled/decoration */
173
+ --text-muted: rgba(0, 0, 0, 0.55); /* alpha-derived · pairs with surface overlays */
142
174
 
143
175
  --lime: #10A37F; /* OpenAI green — primary accent */
144
176
  --lime-deep: #0D8868; /* deeper green — hover / active depth */
@@ -147,7 +179,6 @@
147
179
  --amber: #C2410C;
148
180
  --amber-dim: #FED7AA;
149
181
  --red: #DC2626;
150
- --red-dim: #FEE2E2;
151
182
  --cyan: #0891B2;
152
183
  --magenta: #BE185D;
153
184
  }
@@ -127,7 +127,13 @@
127
127
  * blips, gavel, countdown beeps) all flow into the meeting
128
128
  * recording's audio mix alongside director TTS. Safe to call
129
129
  * before ensureContext fires — we connect now if possible and
130
- * also stash so a later ensureContext call wires it up. */
130
+ * also stash so a later ensureContext call wires it up.
131
+ *
132
+ * NOTE · this only works when `dest` is a node in the SAME
133
+ * AudioContext as typing-sfx. The recorder runs in its own
134
+ * context so this path is a silent no-op for it; the recorder
135
+ * uses `getRecorderStream()` below to bridge across contexts
136
+ * via a MediaStream. Kept for any future intra-context callers. */
131
137
  let _pendingRecorderDest = null;
132
138
  function connectRecorderDestination(dest) {
133
139
  if (!dest) return;
@@ -139,6 +145,30 @@
139
145
  }
140
146
  }
141
147
 
148
+ /** Cross-context recorder bridge · creates a MediaStreamDestination
149
+ * in OUR AudioContext and returns its `.stream` so the recorder's
150
+ * separate AudioContext can pull our SFX output in via
151
+ * `recorderCtx.createMediaStreamSource(stream)`. AudioNodes cannot
152
+ * connect across AudioContexts directly · MediaStream is the only
153
+ * Web Audio bridge between two graphs. The destination node is
154
+ * cached so repeated calls return the same stream. */
155
+ let _recorderStreamDest = null;
156
+ function getRecorderStream() {
157
+ const ctx = ensureContext();
158
+ if (!ctx) return null;
159
+ if (!_recorderStreamDest) {
160
+ try {
161
+ _recorderStreamDest = ctx.createMediaStreamDestination();
162
+ if (_outputNode) _outputNode.connect(_recorderStreamDest);
163
+ } catch (e) {
164
+ console.warn("[typing-sfx] getRecorderStream failed", e && e.message);
165
+ _recorderStreamDest = null;
166
+ return null;
167
+ }
168
+ }
169
+ return _recorderStreamDest.stream;
170
+ }
171
+
142
172
  /** Suspend the AudioContext shortly after `setThinking(false)` so
143
173
  * the system audio session is released for the HTMLAudioElement
144
174
  * TTS path. Browsers (Safari / iOS especially) treat a "running"
@@ -445,5 +475,5 @@
445
475
 
446
476
  // Public surface · attached to window so app.js (and the
447
477
  // user-settings toggle) can reach it without an import.
448
- window.boardroomTypingSfx = { tick, speakerChange, setThinking, gavel, countdownTick, setEnabled, isEnabled, connectRecorderDestination };
478
+ window.boardroomTypingSfx = { tick, speakerChange, setThinking, gavel, countdownTick, setEnabled, isEnabled, connectRecorderDestination, getRecorderStream };
449
479
  })();