@gotcos/glasses-server 6.98.1 → 6.99.16

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.
Files changed (98) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/package.json +2 -1
  3. package/server/index.ts +170 -11
  4. package/server/lib/api-auth.ts +31 -14
  5. package/server/lib/claude-bridge.ts +21 -2
  6. package/server/lib/codex-bridge.ts +21 -2
  7. package/server/lib/cos-thread.ts +582 -0
  8. package/server/lib/display-bus.ts +3 -0
  9. package/server/lib/drop-attachments.ts +55 -0
  10. package/server/lib/drop-destinations.ts +134 -0
  11. package/server/lib/drop-history-search.ts +97 -0
  12. package/server/lib/drop-p2-boot.ts +60 -0
  13. package/server/lib/drop-p2-runtime.ts +237 -0
  14. package/server/lib/drop-p2-settings.ts +174 -0
  15. package/server/lib/drop-resolution.ts +168 -0
  16. package/server/lib/drop-router.ts +325 -0
  17. package/server/lib/drop-service.ts +830 -0
  18. package/server/lib/drop-settings.ts +56 -0
  19. package/server/lib/drop-store.ts +450 -0
  20. package/server/lib/drop-when.ts +265 -0
  21. package/server/lib/glasses-pairing.ts +15 -0
  22. package/server/lib/learning-needs-you.ts +111 -0
  23. package/server/lib/live-cues-v2-settings.ts +2 -1
  24. package/server/lib/live-cues-v2.ts +6 -4
  25. package/server/lib/media-store.ts +80 -0
  26. package/server/lib/meeting-batch-transcribe.ts +15 -2
  27. package/server/lib/meeting-consolidation-run.ts +169 -0
  28. package/server/lib/meeting-store.ts +61 -0
  29. package/server/lib/meeting-title-steer.ts +200 -0
  30. package/server/lib/network-policy.ts +84 -6
  31. package/server/lib/permission-broker.ts +13 -0
  32. package/server/lib/phone-recording.ts +1 -1
  33. package/server/lib/ping-budget.ts +149 -0
  34. package/server/lib/ping-engine.ts +245 -0
  35. package/server/lib/ping-feed-sink.ts +33 -0
  36. package/server/lib/ping-sources.ts +192 -0
  37. package/server/lib/query-job-coordinator.ts +5 -2
  38. package/server/lib/query-job-runtime.ts +45 -4
  39. package/server/lib/query-job-types.ts +10 -1
  40. package/server/lib/reminder-scheduler.ts +153 -0
  41. package/server/lib/tailnet-self-name.ts +103 -0
  42. package/server/lib/tailscale-nudge.ts +40 -2
  43. package/server/lib/task-store.ts +32 -0
  44. package/server/lib/web-ask-admission.ts +56 -0
  45. package/server/lib/web-live-chunks.ts +273 -0
  46. package/server/lib/web-memories-bridge.ts +323 -0
  47. package/server/lib/web-session-auth.ts +492 -0
  48. package/server/lib/web-usage-bridge.ts +154 -0
  49. package/server/lib/work-search.ts +14 -1
  50. package/server/lib/your-move-count.ts +58 -0
  51. package/server/routes/cos-thread.ts +79 -0
  52. package/server/routes/display.ts +11 -1
  53. package/server/routes/drops.ts +262 -0
  54. package/server/routes/health.ts +16 -0
  55. package/server/routes/live-cues.ts +5 -4
  56. package/server/routes/meeting.ts +61 -5
  57. package/server/routes/permission-broker.ts +23 -3
  58. package/server/routes/transcribe-stream.ts +43 -4
  59. package/server/routes/web-control.ts +569 -0
  60. package/server/routes/welcome-context.ts +2 -1
  61. package/server/scripts/web-live-cls/cdp.mjs +57 -0
  62. package/server/scripts/web-live-cls/measure.mjs +80 -0
  63. package/server/scripts/web-live-cls/mock.mjs +97 -0
  64. package/server/scripts/web-live-cls/render.mjs +26 -0
  65. package/server/web-control/app.css +519 -0
  66. package/server/web-control/app.js +3643 -0
  67. package/server/web-control/control.css +325 -0
  68. package/server/web-control/fonts/DMSans.ttf +0 -0
  69. package/server/web-control/fonts/Fraunces.ttf +0 -0
  70. package/server/web-control/fonts/JetBrainsMono.ttf +0 -0
  71. package/server/web-control/index.html +275 -0
  72. package/server/web-control/mark.svg +1 -0
  73. package/server/web-control/memories/d3.min.js +2 -0
  74. package/server/web-control/memories/graph-explorer.css +200 -0
  75. package/server/web-control/memories/graph-explorer.js +748 -0
  76. package/server/web-control/memories/memories-app.js +1561 -0
  77. package/server/web-control/memories/memories-theme.css +73 -0
  78. package/server/web-control/memories/memories.html +72 -0
  79. package/server/web-control/memories/memory-stewardship.js +63 -0
  80. package/server/web-control/memories/memory-workspace.js +216 -0
  81. package/server/web-control/panes/cos-chat.css +174 -0
  82. package/server/web-control/panes/cos-chat.js +1118 -0
  83. package/server/web-control/panes/dictation.js +173 -0
  84. package/server/web-control/panes/meetings.css +314 -0
  85. package/server/web-control/panes/meetings.js +2568 -0
  86. package/server/web-control/panes/messages.js +718 -0
  87. package/server/web-control/panes/questions.css +32 -0
  88. package/server/web-control/panes/questions.js +275 -0
  89. package/server/web-control/panes/sessions.css +452 -0
  90. package/server/web-control/panes/settings.js +292 -0
  91. package/server/web-control/panes/speakers.css +269 -0
  92. package/server/web-control/panes/speakers.js +2755 -0
  93. package/server/web-control/panes/threads.js +200 -0
  94. package/server/web-control/panes/work.css +261 -0
  95. package/server/web-control/panes/work.js +1667 -0
  96. package/server/web-control/shell.js +294 -0
  97. package/server/web-control/theme.js +25 -0
  98. package/server/web-control/vendor/qrcode-encoder.js +10 -0
@@ -0,0 +1,325 @@
1
+ /* COS Control Web: Control's own chrome and pane kit, loaded after app.css.
2
+ Measurements follow Control 0.5.366 (ActivityWindow.swift, COSBrand.swift): the 248pt rail with a
3
+ story under each name from 1100px, the icon rail below that, the navigation bar off Today, and
4
+ the shared pane pieces (tinted hero, text switch with a gold rule, rows, tags, YOU/COS cards).
5
+ Phone sizes are set for reading on the iPhone Duo; the desktop steps down toward Control's. */
6
+
7
+ :root {
8
+ --tint: var(--accent);
9
+ --tertiary: #8b8378;
10
+ --gold: #b98a3e;
11
+ --green: #2f7d4f;
12
+ --amber: #a8641c;
13
+ --amber-bg: rgba(214, 140, 52, 0.14);
14
+ --green-bg: rgba(47, 125, 79, 0.12);
15
+ --gold-bg: rgba(185, 138, 62, 0.14);
16
+ --hairline: var(--row-border);
17
+ }
18
+ :root[data-theme='dark'] {
19
+ --tertiary: #8f8577;
20
+ --gold: #d9b06a;
21
+ --green: #6ccf8f;
22
+ --amber: #e8a46c;
23
+ --amber-bg: rgba(232, 164, 108, 0.16);
24
+ --green-bg: rgba(108, 207, 143, 0.14);
25
+ --gold-bg: rgba(217, 176, 106, 0.16);
26
+ }
27
+ @media (prefers-color-scheme: dark) {
28
+ :root:not([data-theme='light']) {
29
+ --tertiary: #8f8577; --gold: #d9b06a; --green: #6ccf8f; --amber: #e8a46c;
30
+ --amber-bg: rgba(232, 164, 108, 0.16); --green-bg: rgba(108, 207, 143, 0.14); --gold-bg: rgba(217, 176, 106, 0.16);
31
+ }
32
+ }
33
+
34
+ /* Section tints (ActivitySection.tint). */
35
+ [data-tint='messages'] { --tint: rgb(61, 122, 199); }
36
+ [data-tint='speakers'] { --tint: rgb(199, 87, 99); }
37
+ [data-tint='meetings'] { --tint: rgb(209, 133, 56); }
38
+ [data-tint='memories'] { --tint: rgb(122, 92, 184); }
39
+ [data-tint='threads'] { --tint: rgb(56, 145, 99); }
40
+ [data-tint='sessions'] { --tint: rgb(110, 110, 122); }
41
+ [data-tint='work'] { --tint: var(--gold); }
42
+ [data-tint='dictation'] { --tint: rgb(168, 107, 61); }
43
+
44
+ /* ── rail ─────────────────────────────────────────────── */
45
+ .rail-head .rail-brand, .rail-story, .server-text { display: none; }
46
+ .rail-text { display: flex; flex-direction: column; align-items: center; min-width: 0; }
47
+ .rail-settings { display: none; }
48
+ .server-line { display: none; }
49
+ .dot.bad { background: var(--hot); }
50
+ #section-menu .rail-story.hot { color: var(--hot); }
51
+
52
+ @media (min-width: 600px) {
53
+ .rail-head { display: flex; flex-direction: column; align-items: center; }
54
+ .rail-head .mark { width: 30px; height: 30px; margin-bottom: 10px; }
55
+ /* Icon mode: a hot story shows as a dot on the icon (Control's 8pt hot dot). */
56
+ .rail-btn.hot::after { content: ''; position: absolute; top: 7px; right: 15px; width: 8px; height: 8px; border-radius: 50%; background: var(--hot); }
57
+ .rail-settings { display: flex; flex-direction: column; align-items: center; gap: 3px; width: 62px; min-height: 46px; padding: 6px 0 4px; border-radius: 12px; color: var(--grey); }
58
+ .rail-settings .icon { width: 20px; height: 20px; }
59
+ .rail-settings[aria-current='page'] { background: var(--rail-active); color: var(--ink); }
60
+ .server-line { display: flex; align-items: center; justify-content: center; margin: 8px 0 0; }
61
+ }
62
+ @media (min-width: 600px) and (max-height: 720px) {
63
+ .rail-head .mark { width: 26px; height: 26px; margin-bottom: 6px; }
64
+ .rail-settings { min-height: 40px; }
65
+ }
66
+
67
+ /* Full rail (Control's at window width 1100 and up): name and story beside the glyph. */
68
+ @media (min-width: 1100px) {
69
+ .app-shell { grid-template-columns: 248px minmax(0, 1fr); }
70
+ .rail { align-items: stretch; padding: calc(var(--safe-top) + 18px) 12px 14px; gap: 2px; }
71
+ .rail-head { flex-direction: row; align-items: center; gap: 8px; padding: 0 8px 14px; }
72
+ .rail-head .mark { width: 22px; height: 22px; margin: 0; }
73
+ .rail-head .rail-brand { display: inline; font: 500 20px/1 var(--display); color: var(--ink); }
74
+ .rail-btn { width: 100%; flex-direction: row; align-items: flex-start; justify-content: flex-start; gap: 11px; padding: 8px 10px; min-height: 0; border-radius: 10px; border: 1px solid transparent; text-align: left; }
75
+ .rail-btn .icon { width: 18px; height: 18px; margin-top: 2px; flex: none; color: var(--ink); }
76
+ .rail-text { align-items: flex-start; }
77
+ .rail-label { font: 600 14px/1.25 var(--body); letter-spacing: 0; color: var(--ink); }
78
+ .rail-story { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-size: 12.5px; line-height: 1.3; color: var(--sub); margin-top: 1px; }
79
+ .rail-story.hot { color: var(--hot); }
80
+ .rail-btn[aria-current='page'] { background: var(--rail-active); border-color: var(--panel-border); box-shadow: none; }
81
+ .rail-btn.hot::after { display: none; }
82
+ .rail-btn .badge-dot { display: none; }
83
+ .rail-sep { width: auto; height: 12px; background: none; margin: 0; }
84
+ .rail-settings { width: auto; flex-direction: row; justify-content: flex-start; gap: 8px; padding: 6px 10px; min-height: 0; font: 400 13px/1.2 var(--body); color: var(--accent); }
85
+ .rail-settings .icon { display: none; }
86
+ .rail-settings .rail-label { font: 500 13px/1.2 var(--body); color: var(--accent); }
87
+ .server-line { justify-content: flex-start; gap: 7px; padding: 4px 10px 0; margin: 0; }
88
+ .server-text { display: inline; font: 500 11.5px/1 var(--mono); color: var(--ok); }
89
+ .server-line.down .server-text { color: var(--hot); }
90
+ }
91
+
92
+ /* ── navigation bar (off Today, 600px and up) ─────────── */
93
+ .navbar { display: none; }
94
+ @media (min-width: 600px) {
95
+ .navbar:not(.hidden) {
96
+ display: flex; align-items: center; gap: 6px; position: sticky; top: 0; z-index: 20;
97
+ height: calc(44px + var(--safe-top)); padding: var(--safe-top) calc(var(--gutter) + var(--safe-right)) 0 var(--gutter);
98
+ background: var(--page); border-bottom: 1px solid var(--rail-border);
99
+ }
100
+ .nav-btn { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 10px; border-radius: 8px; border: 1px solid var(--panel-border); background: var(--btn-bg); color: var(--ink); font: 500 13px/1 var(--body); flex: none; }
101
+ .nav-btn .icon { width: 15px; height: 15px; stroke-width: 1.8; }
102
+ .nav-btn:disabled { opacity: 0.4; }
103
+ .crumbs { display: flex; align-items: center; min-width: 0; list-style: none; margin: 0 0 0 6px; padding: 0; font: 400 12.5px/1.2 var(--body); color: var(--sub); }
104
+ .crumb { white-space: nowrap; }
105
+ .crumb + .crumb::before { content: '›'; margin: 0 6px; color: var(--tertiary); }
106
+ .crumb.root { font-weight: 600; color: var(--ink); }
107
+ .crumb.current { font-weight: 600; color: var(--ink); }
108
+ .crumb.dim { color: var(--sub); }
109
+ .crumb.detail { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
110
+ .navbar .grow { flex: 1; }
111
+ body.has-navbar .workspace { padding-top: 18px; }
112
+ }
113
+ @media (min-width: 600px) and (max-width: 759px) {
114
+ .nav-btn span { display: none; }
115
+ .nav-btn { padding: 0 8px; }
116
+ }
117
+
118
+ /* ── pane kit: hero ───────────────────────────────────── */
119
+ .cx { --pad: 16px; }
120
+ .cx-hero {
121
+ display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 4px 14px; align-items: center;
122
+ margin: 0 calc(-1 * var(--gutter)) 0; padding: 14px var(--gutter) 14px;
123
+ background: color-mix(in srgb, var(--tint) 4.5%, transparent); border-bottom: 1px solid var(--hairline);
124
+ }
125
+ .cx-glyph { width: 42px; height: 42px; border-radius: 11px; display: grid; place-items: center; background: color-mix(in srgb, var(--tint) 14%, transparent); color: var(--tint); flex: none; }
126
+ .cx-glyph .icon { width: 22px; height: 22px; }
127
+ .cx-hero-text { min-width: 0; }
128
+ .cx-title { font: 500 24px/1.15 var(--display); color: var(--ink); margin: 0; letter-spacing: -0.01em; }
129
+ .cx-detail-line { margin: 3px 0 0; font-size: 13px; color: var(--sub); overflow-wrap: anywhere; }
130
+ .cx-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
131
+ .cx-stats { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 6px; }
132
+ .cx-stat { display: inline-flex; align-items: baseline; gap: 6px; }
133
+ .cx-stat b { font: 600 15px/1 var(--body); color: var(--ink); }
134
+ .cx-stat span { font: 600 9.5px/1 var(--mono); letter-spacing: 0.06em; color: var(--tertiary); }
135
+ @media (max-width: 599px) {
136
+ .cx-hero { grid-template-columns: auto minmax(0, 1fr); margin-top: -4px; }
137
+ .cx-actions { grid-column: 1 / -1; justify-content: flex-start; }
138
+ .cx-glyph { width: 36px; height: 36px; border-radius: 10px; }
139
+ .cx-title { font-size: 22px; }
140
+ }
141
+
142
+ /* Quiet bordered button (Control's .bordered small). */
143
+ .cx-btn { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px; border-radius: 8px; border: 1px solid var(--btn-border); background: var(--btn-bg); color: var(--ink); font: 500 13.5px/1 var(--body); white-space: nowrap; }
144
+ .cx-btn .icon { width: 15px; height: 15px; stroke-width: 1.8; }
145
+ .cx-btn:disabled { opacity: 0.45; }
146
+ .cx-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
147
+ .cx-btn.quiet { border-color: var(--panel-border); }
148
+ @media (max-width: 599px) { .cx-btn { height: 36px; font-size: 14.5px; } }
149
+
150
+ /* ── text switch (COSBrand: words with a 2pt gold rule, never pills) ── */
151
+ .cx-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 12px 0 10px; }
152
+ .cx-switch { display: inline-flex; align-items: baseline; gap: 14px; }
153
+ .cx-switch-label { font-size: 12.5px; color: var(--sub); }
154
+ .cx-switch button { position: relative; padding: 6px 0 7px; font: 500 14px/1 var(--body); color: var(--sub); }
155
+ .cx-switch button[aria-pressed='true'] { color: var(--ink); font-weight: 600; }
156
+ .cx-switch button[aria-pressed='true']::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; border-radius: 1px; background: var(--gold); }
157
+ .cx-search { position: relative; flex: 1 1 220px; max-width: 320px; display: flex; align-items: center; gap: 8px; }
158
+ .cx-search .field { min-height: 36px; padding: 0 12px; font-size: 14px; border-radius: 9px; }
159
+ .cx-search .cx-clear { font-size: 13px; color: var(--accent); font-weight: 600; flex: none; }
160
+ .cx-check { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink); margin-left: auto; }
161
+ .cx-check input { width: 16px; height: 16px; accent-color: var(--gold); }
162
+ @media (max-width: 599px) {
163
+ .cx-search { max-width: none; flex-basis: 100%; order: 3; }
164
+ .cx-search .field { min-height: 40px; font-size: 16px; }
165
+ .cx-switch button { font-size: 15px; padding: 8px 0 9px; }
166
+ }
167
+
168
+ /* Crossing line: "Recent 3 · Archive 2 days ->". */
169
+ .cx-cross { font-size: 12.5px; color: var(--sub); margin: -2px 0 10px; display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; }
170
+ .cx-cross .door { color: var(--gold); font-weight: 600; }
171
+ .cx-cross .door { display: inline-flex; align-items: center; gap: 2px; }
172
+ .cx-cross .door .icon { width: 12px; height: 12px; }
173
+
174
+ /* ── rows ─────────────────────────────────────────────── */
175
+ .cx-list { background: var(--panel); border: 1px solid var(--panel-border); border-radius: 14px; overflow: hidden; }
176
+ .cx-row { display: grid; grid-template-columns: 32px minmax(0, 1fr) 12px; gap: 12px; align-items: start; width: 100%; padding: 13px 14px; text-align: left; color: inherit; position: relative; }
177
+ .cx-row + .cx-row::before { content: ''; position: absolute; top: 0; left: 58px; right: 0; height: 1px; background: var(--hairline); }
178
+ .cx-row:hover { background: var(--qopen); }
179
+ .cx-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
180
+ .cx-row-glyph { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; color: var(--tint); background: color-mix(in srgb, var(--tint) 11%, transparent); position: relative; }
181
+ .cx-row-glyph .icon { width: 17px; height: 17px; }
182
+ .cx-row-glyph .cx-badge-icon { position: absolute; right: -4px; bottom: -4px; width: 17px; height: 17px; border-radius: 5px; background: var(--panel); border: 1px solid var(--panel-border); display: grid; place-items: center; color: var(--sub); }
183
+ .cx-row-glyph .cx-badge-icon .icon { width: 11px; height: 11px; }
184
+ .cx-row-body { min-width: 0; }
185
+ .cx-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 9px; }
186
+ .cx-num { font: 600 12.5px/1.3 var(--body); color: var(--ink); }
187
+ .cx-time { font: 400 11px/1.3 var(--mono); color: var(--tertiary); }
188
+ .cx-tag { font: 600 9.5px/1.3 var(--mono); letter-spacing: 0.06em; color: var(--sub); }
189
+ .cx-model { font: 400 11px/1.3 var(--mono); color: var(--sub); }
190
+ .cx-att { display: inline-flex; align-items: center; gap: 3px; font: 400 11px/1.3 var(--mono); color: var(--sub); }
191
+ .cx-att .icon { width: 12px; height: 12px; }
192
+ .cx-row-title { margin-top: 3px; font-size: 15px; line-height: 1.35; color: var(--ink); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
193
+ .cx-row-title.strong { font-weight: 600; }
194
+ .cx-row-sub { margin-top: 3px; font-size: 13px; line-height: 1.4; color: var(--sub); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
195
+ .cx-row-sub.three { -webkit-line-clamp: 3; }
196
+ .cx-id { margin-top: 3px; font: 400 10.5px/1.3 var(--mono); color: var(--tertiary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
197
+ .cx-row .chev { margin-top: 12px; }
198
+ /* 6.99.15 Messages rows: no glyph column; the ask and its time on one line, the answer, then one quiet line of facts. */
199
+ .cx-msgrow { grid-template-columns: minmax(0, 1fr) 12px; padding: 12px 16px; }
200
+ .cx-row + .cx-msgrow::before { left: 16px; }
201
+ .cx-msg-top { display: flex; align-items: baseline; gap: 12px; }
202
+ .cx-msg-top .cx-row-title { flex: 1 1 auto; min-width: 0; margin-top: 0; font-weight: 600; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
203
+ .cx-msg-top .cx-time { flex: none; font-family: var(--body); font-size: 12px; }
204
+ .cx-msgrow .cx-row-sub { margin-top: 4px; }
205
+ .cx-facts { margin-top: 6px; gap: 2px 12px; }
206
+ .cx-facts .cx-num { font: 500 11.5px/1.3 var(--body); color: var(--tertiary); }
207
+ .cx-facts .cx-tag { font: 500 11.5px/1.3 var(--body); letter-spacing: 0; color: var(--gold); }
208
+ .cx-facts .cx-model, .cx-facts .cx-att { font: 400 11.5px/1.3 var(--body); color: var(--tertiary); }
209
+ .cx-msgrow .chev { margin-top: 8px; }
210
+ .cc-pin .cx-time { font-family: var(--body); font-size: 12px; }
211
+ @media (min-width: 1100px) {
212
+ .cx-row-title { font-size: 13.5px; }
213
+ .cx-row-sub { font-size: 12px; }
214
+ }
215
+
216
+ /* Pills and capsules. */
217
+ .cx-pill { display: inline-flex; align-items: center; height: 18px; padding: 0 7px; border-radius: 5px; font: 600 9.5px/1 var(--mono); letter-spacing: 0.05em; white-space: nowrap; background: var(--qopen); color: var(--sub); }
218
+ .cx-pill.warn { background: var(--amber-bg); color: var(--amber); }
219
+ .cx-pill.ok { background: var(--green-bg); color: var(--green); }
220
+ .cx-pill.gold { background: var(--gold-bg); color: var(--gold); }
221
+ .cx-cap { display: inline-flex; align-items: center; height: 19px; padding: 0 8px; border-radius: 10px; font: 600 11px/1 var(--body); white-space: nowrap; background: var(--qopen); color: var(--sub); }
222
+ .cx-cap.ok { background: var(--green-bg); color: var(--green); }
223
+ .cx-cap.gold { background: var(--gold-bg); color: var(--gold); }
224
+
225
+ .cx-note { font-size: 12px; color: var(--tertiary); margin: 10px 2px 0; }
226
+ .cx-empty { padding: 22px 16px; color: var(--sub); font-size: 14.5px; line-height: 1.45; }
227
+ .cx-notice { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: 12px; background: var(--qopen); border: 1px solid var(--panel-border); color: var(--sub); font-size: 14px; margin: 8px 0; }
228
+ .cx-notice.warn { color: var(--amber); }
229
+ .cx-mark { background: color-mix(in srgb, var(--gold) 28%, transparent); color: inherit; border-radius: 3px; padding: 0 1px; }
230
+
231
+ /* ── detail ───────────────────────────────────────────── */
232
+ .cx-detail-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 10px 16px; padding: 16px 0 12px; border-bottom: 1px solid var(--hairline); margin-bottom: 14px; }
233
+ .cx-detail-title { font: 600 19px/1.25 var(--body); color: var(--ink); margin: 0; }
234
+ .cx-detail-meta { margin-top: 4px; font-size: 12.5px; color: var(--sub); }
235
+ .cx-card { background: var(--panel); border: 1px solid var(--panel-border); border-radius: 14px; padding: 12px 14px 14px; margin-bottom: 12px; }
236
+ .cx-card.you { background: var(--qopen); }
237
+ .cx-card-label { font: 600 9.5px/1 var(--mono); letter-spacing: 0.08em; color: var(--tertiary); margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
238
+ .cx-card-body { font-size: 15px; line-height: 1.5; color: var(--ink); overflow-wrap: anywhere; }
239
+ /* md() keeps line breaks as text (shared with the session reader), so the body keeps them too. */
240
+ .cx-card-body.md { white-space: pre-wrap; }
241
+ .cx-card-body.md strong { font-weight: 600; }
242
+ .cx-card-body.md .quote { display: inline-block; padding-left: 10px; border-left: 3px solid var(--panel-border); color: var(--sub); }
243
+ .cx-card-body.md p { margin: 0 0 0.7em; }
244
+ .cx-card-body.md p:last-child { margin-bottom: 0; }
245
+ .cx-card-body.md ul, .cx-card-body.md ol { margin: 0 0 0.7em; padding-left: 1.3em; }
246
+ .cx-card-body.md h1, .cx-card-body.md h2, .cx-card-body.md h3 { font: 600 15px/1.3 var(--body); margin: 0.9em 0 0.4em; }
247
+ .cx-card-body.md code { font: 400 13px/1.4 var(--mono); background: var(--qopen); padding: 0 3px; border-radius: 4px; }
248
+ .cx-card-body.md pre { white-space: pre-wrap; font: 400 12.5px/1.45 var(--mono); background: var(--qopen); padding: 10px; border-radius: 8px; }
249
+ .cx-strip-label { font: 600 9.5px/1 var(--mono); letter-spacing: 0.08em; color: var(--tertiary); margin: 4px 0 8px; }
250
+ .cx-strip { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
251
+ .cx-thumb { position: relative; width: 148px; height: 104px; border-radius: 10px; overflow: hidden; background: var(--qopen); border: 1px solid var(--panel-border); display: grid; place-items: center; color: var(--sub); }
252
+ .cx-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
253
+ .cx-thumb .cx-thumb-badge { position: absolute; right: 6px; bottom: 6px; font: 600 10px/1 var(--mono); padding: 3px 5px; border-radius: 4px; background: rgba(0, 0, 0, 0.55); color: #fff; }
254
+ .cx-thumb .cx-play { position: absolute; inset: 0; display: grid; place-items: center; color: #fff; }
255
+ .cx-thumb .cx-play .icon { width: 30px; height: 30px; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5)); }
256
+ .cx-figure { margin: 0 0 14px; }
257
+ .cx-figure img { max-width: 100%; height: auto; border-radius: 10px; border: 1px solid var(--panel-border); display: block; }
258
+ .cx-figure figcaption { font-size: 12.5px; color: var(--sub); margin-top: 6px; }
259
+ .cx-find { position: sticky; top: 52px; z-index: 5; display: flex; align-items: center; gap: 8px; padding: 8px 0; background: var(--page); }
260
+ .cx-find .field { min-height: 34px; font-size: 14px; padding: 0 10px; border-radius: 9px; max-width: 260px; }
261
+ .cx-find .cx-find-count { font: 400 11.5px/1 var(--mono); color: var(--sub); }
262
+ .cx-find button { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--panel-border); display: grid; place-items: center; color: var(--ink); }
263
+ .cx-find button .icon { width: 14px; height: 14px; }
264
+ .cx-turn.match .cx-card { background: color-mix(in srgb, var(--green) 7%, var(--panel)); }
265
+ @media (max-width: 599px) { .cx-find { top: calc(var(--topbar-h) + var(--safe-top)); } .cx-thumb { width: 132px; height: 92px; } }
266
+
267
+ /* ── Dictation and Dictionary ─────────────────────────── */
268
+ .cx-pair { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
269
+ .cx-pair .heard { color: var(--sub); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--sub) 60%, transparent); }
270
+ .cx-pair .written { font-weight: 600; color: var(--ink); }
271
+ .cx-pair .icon { width: 12px; height: 12px; color: var(--tertiary); }
272
+ .cx-row-static { cursor: default; grid-template-columns: 32px minmax(0, 1fr) auto; align-items: center; }
273
+ .cx-row-static:hover { background: none; }
274
+ .cx-col-title { font: 600 14px/1.2 var(--body); color: var(--ink); margin: 18px 2px 10px; }
275
+ .cx-dict-cols { display: grid; gap: 4px 22px; }
276
+ @media (min-width: 900px) { .cx-dict-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }
277
+ .cx-inline-form { display: flex; gap: 8px; margin-bottom: 10px; }
278
+ .cx-inline-form .field { flex: 1; min-width: 0; min-height: 36px; font-size: 14px; padding: 0 10px; border-radius: 9px; }
279
+ .cx-chips { display: flex; flex-wrap: wrap; gap: 6px; }
280
+ .cx-chip { display: inline-flex; align-items: center; gap: 4px; height: 28px; padding: 0 4px 0 10px; border-radius: 14px; background: var(--panel); border: 1px solid var(--panel-border); font-size: 13px; color: var(--ink); }
281
+ .cx-chip button, .cx-x { width: 24px; height: 24px; border-radius: 12px; color: var(--sub); font-size: 15px; line-height: 1; display: grid; place-items: center; }
282
+ .cx-chip.more { padding-right: 10px; color: var(--sub); }
283
+ @media (max-width: 599px) { .cx-inline-form .field { min-height: 40px; font-size: 16px; } .cx-chip { height: 32px; } }
284
+
285
+ /* ── Settings (Control's hub categories) ──────────────── */
286
+ .cx-set-cat { margin-top: 8px; }
287
+ .cx-set-group { margin: 10px 0 14px; }
288
+ .cx-set-title { font: 600 13px/1.2 var(--body); color: var(--sub); margin: 12px 2px 4px; }
289
+ .cx-set-blurb { font-size: 13px; color: var(--sub); margin: -4px 2px 8px; }
290
+ .cx-set-row { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 50px; padding: 10px 14px; text-align: left; color: inherit; position: relative; }
291
+ .cx-set-row + .cx-set-row::before, .cx-set-row + .device-code + .cx-set-row::before { content: ''; position: absolute; top: 0; left: 14px; right: 0; height: 1px; background: var(--hairline); }
292
+ .cx-set-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
293
+ .cx-set-name { font-size: 15px; font-weight: 500; color: var(--ink); }
294
+ .cx-set-desc { font-size: 13px; color: var(--sub); overflow-wrap: anywhere; }
295
+ .cx-set-value { font: 400 13px/1.3 var(--mono); color: var(--sub); text-align: right; max-width: 55%; overflow-wrap: anywhere; }
296
+ .cx-set-choice .value { margin-left: auto; color: var(--accent); }
297
+ .cx-set-mac { font-size: 12.5px; color: var(--tertiary); margin: 2px 2px 12px; }
298
+ button.cx-set-row:hover { background: var(--qopen); }
299
+ /* 6.99.11 COS reaching out: compact fields at the end of a Settings row. 16px text keeps iOS Safari from zooming. */
300
+ .cx-set-row .cx-set-num, .cx-set-row .cx-set-time, .cx-set-row .cx-set-day { flex: none; width: auto; min-height: 40px; padding: 8px 10px; }
301
+ .cx-set-row .cx-set-num { width: 84px; text-align: right; font-variant-numeric: tabular-nums; }
302
+ .cx-set-row .cx-set-time { width: 148px; }
303
+ .cx-set-row .cx-set-day { width: 150px; padding-right: 36px; background-position: calc(100% - 20px) 17px, calc(100% - 15px) 17px; }
304
+ .cx-set-row .cx-set-steer { flex: none; width: 46%; min-width: 180px; min-height: 64px; }
305
+ @media (max-width: 599px) {
306
+ .cx-set-row:has(.cx-set-steer) { flex-wrap: wrap; }
307
+ .cx-set-row .cx-set-steer { width: 100%; min-width: 0; }
308
+ }
309
+ @media (min-width: 1100px) { .cx-set-name { font-size: 14px; } }
310
+ @media (min-width: 1100px) { .rail-settings[aria-current='page'] { background: var(--rail-active); border: 1px solid var(--panel-border); } }
311
+
312
+ /* Sign in another device: the QR the phone camera scans. */
313
+ .device-code .qr { display: block; width: 200px; height: 200px; margin: 4px 0 8px; border-radius: 8px; border: 1px solid var(--panel-border); background: #fff; }
314
+ .device-code .qr-hint { font-weight: 600; color: var(--ink); }
315
+
316
+ /* iOS Safari pull-to-refresh reloaded the whole app on a downward drag at the top of a pane. */
317
+ html, body { overscroll-behavior-y: none; }
318
+
319
+ /* Today: From today's meetings. */
320
+ .intake-body { padding: 4px 16px 14px; }
321
+ .intake-row { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-top: 1px solid var(--hairline); font-size: 14.5px; }
322
+ .intake-row:first-child { border-top: 0; }
323
+ .intake-meeting { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
324
+ .intake-n { font: 600 12px/1.6 var(--mono); color: var(--sub); }
325
+ .intake-foot { margin: 6px 0 0; font-size: 12.5px; color: var(--tertiary); }