n-seo 0.1.0

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 (88) hide show
  1. package/.env.example +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +184 -0
  4. package/bin/n-seo.mjs +310 -0
  5. package/docs/ADDING-A-SITE.md +82 -0
  6. package/docs/ARCHITECTURE.md +213 -0
  7. package/docs/DEPLOY.md +300 -0
  8. package/docs/FAQ.md +93 -0
  9. package/docs/INSTANCE.md +365 -0
  10. package/docs/MCP.md +104 -0
  11. package/docs/OPERATING-RULES.md +106 -0
  12. package/docs/PLAYBOOK.md +122 -0
  13. package/docs/PRD.md +249 -0
  14. package/docs/RELEASING.md +189 -0
  15. package/docs/SCHEDULING.md +104 -0
  16. package/docs/SETUP-GOOGLE.md +215 -0
  17. package/docs/examples/campaign.json +59 -0
  18. package/docs/examples/draft.md +43 -0
  19. package/docs/screenshots/overview.png +0 -0
  20. package/ingest/__pycache__/analyze_ga4.cpython-313.pyc +0 -0
  21. package/ingest/__pycache__/analyze_gsc.cpython-313.pyc +0 -0
  22. package/ingest/__pycache__/analyze_metadata.cpython-313.pyc +0 -0
  23. package/ingest/__pycache__/analyze_trends.cpython-313.pyc +0 -0
  24. package/ingest/__pycache__/google_auth.cpython-313.pyc +0 -0
  25. package/ingest/__pycache__/http_util.cpython-313.pyc +0 -0
  26. package/ingest/__pycache__/pull_ga4.cpython-313.pyc +0 -0
  27. package/ingest/__pycache__/pull_gsc.cpython-313.pyc +0 -0
  28. package/ingest/__pycache__/pull_index_status.cpython-313.pyc +0 -0
  29. package/ingest/__pycache__/pull_timeseries.cpython-313.pyc +0 -0
  30. package/ingest/__pycache__/seo_config.cpython-313.pyc +0 -0
  31. package/ingest/analyze_ga4.py +79 -0
  32. package/ingest/analyze_gsc.py +136 -0
  33. package/ingest/analyze_metadata.py +158 -0
  34. package/ingest/analyze_trends.py +145 -0
  35. package/ingest/google_auth.py +238 -0
  36. package/ingest/http_util.py +87 -0
  37. package/ingest/pull_ga4.py +107 -0
  38. package/ingest/pull_gsc.py +111 -0
  39. package/ingest/pull_index_status.py +179 -0
  40. package/ingest/pull_timeseries.py +130 -0
  41. package/ingest/seo_config.py +213 -0
  42. package/n-seo.config.example.json +110 -0
  43. package/ops/__pycache__/daily.cpython-313.pyc +0 -0
  44. package/ops/__pycache__/daily_diff.cpython-313.pyc +0 -0
  45. package/ops/__pycache__/demo_data.cpython-313.pyc +0 -0
  46. package/ops/__pycache__/doctor.cpython-313.pyc +0 -0
  47. package/ops/__pycache__/export_static.cpython-313.pyc +0 -0
  48. package/ops/__pycache__/hn_digest.cpython-313.pyc +0 -0
  49. package/ops/__pycache__/indexnow.cpython-313.pyc +0 -0
  50. package/ops/__pycache__/llm.cpython-313.pyc +0 -0
  51. package/ops/__pycache__/opportunity_scan.cpython-313.pyc +0 -0
  52. package/ops/__pycache__/publish.cpython-313.pyc +0 -0
  53. package/ops/__pycache__/reddit_digest.cpython-313.pyc +0 -0
  54. package/ops/daily.py +250 -0
  55. package/ops/daily_diff.py +151 -0
  56. package/ops/demo_data.py +529 -0
  57. package/ops/doctor.py +266 -0
  58. package/ops/export_static.py +125 -0
  59. package/ops/hn_digest.py +169 -0
  60. package/ops/indexnow.py +107 -0
  61. package/ops/install-launchd.sh +76 -0
  62. package/ops/llm.py +139 -0
  63. package/ops/mcp-smoke-stdio.mjs +61 -0
  64. package/ops/opportunity_scan.py +185 -0
  65. package/ops/publish.py +158 -0
  66. package/ops/reddit_digest.py +168 -0
  67. package/ops/templates/n-seo-daily.service +11 -0
  68. package/ops/templates/n-seo-daily.timer +11 -0
  69. package/ops/templates/n-seo-dashboard.service +15 -0
  70. package/ops/templates/n-seo.cron +3 -0
  71. package/ops/templates/n-seo.daily.plist +29 -0
  72. package/ops/templates/n-seo.dashboard.plist +22 -0
  73. package/package.json +77 -0
  74. package/probes/__pycache__/site_probe.cpython-313.pyc +0 -0
  75. package/probes/site_probe.py +201 -0
  76. package/public/favicon.svg +6 -0
  77. package/public/styles.css +632 -0
  78. package/src/actions.ts +255 -0
  79. package/src/backlog.ts +197 -0
  80. package/src/config.ts +220 -0
  81. package/src/data.ts +895 -0
  82. package/src/insights.ts +22 -0
  83. package/src/mcp-stdio.ts +21 -0
  84. package/src/mcp.ts +490 -0
  85. package/src/server.tsx +260 -0
  86. package/src/settings.tsx +329 -0
  87. package/src/views.tsx +1487 -0
  88. package/tsconfig.json +15 -0
@@ -0,0 +1,632 @@
1
+ /* n-seo dashboard — neutral palette, light + dark, flat cards with hairline borders. */
2
+ :root {
3
+ --ground: #f6f7f9;
4
+ --panel: #fbfbfd;
5
+ --card: #ffffff;
6
+ --ink: #16181d;
7
+ --muted: #5f6672;
8
+ --line: #e3e6eb;
9
+ --accent: #3757d6;
10
+ --accent-bright: #3757d6;
11
+ --accent-soft: rgba(55, 87, 214, 0.12);
12
+ --good: #1a7f4b;
13
+ --good-soft: rgba(26, 127, 75, 0.12);
14
+ --bad: #c2334d;
15
+ --bad-soft: rgba(194, 51, 77, 0.10);
16
+ --warn: #b4620a;
17
+ --warn-soft: rgba(180, 98, 10, 0.12);
18
+ --purple: #6c41f0;
19
+ --code-bg: #eef0f4;
20
+ --topbar: #151821;
21
+ --topbar-ink: rgba(255, 255, 255, 0.78);
22
+ --heat: 55, 87, 214;
23
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
24
+
25
+ /* Traffic-source groups. Okabe-Ito derived: vermillion / indigo / green /
26
+ pink stay separable under deuteranopia and protanopia, and no pair of
27
+ adjacent bands is red-against-green. AI is the vivid one on purpose —
28
+ it is the series people open this chart to read. */
29
+ --grp-ai: #d55e00;
30
+ --grp-search: #3757d6;
31
+ --grp-direct: #7a8194;
32
+ --grp-referral: #009e73;
33
+ --grp-social: #cc79a7;
34
+ --grp-other: #b6bcc8;
35
+ }
36
+ @media (prefers-color-scheme: dark) {
37
+ :root {
38
+ --ground: #0f1115;
39
+ --panel: #151821;
40
+ --card: #1a1e29;
41
+ --ink: #e8eaf0;
42
+ --muted: #9aa3b5;
43
+ --line: rgba(255, 255, 255, 0.12);
44
+ --accent: #8da2ff;
45
+ --accent-bright: #8da2ff;
46
+ --accent-soft: rgba(141, 162, 255, 0.16);
47
+ --good: #4cc48a;
48
+ --good-soft: rgba(76, 196, 138, 0.16);
49
+ --bad: #ff7b93;
50
+ --bad-soft: rgba(255, 123, 147, 0.18);
51
+ --warn: #f0a04b;
52
+ --warn-soft: rgba(240, 160, 75, 0.18);
53
+ --purple: #b9a3ff;
54
+ --code-bg: rgba(255, 255, 255, 0.08);
55
+ --heat: 141, 162, 255;
56
+ --grp-ai: #ff9d5c;
57
+ --grp-search: #8da2ff;
58
+ --grp-direct: #8a93a6;
59
+ --grp-referral: #4cc48a;
60
+ --grp-social: #e79fc8;
61
+ --grp-other: #5b6272;
62
+ }
63
+ }
64
+
65
+ * { box-sizing: border-box; }
66
+ body {
67
+ margin: 0;
68
+ background: var(--ground);
69
+ color: var(--ink);
70
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
71
+ font-size: 14.5px;
72
+ line-height: 1.55;
73
+ }
74
+ main { max-width: 1480px; margin: 0 auto; padding: 28px 24px 80px; }
75
+ h1, h2, h3 { line-height: 1.2; letter-spacing: -0.01em; }
76
+ h1 { font-size: 1.8rem; font-weight: 800; margin: 0.5em 0; }
77
+ h2 { font-size: 1.05rem; font-weight: 700; margin: 2em 0 0.6em; text-transform: uppercase; letter-spacing: 0.06em; }
78
+ h2 small { font-weight: 500; color: var(--muted); font-size: 0.75rem; text-transform: none; letter-spacing: 0; }
79
+ code, .mono { font-family: var(--mono); font-size: 0.85em; }
80
+ code { background: var(--code-bg); padding: 1px 5px; border-radius: 4px; }
81
+ a { color: var(--accent); }
82
+ .muted { color: var(--muted); }
83
+
84
+ /* ---- top bar ---- */
85
+ .topbar {
86
+ display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
87
+ padding: 11px 24px; background: var(--topbar);
88
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
89
+ }
90
+ .brand { display: flex; align-items: center; gap: 9px; font-weight: 800; color: #fff; text-decoration: none; letter-spacing: -0.01em; }
91
+ .brand-mark { flex-shrink: 0; display: block; } /* the mark carries its own brand colors */
92
+ .brand-name { max-width: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
93
+ .brand-sub { color: var(--topbar-ink); font-weight: 500; font-size: 0.8rem; }
94
+ .topbar nav { display: flex; gap: 4px; flex-wrap: wrap; }
95
+ .topbar nav a, .nav-menu summary {
96
+ color: var(--topbar-ink); text-decoration: none; font-size: 0.82rem; font-weight: 600;
97
+ padding: 4px 10px; border-radius: 99px;
98
+ }
99
+ .topbar nav a.on, .topbar nav a:hover, .nav-menu summary.on, .nav-menu summary:hover { color: #fff; background: rgba(141, 162, 255, 0.28); }
100
+ .nav-menu { position: relative; }
101
+ .nav-menu summary { list-style: none; cursor: pointer; }
102
+ .nav-menu summary::-webkit-details-marker { display: none; }
103
+ .nav-panel {
104
+ position: absolute; top: calc(100% + 6px); left: 0; z-index: 50;
105
+ background: var(--topbar); border: 1px solid rgba(255, 255, 255, 0.14);
106
+ border-radius: 10px; padding: 6px; min-width: 220px;
107
+ display: flex; flex-direction: column; gap: 2px;
108
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
109
+ }
110
+ .nav-panel a { display: block; font-family: var(--mono); font-size: 0.78rem; }
111
+ .nav-empty { color: var(--topbar-ink); font-size: 0.78rem; padding: 4px 10px; }
112
+ .notice-bar {
113
+ background: var(--warn-soft); color: var(--warn); font-size: 0.82rem; font-weight: 600;
114
+ padding: 7px 24px; border-bottom: 1px solid var(--line);
115
+ }
116
+ .notice-bar a { color: inherit; }
117
+ .notice-bar code { background: rgba(0, 0, 0, 0.06); }
118
+
119
+ /* ---- generic pieces ---- */
120
+ .domain { font-family: var(--mono); font-size: 0.9rem; font-weight: 500; }
121
+ .dot { width: 9px; height: 9px; border-radius: 50%; }
122
+ .dot.ok { background: var(--good); }
123
+ .dot.warn { background: var(--warn); }
124
+ .dot.none { background: var(--line); }
125
+ .stat-row { display: flex; gap: 24px; flex-wrap: wrap; align-items: flex-end; }
126
+ .stat b { font-size: 1.45rem; font-weight: 800; font-variant-numeric: tabular-nums; display: block; letter-spacing: -0.02em; }
127
+ .stat small { color: var(--muted); font-size: 0.7rem; }
128
+ .chip-row { display: flex; gap: 6px; flex-wrap: wrap; }
129
+ .chip {
130
+ font-size: 0.72rem; font-weight: 700; padding: 2px 9px; border-radius: 99px;
131
+ background: var(--code-bg); color: var(--muted); font-variant-numeric: tabular-nums;
132
+ max-width: 34ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
133
+ text-decoration: none;
134
+ }
135
+ .chip.mono { font-family: var(--mono); max-width: 60ch; }
136
+ .chip.ai, .chip.src { background: var(--accent-soft); color: var(--accent); }
137
+ .chip.good { background: var(--good-soft); color: var(--good); }
138
+ .chip.bad { background: var(--bad-soft); color: var(--bad); }
139
+ .chip.impact { background: var(--good-soft); color: var(--good); }
140
+ .chip.effort.e-S { background: var(--code-bg); color: var(--muted); }
141
+ .chip.effort.e-M { background: var(--warn-soft); color: var(--warn); }
142
+ .chip.effort.e-L { background: var(--bad-soft); color: var(--bad); }
143
+ .chip.tag { background: var(--code-bg); color: var(--muted); }
144
+ .chip.commented { background: var(--good-soft); color: var(--good); }
145
+
146
+ .delta { font-variant-numeric: tabular-nums; font-weight: 700; }
147
+ .delta.up { color: var(--good); }
148
+ .delta.down { color: var(--bad); }
149
+ .delta.flat { color: var(--muted); }
150
+
151
+ .statline { display: flex; gap: 20px; flex-wrap: wrap; color: var(--muted); font-size: 0.88rem; margin-bottom: 10px; }
152
+ .statline b { color: var(--ink); font-variant-numeric: tabular-nums; }
153
+ .ai-badge { color: var(--accent); }
154
+
155
+ .probe-line { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0 4px; }
156
+ .pill { font-size: 0.7rem; font-weight: 700; padding: 2px 9px; border-radius: 99px; }
157
+ .pill.ok { background: var(--good-soft); color: var(--good); }
158
+ .pill.bad { background: var(--bad-soft); color: var(--bad); }
159
+ .pill.neutral { background: var(--code-bg); color: var(--muted); }
160
+
161
+ /* ---- tables ---- */
162
+ .tbl-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 10px; background: var(--card); }
163
+ table { border-collapse: collapse; width: 100%; font-size: 0.85rem; }
164
+ table.slim { max-width: 480px; background: var(--card); border: 1px solid var(--line); border-radius: 10px; }
165
+ table.bare { border: 0; max-width: none; }
166
+ table.bare td { padding: 4px 0; font-size: 0.8rem; }
167
+ table.bare td.mono { padding-left: 12px; color: var(--muted); }
168
+ th { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted); text-align: left; }
169
+ th, td { padding: 7px 14px; border-bottom: 1px solid var(--line); }
170
+ tr:last-child td { border-bottom: 0; }
171
+ td { font-variant-numeric: tabular-nums; }
172
+ td.trunc { max-width: 380px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
173
+ .bad-text { color: var(--bad); font-weight: 700; }
174
+ .empty, .sub { color: var(--muted); font-size: 0.85rem; }
175
+ .flash { background: var(--good-soft); color: var(--good); font-weight: 600; font-size: 0.85rem; padding: 8px 14px; border-radius: 8px; }
176
+
177
+ footer { max-width: 1480px; margin: 0 auto; padding: 16px 24px 40px; color: var(--muted); font-size: 0.75rem; border-top: 1px solid var(--line); }
178
+
179
+ /* ---- buttons + inline forms ---- */
180
+ .btn {
181
+ font: inherit; font-size: 0.8rem; font-weight: 700; cursor: pointer;
182
+ background: var(--accent); color: #fff; border: 1px solid transparent; border-radius: 8px; padding: 7px 14px;
183
+ }
184
+ .btn:hover { filter: brightness(1.08); }
185
+ .btn-quiet { background: transparent; color: var(--muted); border-color: var(--line); }
186
+ .btn-quiet:hover { color: var(--bad); border-color: var(--bad); filter: none; }
187
+ .inline-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
188
+ .inline-form input[type="text"] {
189
+ font: inherit; font-size: 0.82rem; color: var(--ink); background: var(--panel);
190
+ border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; flex: 1; min-width: 200px;
191
+ }
192
+ .modal-actions { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 10px; }
193
+ .modal-note { margin-top: 16px !important; color: var(--muted); font-size: 0.8rem !important; }
194
+
195
+ /* ---- insights ---- */
196
+ .insights-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 14px; margin: 18px 0 8px; }
197
+ .insight { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 16px 20px; min-width: 0; }
198
+ .insight-why { margin-top: 8px; }
199
+ .insight-why > summary { cursor: pointer; list-style: none; font-size: 0.75rem; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: 0.07em; }
200
+ .insight-why > summary::-webkit-details-marker { display: none; }
201
+ .insight-why > summary::after { content: " ▸"; }
202
+ .insight-why[open] > summary::after { content: " ▾"; }
203
+ .insight-why p { margin: 8px 0 0; }
204
+ .tile-body { padding: 12px 16px 16px; }
205
+ .insight.v-deprioritize { opacity: 0.85; }
206
+ .insight-verdict { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--accent); }
207
+ .insight.v-warning .insight-verdict { color: var(--warn); }
208
+ .insight.v-momentum .insight-verdict { color: var(--good); }
209
+ .insight.v-deprioritize .insight-verdict { color: var(--muted); }
210
+ .insight h3 { margin: 4px 0 8px; font-size: 1.05rem; font-weight: 700; }
211
+ .insight p { margin: 0 0 8px; font-size: 0.88rem; max-width: 90ch; color: var(--muted); }
212
+ .insight-move { color: var(--ink) !important; font-weight: 600; }
213
+
214
+ /* ---- strips / rows ---- */
215
+ .strip-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
216
+ .strip-head h2 { margin: 0.4em 0; }
217
+ .action-strip { border: 1px solid var(--line); border-radius: 10px; background: var(--card); overflow: hidden; }
218
+ .wb .action-strip { background: transparent; }
219
+ .strip-row {
220
+ display: flex; align-items: center; gap: 10px;
221
+ padding: 9px 14px; color: var(--ink); text-decoration: none;
222
+ border-bottom: 1px solid var(--line); font-size: 0.88rem;
223
+ }
224
+ .strip-row:last-child { border-bottom: 0; }
225
+ .strip-row:hover { background: var(--accent-soft); }
226
+ .strip-title { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%; min-width: 0; flex-shrink: 1; }
227
+
228
+ /* ---- action queue: grid of clickable cards + detail modals ---- */
229
+ .actions { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 12px; }
230
+ .action {
231
+ appearance: none; text-align: left; font: inherit; color: var(--ink); cursor: pointer;
232
+ background: var(--card); border: 1px solid var(--line);
233
+ border-radius: 12px; padding: 14px 16px; width: 100%;
234
+ display: flex; flex-direction: column; gap: 7px;
235
+ transition: border-color 0.15s;
236
+ }
237
+ .action:hover { border-color: var(--accent); }
238
+ .action-head { display: flex; align-items: center; gap: 6px; }
239
+ .rank {
240
+ font-family: var(--mono); font-size: 0.72rem; color: #fff;
241
+ background: var(--accent); border-radius: 99px; padding: 1px 8px; font-weight: 700;
242
+ }
243
+ @media (prefers-color-scheme: dark) { .rank { color: #0f1115; } }
244
+ .action-title { font-weight: 700; font-size: 0.95rem; line-height: 1.3; letter-spacing: -0.01em; }
245
+ .action-kind { color: var(--accent); font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; }
246
+ .action-why { color: var(--muted); font-size: 0.8rem; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
247
+ .action-foot { display: flex; align-items: center; gap: 6px; margin-top: auto; }
248
+ .action-host { font-family: var(--mono); font-size: 0.7rem; color: var(--muted); }
249
+ .spacer { flex: 1; }
250
+ .details-link { color: var(--accent); font-size: 0.75rem; font-weight: 700; }
251
+ .action-watching { color: var(--muted); font-size: 0.8rem; font-style: italic; }
252
+ .proposal { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
253
+ .proposal .action { flex: 1; }
254
+ .proposal-accept { display: flex; justify-content: flex-end; }
255
+
256
+ .action-modal {
257
+ max-width: 640px; width: calc(100vw - 40px); max-height: 88vh; overflow-y: auto;
258
+ background: var(--card); color: var(--ink);
259
+ border: 1px solid var(--line); border-radius: 14px; padding: 22px 26px;
260
+ }
261
+ .action-modal::backdrop { background: rgba(15, 17, 21, 0.6); backdrop-filter: blur(2px); }
262
+ .modal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
263
+ .modal-head h3 { margin: 2px 0 0; font-size: 1.15rem; font-weight: 800; }
264
+ .close { appearance: none; border: 0; background: var(--code-bg); color: var(--muted); border-radius: 99px; width: 28px; height: 28px; cursor: pointer; font-size: 0.85rem; }
265
+ .close:hover { background: var(--accent); color: #fff; }
266
+ .modal-chips { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0 4px; }
267
+ .action-modal h4 { margin: 16px 0 4px; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--accent); }
268
+ .action-modal p { margin: 0; font-size: 0.88rem; }
269
+ .spec { margin: 4px 0 0; padding-left: 18px; }
270
+ .spec li { font-size: 0.85rem; margin-bottom: 6px; }
271
+
272
+ /* ---- digest briefings ---- */
273
+ .hn-item { border-bottom: 1px solid var(--line); }
274
+ .hn-item:last-child { border-bottom: 0; }
275
+ .hn-item summary { list-style: none; cursor: pointer; }
276
+ .hn-item summary::-webkit-details-marker { display: none; }
277
+ .hn-item[open] > summary { background: var(--accent-soft); }
278
+ .hn-brief { padding: 4px 16px 12px 34px; }
279
+ .hn-brief p { margin: 0 0 7px; font-size: 0.84rem; color: var(--muted); max-width: 85ch; }
280
+ .brief-label { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; color: var(--accent); margin-right: 6px; }
281
+ .brief-note { font-style: italic; font-size: 0.75rem !important; }
282
+ .why { font-size: 0.76rem; color: var(--muted); }
283
+
284
+ /* ---- actions: search + watching rows ---- */
285
+ .search-box {
286
+ font: inherit; font-size: 0.85rem; color: var(--ink);
287
+ background: var(--card); border: 1px solid var(--line); border-radius: 8px;
288
+ padding: 7px 12px; min-width: 280px;
289
+ }
290
+ .search-box:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
291
+ .watch-list { margin-top: 4px; }
292
+ .watch-row {
293
+ appearance: none; font: inherit; text-align: left; cursor: pointer; width: 100%;
294
+ display: flex; align-items: center; gap: 10px;
295
+ padding: 9px 14px; color: var(--muted); background: transparent; border: 0;
296
+ border-bottom: 1px solid var(--line); font-size: 0.85rem;
297
+ }
298
+ .watch-row:last-child { border-bottom: 0; }
299
+ .watch-row:hover { background: var(--accent-soft); }
300
+ .watch-row .strip-title { color: var(--ink); font-weight: 600; }
301
+ .watch-note { font-style: italic; font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; }
302
+
303
+ /* ---- logs ---- */
304
+ .log-day { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 12px 18px; margin-bottom: 12px; }
305
+ .log-day h2 { margin: 4px 0 8px; text-transform: none; letter-spacing: 0; font-size: 0.95rem; }
306
+ .log-lines { margin: 0; padding-left: 18px; }
307
+ .log-lines li { font-size: 0.85rem; color: var(--muted); margin-bottom: 4px; }
308
+ .log-lines li.log-alert { color: var(--bad); font-weight: 700; }
309
+ .ops-log { margin: 0; padding: 14px 16px; font-family: var(--mono); font-size: 0.75rem; line-height: 1.5; white-space: pre-wrap; color: var(--muted); }
310
+
311
+ /* ---- trends: unified grid (charts + heatmap share geometry) ---- */
312
+ .range-picker { display: flex; gap: 4px; }
313
+ .range-picker a { font-size: 0.8rem; font-weight: 700; text-decoration: none; color: var(--muted); padding: 4px 10px; border-radius: 99px; border: 1px solid var(--line); }
314
+ .range-picker a.on { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
315
+ /* The portfolio summary sits above the per-site tiles and is never collapsed. */
316
+ .trend-portfolio { margin-bottom: 18px; }
317
+ .trend-portfolio > .tg-label .chart-title { font-size: 0.8rem; color: var(--ink); }
318
+
319
+ .trend-tile { background: var(--card); border: 1px solid var(--line); border-radius: 12px; margin-bottom: 12px; overflow: hidden; }
320
+ .trend-tile > summary { display: flex; align-items: center; gap: 14px; padding: 13px 18px; cursor: pointer; list-style: none; }
321
+ .trend-tile > summary::-webkit-details-marker { display: none; }
322
+ .trend-tile > summary:hover { background: var(--accent-soft); }
323
+ .trend-tile[open] > summary { border-bottom: 1px solid var(--line); }
324
+ .trend-tile[open] > summary .details-link::after { content: " ▾"; }
325
+ .trend-tile:not([open]) > summary .details-link::after { content: " ▸"; }
326
+ .trend-spark { width: 120px; height: 28px; flex-shrink: 0; }
327
+ .trend-spark polyline { fill: none; stroke: var(--accent); stroke-width: 1.5; }
328
+ .trend-tile .trend-grid { border: 0; border-radius: 0; }
329
+ .trend-grid {
330
+ display: grid; grid-template-columns: 230px 1fr; gap: 2px 10px; align-items: center;
331
+ background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px;
332
+ overflow-x: auto;
333
+ }
334
+ .tg-label { min-width: 0; }
335
+ .chart-title { font-size: 0.72rem; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: 0.07em; }
336
+ .chart-stats { font-size: 0.75rem; color: var(--muted); font-variant-numeric: tabular-nums; }
337
+ .chart-stats b { color: var(--ink); }
338
+ .tg-plot svg { width: 100%; height: 96px; display: block; }
339
+ .tg-plot .grid, .tg-plot .axis { stroke: var(--line); stroke-width: 1; }
340
+ .tg-plot .area-fill { fill: rgba(var(--heat), 0.14); }
341
+ .tg-plot .area-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
342
+ .tg-plot .hover-col { fill: transparent; }
343
+ .tg-plot .hover-col:hover, .tg-plot .hover-col.day-hi { fill: rgba(var(--heat), 0.16); }
344
+ .chart-note { font-size: 0.7rem; color: var(--muted); font-style: italic; }
345
+
346
+ /* stacked source bands + legend */
347
+ .stack-band { stroke: none; }
348
+ .stack-band.grp-ai-assistants { fill: var(--grp-ai); }
349
+ .stack-band.grp-search { fill: var(--grp-search); }
350
+ .stack-band.grp-direct { fill: var(--grp-direct); }
351
+ .stack-band.grp-referral { fill: var(--grp-referral); }
352
+ .stack-band.grp-social { fill: var(--grp-social); }
353
+ .stack-band.grp-other { fill: var(--grp-other); }
354
+ .src-legend { display: flex; flex-wrap: wrap; gap: 2px 10px; margin-top: 6px; }
355
+ .src-key { font-size: 0.7rem; color: var(--muted); display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
356
+ .src-key b { color: var(--ink); font-variant-numeric: tabular-nums; }
357
+ .swatch { width: 9px; height: 9px; border-radius: 2px; display: inline-block; flex-shrink: 0; }
358
+ .swatch.grp-ai-assistants { background: var(--grp-ai); }
359
+ .swatch.grp-search { background: var(--grp-search); }
360
+ .swatch.grp-direct { background: var(--grp-direct); }
361
+ .swatch.grp-referral { background: var(--grp-referral); }
362
+ .swatch.grp-social { background: var(--grp-social); }
363
+ .swatch.grp-other { background: var(--grp-other); }
364
+ /* the AI line runs on its own axis, in the AI group's colour */
365
+ .tg-plot.tone-ai .area-line { stroke: var(--grp-ai); }
366
+ .tg-plot.tone-ai .area-fill { fill: var(--grp-ai); opacity: 0.16; }
367
+
368
+ .tg-months { display: grid; grid-template-columns: repeat(var(--n), 1fr); }
369
+ .tg-months span { font-size: 9px; color: var(--muted); font-family: var(--mono); overflow: visible; white-space: nowrap; }
370
+ .tg-cells { display: grid; grid-template-columns: repeat(var(--n), 1fr); gap: 2px; }
371
+ .hm-label { font-family: var(--mono); font-size: 0.72rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: 6px; }
372
+ .hm-cell { height: 12px; border-radius: 2px; position: relative; min-width: 3px; }
373
+ .hm-cell:hover, .hm-cell.day-hi { outline: 2px solid var(--accent); outline-offset: 0; z-index: 5; }
374
+ .hm-tip {
375
+ display: none; position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
376
+ background: var(--topbar); color: #fff; font-size: 0.72rem;
377
+ padding: 5px 9px; border-radius: 6px; z-index: 20; border: 1px solid rgba(255, 255, 255, 0.18);
378
+ width: max-content; max-width: 260px; white-space: normal; text-align: left;
379
+ }
380
+ .hm-cell:hover .hm-tip { display: block; }
381
+ .hm-cell:nth-child(-n+10) .hm-tip { left: 0; right: auto; transform: none; }
382
+ .hm-cell:nth-last-child(-n+10) .hm-tip { left: auto; right: 0; transform: none; }
383
+ .trend-empty { display: flex; align-items: center; gap: 10px; padding: 12px 16px; color: var(--muted); flex-wrap: wrap; }
384
+ .trend-empty-note { font-size: 0.8rem; }
385
+
386
+ /* ---- site page: wide tables beside narrow lists ----
387
+ The query tables need room for five numeric columns; AI sources are a name
388
+ and a number. Giving both the full width wasted most of the screen. */
389
+ .site-cols { display: grid; grid-template-columns: 2fr 1fr; gap: 0 28px; align-items: start; }
390
+ .site-cols > * { min-width: 0; }
391
+ .site-col-side .mini-list { background: var(--card); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
392
+ .mini-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 7px 12px; border-bottom: 1px solid var(--line); font-size: 0.83rem; }
393
+ .mini-row:last-child { border-bottom: 0; }
394
+ .mini-row b { font-variant-numeric: tabular-nums; flex-shrink: 0; }
395
+ .mini-row .trunc { max-width: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
396
+ @media (max-width: 1100px) { .site-cols { grid-template-columns: 1fr; gap: 0; } }
397
+
398
+ /* ---- in-app markdown docs ---- */
399
+ .doc-body h3 { font-size: 1rem; margin: 1.4em 0 0.4em; text-transform: none; letter-spacing: 0; }
400
+ .doc-body h4 { font-size: 0.9rem; margin: 1.1em 0 0.3em; color: var(--accent); }
401
+ .doc-body p, .doc-body li { font-size: 0.86rem; color: var(--muted); max-width: 95ch; }
402
+ .doc-body .md-list { margin: 0.3em 0; padding-left: 20px; }
403
+
404
+ /* ---- campaign presentation ---- */
405
+ .or-h { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--muted); margin: 1.6em 0 0.6em; }
406
+ .or-h small { text-transform: none; letter-spacing: 0; font-weight: 500; }
407
+ .or-plan { cursor: default; }
408
+ .or-note { font-size: 0.76rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 34%; }
409
+ .or-plan:hover .or-note { white-space: normal; max-width: 40%; }
410
+ .email-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
411
+ .email-card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
412
+ .email-head { background: var(--panel); border-bottom: 1px solid var(--line); padding: 10px 16px; }
413
+ .email-meta { font-family: var(--mono); font-size: 0.7rem; color: var(--muted); }
414
+ .email-meta span { opacity: 0.7; }
415
+ .email-subject { font-weight: 700; font-size: 0.9rem; margin-top: 6px; }
416
+ .email-body { margin: 0; padding: 14px 16px; font-family: inherit; font-size: 0.84rem; line-height: 1.6; color: var(--ink); white-space: pre-wrap; }
417
+ .or-table td { font-size: 0.8rem; vertical-align: top; }
418
+ .or-cell { max-width: 260px; }
419
+
420
+ /* ---- content workbench: numbered pipeline sections ---- */
421
+ .wb-nav { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; margin: 14px 0 6px; }
422
+ .wb-nav a {
423
+ display: flex; align-items: center; gap: 8px;
424
+ background: var(--card); border: 1px solid var(--line); border-radius: 12px;
425
+ padding: 10px 16px; text-decoration: none; color: var(--ink); font-size: 0.85rem; font-weight: 700;
426
+ }
427
+ .wb-nav a:hover { border-color: var(--accent); }
428
+ .wb-nav a small { color: var(--muted); font-weight: 500; font-size: 0.72rem; }
429
+ .wb-num {
430
+ display: inline-flex; align-items: center; justify-content: center;
431
+ width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
432
+ background: var(--accent); color: #fff; font-family: var(--mono); font-size: 0.75rem; font-weight: 700;
433
+ }
434
+ @media (prefers-color-scheme: dark) { .wb-num { color: #0f1115; } }
435
+ .wb { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 18px 22px 22px; margin-top: 20px; }
436
+ .wb-head { display: flex; gap: 14px; align-items: flex-start; margin: 0 0 4px; }
437
+ .wb-head .wb-num { margin-top: 4px; width: 26px; height: 26px; font-size: 0.85rem; }
438
+ .wb-head h2 { margin: 0; font-size: 1.15rem; text-transform: none; letter-spacing: -0.01em; }
439
+ .wb-head .sub { margin: 6px 0 0; max-width: 95ch; }
440
+ .wb-sub { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink); margin: 1.8em 0 0.4em; }
441
+ .wb-sub small { text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--muted); }
442
+ .wb-fold { border: 1px solid var(--line); border-radius: 10px; background: var(--card); margin-top: 14px; }
443
+ .wb-fold > summary { cursor: pointer; list-style: none; padding: 11px 16px; font-weight: 700; font-size: 0.88rem; }
444
+ .wb-fold > summary::-webkit-details-marker { display: none; }
445
+ .wb-fold > summary::before { content: "▸ "; color: var(--accent); }
446
+ .wb-fold[open] > summary::before { content: "▾ "; }
447
+ .wb-fold > summary small { font-weight: 500; color: var(--muted); }
448
+ .wb-fold > *:not(summary) { margin: 0 16px 14px; }
449
+ .wb-fold .email-grid { margin-top: 4px; }
450
+
451
+ /* ---- drafts ---- */
452
+ .copy-btn { font: inherit; font-size: 0.72rem; font-weight: 700; cursor: pointer; background: var(--accent-soft); color: var(--accent); border: 0; border-radius: 99px; padding: 3px 12px; }
453
+ .copy-btn:hover { background: var(--accent); color: #fff; }
454
+ .draft-body { max-height: 480px; overflow-y: auto; }
455
+ .draft-row { align-items: center; gap: 12px; padding: 12px 14px; }
456
+ .draft-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex-shrink: 1; max-width: 58%; }
457
+ .draft-row-title { font-weight: 700; }
458
+ .draft-row-action { font-size: 0.78rem; color: var(--muted); }
459
+ .chip.ch-devto { background: rgba(108, 65, 240, 0.14); color: var(--purple); }
460
+ .chip.ch-github { background: var(--code-bg); color: var(--ink); }
461
+ .chip.ch-linkedin { background: rgba(10, 102, 194, 0.14); color: #0a66c2; }
462
+ .chip.ch-reddit { background: rgba(255, 69, 0, 0.14); color: #c63d00; }
463
+ .chip.ch-hackernews, .chip.ch-hn { background: var(--warn-soft); color: var(--warn); }
464
+ @media (prefers-color-scheme: dark) {
465
+ .chip.ch-linkedin { color: #7ab8ff; background: rgba(10, 102, 194, 0.3); }
466
+ .chip.ch-reddit { color: #ff8a5e; background: rgba(255, 69, 0, 0.25); }
467
+ }
468
+ .crumb { margin: 4px 0 10px; font-size: 0.85rem; }
469
+ .crumb a { text-decoration: none; font-weight: 600; }
470
+ .draft-title { max-width: 34ch; line-height: 1.25; }
471
+ .draft-todo { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 18px; margin: 14px 0 18px; max-width: 860px; }
472
+ .draft-todo h2 { margin: 0 0 6px; }
473
+ .draft-todo-action { margin: 0 0 6px; font-weight: 600; }
474
+ .draft-page-body { background: var(--card); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; max-width: 860px; }
475
+ .draft-body-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 16px; border-bottom: 1px solid var(--line); font-weight: 700; font-size: 0.85rem; }
476
+ .draft-body-head small { color: var(--muted); font-weight: 500; }
477
+ .draft-page-body .draft-body { max-height: none; }
478
+
479
+ /* ---- reddit hunt chips + campaign summary ---- */
480
+ .hunt-row { display: flex; gap: 8px; flex-wrap: wrap; }
481
+ .hunt-chip {
482
+ display: inline-flex; align-items: center; gap: 6px;
483
+ background: var(--panel); border: 1px solid var(--line); border-radius: 99px;
484
+ padding: 6px 14px; text-decoration: none; color: var(--accent); font-size: 0.83rem; font-weight: 700;
485
+ }
486
+ .hunt-chip:hover { border-color: var(--accent); }
487
+ .hunt-go { font-weight: 700; }
488
+ .campaign-sum { max-width: 760px; flex-direction: row; align-items: center; justify-content: space-between; margin: 0; border-radius: 0; border-left: 0; border-right: 0; border-top: 0; }
489
+ .campaign-sum:last-child { border-bottom: 0; }
490
+
491
+ /* ---- scan panel + conversions ---- */
492
+ .scan-panel { border: 1px dashed var(--accent); border-radius: 12px; padding: 12px 18px 16px; margin-bottom: 20px; }
493
+ .scan-panel h2 { margin-top: 4px; }
494
+ .funnel-panel { margin-top: 22px; padding: 14px 18px; background: var(--card); border: 1px solid var(--line); border-radius: 12px; }
495
+ .funnel-panel h2 { margin: 0; }
496
+ .funnel-panel .stat-row { margin-top: 8px; }
497
+ .funnel-line { color: var(--muted); font-size: 0.85rem; margin: 18px 0 0; }
498
+ .funnel-line b { color: var(--ink); }
499
+
500
+ /* ---- overview: hero + Today board ---- */
501
+ .ov-hero { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
502
+ .ov-hero h1 { margin-bottom: 0.2em; }
503
+ .ov-hero .sub { margin: 0 0 6px; }
504
+ .ov-status { align-items: center; padding-bottom: 10px; }
505
+ .ov-status .chip { padding: 5px 12px; }
506
+ .today-grid { columns: 250px; column-gap: 18px; margin-top: 10px; }
507
+ .today-col { min-width: 0; break-inside: avoid; margin-bottom: 22px; }
508
+ .today-verb { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.09em; display: flex; align-items: center; gap: 7px; margin-bottom: 2px; }
509
+ .today-count { font-family: var(--mono); font-size: 0.68rem; font-weight: 700; background: var(--code-bg); color: var(--muted); border-radius: 99px; padding: 0 7px; }
510
+ .v-fix .today-verb { color: var(--bad); }
511
+ .v-approve .today-verb { color: var(--warn); }
512
+ .v-publish .today-verb { color: var(--accent); }
513
+ .v-comment .today-verb { color: var(--purple); }
514
+ .v-ship .today-verb { color: var(--good); }
515
+ .today-blurb { margin: 0 0 8px; font-size: 0.74rem; color: var(--muted); }
516
+ .today-item {
517
+ display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
518
+ background: var(--panel); border: 1px solid var(--line); border-radius: 10px;
519
+ padding: 10px 13px; margin-bottom: 8px; text-decoration: none; color: var(--ink);
520
+ transition: border-color 0.15s;
521
+ }
522
+ .today-item:hover { border-color: var(--accent); }
523
+ .today-title { font-size: 0.83rem; font-weight: 600; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
524
+ .today-item .chip { flex-shrink: 0; }
525
+ .today-more { font-size: 0.78rem; font-weight: 700; text-decoration: none; }
526
+
527
+ /* ---- compact portfolio strip ---- */
528
+ .p-row {
529
+ display: grid; align-items: center; gap: 10px;
530
+ grid-template-columns: minmax(170px, 1.3fr) 14px 1fr 0.8fr 1.2fr 0.6fr 0.7fr 24px;
531
+ padding: 9px 14px; border-bottom: 1px solid var(--line);
532
+ color: var(--ink); text-decoration: none; font-size: 0.85rem;
533
+ }
534
+ .p-row:last-child { border-bottom: 0; }
535
+ .p-row:hover { background: var(--accent-soft); }
536
+ .p-row.p-head { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.09em; color: var(--muted); font-weight: 700; background: transparent; }
537
+ .p-num { font-variant-numeric: tabular-nums; font-weight: 600; }
538
+
539
+ /* ---- indexing ---- */
540
+ .idx-host { margin: 26px 0 0; }
541
+ .idx-host h2 { margin: 0 0 8px; font-size: 1rem; }
542
+ .idx-host h2 small { font-weight: 400; color: var(--muted); margin-left: 8px; }
543
+ .idx-group td { background: var(--panel); padding-top: 14px; }
544
+ .idx-help { color: var(--muted); font-size: 0.82rem; margin-left: 8px; }
545
+ .idx-stale { color: var(--muted); font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; }
546
+ .idx-sitemap { margin: -4px 0 8px; font-size: 0.78rem; color: var(--muted); }
547
+
548
+ /* ---- settings ---- */
549
+ .settings-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; }
550
+ .s-card { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
551
+ .s-card.s-on { border-color: var(--accent); }
552
+ .s-card .sub { margin: 0; }
553
+ .s-wide { margin-top: 4px; }
554
+ .s-title { font-weight: 700; font-size: 0.92rem; }
555
+ .s-toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; }
556
+ .s-toggle input { width: 16px; height: 16px; accent-color: var(--accent); margin: 0; }
557
+ .s-needs { margin: 0; font-size: 0.75rem; color: var(--warn); font-weight: 600; }
558
+ .s-help { margin: 0; font-size: 0.75rem; color: var(--muted); }
559
+ .s-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.78rem; font-weight: 600; color: var(--muted); margin-top: 4px; flex: 1; }
560
+ .s-field small { font-weight: 500; }
561
+ .s-field input[type="text"], .s-field textarea {
562
+ font: inherit; font-size: 0.84rem; color: var(--ink); background: var(--panel);
563
+ border: 1px solid var(--line); border-radius: 8px; padding: 7px 10px; width: 100%; font-weight: 400;
564
+ }
565
+ .s-field textarea { font-family: var(--mono); font-size: 0.78rem; resize: vertical; min-height: 64px; }
566
+ .s-field input:focus, .s-field textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }
567
+ .s-row { display: flex; gap: 14px; flex-wrap: wrap; }
568
+ .s-save { display: flex; align-items: center; gap: 14px; margin: 22px 0 8px; position: sticky; bottom: 0; background: var(--ground); padding: 12px 0; border-top: 1px solid var(--line); }
569
+
570
+ /* ---- responsive: phones and small tablets ---- */
571
+ @media (max-width: 720px) {
572
+ main { padding: 18px 14px 56px; }
573
+ footer { padding: 14px 14px 32px; }
574
+ h1 { font-size: 1.45rem; }
575
+ h2 { margin: 1.6em 0 0.5em; }
576
+ .topbar { gap: 10px; padding: 10px 14px; position: relative; }
577
+ .topbar nav { gap: 2px; }
578
+ .topbar nav a, .nav-menu summary { padding: 7px 11px; }
579
+ .brand-sub { display: none; }
580
+ .nav-menu { position: static; }
581
+ .nav-panel { position: absolute; top: 100%; left: 12px; right: 12px; width: auto; min-width: 0; max-width: none; margin-top: 6px; }
582
+ .strip-head { flex-wrap: wrap; align-items: flex-start; gap: 8px; }
583
+ .search-box { min-width: 0; width: 100%; padding: 9px 12px; }
584
+ .actions, .email-grid, .settings-grid { grid-template-columns: 1fr; }
585
+ .actions > * { min-width: 0; }
586
+ .action-title, .action-why, .action-watching { overflow-wrap: anywhere; }
587
+ .action-foot, .action-head { flex-wrap: wrap; }
588
+ .tg-cells { gap: 1px; }
589
+ .hm-cell { min-width: 2px; }
590
+ /* the summary row squeezed its chips down to "~2…" at phone widths */
591
+ .trend-tile > summary { flex-wrap: wrap; gap: 6px 10px; padding: 11px 12px; }
592
+ .trend-tile > summary .domain { width: 100%; }
593
+ .trend-tile > summary .spacer { display: none; }
594
+ .trend-spark { width: 96px; }
595
+ .src-legend { gap: 2px 8px; }
596
+ .trend-grid { grid-template-columns: 1fr; gap: 0; padding: 12px 12px; }
597
+ .trend-grid > .tg-label:empty { display: none; }
598
+ .trend-grid > .tg-label { padding: 10px 0 4px; }
599
+ .hm-label { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; padding-right: 0; }
600
+ .tg-plot svg { height: 82px; }
601
+ .hm-tip { max-width: min(240px, calc(100vw - 48px)); }
602
+ .strip-row, .watch-row { padding: 11px 12px; }
603
+ .strip-title { max-width: none; }
604
+ .watch-row { flex-wrap: wrap; }
605
+ .watch-note { max-width: 100%; white-space: normal; overflow-wrap: anywhere; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
606
+ .p-row { grid-template-columns: minmax(120px, 1.4fr) 10px 1fr 0.9fr 18px; }
607
+ .p-row > :nth-child(5), .p-row > :nth-child(6), .p-row > :nth-child(7) { display: none; }
608
+ table.slim { max-width: 100%; }
609
+ td.trunc { max-width: 200px; }
610
+ th, td { padding: 7px 10px; }
611
+ .action-modal { width: calc(100vw - 24px); padding: 18px 16px; }
612
+ .close { width: 34px; height: 34px; }
613
+ .hn-brief { padding: 4px 14px 12px 16px; }
614
+ .log-day { padding: 12px 14px; }
615
+ .stat-row { gap: 16px; }
616
+ .insight { padding: 14px 15px; }
617
+ .campaign-sum { flex-direction: column; align-items: flex-start; }
618
+ }
619
+
620
+ /* Sign-out link injected into the static export when the mirror sits behind
621
+ an auth proxy (modules.staticExport.signOutUrl). Absent on the live
622
+ dashboard, so it only ever renders in the topbar of an exported page. */
623
+ .signout {
624
+ margin-left: auto; color: var(--topbar-ink); text-decoration: none;
625
+ font-size: 0.8rem; font-weight: 600; padding: 5px 11px; border-radius: 99px;
626
+ border: 1px solid rgba(255, 255, 255, 0.18); white-space: nowrap;
627
+ }
628
+ .signout:hover { color: #fff; background: rgba(255, 255, 255, 0.12); }
629
+
630
+ /* Checkbox rows in Settings (publish: delete / dry run). */
631
+ .s-check { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; color: var(--muted); margin-top: 8px; }
632
+ .s-check input { margin: 0; }