feat(store): reconcile globals.css with the approved design tokens #52

Merged
yuris merged 3 commits from feat/45-design-tokens into main 2026-08-27 09:25:59 +00:00
Owner

Closes #45.

What changed

Two commits: the token reconciliation that needed no decision, then the palette
and font swap that #40 gated.

apps/store/app/globals.css — tokens (74e7909)

  • --container 1180px -> 1280px.
  • Section rhythm moves off --s10 onto the design's --section-gap steps: --section-y: 44px, 36px in a new max-width: 1120px query, 28px in the existing 700px one. Name stays --section-y.
  • .list-cards deleted; .cards is now the one rule for both.

apps/store/app/globals.css — palette (a69b09e)

  • --accent #b4471f -> #2d543c. --accent-hover: #1d442d and --accent-soft: #e4f3e8 are the design's own oklch derivations of the new hue resolved to hex — oklch(from --accent calc(l - 0.06) c h) and oklch(from --accent 0.95 calc(c * 0.35) h). Hex, not oklch(from ...): relative colour syntax needs Chrome 119+, and on a miss the declaration is simply invalid, which for a button background on a static export is not a risk worth taking. I chose hex throughout, consistently — no oklch in the file.
  • --ok unchanged at #2d543c; --warn: #8a5a12 and --off: #8c3a32 added for #47's tri-state chips.
  • .stock-badge.is-out now uses --off. This was the correctness bug: it borrowed --accent, so a green accent renders "Habis" in the available colour.
  • --accent-on-dark: #a9c6b2 added, and .service-band .eyebrow moves onto it from a hardcoded rust #e08a5f — the only hex literal left outside :root, and a rust tint that could not survive the repaint.
  • .stock-badge.is-indent { color: var(--warn); } — added in round 2, see below.

Round 2: the is-indent guard (c534fbe)

A cross-PR interaction neither this PR nor #53 could see alone. #53 (#41/#42) introduces the indent availability state, and its badge routes through the same .stock-badge element. This PR repoints .stock-badge.is-out from --accent to --off. Compose them and "Indent" renders in the out-of-stock colour on an item you can order — the same class of bug the issue calls out for .is-out, arriving by composition rather than in one diff.

Demonstrated, not argued. On the built page at /products, Author41's class placed next to a real Habis badge:

label class measured colour
Habis .is-out rgb(140, 58, 50) --off
Indent .is-out rgb(140, 58, 50) byte-identical to Habis — the defect
Indent .is-indent rgb(138, 90, 18) --warn, visibly amber against the red-brown

Both render at 12.48px/700 with background-color: rgba(0, 0, 0, 0), so the new state is consistent with the rest of the sheet.

One colour declaration, deliberately, and no tint. .stock-badge in this sheet is text-only — "absence is the signal" — and .is-out carries no fill, so hue is already the sole separator between states. A tinted pill on is-indent alone would make it the only filled badge in the file and pre-empt #47, which owns the tinted-background-and-border tri-state chip treatment. This is the dispatcher's call, recorded here so the missing tint is not read as an omission: #52 supplies --ok/--warn/--off plus a single is-indent colour rule to prevent a live miscolour; the tinted chips are #47's scope. The badge block's comment now says the same thing in the file.

No .tsx here. availabilityBadgeClass is Author41's and already pushed on #53 at b1c6681 (.tsx only, globals.css untouched). This rule is inert until that lands, so either merge order is safe — each PR carries its own half.

apps/store/app/globals.css — typography (a69b09e)

  • h1-h4, .brand, .footer-brand, .price, .button, .cart-button, .cart-count, .qty-stepper button get --font-display; body gets --font-body.
  • .eyebrow, .product-type, .image-placeholder drop from font-weight: 900 to 700. IBM Plex Sans has no 900; the other 900s sit on Archivo and keep it.
  • h1 cap 2.3rem -> 2.5rem, the design's figure. line-height: 1.15 and font-variant-numeric: tabular-nums on .price untouched.
  • button, input, select, textarea { font-family: inherit }. See Deviations.

apps/store/app/layout.tsx — Inter -> Archivo + IBM_Plex_Sans via next/font/google, subsets: ["latin"], display: "swap", exposed as --font-display / --font-body. Both variable, so one file each covers 100-900 and 100-700. This is the only .tsx change in the PR.

The grid question, decided: 170px stays

Recorded as the issue requires. The design's minmax(200px, 1fr) is looser than what shipped, and bed5b85 narrowed 200px -> 170px after #33 landed — it is the newer deliberate call, not drift. Measured in the browser at 1440px on the built page: .product-grid resolves to six 195px cells. At 200px the same row holds five. Losing a sixth of a desktop row to match a mockup figure is a regression, so the design's cell is declined and the deviation is recorded here.

The other two table rows in the issue landed as drawn: --container 1280px, section rhythm 44/36/28. Radii keep --r-sm/md/lg per the settled instruction — --r1/r2/r3 is 8/12/16 under different names, and renaming touches every rule for zero visual gain.

The .service-band ghost-button check

The issue asked me to look for the mockup's .rail-card .btn--ghost defect (dark green on the dark band, ~1.3:1) before copying anything. Two findings:

  1. .button.secondary was already safe. .service-band .button.secondary overrides color to --surface, so it is white-on---dark at 15.62:1. Nothing copied, nothing to fix.
  2. The primary button had the defect, and the green created it. --accent measures 1.82:1 against --dark: #1b2620. Its white label stays legible (8.59:1) but the fill itself vanishes into the band, so the control reads as flat text and loses all hierarchy against the outlined secondary next to it. Rust did not do this; green does. Fixed by inverting on dark — --accent-on-dark fill, --ink label — which is the same move the design's .section--ink makes. Screenshot below.

--focus re-checked, kept

#1b4d8f vs the green measures 1.02:1 — near-identical lightness (oklch L 0.42 vs 0.41). Kept anyway, on three measurements: 101 degrees of hue separation (a blue/green pair, not a confusable one), 8.38:1 against white so the ring reads on the page, and outline-offset: 4px guarantees 4px of page background between ring and any accent fill, so they never touch. Verified visually — the ring reads as blue against a green-bordered tile.

.category-tile:hover vs the ready chip

Measured #e4f3e8 fill plus an --accent border. #47's ready chip will share that tint, but this is the design's own reuse: assets/tokens.css puts --accent-soft on both .cat-tile:hover and .tag--ready. They separate by scale and border, not tint, and a full-tile hover surface does not read as an inline pill. No change.

Verified

No workflow triggers on pull_request — both .forgejo/workflows/*.yml fire only on push to main plus workflow_dispatch, and both deploy. The forge will never check this PR. Every green light below is a command I ran locally. Round 1 was verified at a69b09e; the round-2 delta was re-verified at c534fbe — build 36/36 routes, biome check on the two changed files still 2 warnings (the pre-existing pair, unchanged count), badge colours measured on the rebuilt page as tabled above.

The build needs a catalog endpoint, so I served the 20 published items from the production management API as a local JSON fixture on :4321 (two flipped to inStock: false so the "Habis" path actually renders). Nothing in the repo was touched for it.

$ pnpm --filter garage-store build
✓ Compiled successfully in 4.9s
✓ Generating static pages using 13 workers (36/36) in 611ms
   36 routes, 20 /products/[slug] + 6 /services/[slug]

$ pnpm --filter garage-store test
# tests 17
# pass 17
# fail 0

$ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx
Checked 2 files in 13ms. No fixes applied.
Found 2 warnings.        exit 0

Those 2 warnings are the pre-existing .image-placeholder descending-specificity pair. biome check on the same file at origin/main reports the same 2 — my diff adds none. No repo-wide format or lint run; changed files only.

Fonts, checked against the built output, not the source:

$ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' apps/store/out/ | wc -l
0
$ du -cb apps/store/out/_next/static/media/*.p.*.woff2 | tail -1
75180   total

Zero references anywhere in out/. 75KB of preloaded latin woff2 for two families; Inter alone was 48KB, so two families cost +27KB. @font-face in the emitted CSS: Archivo 100 900 and IBM Plex Sans 100 700, both src: url(/_next/static/media/...).

Visual verification — built the export, served out/ on :4400, drove it with a real Chromium at 1440x1000 and 375x812 across /, /products and /products/bridgestone-ecopia-ep300-205-65r16.

Computed values off the live page at 1440px:

measured
--accent / --accent-hover / --accent-soft #2d543c / #1d442d / #e4f3e8
--warn / --off / --accent-on-dark #8a5a12 / #8c3a32 / #a9c6b2
--container / --section-y 1280px / 44px, .section padding 44/44
.product-grid 6 cols x 195.3px
h1 Archivo, 40px (= 2.5rem cap), line-height 46px (= 1.15)
body "IBM Plex Sans", "IBM Plex Sans Fallback", Arial
.price Archivo, tabular-nums
.stock-badge.is-out rgb(140, 58, 50) = #8c3a32, vs .product-type at rgb(45, 84, 60)
<link> to googleapis/gstatic in DOM 0

What I actually saw, per viewport:

1440px, / — green throughout: "Keranjang" cart pill, the "Cari" submit, every .eyebrow, every .product-type label. Six product cards per row. On the Bridgestone EP300 card, "Habis" sits under Rp 1.110.000 in a clear red-brown, unmistakably a different hue from the green BAN label two lines above it — this is the bug from the issue, and it renders correctly. h1 "Beli sparepart, bisa sekalian dipasang." breaks over two lines in Archivo at 40px; the retail scale holds, no cramping from the retained -0.02em tracking.

1440px, service band — "SEKALIAN SERVIS" eyebrow in light green (was rust), fully legible. "Lihat layanan" is now a light-green filled pill with a dark ink label and reads unambiguously as the primary; "Pesan barang khusus" is the white-outlined ghost beside it. Before the inversion the primary was a dark green fill on a near-black band and the two were nearly indistinguishable.

1440px, focus + hover — focusing .category-tile gives rgb(27, 77, 143) 3px at 4px offset: reads as blue against the tile, no confusion with the accent. Hovering the same tile gives rgb(228, 243, 232) with a green border — quiet, tile-scale, not chip-like.

1440px, /products — 6 columns, both "Habis" badges rgb(140, 58, 50), active "Semua" filter chip green with a white label, inactive chips white with muted text.

1440px, detail page — the user's fixes measured live: detail .price 30.4px vs .product-detail .product-card .price 18.4px with padding-top: 8px, so the price hierarchy and the related-card sizing are both intact; .product-gallery .image-placeholder measures 450x300, exactly 3:2 with the 300px cap engaged.

375px, / — --section-y steps to 28px. .photo-strip is two 163.5px columns with the third image on its own row: the user's tablet pairing holds down to mobile. .product-grid and .category-tiles two-up. h1 28.8px at the clamp floor, line-height 33.12px (1.15). Service band stacks, both buttons legible.

375px, /products and detail — filter chips and the search input both render "IBM Plex Sans" (they rendered Arial before, see Deviations). Detail page single column, price 30.4px Archivo, "Habis" red-brown directly beneath it, placeholder 343x229 = 3:2, sticky buy bar green.

The user's three committed fixes from 0d6a787 are all present and effective — nothing in this diff overrides any of them, and all three were measured on the rendered page above:

  • .product-detail .product-card .price at line 630 — 18.4px / padding-top: 8px / margin: 0, confirmed live.
  • .product-gallery .image-placeholder 3:2 + max-height: 300px at line 657 — rendered 450x300.
  • .photo-strip two columns in the 900px query — rendered as 163.5px x 2 at 375px.

Merging this deploys to production. The push to main triggers deploy-store.yml, which runs wrangler pages deploy. This is a live repaint of the storefront, not a staged change.

Deviations

  1. .product-grid keeps minmax(170px, 1fr); the design's 200px is declined. Measured: 6 cells per desktop row at 170px, 5 at 200px. Rationale above.
  2. Radii keep --r-sm/md/lg. Per the settled instruction — identical values, rename touches every rule and every future rebase for no visual gain.
  3. Section rhythm keeps the name --section-y, adopts the design's values. 44/36/28 match --section-gap exactly. --section-y describes symmetric block padding, which is what the token does; "gap" does not.
  4. Tints are hex, not oklch(). Stated as a choice the issue allows. The whole file is hex — no mixed convention.
  5. One extra rule not in the issue: button, input, select, textarea { font-family: inherit }. Form controls never inherit a font family from body, and origin/main's globals.css sets font-family in only three places (body and two inherits on .qty-stepper input / .cart-field). So .product-search and the .product-filter chips have rendered UA Arial since #33 — hidden while the body font was Inter, obvious the moment it became Plex. I judged a body-font swap that leaves the most prominent controls on /products in Arial to be an incomplete swap, and the design sets font: inherit on .btn, .chip and .search input for exactly this reason. Six lines, no cascade effect on the Archivo group (.qty-stepper button is more specific). Say the word and I will drop it.
  6. .service-band .button:not(.secondary) inverts on dark. Not requested as a change, but the issue asked me to check that band for the ghost-button defect, and the green accent creates one at 1.82:1. Reverting this leaves a primary CTA that reads as plain text.
  7. Round-1 nit declined (dispatcher decision). apps/store/app/services/page.tsx:27 still carries className="cards list-cards" after the .list-cards rule was deleted — a dead class token with no rendered effect. Not touched: #45's acceptance is explicitly "every existing class keeps working with no .tsx changes, same as #33", with layout.tsx for the fonts as the sole exception, and #48 rewrites services/page.tsx wholesale — editing it here creates an overlap for zero visual difference. Main is carrying it into #48's task as a required cleanup, so it is not lost.
  8. The build fixture is not in the repo. fetchCatalog needs a live GARAGE_API_URL; management is not running locally and my local key is not the production one (401). I served real production catalog data as static JSON on :4321. No repo file, no dependency, nothing committed.

No new dependencies. No lockfile change. No debug output. No formatter run. git show --stat on both commits touches only apps/store/app/globals.css and apps/store/app/layout.tsx.

Closes #45. ## What changed Two commits: the token reconciliation that needed no decision, then the palette and font swap that #40 gated. **`apps/store/app/globals.css` — tokens (74e7909)** - `--container` 1180px -> 1280px. - Section rhythm moves off `--s10` onto the design's `--section-gap` steps: `--section-y: 44px`, 36px in a new `max-width: 1120px` query, 28px in the existing 700px one. Name stays `--section-y`. - `.list-cards` deleted; `.cards` is now the one rule for both. **`apps/store/app/globals.css` — palette (a69b09e)** - `--accent` `#b4471f` -> `#2d543c`. `--accent-hover: #1d442d` and `--accent-soft: #e4f3e8` are the design's own oklch derivations of the new hue resolved to hex — `oklch(from --accent calc(l - 0.06) c h)` and `oklch(from --accent 0.95 calc(c * 0.35) h)`. Hex, not `oklch(from ...)`: relative colour syntax needs Chrome 119+, and on a miss the declaration is simply invalid, which for a button background on a static export is not a risk worth taking. I chose hex throughout, consistently — no oklch in the file. - `--ok` unchanged at `#2d543c`; `--warn: #8a5a12` and `--off: #8c3a32` added for #47's tri-state chips. - **`.stock-badge.is-out` now uses `--off`.** This was the correctness bug: it borrowed `--accent`, so a green accent renders "Habis" in the available colour. - `--accent-on-dark: #a9c6b2` added, and `.service-band .eyebrow` moves onto it from a hardcoded rust `#e08a5f` — the only hex literal left outside `:root`, and a rust tint that could not survive the repaint. - `.stock-badge.is-indent { color: var(--warn); }` — added in round 2, see below. ### Round 2: the `is-indent` guard (c534fbe) A cross-PR interaction neither this PR nor #53 could see alone. #53 (#41/#42) introduces the `indent` availability state, and its badge routes through the same `.stock-badge` element. This PR repoints `.stock-badge.is-out` from `--accent` to `--off`. Compose them and "Indent" renders in the out-of-stock colour on an item you *can* order — the same class of bug the issue calls out for `.is-out`, arriving by composition rather than in one diff. Demonstrated, not argued. On the built page at `/products`, Author41's class placed next to a real Habis badge: | label | class | measured colour | | |---|---|---|---| | Habis | `.is-out` | `rgb(140, 58, 50)` | `--off` | | Indent | `.is-out` | `rgb(140, 58, 50)` | **byte-identical to Habis — the defect** | | Indent | `.is-indent` | `rgb(138, 90, 18)` | `--warn`, visibly amber against the red-brown | Both render at 12.48px/700 with `background-color: rgba(0, 0, 0, 0)`, so the new state is consistent with the rest of the sheet. **One colour declaration, deliberately, and no tint.** `.stock-badge` in this sheet is text-only — "absence is the signal" — and `.is-out` carries no fill, so hue is already the sole separator between states. A tinted pill on `is-indent` alone would make it the only filled badge in the file and pre-empt #47, which owns the tinted-background-and-border tri-state chip treatment. This is the dispatcher's call, recorded here so the missing tint is not read as an omission: **#52 supplies `--ok`/`--warn`/`--off` plus a single `is-indent` colour rule to prevent a live miscolour; the tinted chips are #47's scope.** The badge block's comment now says the same thing in the file. **No `.tsx` here.** `availabilityBadgeClass` is Author41's and already pushed on #53 at `b1c6681` (`.tsx` only, `globals.css` untouched). This rule is inert until that lands, so **either merge order is safe** — each PR carries its own half. **`apps/store/app/globals.css` — typography (a69b09e)** - `h1-h4`, `.brand`, `.footer-brand`, `.price`, `.button`, `.cart-button`, `.cart-count`, `.qty-stepper button` get `--font-display`; `body` gets `--font-body`. - `.eyebrow`, `.product-type`, `.image-placeholder` drop from `font-weight: 900` to 700. IBM Plex Sans has no 900; the other 900s sit on Archivo and keep it. - `h1` cap 2.3rem -> 2.5rem, the design's figure. `line-height: 1.15` and `font-variant-numeric: tabular-nums` on `.price` untouched. - `button, input, select, textarea { font-family: inherit }`. See Deviations. **`apps/store/app/layout.tsx`** — `Inter` -> `Archivo` + `IBM_Plex_Sans` via `next/font/google`, `subsets: ["latin"]`, `display: "swap"`, exposed as `--font-display` / `--font-body`. Both variable, so one file each covers 100-900 and 100-700. This is the only `.tsx` change in the PR. ### The grid question, decided: **170px stays** Recorded as the issue requires. The design's `minmax(200px, 1fr)` is looser than what shipped, and `bed5b85` narrowed 200px -> 170px *after* #33 landed — it is the newer deliberate call, not drift. Measured in the browser at 1440px on the built page: `.product-grid` resolves to **six 195px cells**. At 200px the same row holds five. Losing a sixth of a desktop row to match a mockup figure is a regression, so the design's cell is declined and the deviation is recorded here. The other two table rows in the issue landed as drawn: `--container` 1280px, section rhythm 44/36/28. Radii keep `--r-sm/md/lg` per the settled instruction — `--r1/r2/r3` is 8/12/16 under different names, and renaming touches every rule for zero visual gain. ### The `.service-band` ghost-button check The issue asked me to look for the mockup's `.rail-card .btn--ghost` defect (dark green on the dark band, ~1.3:1) before copying anything. Two findings: 1. **`.button.secondary` was already safe.** `.service-band .button.secondary` overrides `color` to `--surface`, so it is white-on-`--dark` at 15.62:1. Nothing copied, nothing to fix. 2. **The *primary* button had the defect, and the green created it.** `--accent` measures **1.82:1** against `--dark: #1b2620`. Its white label stays legible (8.59:1) but the fill itself vanishes into the band, so the control reads as flat text and loses all hierarchy against the outlined secondary next to it. Rust did not do this; green does. Fixed by inverting on dark — `--accent-on-dark` fill, `--ink` label — which is the same move the design's `.section--ink` makes. Screenshot below. ### `--focus` re-checked, kept `#1b4d8f` vs the green measures **1.02:1** — near-identical lightness (oklch L 0.42 vs 0.41). Kept anyway, on three measurements: 101 degrees of hue separation (a blue/green pair, not a confusable one), 8.38:1 against white so the ring reads on the page, and `outline-offset: 4px` guarantees 4px of page background between ring and any accent fill, so they never touch. Verified visually — the ring reads as blue against a green-bordered tile. ### `.category-tile:hover` vs the ready chip Measured `#e4f3e8` fill plus an `--accent` border. #47's ready chip will share that tint, but this is the design's own reuse: `assets/tokens.css` puts `--accent-soft` on both `.cat-tile:hover` and `.tag--ready`. They separate by scale and border, not tint, and a full-tile hover surface does not read as an inline pill. No change. ## Verified No workflow triggers on `pull_request` — both `.forgejo/workflows/*.yml` fire only on `push` to `main` plus `workflow_dispatch`, and both *deploy*. **The forge will never check this PR.** Every green light below is a command I ran locally. Round 1 was verified at `a69b09e`; the round-2 delta was re-verified at `c534fbe` — build 36/36 routes, `biome check` on the two changed files still 2 warnings (the pre-existing pair, unchanged count), badge colours measured on the rebuilt page as tabled above. The build needs a catalog endpoint, so I served the 20 published items from the production management API as a local JSON fixture on `:4321` (two flipped to `inStock: false` so the "Habis" path actually renders). Nothing in the repo was touched for it. ``` $ pnpm --filter garage-store build ✓ Compiled successfully in 4.9s ✓ Generating static pages using 13 workers (36/36) in 611ms 36 routes, 20 /products/[slug] + 6 /services/[slug] $ pnpm --filter garage-store test # tests 17 # pass 17 # fail 0 $ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx Checked 2 files in 13ms. No fixes applied. Found 2 warnings. exit 0 ``` Those 2 warnings are the pre-existing `.image-placeholder` descending-specificity pair. `biome check` on the same file at `origin/main` reports the same 2 — my diff adds none. No repo-wide format or lint run; changed files only. **Fonts, checked against the built output, not the source:** ``` $ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' apps/store/out/ | wc -l 0 $ du -cb apps/store/out/_next/static/media/*.p.*.woff2 | tail -1 75180 total ``` Zero references anywhere in `out/`. 75KB of preloaded latin woff2 for two families; Inter alone was 48KB, so two families cost +27KB. `@font-face` in the emitted CSS: `Archivo 100 900` and `IBM Plex Sans 100 700`, both `src: url(/_next/static/media/...)`. **Visual verification** — built the export, served `out/` on `:4400`, drove it with a real Chromium at 1440x1000 and 375x812 across `/`, `/products` and `/products/bridgestone-ecopia-ep300-205-65r16`. Computed values off the live page at 1440px: | | measured | |---|---| | `--accent` / `--accent-hover` / `--accent-soft` | `#2d543c` / `#1d442d` / `#e4f3e8` | | `--warn` / `--off` / `--accent-on-dark` | `#8a5a12` / `#8c3a32` / `#a9c6b2` | | `--container` / `--section-y` | `1280px` / `44px`, `.section` padding 44/44 | | `.product-grid` | 6 cols x 195.3px | | `h1` | Archivo, 40px (= 2.5rem cap), line-height 46px (= 1.15) | | `body` | `"IBM Plex Sans", "IBM Plex Sans Fallback", Arial` | | `.price` | Archivo, `tabular-nums` | | `.stock-badge.is-out` | `rgb(140, 58, 50)` = `#8c3a32`, vs `.product-type` at `rgb(45, 84, 60)` | | `<link>` to googleapis/gstatic in DOM | 0 | What I actually saw, per viewport: **1440px, `/`** — green throughout: "Keranjang" cart pill, the "Cari" submit, every `.eyebrow`, every `.product-type` label. Six product cards per row. On the Bridgestone EP300 card, "Habis" sits under `Rp 1.110.000` in a clear red-brown, unmistakably a different hue from the green `BAN` label two lines above it — this is the bug from the issue, and it renders correctly. h1 "Beli sparepart, bisa sekalian dipasang." breaks over two lines in Archivo at 40px; the retail scale holds, no cramping from the retained `-0.02em` tracking. **1440px, service band** — "SEKALIAN SERVIS" eyebrow in light green (was rust), fully legible. "Lihat layanan" is now a light-green filled pill with a dark ink label and reads unambiguously as the primary; "Pesan barang khusus" is the white-outlined ghost beside it. Before the inversion the primary was a dark green fill on a near-black band and the two were nearly indistinguishable. **1440px, focus + hover** — focusing `.category-tile` gives `rgb(27, 77, 143)` 3px at 4px offset: reads as blue against the tile, no confusion with the accent. Hovering the same tile gives `rgb(228, 243, 232)` with a green border — quiet, tile-scale, not chip-like. **1440px, `/products`** — 6 columns, both "Habis" badges `rgb(140, 58, 50)`, active "Semua" filter chip green with a white label, inactive chips white with muted text. **1440px, detail page** — the user's fixes measured live: detail `.price` 30.4px vs `.product-detail .product-card .price` 18.4px with `padding-top: 8px`, so the price hierarchy and the related-card sizing are both intact; `.product-gallery .image-placeholder` measures **450x300**, exactly 3:2 with the 300px cap engaged. **375px, `/`** — `--section-y` steps to 28px. `.photo-strip` is two 163.5px columns with the third image on its own row: the user's tablet pairing holds down to mobile. `.product-grid` and `.category-tiles` two-up. h1 28.8px at the clamp floor, line-height 33.12px (1.15). Service band stacks, both buttons legible. **375px, `/products` and detail** — filter chips and the search input both render `"IBM Plex Sans"` (they rendered Arial before, see Deviations). Detail page single column, price 30.4px Archivo, "Habis" red-brown directly beneath it, placeholder 343x229 = 3:2, sticky buy bar green. **The user's three committed fixes from `0d6a787` are all present and effective** — nothing in this diff overrides any of them, and all three were measured on the rendered page above: - `.product-detail .product-card .price` at line 630 — 18.4px / `padding-top: 8px` / `margin: 0`, confirmed live. - `.product-gallery .image-placeholder` 3:2 + `max-height: 300px` at line 657 — rendered 450x300. - `.photo-strip` two columns in the 900px query — rendered as 163.5px x 2 at 375px. **Merging this deploys to production.** The push to `main` triggers `deploy-store.yml`, which runs `wrangler pages deploy`. This is a live repaint of the storefront, not a staged change. ## Deviations 1. **`.product-grid` keeps `minmax(170px, 1fr)`; the design's 200px is declined.** Measured: 6 cells per desktop row at 170px, 5 at 200px. Rationale above. 2. **Radii keep `--r-sm/md/lg`.** Per the settled instruction — identical values, rename touches every rule and every future rebase for no visual gain. 3. **Section rhythm keeps the name `--section-y`, adopts the design's values.** 44/36/28 match `--section-gap` exactly. `--section-y` describes symmetric block padding, which is what the token does; "gap" does not. 4. **Tints are hex, not `oklch()`.** Stated as a choice the issue allows. The whole file is hex — no mixed convention. 5. **One extra rule not in the issue: `button, input, select, textarea { font-family: inherit }`.** Form controls never inherit a font family from `body`, and `origin/main`'s `globals.css` sets `font-family` in only three places (`body` and two `inherit`s on `.qty-stepper input` / `.cart-field`). So `.product-search` and the `.product-filter` chips have rendered UA Arial since #33 — hidden while the body font was Inter, obvious the moment it became Plex. I judged a body-font swap that leaves the most prominent controls on `/products` in Arial to be an incomplete swap, and the design sets `font: inherit` on `.btn`, `.chip` and `.search input` for exactly this reason. Six lines, no cascade effect on the Archivo group (`.qty-stepper button` is more specific). Say the word and I will drop it. 6. **`.service-band .button:not(.secondary)` inverts on dark.** Not requested as a change, but the issue asked me to check that band for the ghost-button defect, and the green accent creates one at 1.82:1. Reverting this leaves a primary CTA that reads as plain text. 7. **Round-1 nit declined (dispatcher decision).** `apps/store/app/services/page.tsx:27` still carries `className="cards list-cards"` after the `.list-cards` rule was deleted — a dead class token with no rendered effect. Not touched: #45's acceptance is explicitly "every existing class keeps working with no `.tsx` changes, same as #33", with `layout.tsx` for the fonts as the sole exception, and #48 rewrites `services/page.tsx` wholesale — editing it here creates an overlap for zero visual difference. Main is carrying it into #48's task as a required cleanup, so it is not lost. 8. **The build fixture is not in the repo.** `fetchCatalog` needs a live `GARAGE_API_URL`; management is not running locally and my local key is not the production one (401). I served real production catalog data as static JSON on `:4321`. No repo file, no dependency, nothing committed. No new dependencies. No lockfile change. No debug output. No formatter run. `git show --stat` on both commits touches only `apps/store/app/globals.css` and `apps/store/app/layout.tsx`.
Takes the three measurable differences from assets/tokens.css that are not
decisions:

- --container 1180px -> 1280px.
- Section rhythm moves off --s10 to the design's --section-gap steps: 44px,
  36px under 1120px, 28px under 700px. Name stays --section-y; it describes
  symmetric block padding better than "gap" and renaming touches every rule
  for no visual change.
- .cards and .list-cards collapse to one rule. The 280px cell resolved to the
  same column count as 260px on desktop and only cost a column at tablet
  widths, so the override was a denser-grid regression with no upside.

Deliberately unchanged: .product-grid keeps minmax(170px, 1fr). The design's
200px is looser than what shipped, and at the new 1280px container it drops a
desktop row from six cells to five. Radii keep --r-sm/md/lg; --r1/r2/r3 is the
same 8/12/16 under different names.
#40.2 and #40.3, answered.

Palette. --accent goes rust #b4471f -> the design green #2d543c. The tints are
re-derived from the new hue, not carried across: --accent-hover #1d442d is the
design's oklch(from --accent calc(l - 0.06) c h) and --accent-soft #e4f3e8 its
oklch(from --accent 0.95 calc(c * 0.35) h), both resolved to hex because a
static export should not stake a button's background on Chrome 119+ relative
colour syntax. --ok keeps #2d543c; --warn and --off join it so #47 has all
three availability hues.

.stock-badge.is-out moves from --accent to --off. It borrowed the accent, so a
green accent rendered "Habis" in the available colour. Measured #8c3a32,
7.59:1 on white.

The dark band needed two fixes the green exposed. Its .eyebrow was a hardcoded
rust #e08a5f, the one hex literal left outside :root, and the primary button's
green fill is 1.82:1 against --dark, which flattens it into text and erases the
hierarchy against the outlined secondary. Both now use --accent-on-dark
#a9c6b2: 8.48:1 on the band, 9.08:1 for the ink label on the button.

--focus stays #1b4d8f. Re-checked as the issue asks: 1.02:1 against the green,
but 101 degrees of hue from it, 8.38:1 on white, and outline-offset keeps 4px
of page background between ring and fill, so they never abut.

Typography. Archivo for display, IBM Plex Sans for body, replacing Inter, both
through next/font so the export self-hosts them and nothing reaches
fonts.googleapis.com. Both are variable, so two files cover 100-900 and
100-700. Plex tops out at 700, so .eyebrow, .product-type and
.image-placeholder come down from 900 rather than claiming a weight the font
cannot cut. h1 takes the design's 2.5rem cap; Archivo sets narrower than Inter
and 2.3rem read short of the retail scale.

Form controls get font-family: inherit. They never inherited it from body, so
.product-search and the .product-filter chips have been rendering UA Arial
since #33 — invisible next to Inter, obvious next to Plex. The design sets
font: inherit on every control for the same reason.
yuris left a comment

VERDICT: approve-with-nits

Reviewed by rebuilding a69b09e in a throwaway detached worktree and driving the
built export in Chromium at 1440px and 375px. Catalog fixture: I pulled the live
production inventory through the management MCP, mapped it through the same
projection apps/management/lib/catalog.ts uses (published + slug only, sellPrice > 0
else null, inStock = stock > 0), flipped bridgestone-ecopia-ep300-205-65r16 and
oli-shell-helix-hx7-5w-40-4l to inStock: false, and served it on :4521.
Nothing committed, nothing in the repo.

Findings

apps/store/app/services/page.tsx:L27: nit: 'className="cards list-cards"' still carries 'list-cards' after the rule was deleted, so the markup names a class no stylesheet defines. Either drop the token here or leave it for #48, which restructures /services anyway — but it should not be discovered by the next person grepping for a rule that no longer exists.

No blockers. No should-level findings.

The correctness bug — confirmed independently

.stock-badge.is-out renders rgb(140, 58, 50) = #8c3a32 = --off, measured off
the live page in four places: the /products grid (both out-of-stock cards), the
/ featured grid, and the PDP at 1440px and 375px. The in-stock PDP badge renders
rgb(45, 84, 60) = --ok, so both states resolve to their intended token.

Checked for overrides rather than trusting the declaration: globals.css contains
exactly two .stock-badge rules — the base at L495 (--ok) and .is-out at L501
(--off), the more specific one last. No media-query variant, no !important
elsewhere, and the computed value is the final proof. On the same card, .product-type
reads rgb(45, 84, 60) two lines above the badge, so "Habis" is unmistakably not the
available colour.

The user's three fixes from 0d6a787 — all three effective on the rendered page

  • Detail price hierarchy: .product-detail .price computes 30.4px, .product-detail .product-card .price computes 18.4px with padding-top: 8px and margin: 0. Both branches of the fix survive.
  • Gallery cap: .product-gallery .image-placeholder renders 450x300 with aspect-ratio: 3 / 2 and max-height: 300px engaged (300 = the cap, not the natural 3:2 height at that width).
  • Photo strip: two columns at 900px (418px x 2), 700px (326px x 2) and 375px (163.5px x 2), third image on its own row. Verified by measuring the children, not just the template.

Nothing in this diff overrides any of them.

Fonts

grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' out/ → 0. Zero
<link> to either host in the live DOM. Emitted @font-face blocks declare
Archivo 100 900 and IBM Plex Sans 100 700, both src: url(/_next/static/media/…)
— self-hosted and genuinely variable, so the single-file-per-family claim holds.
Preloaded latin woff2 total 75180 bytes, matching your figure to the byte.

Retail scale survived the metric change: h1 computes Archivo 40px / 46px at 1440px
(2.5rem cap, 1.15) and 28.8px / 33.12px at 375px. .price computes Archivo with
font-variant-numeric: tabular-nums on the card, the PDP and the cart total.
body computes "IBM Plex Sans", "IBM Plex Sans Fallback", Arial.

I also checked the weight story you did not spell out: six font-weight: 900
declarations remain, and every one of them lands on an element in the Archivo group
(.brand, .footer-grid h2, .footer-brand, .cart-count, .availability-note h2,
.qty-stepper button). The three you demoted to 700 are exactly the three that render
under Plex — and 700 is closer to the design, which puts .eyebrow on --font-body
at 600. Internally consistent.

The three flagged calls

1. Grid — 170px stays. Accepted. Verified 6 cells x 195.3px at 1440px and 5 at
1120px. Your history claim checks out: 9bc810c shipped minmax(200px, 1fr) and
bed5b85 narrowed it to 170px afterwards, so 170 is the newer value, not drift from
the design. The issue permits either number if recorded, the record is accurate, and
the argument stands on its own regardless of history — losing a sixth of a desktop
row on a parts catalogue to match a mockup figure is a real regression. Declining the
design's looser cell is the right call.

2. --accent-on-dark — not scope creep. I measured every ratio you quoted and
they all reproduce exactly: --accent vs --dark 1.82:1, white label on accent
8.59:1, .button.secondary white on --dark 15.62:1, --focus vs --accent
1.02:1, --focus vs white 8.38:1. The band's primary really did flatten into the
background. What settles it for me is that the design's own lighten is not a fix:
oklch(from #2d543c calc(l + 0.08) c h) resolves to #436b52, which measures
2.58:1 on --dark — still invisible. A new value was required. #a9c6b2 measures
8.48:1 on the band with a 9.08:1 ink label, and the rendered result reads
unambiguously as the primary next to the outlined secondary. The issue told you to
check that band; fixing the defect you found there is in scope.

3. button, input, select, textarea { font-family: inherit } — keep it. I checked
the premise instead of taking it: origin/main's globals.css sets font-family in
exactly three places (body and two inherits on .qty-stepper input / .cart-field),
so .product-search and the .product-filter chips genuinely fell back to the UA
font. After the rule they compute "IBM Plex Sans"; the cart's qty inputs and the
<textarea> do too, while every .button, .cart-button and .qty-stepper button
still computes Archivo — the class selectors win, no cascade damage. A body-font swap
that leaves the most prominent control on /products in Arial is an incomplete swap.
Six lines is the right price.

Tokens, rhythm, layout

  • --warn: #8a5a12 and --off: #8c3a32 both present and computed off the live root, so #47 is unblocked. --off reads 6.96:1 on --bg, --warn 5.91:1 on white.
  • Your hex resolutions are the design's formulas, checked against Chrome's own relative-colour engine rather than by eye: oklch(from #2d543c calc(l - 0.06) c h) paints #1d442d, exactly your --accent-hover. oklch(from #2d543c 0.95 calc(c * 0.35) h) paints #e4f3e9 against your #e4f3e8 — one unit of blue, i.e. rounding. Choosing hex over oklch(from …) on a static export is the right trade.
  • Every hex literal in the file now lives inside :root; the rust #e08a5f was the last one out and no rust value survives anywhere on any route (swept /, /products, a PDP, /services, /cart, /contact, /cara-pesan for the three old rust RGBs — zero hits).
  • Section rhythm steps 44 / 36 / 28 at 1440 / 1120 / 700, matching the design's --section-gap including the 28px mobile step. The new max-width: 1120px block sits before the 700px block, so the mobile value wins where both match — verified, not assumed.
  • --container computes 1280px. --s10 still has four consumers, so moving --section-y off it left nothing dead.
  • .list-cards fold: /services renders 4 columns at 1440px and 3 at 900px, where the old 280px cell gave 2. Denser, one rule, no consumer broken — the only .list-cards reference left anywhere is the dead class token above.
  • --focus kept: the ring computes rgb(27, 77, 143) 3px at 4px offset and reads as blue against a green-bordered tile. .category-tile:hover computes #e4f3e8 with a --accent border — tile-scale, not pill-like.

Evidence audit

Re-ran your two verifiable claims in my own tree: pnpm --filter garage-store test
→ # tests 17 / # pass 17 / # fail 0. pnpm exec biome check on the two changed
files → Found 2 warnings, exit 0, and both are the pre-existing
noDescendingSpecificity pair on .image-placeholder / .cart-line .image-placeholder,
neither of which your diff introduces. Build: 40 static routes, compiled clean.

Every number in your PR body that I could independently measure came back identical.
That is the standard this repo needs, given no CI ever looks at a pull request here
and merging deploys the storefront.

VERDICT: approve-with-nits Reviewed by rebuilding `a69b09e` in a throwaway detached worktree and driving the built export in Chromium at 1440px and 375px. Catalog fixture: I pulled the live production inventory through the management MCP, mapped it through the same projection `apps/management/lib/catalog.ts` uses (published + slug only, `sellPrice > 0` else null, `inStock = stock > 0`), flipped `bridgestone-ecopia-ep300-205-65r16` and `oli-shell-helix-hx7-5w-40-4l` to `inStock: false`, and served it on `:4521`. Nothing committed, nothing in the repo. ## Findings `apps/store/app/services/page.tsx:L27: nit: 'className="cards list-cards"' still carries 'list-cards' after the rule was deleted, so the markup names a class no stylesheet defines. Either drop the token here or leave it for #48, which restructures /services anyway — but it should not be discovered by the next person grepping for a rule that no longer exists.` No blockers. No `should`-level findings. ## The correctness bug — confirmed independently `.stock-badge.is-out` renders `rgb(140, 58, 50)` = `#8c3a32` = `--off`, measured off the live page in four places: the `/products` grid (both out-of-stock cards), the `/` featured grid, and the PDP at 1440px and 375px. The in-stock PDP badge renders `rgb(45, 84, 60)` = `--ok`, so both states resolve to their intended token. Checked for overrides rather than trusting the declaration: `globals.css` contains exactly two `.stock-badge` rules — the base at L495 (`--ok`) and `.is-out` at L501 (`--off`), the more specific one last. No media-query variant, no `!important` elsewhere, and the computed value is the final proof. On the same card, `.product-type` reads `rgb(45, 84, 60)` two lines above the badge, so "Habis" is unmistakably not the available colour. ## The user's three fixes from `0d6a787` — all three effective on the rendered page - Detail price hierarchy: `.product-detail .price` computes 30.4px, `.product-detail .product-card .price` computes 18.4px with `padding-top: 8px` and `margin: 0`. Both branches of the fix survive. - Gallery cap: `.product-gallery .image-placeholder` renders **450x300** with `aspect-ratio: 3 / 2` and `max-height: 300px` engaged (300 = the cap, not the natural 3:2 height at that width). - Photo strip: two columns at 900px (418px x 2), 700px (326px x 2) and 375px (163.5px x 2), third image on its own row. Verified by measuring the children, not just the template. Nothing in this diff overrides any of them. ## Fonts `grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' out/` → **0**. Zero `<link>` to either host in the live DOM. Emitted `@font-face` blocks declare `Archivo 100 900` and `IBM Plex Sans 100 700`, both `src: url(/_next/static/media/…)` — self-hosted and genuinely variable, so the single-file-per-family claim holds. Preloaded latin woff2 total 75180 bytes, matching your figure to the byte. Retail scale survived the metric change: `h1` computes Archivo 40px / 46px at 1440px (2.5rem cap, 1.15) and 28.8px / 33.12px at 375px. `.price` computes Archivo with `font-variant-numeric: tabular-nums` on the card, the PDP and the cart total. `body` computes `"IBM Plex Sans", "IBM Plex Sans Fallback", Arial`. I also checked the weight story you did not spell out: six `font-weight: 900` declarations remain, and every one of them lands on an element in the Archivo group (`.brand`, `.footer-grid h2`, `.footer-brand`, `.cart-count`, `.availability-note h2`, `.qty-stepper button`). The three you demoted to 700 are exactly the three that render under Plex — and 700 is closer to the design, which puts `.eyebrow` on `--font-body` at 600. Internally consistent. ## The three flagged calls **1. Grid — 170px stays. Accepted.** Verified 6 cells x 195.3px at 1440px and 5 at 1120px. Your history claim checks out: `9bc810c` shipped `minmax(200px, 1fr)` and `bed5b85` narrowed it to 170px afterwards, so 170 is the newer value, not drift from the design. The issue permits either number if recorded, the record is accurate, and the argument stands on its own regardless of history — losing a sixth of a desktop row on a parts catalogue to match a mockup figure is a real regression. Declining the design's looser cell is the right call. **2. `--accent-on-dark` — not scope creep.** I measured every ratio you quoted and they all reproduce exactly: `--accent` vs `--dark` **1.82:1**, white label on accent 8.59:1, `.button.secondary` white on `--dark` **15.62:1**, `--focus` vs `--accent` 1.02:1, `--focus` vs white 8.38:1. The band's primary really did flatten into the background. What settles it for me is that the design's own lighten is not a fix: `oklch(from #2d543c calc(l + 0.08) c h)` resolves to `#436b52`, which measures **2.58:1** on `--dark` — still invisible. A new value was required. `#a9c6b2` measures 8.48:1 on the band with a 9.08:1 ink label, and the rendered result reads unambiguously as the primary next to the outlined secondary. The issue told you to check that band; fixing the defect you found there is in scope. **3. `button, input, select, textarea { font-family: inherit }` — keep it.** I checked the premise instead of taking it: `origin/main`'s `globals.css` sets `font-family` in exactly three places (`body` and two `inherit`s on `.qty-stepper input` / `.cart-field`), so `.product-search` and the `.product-filter` chips genuinely fell back to the UA font. After the rule they compute `"IBM Plex Sans"`; the cart's qty inputs and the `<textarea>` do too, while every `.button`, `.cart-button` and `.qty-stepper button` still computes Archivo — the class selectors win, no cascade damage. A body-font swap that leaves the most prominent control on `/products` in Arial is an incomplete swap. Six lines is the right price. ## Tokens, rhythm, layout - `--warn: #8a5a12` and `--off: #8c3a32` both present and computed off the live root, so #47 is unblocked. `--off` reads 6.96:1 on `--bg`, `--warn` 5.91:1 on white. - Your hex resolutions are the design's formulas, checked against Chrome's own relative-colour engine rather than by eye: `oklch(from #2d543c calc(l - 0.06) c h)` paints **`#1d442d`**, exactly your `--accent-hover`. `oklch(from #2d543c 0.95 calc(c * 0.35) h)` paints `#e4f3e9` against your `#e4f3e8` — one unit of blue, i.e. rounding. Choosing hex over `oklch(from …)` on a static export is the right trade. - Every hex literal in the file now lives inside `:root`; the rust `#e08a5f` was the last one out and no rust value survives anywhere on any route (swept `/`, `/products`, a PDP, `/services`, `/cart`, `/contact`, `/cara-pesan` for the three old rust RGBs — zero hits). - Section rhythm steps 44 / 36 / 28 at 1440 / 1120 / 700, matching the design's `--section-gap` including the 28px mobile step. The new `max-width: 1120px` block sits before the 700px block, so the mobile value wins where both match — verified, not assumed. - `--container` computes 1280px. `--s10` still has four consumers, so moving `--section-y` off it left nothing dead. - `.list-cards` fold: `/services` renders 4 columns at 1440px and **3** at 900px, where the old 280px cell gave 2. Denser, one rule, no consumer broken — the only `.list-cards` reference left anywhere is the dead class token above. - `--focus` kept: the ring computes `rgb(27, 77, 143)` 3px at 4px offset and reads as blue against a green-bordered tile. `.category-tile:hover` computes `#e4f3e8` with a `--accent` border — tile-scale, not pill-like. ## Evidence audit Re-ran your two verifiable claims in my own tree: `pnpm --filter garage-store test` → `# tests 17 / # pass 17 / # fail 0`. `pnpm exec biome check` on the two changed files → `Found 2 warnings`, exit 0, and both are the pre-existing `noDescendingSpecificity` pair on `.image-placeholder` / `.cart-line .image-placeholder`, neither of which your diff introduces. Build: 40 static routes, compiled clean. Every number in your PR body that I could independently measure came back identical. That is the standard this repo needs, given no CI ever looks at a pull request here and merging deploys the storefront.
Cross-PR guard for #53, which introduces the `indent` availability state. Its
badge routes through the same `.stock-badge` element, and this PR repoints
`.stock-badge.is-out` from `--accent` to `--off` — so composing the two renders
"Indent" in the out-of-stock colour on an orderable item. Measured on the built
page at /products by putting Author41's class next to a real Habis badge:

  Habis     .is-out     rgb(140, 58, 50)  = --off
  Indent    .is-out     rgb(140, 58, 50)  identical, the defect
  Indent    .is-indent  rgb(138, 90, 18)  = --warn

Same class of bug the issue calls out for `.is-out`, arriving by composition
rather than in one diff.

One colour declaration and nothing else. This badge carries no fill, so hue is
already the sole separator between states; a tinted pill on `is-indent` alone
would make it the only filled badge in the sheet and pre-empt #47, which owns
the tinted-background-and-border chip treatment. The badge block's comment now
says so.

The rule is inert until #53 ships `availabilityBadgeClass`, so either merge
order is safe.
Author
Owner

Round-2 verification, re-run at c534fbe — the exact commit up for merge

Not "tests passed earlier". Every command below was run against c534fbe after the push.

$ pnpm --filter garage-store build
✓ Compiled successfully in 13.8s
✓ Generating static pages using 13 workers (36/36) in 620ms

$ pnpm --filter garage-store test
# tests 17
# pass 17
# fail 0

$ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx
Checked 2 files in 14ms. No fixes applied.
Found 2 warnings.        exit 0

$ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' apps/store/out/ | wc -l
0

The 2 warnings are the pre-existing .image-placeholder descending-specificity pair; biome check on origin/main reports the same 2. No repo-wide format or lint run.

Diff scope — all three commits, nothing else touched:

74e7909  refactor(store): reconcile the token layer with the approved design
         apps/store/app/globals.css | 21 +++++++------
a69b09e  feat(store): repaint the store green and move to Archivo + IBM Plex Sans
         apps/store/app/globals.css | 74 ++++++++++++++++++++-------
         apps/store/app/layout.tsx  | 18 ++++++---
c534fbe  fix(store): colour the indent badge with --warn, not the habis red
         apps/store/app/globals.css |  9 +++--

Rendered measurements at c534fbe

Rebuilt the export, served out/, drove a real Chromium at both viewports again:

1440 / 375 /products 1440 detail
--accent #2d543c #2d543c #2d543c
.stock-badge.is-out rgb(140, 58, 50) rgb(140, 58, 50) rgb(140, 58, 50)
.stock-badge.is-indent rgb(138, 90, 18) rgb(138, 90, 18) rgb(138, 90, 18)
h1 40px / 46px / Archivo 28.8px / 33.12px / Archivo 40px / 46px / Archivo
body font "IBM Plex Sans" "IBM Plex Sans" "IBM Plex Sans"
.product-grid 6 x 195.3px 2 x 165.5px 3 x 214.2px (narrow column)
--section-y 44px 28px 44px
<link> to googleapis/gstatic 0 0 0

The user's three fixes from 0d6a787, measured on the rendered page at this commit:

  • detail .price 30.4px vs .product-detail .product-card .price 18.4px — price hierarchy and related-card sizing both live
  • .product-gallery .image-placeholder 450x300, max-height: 300px engaged — exactly 3:2, no 800px empty square
  • .photo-strip 2 x 163.5px at 375px — the tablet pairing holds down to mobile

Merging this PR deploys to production. No workflow triggers on pull_request; deploy-store.yml fires on push to main and runs wrangler pages deploy. There are no PR checks on this repo and there never were — every number above is a local measurement, not CI.

## Round-2 verification, re-run at `c534fbe` — the exact commit up for merge Not "tests passed earlier". Every command below was run against `c534fbe` after the push. ``` $ pnpm --filter garage-store build ✓ Compiled successfully in 13.8s ✓ Generating static pages using 13 workers (36/36) in 620ms $ pnpm --filter garage-store test # tests 17 # pass 17 # fail 0 $ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx Checked 2 files in 14ms. No fixes applied. Found 2 warnings. exit 0 $ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' apps/store/out/ | wc -l 0 ``` The 2 warnings are the pre-existing `.image-placeholder` descending-specificity pair; `biome check` on `origin/main` reports the same 2. No repo-wide format or lint run. Diff scope — all three commits, nothing else touched: ``` 74e7909 refactor(store): reconcile the token layer with the approved design apps/store/app/globals.css | 21 +++++++------ a69b09e feat(store): repaint the store green and move to Archivo + IBM Plex Sans apps/store/app/globals.css | 74 ++++++++++++++++++++------- apps/store/app/layout.tsx | 18 ++++++--- c534fbe fix(store): colour the indent badge with --warn, not the habis red apps/store/app/globals.css | 9 +++-- ``` ### Rendered measurements at `c534fbe` Rebuilt the export, served `out/`, drove a real Chromium at both viewports again: | | 1440 `/` | 375 `/products` | 1440 detail | |---|---|---|---| | `--accent` | `#2d543c` | `#2d543c` | `#2d543c` | | `.stock-badge.is-out` | `rgb(140, 58, 50)` | `rgb(140, 58, 50)` | `rgb(140, 58, 50)` | | `.stock-badge.is-indent` | `rgb(138, 90, 18)` | `rgb(138, 90, 18)` | `rgb(138, 90, 18)` | | `h1` | 40px / 46px / Archivo | 28.8px / 33.12px / Archivo | 40px / 46px / Archivo | | `body` font | `"IBM Plex Sans"` | `"IBM Plex Sans"` | `"IBM Plex Sans"` | | `.product-grid` | 6 x 195.3px | 2 x 165.5px | 3 x 214.2px (narrow column) | | `--section-y` | 44px | 28px | 44px | | `<link>` to googleapis/gstatic | 0 | 0 | 0 | **The user's three fixes from `0d6a787`, measured on the rendered page at this commit:** - detail `.price` **30.4px** vs `.product-detail .product-card .price` **18.4px** — price hierarchy and related-card sizing both live - `.product-gallery .image-placeholder` **450x300**, `max-height: 300px` engaged — exactly 3:2, no 800px empty square - `.photo-strip` **2 x 163.5px** at 375px — the tablet pairing holds down to mobile **Merging this PR deploys to production.** No workflow triggers on `pull_request`; `deploy-store.yml` fires on push to `main` and runs `wrangler pages deploy`. There are no PR checks on this repo and there never were — every number above is a local measurement, not CI.
yuris left a comment

VERDICT: approve

Round 2, reviewing only a69b09e..c534fbe (7 insertions / 2 deletions, apps/store/app/globals.css). Round 1's findings are not re-litigated and the declined nit is not re-raised.

No findings

0 blockers, 0 shoulds, 0 nits.

How I measured it

Rebuilt c534fbe in a throwaway detached worktree. The build needs a catalog endpoint, so I re-derived the fixture from live production inventory through the management MCP, mapped it with the exact projection apps/management/lib/catalog.ts uses (published + non-null slug, category?.trim() || "Lainnya", price = sellPrice > 0 ? sellPrice : null, inStock = stock > 0), flipped bridgestone-ecopia-ep300-205-65r16 and oli-shell-helix-hx7-5w-40-4l to inStock: false, and served it on :4521. Built export served on :4522, driven with a real Chromium at 1440x1000. Nothing committed, nothing in the repo, nothing in the main checkout.

is-indent does not exist in this branch's .tsx, so I did what you did: constructed the three class strings availabilityBadgeClass returns at b1c6681 and injected them as real DOM nodes into a live product card next to the genuine Habis badge, then read getComputedStyle. Not a simulated stylesheet — the shipped CSS chunk resolving against real elements in real cascade context.

The three colours — all three reproduce exactly

label class measured
Habis .is-out rgb(140, 58, 50) --off #8c3a32
Indent .is-out rgb(140, 58, 50) byte-identical to Habis — the defect is real
Indent .is-indent rgb(138, 90, 18) --warn #8a5a12
Ready .stock-badge (span) rgb(45, 84, 60) --ok

--warn computes #8a5a12 off the live root and rgb(138, 90, 18) is its exact paint. All four badges render 12.48px / 700 with background-color: rgba(0, 0, 0, 0), border-width: 0px, padding: 0px — the new state is dimensionally identical to the existing ones, so it introduces no fill and no box.

The defect is not hypothetical: I rendered Indent through .is-out and it is the same three bytes as Habis. Composing #53's original approach with this PR's --off repoint would have shipped an orderable item wearing the out-of-stock colour to production. Splitting the fix across both PRs was the right call.

Visually distinguishable. Screenshotted the card with Habis, Indent and Indent (via is-out) stacked. Habis and the defective Indent are indistinguishable; the fixed Indent reads as clear amber against the red-brown. The red channel barely moves (140 vs 138) — the separation is entirely the hue shift, green 58 to 90 and blue 50 to 18. Amber against red-brown is a legible pair at 12.48px/700, and --warn measures 5.91:1 on white per round 1.

I also checked the tint decision on its merits rather than on the argument: .is-out paints no background, so adding one to .is-indent alone would have made it the only filled badge in the file. Colour-only is internally consistent with the sheet. It is recorded honestly in the PR body as the dispatcher's call, and the in-file comment now says the same thing ("The tinted chip treatment is #47's"), so a later reader cannot mistake the missing tint for an omission.

Inert on this branch — proven, not assumed

is-indent occurs exactly once in the entire built export, and that one occurrence is the CSS rule itself in _next/static/chunks/1nzub7-7qa9ou.css. Zero occurrences in any emitted HTML or JS across all 36 routes, against 100 occurrences of is-out in HTML. No element on any route can match the new selector, so merging #52 before #53 produces zero rendered change versus a69b09e — the rest of the delta is a comment.

Confirmed the other half genuinely does not collide: b1c6681 does not touch globals.css at all (not in its 23-file changed list) and defines is-indent in no CSS anywhere. This rule is the only definition. Either merge order is safe.

Round 1's correctness fix survives the third rule

.stock-badge.is-out still paints rgb(140, 58, 50) = --off on the live page, unchanged.

Round 1's invariant was "two .stock-badge rules, the more specific one last". I re-checked it against the emitted CSS rather than the source, by byte offset:

10653  .stock-badge{color:var(--ok);...}          specificity (0,1,0)
10736  .stock-badge.is-out{color:var(--off)}      specificity (0,2,0)
10773  .stock-badge.is-indent{color:var(--warn)}  specificity (0,2,0)

Exactly three .stock-badge rules in the file, base first, both modifiers after. Brace depth at all three offsets is 0, so none sits inside an @media or @supports — which also means the badge colour cannot vary by viewport, so re-measuring at 375px would prove nothing the rule enumeration does not already prove. No !important touches the badge; the sheet's one !important is the pre-existing .product-type, a different element, and it computes rgb(45, 84, 60) two lines above the badge on the same card.

The two modifiers tie at (0,2,0) with .is-indent last, so if both classes ever landed on one element is-indent would win. availabilityBadgeClass returns exactly one of them, never both, so the tie is unreachable. More usefully, both modifiers at (0,2,0) beat .product-card p at (0,1,1) — I verified .is-indent on a <p> inside a real product card, which is precisely how #53 uses it, and it computes rgb(138, 90, 18), not the muted card-paragraph colour. The new rule is robust in the context that will consume it.

One thing I probed and cleared: a bare stock-badge on a <p> inside .product-card computes rgb(90, 97, 89) = --ink-muted, because .product-card p (0,1,1) outranks the base rule (0,1,0). Unreachable in practice — #53's card returns null for ready and never emits the bare class there, and the PDP uses a <span>, where I measured the base rule winning correctly at rgb(45, 84, 60). Pre-existing, untouched by this delta, not a finding against this PR; noting it only so nobody rediscovers it as a surprise while integrating #53.

Evidence audit at this exact commit

Your round-2 numbers are the only verification that exists on this repo, so I re-ran them rather than reading them:

  • pnpm --filter garage-store build — compiled clean, 36 routes, 24 /products/[slug] + 6 /services/[slug]. Matches.
  • pnpm --filter garage-store test — # tests 17 / # pass 17 / # fail 0. Matches.
  • pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx — Found 2 warnings, exit 0, both the pre-existing noDescendingSpecificity pair on .image-placeholder. Count unchanged, which is worth stating explicitly given the added rule is itself a specificity neighbour of the two it sits beside.

Nothing in the delta is a .tsx change, so #45's "no .tsx changes" acceptance is still intact.

Good discipline on this one. You found a bug neither PR could see from inside itself, demonstrated it by rendering the broken composition rather than describing it, and split the fix so each half is safe alone in either merge order. Merging deploys the storefront, and this delta is the difference between a correct repaint and shipping an orderable part in the sold-out colour.

VERDICT: approve Round 2, reviewing only `a69b09e..c534fbe` (7 insertions / 2 deletions, `apps/store/app/globals.css`). Round 1's findings are not re-litigated and the declined nit is not re-raised. ## No findings 0 blockers, 0 `should`s, 0 nits. ## How I measured it Rebuilt `c534fbe` in a throwaway detached worktree. The build needs a catalog endpoint, so I re-derived the fixture from live production inventory through the management MCP, mapped it with the exact projection `apps/management/lib/catalog.ts` uses (published + non-null slug, `category?.trim() || "Lainnya"`, `price = sellPrice > 0 ? sellPrice : null`, `inStock = stock > 0`), flipped `bridgestone-ecopia-ep300-205-65r16` and `oli-shell-helix-hx7-5w-40-4l` to `inStock: false`, and served it on `:4521`. Built export served on `:4522`, driven with a real Chromium at 1440x1000. Nothing committed, nothing in the repo, nothing in the main checkout. `is-indent` does not exist in this branch's `.tsx`, so I did what you did: constructed the three class strings `availabilityBadgeClass` returns at `b1c6681` and injected them as real DOM nodes into a live product card next to the genuine `Habis` badge, then read `getComputedStyle`. Not a simulated stylesheet — the shipped CSS chunk resolving against real elements in real cascade context. ## The three colours — all three reproduce exactly | label | class | measured | | |---|---|---|---| | `Habis` | `.is-out` | `rgb(140, 58, 50)` | `--off` `#8c3a32` | | `Indent` | `.is-out` | `rgb(140, 58, 50)` | **byte-identical to Habis — the defect is real** | | `Indent` | `.is-indent` | `rgb(138, 90, 18)` | `--warn` `#8a5a12` | | `Ready` | `.stock-badge` (span) | `rgb(45, 84, 60)` | `--ok` | `--warn` computes `#8a5a12` off the live root and `rgb(138, 90, 18)` is its exact paint. All four badges render 12.48px / 700 with `background-color: rgba(0, 0, 0, 0)`, `border-width: 0px`, `padding: 0px` — the new state is dimensionally identical to the existing ones, so it introduces no fill and no box. The defect is not hypothetical: I rendered `Indent` through `.is-out` and it is the same three bytes as `Habis`. Composing #53's original approach with this PR's `--off` repoint would have shipped an orderable item wearing the out-of-stock colour to production. Splitting the fix across both PRs was the right call. **Visually distinguishable.** Screenshotted the card with `Habis`, `Indent` and `Indent (via is-out)` stacked. `Habis` and the defective `Indent` are indistinguishable; the fixed `Indent` reads as clear amber against the red-brown. The red channel barely moves (140 vs 138) — the separation is entirely the hue shift, green 58 to 90 and blue 50 to 18. Amber against red-brown is a legible pair at 12.48px/700, and `--warn` measures 5.91:1 on white per round 1. I also checked the tint decision on its merits rather than on the argument: `.is-out` paints no background, so adding one to `.is-indent` alone would have made it the only filled badge in the file. Colour-only is internally consistent with the sheet. It is recorded honestly in the PR body as the dispatcher's call, and the in-file comment now says the same thing ("The tinted chip treatment is #47's"), so a later reader cannot mistake the missing tint for an omission. ## Inert on this branch — proven, not assumed `is-indent` occurs **exactly once in the entire built export**, and that one occurrence is the CSS rule itself in `_next/static/chunks/1nzub7-7qa9ou.css`. Zero occurrences in any emitted HTML or JS across all 36 routes, against 100 occurrences of `is-out` in HTML. No element on any route can match the new selector, so merging #52 before #53 produces zero rendered change versus `a69b09e` — the rest of the delta is a comment. Confirmed the other half genuinely does not collide: `b1c6681` does not touch `globals.css` at all (not in its 23-file changed list) and defines `is-indent` in no CSS anywhere. This rule is the only definition. Either merge order is safe. ## Round 1's correctness fix survives the third rule `.stock-badge.is-out` still paints `rgb(140, 58, 50)` = `--off` on the live page, unchanged. Round 1's invariant was "two `.stock-badge` rules, the more specific one last". I re-checked it against the *emitted* CSS rather than the source, by byte offset: ``` 10653 .stock-badge{color:var(--ok);...} specificity (0,1,0) 10736 .stock-badge.is-out{color:var(--off)} specificity (0,2,0) 10773 .stock-badge.is-indent{color:var(--warn)} specificity (0,2,0) ``` Exactly three `.stock-badge` rules in the file, base first, both modifiers after. Brace depth at all three offsets is 0, so none sits inside an `@media` or `@supports` — which also means the badge colour cannot vary by viewport, so re-measuring at 375px would prove nothing the rule enumeration does not already prove. No `!important` touches the badge; the sheet's one `!important` is the pre-existing `.product-type`, a different element, and it computes `rgb(45, 84, 60)` two lines above the badge on the same card. The two modifiers tie at (0,2,0) with `.is-indent` last, so if both classes ever landed on one element `is-indent` would win. `availabilityBadgeClass` returns exactly one of them, never both, so the tie is unreachable. More usefully, both modifiers at (0,2,0) beat `.product-card p` at (0,1,1) — I verified `.is-indent` on a `<p>` inside a real product card, which is precisely how #53 uses it, and it computes `rgb(138, 90, 18)`, not the muted card-paragraph colour. The new rule is robust in the context that will consume it. One thing I probed and cleared: a bare `stock-badge` on a `<p>` inside `.product-card` computes `rgb(90, 97, 89)` = `--ink-muted`, because `.product-card p` (0,1,1) outranks the base rule (0,1,0). Unreachable in practice — #53's card returns `null` for `ready` and never emits the bare class there, and the PDP uses a `<span>`, where I measured the base rule winning correctly at `rgb(45, 84, 60)`. Pre-existing, untouched by this delta, not a finding against this PR; noting it only so nobody rediscovers it as a surprise while integrating #53. ## Evidence audit at this exact commit Your round-2 numbers are the only verification that exists on this repo, so I re-ran them rather than reading them: - `pnpm --filter garage-store build` — compiled clean, **36 routes**, 24 `/products/[slug]` + 6 `/services/[slug]`. Matches. - `pnpm --filter garage-store test` — `# tests 17 / # pass 17 / # fail 0`. Matches. - `pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx` — `Found 2 warnings`, exit 0, both the pre-existing `noDescendingSpecificity` pair on `.image-placeholder`. Count unchanged, which is worth stating explicitly given the added rule is itself a specificity neighbour of the two it sits beside. Nothing in the delta is a `.tsx` change, so #45's "no `.tsx` changes" acceptance is still intact. Good discipline on this one. You found a bug neither PR could see from inside itself, demonstrated it by rendering the broken composition rather than describing it, and split the fix so each half is safe alone in either merge order. Merging deploys the storefront, and this delta is the difference between a correct repaint and shipping an orderable part in the sold-out colour.
Author
Owner

Pre-merge verification against the moved base (22cfddd)

main advanced to 22cfddd when #51 landed, so the tree this squash actually produces is no longer the tree round 2 was reviewed on. Verified the combined result rather than assuming it.

Method, so this is auditable: rebased c534fbe onto origin/main on a throwaway local branch (39ae526), verified there, then deleted it. Nothing was force-pushed — the PR head stays c534fbe, so review id=11's approve is not voided. The merge below is the reviewed commit.

Zero file overlap with #51, confirmed by set intersection:

#51 touched:  apps/store/app/products/[slug]/page.tsx
              apps/store/app/products/page.tsx
              apps/store/lib/cart.test.ts
              apps/store/lib/cart.ts
              apps/store/package.json
              apps/store/postcss.config.mjs
              pnpm-lock.yaml
this PR:      apps/store/app/globals.css
              apps/store/app/layout.tsx
intersection: (empty)

One real interaction, and it is fine

#51 removed Tailwind and deleted apps/store/postcss.config.mjs, so globals.css no longer passes through the @tailwindcss/postcss plugin — it is now processed by Next natively. That is a build-pipeline change under a file this PR rewrites, which is exactly the kind of thing that deserves a check rather than a shrug. Checked: every token emits intact.

$ grep -oE '\-\-(accent|accent-hover|accent-soft|accent-on-dark|ok|warn|off|focus|container|section-y):[^;]*' out/_next/static/**/*.css
--accent:#2d543c        --accent-hover:#1d442d   --accent-soft:#e4f3e8
--accent-on-dark:#a9c6b2 --ok:#2d543c            --warn:#8a5a12
--off:#8c3a32           --focus:#1b4d8f          --container:1280px
--section-y:44px  ...  --section-y:36px (1120px)  ...  --section-y:28px (700px)

One emitted stylesheet, and the minifier even folds the 900px query's three selectors into .product-grid, .category-tiles, .photo-strip — same declarations, no loss.

Commands, run on the rebased tree

$ pnpm install --frozen-lockfile
Lockfile is up to date, resolution step is skipped
Already up to date

$ pnpm --filter garage-store build
✓ Compiled successfully in 6.3s
✓ Generating static pages using 13 workers (36/36) in 618ms

$ pnpm --filter garage-store test
# tests 56
# pass 56
# fail 0

$ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx
Checked 2 files in 13ms. No fixes applied.
Found 2 warnings.        exit 0

$ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' out/ | wc -l
0

56 tests, up from 17 — #51's cart.test.ts joined the suite and passes alongside this change. The 2 biome warnings are still the pre-existing .image-placeholder pair.

Rendered, on the merged tree

1440 / 1440 /products 1440 detail 375 detail
.stock-badge.is-out rgb(140, 58, 50) rgb(140, 58, 50) rgb(140, 58, 50) rgb(140, 58, 50)
.stock-badge.is-indent rgb(138, 90, 18) rgb(138, 90, 18) rgb(138, 90, 18) rgb(138, 90, 18)
--accent #2d543c #2d543c #2d543c #2d543c
--section-y 44px 44px 44px 28px
h1 40 / 46px Archivo 40 / 46px Archivo 40 / 46px Archivo 28.8 / 33.12px Archivo
body font "IBM Plex Sans" "IBM Plex Sans" "IBM Plex Sans" "IBM Plex Sans"
.product-grid 6 x 195.3px 6 x 195.3px 3 x 214.2px 2 x 165.5px
googleapis <link> 0 0 0 0
elements with no resolved font-family 0 0 0 0
  • .service-band .button:not(.secondary) → rgb(169, 198, 178) fill with rgb(23, 32, 24) label. The inversion holds; no 1.82:1 fill on the dark band.
  • .product-filter → "IBM Plex Sans", not UA Arial.
  • The user's three fixes from 0d6a787, re-measured on the merged tree: detail .price 30.4px vs related-card .price 18.4px; .product-gallery .image-placeholder 450x300 at 1440 and 343x229 at 375, both exactly 3:2 with the cap engaged; .photo-strip two columns in the 900px query. All three intact, none overridden.

Gate

  • Newest verdict: review id=11, approve at 09:16:43Z, post-dating c534fbe. 0 blockers, 0 shoulds, 0 nits.
  • mergeable re-fetched immediately before the merge call.
  • Squash with head_commit_id compare-and-swap on c534fbe so nothing unverified can slip in. No force_merge, no merge_when_checks_succeed.
  • Merging this deploys the storefront to production. The push to main triggers deploy-store.yml → wrangler pages deploy. There are no pull_request checks on this repo; every number above is a local measurement.
## Pre-merge verification against the moved base (`22cfddd`) `main` advanced to `22cfddd` when #51 landed, so the tree this squash actually produces is no longer the tree round 2 was reviewed on. Verified the *combined* result rather than assuming it. **Method, so this is auditable:** rebased `c534fbe` onto `origin/main` on a throwaway local branch (`39ae526`), verified there, then deleted it. **Nothing was force-pushed** — the PR head stays `c534fbe`, so review id=11's `approve` is not voided. The merge below is the reviewed commit. Zero file overlap with #51, confirmed by set intersection: ``` #51 touched: apps/store/app/products/[slug]/page.tsx apps/store/app/products/page.tsx apps/store/lib/cart.test.ts apps/store/lib/cart.ts apps/store/package.json apps/store/postcss.config.mjs pnpm-lock.yaml this PR: apps/store/app/globals.css apps/store/app/layout.tsx intersection: (empty) ``` ### One real interaction, and it is fine #51 **removed Tailwind and deleted `apps/store/postcss.config.mjs`**, so `globals.css` no longer passes through the `@tailwindcss/postcss` plugin — it is now processed by Next natively. That is a build-pipeline change under a file this PR rewrites, which is exactly the kind of thing that deserves a check rather than a shrug. Checked: every token emits intact. ``` $ grep -oE '\-\-(accent|accent-hover|accent-soft|accent-on-dark|ok|warn|off|focus|container|section-y):[^;]*' out/_next/static/**/*.css --accent:#2d543c --accent-hover:#1d442d --accent-soft:#e4f3e8 --accent-on-dark:#a9c6b2 --ok:#2d543c --warn:#8a5a12 --off:#8c3a32 --focus:#1b4d8f --container:1280px --section-y:44px ... --section-y:36px (1120px) ... --section-y:28px (700px) ``` One emitted stylesheet, and the minifier even folds the 900px query's three selectors into `.product-grid, .category-tiles, .photo-strip` — same declarations, no loss. ### Commands, run on the rebased tree ``` $ pnpm install --frozen-lockfile Lockfile is up to date, resolution step is skipped Already up to date $ pnpm --filter garage-store build ✓ Compiled successfully in 6.3s ✓ Generating static pages using 13 workers (36/36) in 618ms $ pnpm --filter garage-store test # tests 56 # pass 56 # fail 0 $ pnpm exec biome check apps/store/app/globals.css apps/store/app/layout.tsx Checked 2 files in 13ms. No fixes applied. Found 2 warnings. exit 0 $ grep -rIoh 'fonts\.googleapis\.com\|fonts\.gstatic\.com' out/ | wc -l 0 ``` 56 tests, up from 17 — #51's `cart.test.ts` joined the suite and passes alongside this change. The 2 biome warnings are still the pre-existing `.image-placeholder` pair. ### Rendered, on the merged tree | | 1440 `/` | 1440 `/products` | 1440 detail | 375 detail | |---|---|---|---|---| | `.stock-badge.is-out` | `rgb(140, 58, 50)` | `rgb(140, 58, 50)` | `rgb(140, 58, 50)` | `rgb(140, 58, 50)` | | `.stock-badge.is-indent` | `rgb(138, 90, 18)` | `rgb(138, 90, 18)` | `rgb(138, 90, 18)` | `rgb(138, 90, 18)` | | `--accent` | `#2d543c` | `#2d543c` | `#2d543c` | `#2d543c` | | `--section-y` | 44px | 44px | 44px | 28px | | `h1` | 40 / 46px Archivo | 40 / 46px Archivo | 40 / 46px Archivo | 28.8 / 33.12px Archivo | | `body` font | `"IBM Plex Sans"` | `"IBM Plex Sans"` | `"IBM Plex Sans"` | `"IBM Plex Sans"` | | `.product-grid` | 6 x 195.3px | 6 x 195.3px | 3 x 214.2px | 2 x 165.5px | | googleapis `<link>` | 0 | 0 | 0 | 0 | | elements with no resolved font-family | 0 | 0 | 0 | 0 | - `.service-band .button:not(.secondary)` → `rgb(169, 198, 178)` fill with `rgb(23, 32, 24)` label. The inversion holds; no 1.82:1 fill on the dark band. - `.product-filter` → `"IBM Plex Sans"`, not UA Arial. - **The user's three fixes from `0d6a787`, re-measured on the merged tree:** detail `.price` **30.4px** vs related-card `.price` **18.4px**; `.product-gallery .image-placeholder` **450x300** at 1440 and **343x229** at 375, both exactly 3:2 with the cap engaged; `.photo-strip` two columns in the 900px query. All three intact, none overridden. ### Gate - Newest verdict: review **id=11, `approve`** at `09:16:43Z`, post-dating `c534fbe`. 0 blockers, 0 shoulds, 0 nits. - `mergeable` re-fetched immediately before the merge call. - Squash with `head_commit_id` compare-and-swap on `c534fbe` so nothing unverified can slip in. No `force_merge`, no `merge_when_checks_succeed`. - **Merging this deploys the storefront to production.** The push to `main` triggers `deploy-store.yml` → `wrangler pages deploy`. There are no `pull_request` checks on this repo; every number above is a local measurement.
yuris merged commit a594020ede into main 2026-08-27 09:25:59 +00:00
yuris deleted branch feat/45-design-tokens 2026-08-27 09:25:59 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/garage!52
No description provided.