@thebattlearena/corner 0.9.1-beta.70 → 0.10.1-beta.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -26
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/ring/agents/cornerman.md +32 -6
- package/ring/agents/rudo.md +2 -1
- package/ring/agents/wrestler.md +19 -4
- package/ring/hooks/hooks.json +42 -0
- package/ring/scripts/bout-stats-render.mjs +366 -0
- package/ring/scripts/bout-stats.mjs +360 -0
- package/ring/scripts/brief-build.mjs +130 -0
- package/ring/scripts/brief-guard.mjs +154 -0
- package/ring/scripts/brief.mjs +272 -0
- package/ring/scripts/compact-hook.mjs +110 -0
- package/ring/scripts/config.mjs +25 -1
- package/ring/scripts/contract-endpoints.mjs +163 -0
- package/ring/scripts/contract-preview.mjs +34 -14
- package/ring/scripts/fight.mjs +27 -0
- package/ring/scripts/findings.mjs +82 -3
- package/ring/scripts/format-gate.mjs +723 -0
- package/ring/scripts/format-worker.mjs +120 -0
- package/ring/scripts/front-routes.mjs +345 -0
- package/ring/scripts/judges-card.mjs +324 -3
- package/ring/scripts/lookout.mjs +22 -4
- package/ring/scripts/mask-guard.mjs +34 -1
- package/ring/scripts/page-embed.mjs +112 -0
- package/ring/scripts/play-by-play.mjs +155 -7
- package/ring/scripts/preparation-collect.mjs +177 -0
- package/ring/scripts/preparation.mjs +547 -0
- package/ring/scripts/round-bell.mjs +9 -2
- package/ring/scripts/rule-ids.mjs +124 -0
- package/ring/scripts/tech-plan-log.mjs +81 -0
- package/ring/scripts/tech-plan.mjs +191 -0
- package/ring/scripts/verdicts-log.mjs +75 -0
- package/ring/scripts/verdicts.mjs +205 -0
- package/ring/skills/play-by-play/SKILL.md +235 -33
- package/ring/skills/play-by-play/templates/blocks.css +196 -0
- package/ring/skills/play-by-play/templates/chronicle.html +358 -55
- package/ring/skills/play-by-play/templates/endpoints-view.js +1153 -0
- package/ring/skills/play-by-play/templates/interview-form.js +516 -0
- package/ring/skills/play-by-play/templates/now-panel.js +719 -0
- package/ring/skills/referee/SKILL.md +181 -56
- package/ring/skills/referee/references/api-diagram.md +56 -0
- package/ring/skills/referee/references/precedents.md +11 -0
- package/ring/skills/referee/references/rulebook.md +97 -6
- package/ring/skills/referee/templates/api-diagram.html +115 -0
- package/ring/skills/referee/templates/cornerman-batch.md +17 -11
- package/ring/skills/referee/templates/cornerman-concept.md +28 -30
- package/ring/skills/referee/templates/cornerman-range.md +16 -11
- package/ring/skills/referee/templates/escalation.md +7 -1
- package/ring/skills/referee/templates/round-bell-body.md +4 -0
- package/ring/skills/referee/templates/wrestler-brief.md +28 -41
- package/ring/skills/referee/templates/wrestler-cutoff.md +4 -0
- package/ring/skills/referee/templates/wrestler-resume.md +4 -0
- package/ring/skills/sparring/SKILL.md +13 -4
- package/ring/skills/sparring/templates/contrato.md +29 -0
- package/ring/skills/sparring/templates/rudo-brief.md +7 -0
- package/ring/skills/title-defense/SKILL.md +2 -2
- 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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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}
|