@thebattlearena/corner 0.9.1-beta.70 → 0.10.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 (45) hide show
  1. package/package.json +1 -1
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/ring/agents/cornerman.md +20 -3
  4. package/ring/agents/wrestler.md +11 -0
  5. package/ring/hooks/hooks.json +20 -0
  6. package/ring/scripts/bout-stats-render.mjs +366 -0
  7. package/ring/scripts/bout-stats.mjs +360 -0
  8. package/ring/scripts/config.mjs +1 -1
  9. package/ring/scripts/contract-endpoints.mjs +163 -0
  10. package/ring/scripts/contract-preview.mjs +34 -14
  11. package/ring/scripts/fight.mjs +27 -0
  12. package/ring/scripts/findings.mjs +3 -1
  13. package/ring/scripts/format-gate.mjs +723 -0
  14. package/ring/scripts/format-worker.mjs +120 -0
  15. package/ring/scripts/front-routes.mjs +345 -0
  16. package/ring/scripts/judges-card.mjs +324 -3
  17. package/ring/scripts/lookout.mjs +22 -4
  18. package/ring/scripts/mask-guard.mjs +34 -1
  19. package/ring/scripts/page-embed.mjs +112 -0
  20. package/ring/scripts/play-by-play.mjs +155 -7
  21. package/ring/scripts/preparation-collect.mjs +177 -0
  22. package/ring/scripts/preparation.mjs +547 -0
  23. package/ring/scripts/round-bell.mjs +9 -2
  24. package/ring/scripts/tech-plan-log.mjs +81 -0
  25. package/ring/scripts/tech-plan.mjs +191 -0
  26. package/ring/scripts/verdicts-log.mjs +75 -0
  27. package/ring/scripts/verdicts.mjs +205 -0
  28. package/ring/skills/play-by-play/SKILL.md +235 -33
  29. package/ring/skills/play-by-play/templates/blocks.css +196 -0
  30. package/ring/skills/play-by-play/templates/chronicle.html +358 -55
  31. package/ring/skills/play-by-play/templates/endpoints-view.js +1153 -0
  32. package/ring/skills/play-by-play/templates/interview-form.js +516 -0
  33. package/ring/skills/play-by-play/templates/now-panel.js +719 -0
  34. package/ring/skills/referee/SKILL.md +136 -42
  35. package/ring/skills/referee/references/api-diagram.md +56 -0
  36. package/ring/skills/referee/references/precedents.md +11 -0
  37. package/ring/skills/referee/references/rulebook.md +65 -4
  38. package/ring/skills/referee/templates/api-diagram.html +115 -0
  39. package/ring/skills/referee/templates/escalation.md +7 -1
  40. package/ring/skills/referee/templates/round-bell-body.md +4 -0
  41. package/ring/skills/referee/templates/wrestler-brief.md +3 -0
  42. package/ring/skills/sparring/SKILL.md +10 -2
  43. package/ring/skills/sparring/templates/contrato.md +29 -0
  44. package/ring/skills/sparring/templates/rudo-brief.md +7 -0
  45. package/ring/skills/weigh-in/SKILL.md +6 -0
@@ -56,6 +56,42 @@ The milestones exist only if the sparring's referee marked them when they happen
56
56
  or bring its time through `--extra` when someone provided it.
57
57
  `/ring:play-by-play <pr>` on a bout opened from a sparring prints the same whole fight.
58
58
 
59
+ **Once a bout opened from the sparring, the sparring folds.** The JSON's `fold` (`folded`, the
60
+ call, its time, the evidence links, and in `media` the files the note names without a link,
61
+ such as the video) says so: the first and second falls go into the closed stage «El rudo» of «La
62
+ crónica», with a one-line summary (KO, date, link to the evidence), and the stages that follow
63
+ tell the third fall, «la pelea de los técnicos» (§3). Before any bout nothing folds: «El rudo» is
64
+ the live stage.
65
+
66
+ ### The preparation: «el pesaje del contrato» and «la entrevista»
67
+
68
+ Every bout (from a sparring or not) carries its preparation, told before its first round
69
+ (`weighIn` and `interview` at the top level of a bout's JSON, in each of `bouts[]` in a fight's):
70
+
71
+ - **`weighIn`, «el pesaje del contrato»**: `promotion` (the deliverable PR, its branch and the
72
+ stack base; from a sparring also the `mainevent` → `release` PR and the sparring's draft and
73
+ worktree), `size` (files and lines against the base when the bout opened, `atOpen`, and `now`),
74
+ `diagram` (the bout's `db-diagram.html`, §2b of the referee), `contractFindings` (the
75
+ cornerman's findings on the contract: those outside a commit range, by severity and by state)
76
+ and `scale`: does the contract make weight? `madeWeight` is false until
77
+ `judges-card.mjs approve-contract` records the judge's yes (its time, D id and quote). Say «el
78
+ pesaje del contrato», never just «el pesaje»: `/ring:weigh-in` is the command that configures
79
+ the host's gates.
80
+ - **`interview`, «la entrevista»**: each escalation to the judge (`escalations[]`, with its
81
+ `questions`: number, question, options, the referee's `recommended` and `why`, and `context`, the
82
+ Markdown that explains it), and under each
83
+ question the judge's `answers` with their time; `unanswered` lists the questions still waiting.
84
+ `referee` holds the decisions the referee took itself under `delegation: referee` in the same
85
+ window, told apart, «del réferi». The window ends at the judge's contract approval, else at the
86
+ wrestler's first commit (`until`; null while it lasts).
87
+ Every time in both carries its `local` next to it, as the events do: use that one on the page.
88
+
89
+ The escalations and answers exist only if the referee logged them (`judges-card.mjs log <pr>
90
+ escalation <file>` right after sending, `log <pr> answer [E<n>] <the judge's words>` when the judge
91
+ answers). Without `interview.jsonl` the JSON says so in `notes`: tell the interview from what the
92
+ judges card says, without times, as a gap. The events the preparation tells carry `stage`
93
+ (`pesaje` or `entrevista`); do not tell them again in the rounds.
94
+
59
95
  Times the records do not hold (a battery run the host logs elsewhere, a screenshot the human
60
96
  sent) enter only through `--extra` (an array of `{ at, type, title, detail, ref }`, inline or as
61
97
  the path of a JSON file), and they stay marked `provided`.
@@ -69,40 +105,203 @@ the page comes from an event's `local` field.
69
105
 
70
106
  ## 3 · Render
71
107
 
72
- Fill `$RING/skills/play-by-play/templates/chronicle.html`. It is derived from the hand-written
73
- chronicle of a real bout and already follows the Artifact page contract (title, theme tokens with
74
- dark mode, no external scripts, only the Google Fonts stylesheet). Keep its structure:
75
-
76
- - masthead with the live status, headline with the PR number, a two-sentence dek, the byline;
77
- - the fight card (champion vs challenger) and the crew line;
78
- - the tally (duration, Copilot findings, commits, fouls) and the legend of the eight tags;
79
- - one section per round, each beat with its time in the margin and its source in `.ref`; in a
80
- fight from a sparring, the rounds go under a header per fall (`h3.fall`), the card is the rudo
81
- against the judge, and the first beat of each day says the day;
82
- - exactly two pull quotes in the whole chronicle;
83
- - the judges' card table and the sources footer.
84
-
85
- ## 4 · Publish
86
-
87
- Write the page to `<bout dir>/play-by-play.html`, always that path. Publish it with the Artifact
88
- tool. If `state.json` already has `playByPlayUrl`, publish with `url` set to it, so the link
89
- never changes. After **every** publish, the first and each republish, run
90
- `node "$RING/scripts/judges-card.mjs" play-by-play-url <pr> <url>`: the first time it records the
91
- link (a second, different link is refused), and every time it stamps `playByPlay.publishedAt`.
92
- `mask-guard` reads that stamp: once a bout record (`decisions.md`, `rebounds.md`,
93
- `title-defense.md`, `judges-card.md`, `round-*.md`) changes after it, the Copilot re-request and
94
- `judges-card.mjs close` are blocked until the chronicle is republished and stamped again.
108
+ Fill `$RING/skills/play-by-play/templates/chronicle.html`. It follows the Artifact page contract
109
+ (title, theme tokens with dark mode, only the Google Fonts stylesheet; js-yaml from jsdelivr for
110
+ the contract) and its reading column is 80% wide, full width at 620 px or less. You write markup and
111
+ prose only: every script and the styles of what `embed` puts in the page come from `embed` (§4).
112
+ The page is organized as «Tablero del ahora + historial»: it opens with what the judge has to do,
113
+ not with the story, and the story is below, folded by stage. Top to bottom:
95
114
 
96
- Then offer, once, to put the link in the PR body; do it only with the human's yes.
115
+ 1. **The bill:** the masthead with the live status (what the fight waits on now), the headline
116
+ with the PR number in `em`, and next to it one line of facts (`span.meta`: duration, commits,
117
+ Copilot, fouls).
118
+ 2. **«Qué necesita de ti ahora»** (`section.now` with `data-ring-now`, dark in both themes). You
119
+ write only its frame: in `div.now-bouts` one `div.now-bout` per bout with `data-pr`,
120
+ `data-label` («El back», «El front») and `data-frozen` (empty, or why the fight is frozen, from
121
+ the JSON: «espera el SDK de #1212»), and inside it that bout's `p.now-ball`, who has the ball and
122
+ what comes next («El wrestler trabaja en el concepto 2; el próximo round-bell, ~18:00»), and its
123
+ `p.now-news`, its latest event with its `local` time: the script keeps them in the bout's card.
124
+ A ball or news of the whole fight goes after `div.now-bouts`. `now-panel.js` draws the rest
125
+ live (§4).
126
+ 3. **«La crónica»** (`section.chronicle`): its header with «Abrir todo» (`data-ring-open-all`),
127
+ then one `details.stage` per stage, in this order, skipping a stage the fight does not have:
128
+ - **El rudo** (`data-stage="rudo"`, a fight from a sparring): the first and second falls, each
129
+ opening with `h3.fall`, their rounds and their round table. The summary line: the call (KO),
130
+ its date and time from `fold.at`, the rounds, the commits, «sin equipo» when there was no
131
+ second fall, the links of `fold.evidence` and, as text, the files of `fold.media`.
132
+ - **El relevo** (`relevo`): the hand-off, the bouts opened from the KO with their branches.
133
+ - **El pesaje del contrato** (`pesaje`; never just «El pesaje»: `/ring:weigh-in` is another
134
+ thing): per bout the facts of `weighIn.promotion` and `weighIn.size`, the contract findings by
135
+ severity, the scale (`div.scale` with `data-ring-scale="<pr>"` and
136
+ `data-approved="<weighIn.scale.approvedByJudge>"`, red «No da el peso» with «N de M» from
137
+ `weighIn.verdicts`, green «Da el peso» only when `weighIn.scale.madeWeight`), then
138
+ - a backend bout: `h4.sub` «Endpoints del contrato» and the comment `ring:api-contract #<pr>`;
139
+ - a frontend bout: `h4.sub` «Rutas del frontend» and the comment `ring:front-routes #<pr>`
140
+ (and `ring:api-contract #<pr>` too when its repo carries a spec);
141
+ - `h4.sub` «Base de datos» and the comment `ring:db-diagram #<pr>` when the bout has a
142
+ diagram.
143
+ - **La entrevista** (`entrevista`): per escalation an `ol.qa` with
144
+ `data-escalation="pr<pr>-E<k>"`; per question an `li` with its `p.q` starting with the
145
+ question's number, its `ul` of options (`li.rec` for the recommended one, its key in `i`, its
146
+ text after it) and its `.answer` box: «<judge>, HH:MM: <option> — <text>» for a logged
147
+ answer (with its images as `img src="/_blob/<asset id>"`), «Sin respuesta del juez todavía»
148
+ (`.answer.pending`) otherwise. Then `div.byref` «Del réferi» with `interview.referee`.
149
+ - **El plan del técnico** (`plan`), with the comment `ring:tech-plan #<pr>`, when the bout has
150
+ a `plan-tecnico.md`.
151
+ - **La pelea del técnico** (`pelea`): the fight card and the crew line, the tally, the legend of
152
+ the eight tags, the rounds (one section each, each beat with its time in the margin and its
153
+ source in `.ref`; the first beat of each day says the day; the events with `stage` are not
154
+ told again) and the judges' card table.
155
+
156
+ Each `summary` holds `span.ico`, the stage's name in `b`, its chip and **one** line of facts
157
+ (`span.line`). `data-state` is `done` (green chip: KO, HECHO, DA EL PESO), `live` (red chip, EN
158
+ CURSO: what the fight is doing now) or `todo` (gray, POR EMPEZAR); a count chip (the plan,
159
+ «0 / 41») takes `class="chip count"`. Every live stage carries `open`; done and todo stages
160
+ stay closed. With several bouts, each stage holds every bout, each part opening with
161
+ `h3.bout data-pr="<pr>"` and no `id`: it repeats per stage, and an `id` must be unique (if one
162
+ is needed, `bout-<pr>-<stage>`). The panel's link to a bout goes to its `h3.bout` in an open
163
+ stage. Before any bout opened, «El rudo» is the live stage.
164
+ 4. At most two pull quotes in the whole chronicle; the sources footer.
165
+ 5. The last line of the page: the comment `ring:page-scripts`.
166
+
167
+ Skip a part the JSON leaves empty. Placeholders are HTML comments exactly as written here.
168
+
169
+ ### The final edition: "El cierre en números"
170
+
171
+ When the status is "Final" (at `close`), add the numbers of the bout before the judges' card:
172
+
173
+ ```bash
174
+ node "$RING/scripts/bout-stats.mjs" <pr> --tz <the human's time zone> --html [--lang es|en] \
175
+ --moments <bout dir>/tools/moments.json
176
+ ```
177
+
178
+ It reads git in the wrestler's worktree (`origin/<base>...HEAD`) and the bout dir, and prints the
179
+ block: the tally (duration, commits, round-bells, lines), the weigh-in (code against tests, with
180
+ lockfiles and config as dead weight), round by round (commits per day between the ropes), the
181
+ fight clock (commits per hour, the small hours shaded), the CompuBox (commits by conventional
182
+ type) and the referee's card (decisions, rebounds, findings, contracts, fouls). Without `--html`
183
+ it prints the same numbers as JSON. Paste the block as printed: its numbers come from the records,
184
+ never from an estimate, and the template's `cn-*` styles draw it in both themes.
185
+
186
+ - **Moments** (`--moments`): five to seven `{ at, title, tag }` you pick from the play-by-play
187
+ JSON, each `at` copied from its event (the script refuses a moment without a valid time). They
188
+ become the highlight reel, on a line from the first to the last.
189
+ - **Changelog**: write it yourself in the block's CHANGELOG slot, one `<li>` per area of what the
190
+ PR ships, from the PR body and the rounds' titles. Nothing that did not ship.
191
+ - If the worktree is gone, the block keeps the referee's card and says nothing about lines or
192
+ commits (`notes` in the JSON says why).
193
+
194
+ ## 4 · Embed
97
195
 
98
- **A fight from a sparring has one page and one link.** Write it to
99
- `<sparring dir>/play-by-play.html` (the JSON's `page`), always that path, whether it is refreshed
100
- from the sparring or from one of its bouts. If `sparring.json` already has `playByPlayUrl`,
101
- publish with `url` set to it. After every publish from the sparring, run
102
- `node "$RING/scripts/sparring.mjs" play-by-play-url <slug> <url>`; from a bout,
103
- `judges-card.mjs play-by-play-url <pr> <url>` as always, which refuses any link but the fight's and
104
- also stamps the sparring. The sparring keeps its first link for the whole fight, through every
105
- bout of every repo.
196
+ Write the page to its path (§5), then, after every rewrite and before every publish:
197
+
198
+ ```bash
199
+ node "$RING/scripts/play-by-play.mjs" embed <page> <weighIn.diagram.path of each bout with one>… \
200
+ --contract <boutDir of each bout> … --interview <boutDir of each bout> … \
201
+ --routes <boutDir of each frontend bout> … --plan <boutDir of each bout with a plan> …
202
+ ```
203
+
204
+ Read its output: `warnings` (a context logged without its sections, say) is something to fix, not
205
+ to ignore. It is idempotent: each block sits between begin and end markers and is replaced. It puts:
206
+
207
+ - each diagram as an iframe whose `srcdoc` is the diagram page, escaped whole (its own `</body>`
208
+ never reaches the page);
209
+ - «Endpoints del contrato»: every spec file a changed spec root reaches, at the stack base and as
210
+ the worktree proposes it (what `contract-preview.mjs` serves), drawn in the page;
211
+ - «Rutas del frontend»: every Next.js route the deliverable adds, changes or removes
212
+ (`pages/**`, `app/**/page|layout|route`), read from git;
213
+ - each question's context (`## Contexto <n> · …`, logged with the escalation) before its `ol.qa`;
214
+ - «El plan del técnico» from `plan-tecnico.md` and the stamps of `plan.jsonl`;
215
+ - at the very end, never inside a `srcdoc`, the styles of all of it (`templates/blocks.css`, so a
216
+ page written from an older template regenerates the same) and the scripts
217
+ (`templates/interview-form.js`, `templates/endpoints-view.js` and, when the page has
218
+ `data-ring-now`, `templates/now-panel.js`). The scripts share a page bus, `window.ringPage`:
219
+ the interview and the boards register each question and each verdict with the function that
220
+ saves it, and the panel reads and calls them, so an answer given in the panel or in the stage
221
+ is the same doc and both repaint.
222
+
223
+ ### What the page does for the judge
224
+
225
+ - **«Qué necesita de ti ahora» counts and resolves on top.** Four counters, live from
226
+ `respuestas`, `veredictos` and the frame you wrote: questions without an answer (per bout),
227
+ pending verdicts (endpoints and routes without a current one: none or outdated), red scales and
228
+ frozen fights. Per bout, a card with «pregunta N de M», the scale chip («No da el peso · 0/34»),
229
+ the frozen line, the next question with its options, «Ver más información», «Guardar respuesta»
230
+ (disabled until the judge picks, nothing preselected) and «Comentario o imagen», then the next
231
+ two endpoints or routes with «Se aprueba» and «Se mejora» (a required comment, images), and «Ver
232
+ la cola» with the rest. «Aceptar las N recomendaciones del réferi» saves nothing on the click:
233
+ it opens a dialog that lists every question with the recommended option it would save, each
234
+ one can be unchecked, and only «Guardar N respuestas» writes them. Under it «Revisar una por
235
+ una» and the line «cada respuesta vuelve al réferi como rebote». With nothing pending, the
236
+ panel reads «Nada que pedirte ahora» and shows who has the ball and the latest news in large
237
+ type; a bout with everything approved but no `approve-contract` says that is what is missing.
238
+ On a phone the counters go two by two, the cards stack and «Guardar respuesta» stays fixed at
239
+ the bottom of the question. A link to a folded stage (an origin of the plan, «En la crónica»)
240
+ opens it first.
241
+ - **The interview is answered from the page.** Nothing comes picked, not even the recommended
242
+ option (it keeps its badge), and «Guardar respuesta» stays disabled until the judge picks one.
243
+ Each option is itself selectable and its text editable (what is saved is the text as left);
244
+ an «Otro» row has an open field; a comment is optional; images are dropped or attached.
245
+ «Ver más información» opens a modal with the question's context in Markdown (Esc or a click
246
+ outside closes it, the focus stays in it, it reads in dark mode and on a phone). The answer is
247
+ saved to `respuestas` and painted live with the time.
248
+ - **Endpoints and routes take a verdict each.** Cards with a chip per method (or PÁGINA, API
249
+ ROUTE, GLOBAL for a route), the path, the summary, the badge «nueva», «cambia» or «se quita»
250
+ apart, the spec file when there are several; filters (todas, nuevas, cambian, se quitan, sin
251
+ veredicto, se aprueba, se mejora, desactualizadas); «N de M aprobados» with its bar; the schemas
252
+ as compact tables (field, type, null, required, values and description); a route shows its
253
+ params, the components, hooks and services that change, the navigation that changes, the
254
+ back-end calls that can be read, and «sólo sparring» when it must leave the delivery. Every
255
+ added or changed one has «Se aprueba» and «Se mejora» (a required comment, images), saved in
256
+ `veredictos` with the fingerprint of its contract (the operation with every schema it expands;
257
+ a route's files): when the contract changes, the verdict reads «desactualizado» and asks again.
258
+ «Pendientes del contrato», above the cards, lists the current «se mejora». The judge's view
259
+ also saves the contract's state (`pr<n>-contrato`, `pr<n>-rutas`) so the referee can tell which
260
+ verdicts are current.
261
+ - **The scale** counts the endpoints and routes of the bout: green only with all of them approved
262
+ with their current fingerprint and the judge's `approve-contract`; before that, «N de M». In a
263
+ frontend bout the SDK it consumes is not counted by the scale: it is a point of the technical
264
+ plan («consumir el SDK publicado»), and the bout does not close with it open.
265
+ - **The plan** shows each group with «N de M hechos» and its bar, each point with its state chip
266
+ (pendiente gray, espera al juez amber, en curso blue, hecho green), its inline Markdown, its
267
+ origin linked to the question (`E<k>-<n>`) or card (a verdict's key) it cites when it is on the
268
+ page, its last change, filters by state, and the tickets left out at the end.
269
+
270
+ ## 5 · Publish
271
+
272
+ Write the page to `<bout dir>/play-by-play.html`; a fight from a sparring, to
273
+ `<sparring dir>/play-by-play.html` (the JSON's `page`), always that path. Publish it with the
274
+ Artifact tool. **Every publish that is the page's first, or the first after the page lacked one of
275
+ these rules, declares the capabilities in full** (a non-empty `capabilities` replaces the stored
276
+ set); later republishes omit `capabilities`, which carries them forward:
277
+
278
+ ```json
279
+ {"db": {"rules": [{"path": "respuestas", "read": "view", "write": "admin"}, {"path": "veredictos", "read": "view", "write": "admin"}]}, "user": {}, "assets": {}}
280
+ ```
281
+
282
+ Only who can edit the page (the judge) answers, gives verdicts and uploads images; everyone else
283
+ reads. Right after that publish, check it once: `ArtifactData` `list` of `respuestas` and of
284
+ `veredictos` (empty is fine), then one `set` with `as_level: "interact"` on a throwaway doc of
285
+ each (`ring-level-check`): the rules must refuse it (at a lowered level a refused write reads as
286
+ not found). If it is written, the capabilities are wrong: fix them before the judge uses the page,
287
+ and delete the throwaway doc.
288
+
289
+ If `state.json` (or, for a fight, `sparring.json`) already has `playByPlayUrl`, publish with `url`
290
+ set to it, so the link never changes. After **every** publish run
291
+ `node "$RING/scripts/judges-card.mjs" play-by-play-url <pr> <url>` (from the sparring,
292
+ `node "$RING/scripts/sparring.mjs" play-by-play-url <slug> <url>`): the first time it records the
293
+ link (a second, different link is refused), every time it stamps `playByPlay.publishedAt`, and a
294
+ bout from a sparring shares the fight's one link through every bout of every repo. `mask-guard`
295
+ reads that stamp: once a bout record (`decisions.md`, `rebounds.md`, `title-defense.md`,
296
+ `judges-card.md`, `round-*.md`, `interview.jsonl`, `verdicts.jsonl`, `plan.jsonl`,
297
+ `plan-tecnico.md`) changes after it, the Copilot re-request and `judges-card.mjs close` are blocked
298
+ until the chronicle is republished and stamped again.
299
+
300
+ **Several repos, one page.** Each repo's referee keeps its own bout dir (its interview, verdicts,
301
+ plan, diagram); the fight's JSON lists every bout with its `boutDir`, and whichever session
302
+ regenerates the page passes every bout dir to `embed`, so the one page joins them all.
303
+
304
+ Then offer, once, to put the link in the PR body; do it only with the human's yes.
106
305
 
107
306
  ## Rules
108
307
 
@@ -122,7 +321,10 @@ bout of every repo.
122
321
  ## When it runs
123
322
 
124
323
  The referee and title-defense refresh it after each round-bell and at each bell (the Copilot
125
- re-request); `close` publishes the final edition, with the status "Final".
324
+ re-request); `close` publishes the final edition, with the status "Final". During the preparation
325
+ the referee also regenerates it (with `embed`) and republishes it after every change to the
326
+ contract, after logging the judge's answers (`page-answers`) and after logging the verdicts
327
+ (`page-verdicts`), so the page the judge reviews is always the current contract.
126
328
 
127
329
  In a sparring, the referee of the sparring publishes it first at `open` and refreshes it after
128
330
  each verdict, after the vestidor, when the defense opens and at the bell. Those refreshes report;
@@ -0,0 +1,196 @@
1
+ /* What play-by-play.mjs embed puts into a chronicle page (diagram, interview form, endpoints,
2
+ routes, verdicts, technical plan, context modal). embed injects this file with the scripts, so a
3
+ page written from an older template gets the rules too. It uses the template's tokens. */
4
+ iframe.diagram{display:block;width:100%;height:min(760px,80vh);border:1px solid var(--rule);background:#fff;margin:6px 0 16px;filter:var(--diagram-filter)}
5
+ /* La entrevista contestada desde la página (interview-form.js) */
6
+ .ans-form{margin:10px 0 4px;padding:14px 16px;border:1px solid var(--rule);border-radius:6px;background:var(--paper,transparent);display:grid;gap:10px}
7
+ .ans-form .opts{display:flex;flex-wrap:wrap;gap:8px}
8
+ .ans-form label.opt{display:flex;align-items:center;gap:8px;padding:6px 12px;border:1px solid var(--rule);border-radius:99px;cursor:pointer;font-size:15px}
9
+ .ans-form label.opt:has(input:checked){border-color:var(--blue);background:var(--blue-soft);color:var(--blue);font-weight:600}
10
+ .ans-form textarea{width:100%;box-sizing:border-box;min-height:64px;font:inherit;font-size:15px;padding:8px 10px;border:1px solid var(--rule);border-radius:6px;background:transparent;color:inherit;resize:vertical}
11
+ .ans-form .drop{border:2px dashed var(--rule);border-radius:6px;padding:12px;text-align:center;font-size:14px;color:var(--muted);cursor:pointer}
12
+ .ans-form .drop.over{border-color:var(--blue);color:var(--blue);background:var(--blue-soft)}
13
+ .ans-form .thumbs{display:flex;flex-wrap:wrap;gap:8px}
14
+ .ans-form .thumbs img{height:72px;border-radius:4px;border:1px solid var(--rule)}
15
+ .ans-form .row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
16
+ .ans-form button.save{font:inherit;font-weight:700;padding:7px 16px;border-radius:6px;border:0;background:var(--blue);color:#fff;cursor:pointer}
17
+ .ans-form button.save:disabled{opacity:.5;cursor:default}
18
+ .ans-form .status{font-size:13px;color:var(--muted)}
19
+ .answer.done{background:var(--blue-soft);color:var(--ink,inherit);border-left-color:var(--blue)}
20
+ .answer.done img{height:64px;border-radius:4px;margin:6px 6px 0 0;vertical-align:top}
21
+ ol.qa ul li.selectable{cursor:pointer;padding-left:58px;border-radius:4px}
22
+ ol.qa ul li.selectable:hover{background:var(--blue-soft)}
23
+ ol.qa ul li.selectable input.pick{position:absolute;left:0;top:9px;margin:0;accent-color:var(--blue);width:16px;height:16px;cursor:pointer}
24
+ ol.qa ul li.selectable > i{left:26px}
25
+ ol.qa ul li.selectable:has(input.pick:checked){background:var(--blue-soft);outline:1px solid var(--blue)}
26
+ ol.qa .otext{outline:none;border-bottom:1px dashed transparent}
27
+ ol.qa .otext:focus,ol.qa li.selectable:hover .otext{border-bottom-color:var(--muted)}
28
+ ol.qa li.other input.otro-text{width:100%;box-sizing:border-box;font:inherit;font-size:16px;padding:4px 8px;border:1px solid var(--rule);border-radius:4px;background:transparent;color:inherit}
29
+ /* Endpoints del contrato (endpoints-view.js) */
30
+ .endpoints{margin:6px 0 16px}
31
+ .endpoints-wait,.endpoints .muted{font-family:var(--f-label);font-size:15px;color:var(--muted)}
32
+ .spec-root h4{font-family:var(--f-label);font-size:15px;letter-spacing:.02em;margin:14px 0 8px;font-weight:600}
33
+ details.op{background:var(--paper);border:1px solid var(--rule);border-left:4px solid var(--rule);margin:0 0 6px}
34
+ details.op.added{border-left-color:var(--green)} details.op.changed{border-left-color:var(--gold)} details.op.removed{border-left-color:var(--red)}
35
+ details.op>summary{cursor:pointer;padding:8px 12px;display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;font-family:var(--f-label);font-size:15.5px}
36
+ details.op>summary code{overflow-wrap:anywhere}
37
+ details.op>summary .sum{color:var(--muted);flex:1 1 20ch}
38
+ details.op>summary{list-style:none} details.op>summary::-webkit-details-marker{display:none}
39
+ details.op>summary .path{font-family:var(--f-mono);font-size:13.5px;background:none;padding:0}
40
+ details.op .badge{margin-left:auto;font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:1px 8px;border-radius:99px;white-space:nowrap}
41
+ details.op .badge.added{background:var(--green-soft);color:var(--green)} details.op .badge.changed{background:var(--gold-soft);color:var(--gold)} details.op .badge.removed{background:var(--red-soft);color:var(--red)}
42
+ .m.route-page{background:var(--navy-soft);color:var(--navy)} .m.route-api{background:var(--blue-soft);color:var(--blue)} .m.route-global{background:var(--ink);color:var(--canvas)}
43
+ .op-body p.desc.warn{color:var(--red);font-weight:600}
44
+ .nav-changes{margin:0 0 12px;padding:10px 14px;background:var(--paper);border:1px solid var(--rule);font-size:15px}
45
+ .nav-changes h4{font-family:var(--f-label);font-weight:700;font-size:14px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:0 0 4px}
46
+ .nav-changes ul{margin:0;padding-left:18px}
47
+ /* El plan del técnico */
48
+ .tech-plan .plan-group{margin:0 0 14px}
49
+ .tech-plan .ep-progress b{font-family:var(--f-label);font-weight:700;font-size:16px;color:var(--ink)}
50
+ ul.plan-items{list-style:none;margin:0;padding:0}
51
+ li.plan-item{background:var(--paper);border:1px solid var(--rule);padding:8px 12px;margin:0 0 5px;font-size:15.5px;line-height:1.5}
52
+ li.plan-item .st{font-family:var(--f-label);font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:1px 8px;border-radius:99px;margin-right:8px;white-space:nowrap}
53
+ .st-pendiente{background:var(--grey-soft);color:var(--muted)} .st-espera-al-juez{background:var(--gold-soft);color:var(--gold)} .st-en-curso{background:var(--blue-soft);color:var(--blue)} .st-hecho{background:var(--green-soft);color:var(--green)}
54
+ li.plan-item .origins,li.plan-item .since{font-family:var(--f-label);font-size:14px;color:var(--muted)}
55
+ .plan-tickets{margin:10px 0 0;font-size:15px;color:var(--muted)}
56
+ .plan-tickets h4{font-family:var(--f-label);font-weight:700;font-size:14px;letter-spacing:.1em;text-transform:uppercase;margin:0 0 4px}
57
+ details.op .spec-tag{font-family:var(--f-mono);font-size:11.5px;color:var(--muted);border:1px solid var(--rule);border-radius:3px;padding:0 5px}
58
+ details.op .vdot{width:10px;height:10px;border-radius:50%;background:var(--rule);flex:none}
59
+ details.op[data-verdict="approved"] .vdot{background:var(--green)} details.op[data-verdict="improve"] .vdot{background:var(--red)} details.op[data-verdict="stale"] .vdot{background:var(--gold)}
60
+ details.op[open]{box-shadow:0 2px 10px rgb(0 0 0 / .06)}
61
+ .ep-progress{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 8px;margin:4px 0 10px;font-family:var(--f-label);font-size:15px;color:var(--muted)}
62
+ .ep-progress b{font-family:var(--f-display);font-weight:400;font-size:24px;color:var(--ink)}
63
+ .ep-progress .bar{flex:1 0 100%;height:8px;background:var(--grey-soft);border-radius:99px;overflow:hidden}
64
+ .ep-progress .bar i{display:block;height:100%;width:0;background:var(--green);transition:width .3s}
65
+ .ep-filters{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}
66
+ .ep-filters button{font-family:var(--f-label);font-size:14px;font-weight:600;padding:4px 12px;border-radius:99px;border:1px solid var(--rule);background:var(--paper);color:var(--ink);cursor:pointer}
67
+ .ep-filters button[aria-pressed="true"]{background:var(--ink);color:var(--canvas);border-color:var(--ink)}
68
+ .ep-pending{margin:0 0 12px}
69
+ .ep-pending:empty{display:none}
70
+ .ep-pending h4{font-family:var(--f-label);font-weight:700;font-size:14px;letter-spacing:.1em;text-transform:uppercase;color:var(--red);margin:0 0 6px}
71
+ .ep-pending ul{margin:0;padding:10px 14px 10px 30px;background:var(--red-soft);border-left:4px solid var(--red);font-size:15px}
72
+ .ep-pending li{margin:4px 0}
73
+ .ep-pending img,.verdict img{height:56px;border-radius:4px;margin:4px 6px 0 0;vertical-align:top}
74
+ .verdict{border-top:1px solid var(--rule);padding:10px 14px 12px;display:grid;gap:8px}
75
+ .verdict-state{margin:0;font-family:var(--f-label);font-size:15px;color:var(--muted)}
76
+ .verdict .v{font-weight:700;padding:1px 8px;border-radius:3px;margin-right:4px}
77
+ .v.approved{background:var(--green-soft);color:var(--green)} .v.improve{background:var(--red-soft);color:var(--red)} .v.stale{background:var(--gold-soft);color:var(--gold)} .v.none{background:var(--grey-soft);color:var(--muted)}
78
+ .verdict-buttons{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
79
+ .verdict-buttons button{font:inherit;font-family:var(--f-label);font-weight:700;font-size:15px;padding:6px 14px;border-radius:6px;cursor:pointer;border:2px solid currentColor;background:var(--paper)}
80
+ .verdict-buttons .approve{color:var(--green)} .verdict-buttons .improve-btn{color:var(--red)}
81
+ .verdict-buttons .status{font-size:13px;color:var(--muted)}
82
+ .schema-tbl table{min-width:520px;font-size:14px}
83
+ .schema-tbl td,.schema-tbl th{padding:5px 10px}
84
+ .schema-tbl td.req{color:var(--red);font-weight:700}
85
+ .op-body .tbl{margin:4px 0 8px}
86
+ /* «Ver más información»: the question's context in a modal */
87
+ ol.qa .q button.more{font-family:var(--f-label);font-size:14px;font-weight:700;color:var(--blue);background:none;border:0;border-bottom:1px dashed currentColor;padding:0;cursor:pointer;margin-left:4px}
88
+ dialog.ctx-dialog{width:min(760px,calc(100vw - 32px));max-height:min(80vh,900px);padding:0;border:2px solid var(--ink);background:var(--paper);color:var(--ink);overflow:auto}
89
+ dialog.ctx-dialog::backdrop{background:rgb(0 0 0 / .55)}
90
+ .ctx-head{position:sticky;top:0;display:flex;align-items:flex-start;gap:12px;padding:14px 18px;background:var(--paper);border-bottom:1px solid var(--rule)}
91
+ .ctx-head h3{flex:1;margin:0;font-family:var(--f-display);font-weight:400;font-size:22px;text-transform:uppercase;line-height:1.1}
92
+ .ctx-close{font:inherit;font-size:24px;line-height:1;background:none;border:0;color:var(--muted);cursor:pointer;padding:0 4px}
93
+ .ctx-body{padding:6px 18px 18px;font-size:16px;line-height:1.6}
94
+ .ctx-body pre{background:var(--grey-soft);padding:10px 12px;overflow-x:auto;font-size:13px}
95
+ .ctx-body table{min-width:0}
96
+ .ctx-body h3,.ctx-body h4,.ctx-body h5,.ctx-body h6{font-family:var(--f-label);letter-spacing:.04em;margin:16px 0 4px}
97
+ .m{font-family:var(--f-mono);font-size:12px;padding:1px 6px;border-radius:3px;background:var(--grey-soft)}
98
+ .m.get{background:var(--blue-soft);color:var(--blue)} .m.post{background:var(--green-soft);color:var(--green)} .m.patch,.m.put{background:var(--gold-soft);color:var(--gold)} .m.delete{background:var(--red-soft);color:var(--red)}
99
+ .op-body{padding:4px 14px 12px;border-top:1px solid var(--rule);font-size:15px;line-height:1.5}
100
+ .op-body h5{font-family:var(--f-label);font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin:14px 0 4px}
101
+ .op-body h5.err{color:var(--red)}
102
+ ul.fields{list-style:none;margin:0;padding:0 0 0 14px;border-left:1px solid var(--rule)}
103
+ ul.fields li{margin:3px 0}
104
+ ul.fields .name{font-weight:700} ul.fields .type,ul.fields .enum{font-family:var(--f-mono);font-size:12.5px;color:var(--muted)}
105
+ ul.fields .req{font-family:var(--f-label);font-size:12px;font-weight:700;color:var(--red);text-transform:uppercase}
106
+ .op-body p.desc,ul.fields p.desc{margin:2px 0;color:var(--muted);font-size:14px}
107
+ /* «Qué necesita de ti ahora» (now-panel.js): the judge's queue on top, dark in both themes */
108
+ section.now{--now-bg:#16151c;--now-ink:#f3f2ec;--now-muted:#a4a3b1;--now-tile:#22212b;margin:26px 0 8px;padding:28px 32px 26px;background:var(--now-bg);color:var(--now-ink);display:grid;gap:18px}
109
+ @media (prefers-color-scheme: dark){:root:not([data-theme="light"]) section.now{--now-bg:#0a0a0f;--now-tile:#1c1b25;border:1px solid var(--rule)}}
110
+ :root[data-theme="dark"] section.now{--now-bg:#0a0a0f;--now-tile:#1c1b25;border:1px solid var(--rule)}
111
+ .now-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;gap:8px 24px}
112
+ .now-kicker{font-family:var(--f-label);font-weight:700;font-size:13px;letter-spacing:.16em;text-transform:uppercase;color:#e8b62f}
113
+ .now-head h2{font-family:var(--f-display);font-weight:400;font-size:clamp(30px,5vw,46px);line-height:1;text-transform:uppercase;margin:6px 0 0}
114
+ .now-wait{display:grid;text-align:right;font-family:var(--f-label);font-size:15px;color:var(--now-muted)}
115
+ .now-wait em{font-style:normal;color:#e8b62f}
116
+ .now-counters{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}
117
+ .now-count{background:var(--now-tile);border-left:4px solid var(--now-muted);padding:14px 16px;display:grid;gap:2px;min-width:0}
118
+ .now-count b{font-family:var(--f-display);font-weight:400;font-size:44px;line-height:1}
119
+ .now-count span{font-family:var(--f-label);font-weight:700;font-size:14px;letter-spacing:.08em;text-transform:uppercase}
120
+ .now-count small{font-family:var(--f-label);font-size:14px;color:var(--now-muted);overflow-wrap:anywhere}
121
+ .now-count.red{border-left-color:#ff5a6e} .now-count.gold{border-left-color:#e8b62f} .now-count.grey{border-left-color:#a4a3b1} .now-count.zero{border-left-color:#5fd08e}
122
+ .now-count.zero b{color:var(--now-muted)}
123
+ .now-bouts{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));gap:18px}
124
+ .now-bout{background:var(--paper);color:var(--ink);padding:16px 18px;display:grid;gap:10px;align-content:start;min-width:0}
125
+ .now-bout-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px}
126
+ .now-bout-head h3{font-family:var(--f-display);font-weight:400;font-size:24px;text-transform:uppercase;margin:0}
127
+ .now-bout-head h3 a{color:inherit;text-decoration:none}
128
+ .now-of{font-family:var(--f-label);font-size:14px;color:var(--muted)}
129
+ .now-scale{margin-left:auto;font-family:var(--f-label);font-weight:700;font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;padding:2px 8px;border-radius:3px;white-space:nowrap}
130
+ .now-scale.red{background:var(--red-soft);color:var(--red)} .now-scale.green{background:var(--green-soft);color:var(--green)}
131
+ .now-frozen{margin:0;font-family:var(--f-label);font-size:14.5px;padding:4px 10px;background:var(--grey-soft);border-left:3px solid var(--muted)}
132
+ .now-none{margin:0;font-family:var(--f-label);font-size:15.5px;color:var(--muted)}
133
+ .now-q{border:2px solid var(--red);padding:12px 14px;display:grid;gap:8px}
134
+ .now-q-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px}
135
+ .now-q-head b{flex:1 1 24ch;font-weight:600;font-size:16.5px;line-height:1.4}
136
+ .now-q-head time{font-family:var(--f-mono);font-size:12.5px;color:var(--muted)}
137
+ .now-q-head button.more{font-family:var(--f-label);font-size:14px;font-weight:700;color:var(--blue);background:none;border:0;border-bottom:1px dashed currentColor;padding:0;cursor:pointer}
138
+ .now-opts{display:grid;gap:4px}
139
+ .now-opt{display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;align-items:start;gap:8px;padding:5px 8px;border-radius:4px;cursor:pointer;font-size:15.5px;line-height:1.45}
140
+ .now-opt:hover,.now-opt:has(input:checked){background:var(--blue-soft)}
141
+ .now-opt input{margin:3px 0 0;accent-color:var(--blue);width:16px;height:16px}
142
+ .now-opt i{font-family:var(--f-mono);font-style:normal;font-weight:700;color:var(--blue)}
143
+ .now-opt em{font-family:var(--f-label);font-style:normal;font-weight:700;font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--blue);background:var(--blue-soft);padding:1px 6px;border-radius:3px;white-space:nowrap}
144
+ .now-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px}
145
+ .now-bout button.save,.now-confirm button.save{font:inherit;font-family:var(--f-label);font-weight:700;font-size:15px;padding:6px 14px;border-radius:4px;border:0;background:var(--blue);color:#fff;cursor:pointer}
146
+ .now-bout button.save:disabled,.now-confirm button.save:disabled{opacity:.45;cursor:default}
147
+ .now-bout button.ghost,.now-confirm button.ghost{font:inherit;font-family:var(--f-label);font-weight:600;font-size:15px;padding:5px 12px;border-radius:4px;border:1px solid var(--rule);background:var(--paper);color:var(--ink);cursor:pointer}
148
+ .now-bout a.jump{font-family:var(--f-label);font-size:14px}
149
+ .now-bout .status,.now-confirm .status{font-family:var(--f-label);font-size:13.5px;color:var(--muted)}
150
+ .now-extra{display:grid;gap:8px}
151
+ .now-extra[hidden]{display:none}
152
+ .now-extra textarea{width:100%;min-height:60px;font:inherit;font-size:15px;padding:8px 10px;border:1px solid var(--rule);border-radius:4px;background:transparent;color:inherit;resize:vertical}
153
+ .now-extra .drop{border:2px dashed var(--rule);border-radius:4px;padding:10px;text-align:center;font-family:var(--f-label);font-size:14px;color:var(--muted);cursor:pointer}
154
+ .now-extra .drop.over{border-color:var(--blue);color:var(--blue);background:var(--blue-soft)}
155
+ .now-extra .thumbs{display:flex;flex-wrap:wrap;gap:6px} .now-extra .thumbs img{height:56px;border-radius:3px}
156
+ .now-sub{font-family:var(--f-label);font-weight:700;font-size:13.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin:4px 0 0}
157
+ .now-ep{border:1px solid var(--rule);padding:7px 10px;display:grid;gap:6px}
158
+ .now-ep-line{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
159
+ .now-ep-line a.path{font-family:var(--f-mono);font-size:13.5px;color:var(--ink);text-decoration:none;flex:1 1 18ch;min-width:0;overflow-wrap:break-word;word-break:break-word}
160
+ .now-ep .was{display:none;font-family:var(--f-label);font-size:12px;font-weight:700;text-transform:uppercase;color:var(--gold);background:var(--gold-soft);padding:0 6px;border-radius:3px}
161
+ .now-ep.stale .was{display:inline}
162
+ .now-ep-buttons{display:flex;gap:6px;margin-left:auto}
163
+ .now-ep-buttons button{font:inherit;font-family:var(--f-label);font-weight:700;font-size:14px;padding:3px 10px;border-radius:4px;border:1px solid var(--rule);background:var(--paper);cursor:pointer}
164
+ .now-ep-buttons .approve{color:var(--green)} .now-ep-buttons .improve-btn{color:var(--gold)}
165
+ .now-ep .status:empty{display:none}
166
+ button.now-queue{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;width:100%;text-align:left;font:inherit;font-family:var(--f-label);font-size:15.5px;padding:9px 12px;border:1px solid var(--rule);background:var(--paper);color:var(--ink);cursor:pointer}
167
+ button.now-queue b{font-family:var(--f-body);font-weight:600}
168
+ button.now-queue .muted{color:var(--muted);font-size:14px}
169
+ button.now-queue .go{margin-left:auto;color:var(--blue);font-weight:700}
170
+ .now-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px 14px;font-family:var(--f-label);font-size:15px;color:var(--now-muted)}
171
+ .now-actions[hidden]{display:none}
172
+ .now-actions button{font:inherit;font-family:var(--f-label);font-weight:700;font-size:16px;padding:7px 16px;border-radius:3px;cursor:pointer}
173
+ .now-accept{background:#c99a14;color:#16151c;border:0} .now-one{background:transparent;color:var(--now-ink);border:1px solid var(--now-muted)}
174
+ .now-status{display:grid;gap:4px;font-family:var(--f-label);font-size:15.5px;color:var(--now-muted)}
175
+ .now-status p{margin:0}
176
+ .now-status .now-ball b,.now-status .now-news b{color:var(--now-ink)}
177
+ section.now.idle .now-status{font-family:var(--f-body);font-size:19px;line-height:1.5;color:var(--now-ink)}
178
+ section.now.idle .now-status .now-news{font-size:16px;color:var(--now-muted)}
179
+ .now-confirm-list{list-style:none;margin:10px 0 14px;padding:0;display:grid;gap:6px}
180
+ .now-confirm-list label{display:grid;grid-template-columns:auto minmax(0,1fr);gap:10px;align-items:start;padding:8px 10px;border:1px solid var(--rule);cursor:pointer;font-size:15px;line-height:1.45}
181
+ .now-confirm-list input{margin-top:4px;width:16px;height:16px;accent-color:var(--blue)}
182
+ @media (max-width:900px){.now-counters{grid-template-columns:repeat(2,minmax(0,1fr))}}
183
+ @media (max-width:620px){
184
+ section.now{padding:20px 16px;margin-inline:-16px}
185
+ .now-wait{text-align:left}
186
+ .now-count b{font-size:34px}
187
+ .now-q .q-actions{position:sticky;bottom:0;z-index:2;background:var(--paper);padding:8px 0;margin:0 0 -4px;border-top:1px solid var(--rule)}
188
+ .now-ep-buttons{margin-left:0;flex-basis:100%}
189
+ .now-ep-line a.path{flex-basis:calc(100% - 80px)}
190
+ .now-opt{grid-template-columns:auto auto minmax(0,1fr)}
191
+ .now-opt em{grid-column:3;justify-self:start}
192
+ }
193
+ .now-bout-status{display:grid;gap:2px;padding-top:8px;border-top:1px solid var(--rule);font-family:var(--f-label);font-size:14.5px;color:var(--muted)}
194
+ .now-bout-status p{margin:0} .now-bout-status b{color:var(--ink)}
195
+ section.now.idle .now-bout-status{font-family:var(--f-body);font-size:16.5px;color:var(--ink)}
196
+ .now-status:empty{display:none}