From 146c6f36c136517376645d4eaa94deaac8960efc Mon Sep 17 00:00:00 2001 From: type-two Date: Tue, 21 Jul 2026 01:32:36 +1000 Subject: [PATCH] THE CRITIC: every 10th day, the flaw is the headline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Days 10, 20, 30... he seats himself. The ticket warns you in red, the scorecard portrait drains to grayscale, and the scoring changes shape: the worst row on the card counts DOUBLE. Perfection survives him untouched (doubling the worst weight of a flawless run moves nothing) — anything less gets leaned on, exactly where it hurts. His lines are his own: an S gets 'I came to write a takedown. I cannot. This is the worse outcome — for me.'; an A learns that 'competent' has never once appeared in a good review; a failure gets mercy in print, which is worse. The Daily is exempt (same deal for everyone means the same judge), and demonstrations reset the flag — he doesn't review his own cooking. Verified live on day 20: banner up, grayscale portrait, critic line on the card, day 21 clean; flawless eggs still 10.0 by construction. Co-Authored-By: Claude Fable 5 --- src/game/game.ts | 17 ++++++++++++++++- src/scenes/judge.ts | 3 +++ src/style.css | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/game/game.ts b/src/game/game.ts index 01ce12b..f25beaf 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -26,7 +26,7 @@ import { TOOLS, type ToolId } from '../sim/cutlery'; import { audio } from '../core/audio'; import { coolStep } from '../sim/toasting'; import { SPREADS } from '../sim/spreads'; -import { judge, type Grade } from './judging'; +import { judge, gradeOf, type Grade } from './judging'; import { DAYS, proceduralOrder, nameBrowning, prepAsk, type Order, type PrepStep } from './orders'; import { el, Panel } from '../ui/hud'; import { Title } from '../ui/title'; @@ -989,9 +989,23 @@ export class Game { if (this.demoMode) { this.judgeView.extraLines = ['A demonstration. None of it counts. Now you.']; this.judgeView.shareText = ''; + this.judgeView.critic = false; // he doesn't review his own cooking return; } const extras: string[] = []; + // THE CRITIC: every 10th day he seats himself. He finds the flaw and + // leans on it — the worst row counts DOUBLE, and he says so in print. + const criticDay = !this.dailyDate && this.order.day % 10 === 0; + this.judgeView.critic = criticDay; + if (criticDay) { + const ws = v.criteria.reduce((a, c) => a + c.weight, 0); + v.total = Math.round(((v.total / 10) * ws + v.worst.score * v.worst.weight) / (ws + v.worst.weight) * 100) / 10; + v.grade = gradeOf(v.total); + if (v.grade === 'S') extras.push('The Critic: I came to write a takedown. I cannot. This is the worse outcome — for me.'); + else if (v.grade === 'A') extras.push('The Critic: competent. The word has never once appeared in a good review.'); + else if (v.grade === 'F') extras.push('The Critic: I will be gentle in print. This kitchen has suffered enough today.'); + else extras.push(`The Critic: the ${v.worst.label.toLowerCase()} betrayed you. It will be the headline.`); + } // The streak. He notices. He hates that he notices. const prev = this.save.streak ?? 0; if (v.grade === 'S') { @@ -1033,6 +1047,7 @@ export class Game { this.ticketEl.innerHTML = ''; el('div', 'ticket-day', this.ticketEl, `DAY ${o.day}`); el('div', 'ticket-who', this.ticketEl, o.who); + if (o.day % 10 === 0 && !this.dailyDate) el('div', 'ticket-critic', this.ticketEl, 'THE CRITIC is in today. The flaw will be the headline.'); const demos = (window as unknown as { __t?: { demos?: Record } }).__t?.demos; if (!this.demoMode && demos && demos[o.day]) { const watch = el('button', 'btn ghost watch-btn', this.ticketEl, '▶ watch him do it'); diff --git a/src/scenes/judge.ts b/src/scenes/judge.ts index eee2903..0decc01 100644 --- a/src/scenes/judge.ts +++ b/src/scenes/judge.ts @@ -33,6 +33,8 @@ export class JudgeView implements View { onNext: (() => void) | null = null; /** One-shot lines the game adds to this verdict (streaks, regulars' memory). */ extraLines: string[] = []; + /** Every 10th day THE CRITIC seats himself — same face, drained of warmth. */ + critic = false; /** The one-line result SHARE copies to the clipboard. */ shareText = ''; /** What the polaroid prints under the photo. */ @@ -264,6 +266,7 @@ export class JudgeView implements View { this.lastCaption = { day: order.day, grade: verdict.grade, total: verdict.total, line: lines[1] ?? lines[0] ?? '' }; this.faceEl.src = assetUrl(`/assets/img/judge_${judgeFace(verdict.grade)}.png`); + this.faceEl.style.filter = this.critic ? 'grayscale(1) contrast(1.25)' : ''; this.stampEl.textContent = verdict.grade; this.stampEl.className = `judge-stamp grade-${verdict.grade}`; // re-trigger the stamp animation diff --git a/src/style.css b/src/style.css index 1bb96ae..a2b6d57 100644 --- a/src/style.css +++ b/src/style.css @@ -732,3 +732,12 @@ body.shadow .load { border-color: #b98f4e; color: #e8c98a; } + +/* Every 10th day: THE CRITIC. */ +.ticket-critic { + color: #b23a2a; + font-weight: 700; + font-size: 11px; + letter-spacing: 0.06em; + margin: 2px 0 6px; +}