
Generated from the spec on 2026-10-31. Source: Sito IDEALMAC.pdf (6 full-page screenshots at 1920px) -> rebuild/ -> sources/2026-10-23-rebuilt. Highlighted steps are decided; the others are still pending.
| # | Topic | Decision | Reason | ||||
|---|---|---|---|---|---|---|---|
| D1 | Source of truth | The design system is derived from the PDF alone; no live site is captured. The six JPEGs are a *brief*, not a capture: they answer colour and 1920px geometry and nothing else | The user has no site to capture for this version | ||||
| D2 | Pipeline entry | The PDF is reconstructed as static HTML in `sources/<date>-rebuilt/`, which is then captured with `capture.mjs` and becomes the project's real capture. `inventory.py`, `detect_layout.py` and `validate.py` run on that capture as usual | Keeps the deterministic half of the system: a raster cannot be measured, authored HTML can. Every value the rebuild invents is a decision here, so the spec never silently inherits a guess | ||||
| D3 | Measured vs. authored | Colours and 1920px band geometry are taken from the pixels and are exact. Type scale, spacing scale, breakpoints, hover/focus states and motion do not exist in the source and are authored — each with its own D-number | Separates fact from proposal; the user approves only the proposals | ||||
| D4 | Perimeter | v0.1.0 covers the six pages of the PDF, Italian only: Home, Presse per gallette, Azienda, Macchinari accessori, Servizi, Fiere ed eventi | The PDF's contents | ||||
| D5 | Navigation | The header keeps all seven menu entries, Contatti included, although no Contatti page exists in this version. The link is inert in the preview and listed under open issues in `DESIGN.md` | The header is one site-wide pattern and must match the material; inventing a page is out of scope | ||||
| D6 | Photography | Photographs are cropped from the page screenshots at measured coordinates. They are page *content*, not system assets, so their resolution does not constrain the spec | Originals are not available; preview fidelity is enough for approval | ||||
| D7 | Leaf motif and icons | The leaf motif is redrawn as clean, scalable SVG and becomes a versioned system asset — it recurs in the title band, the bulleted lists and as background decoration. Logo and the pictogram sets (6 Green Puff features, 4 "valori") are cropped as PNG until the originals arrive | A decorative shape carried as a blurry crop cannot scale or take a token colour; a geometric motif can be rebuilt exactly | ||||
| D8 | Type family | Roboto Condensed, and only two weights exist in this design: 400 and 700. Loaded as four static woff2 subsets (400/700 × latin/latin-ext, 69 KB in all) beside the user's `RobotoCondensed-Regular.ttf` in `sources/fonts/`. Apache 2.0, web use allowed | The user supplied the family; the match is verified against the raster, not assumed — three body lines imply 23.46 / 23.47 / 22.87 px from their advances, and the stem runs across `MISCELATORI` match weight 700 run for run (400 and 500 are a pixel light, 900 a pixel heavy). Evidence in `sources/fonts/README.md`. The first rebuild guessed 500 for the button label, the `SCOPRI` label and the Green Puff feature labels; stem width disproves it (0.15 em against 0.098 for a 500), and correcting them also corrected their size and tracking — 37px / 0.04em / 45px line pitch, not 34px / 0.08em | ||||
| D9 | Reference viewport | The rebuild reproduces the PDF 1:1 at a 1920px viewport: every size in the spec is the size measured on the raster. At 1920 that means body 23.5px, headings ~53px, small text 19.6px, footer 25.6px, body line-height 37px | The capture scale of the screenshots is unknowable from a raster — body lands on no round CSS size at any plausible device-pixel ratio (15.6 at 1.5, 18.8 at 1.25), which is itself a sign the original type is fluid (vw-based). Anchoring at the one viewport we can actually verify keeps `validate.py`'s original-vs-rebuilt height check meaningful | ||||
| D10 | Fluid scale | Type and section padding are authored as fluid values anchored at 1920 (the measured values) and at 390 (authored, D3). Nothing between those two anchors is evidence | The source has no responsive information at all; the curve has to be invented, so it is invented once, explicitly, and reviewed at checkpoint 2 | ||||
| D11 | White on yellow | Kept as it is. `#FFFFFF` on `#E9A901` is 2.07:1 and stays on the title band, the yellow content bands, the "I valori" list and the section tabs. It is recorded in `DESIGN.md` as a brand choice taken with the number in hand, not as an oversight | The user's decision at checkpoint 1. The pairing is the brand's voice on every page; changing it would change the look of the whole site. The consequence is explicit: the site is not AA on those surfaces, and `validate.py` must report the failure rather than hide it (see the open item on the waiver) | ||||
| D12 | Header | One header with a real sticky state. The resting header (logo 416×152) is the system's header; a `header--compact` state (logo 296×108, the menu unchanged, the 0.71 ratio measured on the five inner pages) shrinks it on scroll. Both live in `spec/styles.json`, and the preview implements the behaviour | The user's decision at checkpoint 1. The two logo sizes in the PDF are one header caught in two states, not two headers | ||||
| D12a | Where the sticky lives | The rebuild stays at rest: no scroll behaviour, no JS. `capture.mjs` scrolls every page before it reads the computed styles, so a shrinking header would be measured in its compact state and the inventory would record the wrong header | The rebuild is a measuring instrument (D2); behaviour belongs to the spec and the preview, not to the ruler | ||||
| D13 | Hard line breaks | The rebuild keeps the `<br>` inside paragraphs (five in one paragraph of *Presse per gallette*); `pages/*.json` drops them. The measure of a line is set by the pattern | The user's decision at checkpoint 1. Keeping them in the rebuild preserves the height comparison against the PDF; keeping them in the content would stop the text reflowing | ||||
| D14 | Green Puff badge | `#80BA27` with white text (2.34:1) is kept unchanged: it is a product mark with two uses in the site, not an interface colour. Recorded in `DESIGN.md` beside D11 | The user's decision at checkpoint 1 | ||||
| D16 | Contrast declarations | The two failing pairs are declared as what they are: `text`, with their real ratios. `validate.py` will report them red and `validation.json` will not pass, so `dso.py release` refuses v0.1.0 until someone changes the colour or the validator. No waiver, no `decor` relabelling, no omission | The user's decision. The validator must keep saying what is true; a release that stays open is a visible consequence, and a far better one than a report that says "ok" about a 2.07:1 pairing. The waiver in `core/validate.py` stays a proposal, not a change | ||||
| D15 | Font files | Two static weights, not one variable file. Four subsets (400/700 × latin/latin-ext) replace the variable woff2 the first rebuild used | The user's decision. The design needs exactly two weights (D8), for which the static files are *smaller* — 69 KB against 85 KB — and a static `@font-face` carries a single `font-weight`, so `inventory.py`'s family check works and the false `font-not-loaded` disappears. The findings went from 13 to 12 with no change to shared `core/` | ||||
| D17 | Calibration | Three values the rebuild had invented were re-measured on the raster before any token was written, and the capture was re-run: the rule beside a category tab is `#d6d6d6` 2px on the tab's bottom edge (not `#e3e3e3` 1px centred), the button's border is `#c2c2c2` with no shadow (not `#d9d9d9` plus a soft shadow), and the `\ | ` of the language switch is `#7a7a7a` (not `#999999`) | A guess that reaches the token file becomes a fact about the client's brand. Re-measuring removed a whole colour cluster and the entire shadow scale, both of which were mine | |||
| D18 | One ink | All text is `#2e2d2b`. `#333333` (menu) merges into it at ΔE00 3.10; `#1d1d1b` disappears, because the rebuild had taken it from the logo's wordmark while the PDF's headings measure `#2e2d2b` | The design looked like it carried three near-blacks and carries one | ||||
| D19 | Type scale | 14 measured sizes collapse to 9 steps, worst deviation 1px, and the scale keeps the design's own irregular ratios instead of being forced onto a modular scale | Forcing a ratio would move text by several px and spend the 1:1 fidelity D9 buys. The irregularity is a fact about the design, recorded rather than smoothed away | ||||
| D20 | No shadows | The system has no shadow at all; `radius.pill` is the only radius, because every measured radius was exactly half its element's height | Measured (D17). The single shadow in the inventory was invented by the rebuild | ||||
| D22 | Control border | The spec uses `#949494` (3.03:1) where the raster measures `#c2c2c2` (1.78:1). The rebuild keeps the measured value: a design correction belongs in the spec, never in the instrument that measures | The user's decision at checkpoint 2. Unlike D11 and D14 this hairline is not a brand signature, and it is the button's only boundary | ||||
| D21 | Muted text | `#757575` instead of the measured `#7a7a7a`: one step darker, 4.29:1 → 4.61:1 | The only correction made for accessibility in this checkpoint, and it costs nothing visually. Contrast on *brand* surfaces is a different matter, settled by D11 and D14 | ||||
| D23 | Focus and hover | Authored, because no screenshot can show them: focus is a 3px accent-yellow ring at 2px offset, turning to ink on the yellow and green surfaces; hover inverts the button (yellow fill, ink leaf) and puts a yellow rule under a menu item. Links carry no underline — measured on the raster, in the menu and in the footer alike | The raster cannot answer, so the system has to. The yellow is already the design's signal (rules, leaves, tabs), and the inverted button reuses a pairing the design already contains. Proposed at checkpoint 3; the user may reverse it there | ||||
| D24 | Decorative leaves | `.decor` is a system component: colour and behaviour in the spec, position and size passed by the pattern | The leaf at four scales is the design's most recurring device. Where a decoration sits belongs to the band, not to the shape | ||||
| D25 | Page title band | `.page-title` is a component, not a pattern: one slot, a measured height of 113px, identical on all six pages | A pattern with a single slot repeated six times is a component | ||||
| D26 | Green Puff | The badge carries a wordmark image, not a text label: the lettering is a wide, heavy face that is not Roboto Condensed (cap height implies 28.7px, glyph widths imply 38.9 — a 1.4 ratio no tracking can explain). It is a product logo and ships as an asset, extracted with alpha from the raster | Measured. Two consequences: the type scale no longer carries a "badge" size that never existed, and the contrast question changes shape — a logotype is outside WCAG 1.4.3, so what remains to declare is the pill against the page (2.34:1, `ui`, kept by D14) | ||||
| D27 | No card | This design has no card component. The blocks that look like cards carry no box, border, padding or background: they are compositions of `.heading-rule`, text and `.btn` inside a stack | Naming a component that does not exist invites the next author to put padding inside it, and the system has none to give | ||||
| D28 | The leaf asset | The mask travels as a data URI inside the CSS (307 characters) rather than as a file reference | A relative `url()` does not survive the move into WordPress global styles, and the preview resolves assets from the capture, not from the spec | ||||
| D29 | Rules under features and values | They are separators between stacked items, not underlines on a label. `.feature--divided` and `.value--divided` carry them; the last item of a column has none | The yellow rules sit at y 1384 and 1570 between the three rows of each column, 335 × 8, aligned with the label column. The white one sits once, at y 2366, between the two rows of values, 334 × 12 | ||||
| D30 | The button is not a pill | Radius 20px, measured on the corner curve, which runs 18–20px along each edge. The label sits low in the box, 17px above the bottom edge. The leaf is mirrored: its long axis runs bottom-left to top-right | The left edge reaches its final x only 18px below the top edge; a pill on a 75px box would take 37. The leaf's principal axis measures 135° against the base leaf's 45° | ||||
| D31 | h1 | `h1` is the largest step (4xl, 52px); `.page-title__text` overrides it to 2xl (40px) for its band | A heading element must keep the document's hierarchy. The band is a component and states its own exception, instead of the element being bent to the one place it appears in | ||||
| D32 | Sizes corrected | Person name 40 (was 37), role 30 (was 27); event title 52 (was 45), subtitle 37 (was 30); all of them tight-leaded. The heading rule sits 4px under the line box (was 12). The list leaf is 33px at the top of the first line (was 30px, pushed down 8). The navigation gap is 72px | Cap heights and glyph advances: name 40.0/42.4, role 30.0/31.5, event title 53.3/54.8, subtitle 33.9/37.0. Heading ink bottom 2561 → rule 2577. Leaf 3950–3983 against the line's ink 3965–3986. Menu items 72–85px apart | ||||
| D33 | Menu hover | The hover rule is drawn with `text-decoration` at a 6px offset, not as a box-shadow on the line box | Authored (D23). The box-shadow sat on the bottom of the 54px row, far from the text; the underline follows the glyphs | ||||
| D34 | One pattern, many sections | `band-split` carries 17 of the site's sections, with options for surface, padding, media side and whether the heading keeps its rule. Nine of the fifteen patterns are single-use | The site is four pages of alternating text-and-media bands plus three pages that are each their own thing. Inventing shared patterns for the second group would mean options nobody would ever combine | ||||
| D35 | The hero inside the page model | The hero keeps the content column like every other band and its photograph covers the band through `position: absolute; inset: 0` | A full-bleed image otherwise forces the negative margins rule 9 forbids, and `screenshot.mjs` flagged seven misalignments per page. After the change: zero, on six pages at three viewports | ||||
| D36 | `.pair` | A project layout class states the two columns the four approach blocks, the four values and the six features sit in | The core grid auto-fits on a minimum and offers `--3`, `--4`, `--5` but no `--2`; with four blocks it made four columns. The class took the drift on Azienda from −16% to −2.7% | ||||
| D37 | Decorative placement | A pattern places a leaf by naming one of four placements (`--left-top` and the rest), never by coordinates | Patterns carry global classes only. The leaves land near, not on, the PDF's positions, and that is the price | ||||
| D38 | Section padding from the instrument | `extract_pages.py` reads the padding the rebuild carries on each section and picks the nearest step of the spacing scale | The alternative was one default for every band, which flattened the page's rhythm. The step is chosen from a measurement rather than by eye, and the residual drift is the system's own rhythm | ||||
| D39 | How close is close enough | The spec composes the six pages to a mean 3.9% of the PDF's height (−0.1% to +8.8%), against the rebuild's 0.3%. Accepted as the cost of a system | A design system whose sections breathe on a thirteen-step scale cannot also carry a page builder's 23, 57, 103 and 188px paddings. Pixel parity would mean per-section values in the content, which is not a system | ||||
| D40 | The three brand colours | The client's own values replace the measured ones: yellow `#E9A901` → `#EAA900`, cream `#FAECC9` → `#FAECC8`, ink `#2E2D2B` → `#2E2D2C`. The rebuild keeps the measured values: a correction belongs in the spec, never in the instrument that measures (D22) | A raster carries JPEG noise; the brand carries the truth. The differences are ΔE00 under 1, so nothing else moves: contrast goes 2.07 → 2.06 on the pair D11 keeps, and every other check is unchanged | ||||
| D41 | Typography in the preview | The shared preview now shows every scale step in capitals as well as sentence case, prints each fluid step's two endpoints (its size at the smallest and at the largest viewport of the project's own range), and carries a viewport slider from 100px to the fluid maximum plus 500 that moves the fluid steps exactly as the clamp would | This design is uppercase almost everywhere, and a scale judged only in sentence case hides what the pages actually look like. The endpoints and the slider answer the question the clamp otherwise leaves open: what the type does *between* the two anchors. It is a change to shared `core/`, so it reaches every project — the other three were re-rendered as a smoke test and all still produce 10/10 sections | ||||
| D42 | Leading | Two steps only: `tight` 1.0 for display type (headings, the title band, the menu, every label and tab — measured between 1.0 and 1.09) and `normal` 1.2 for everything that runs over more than one line. The measured 1.22, 1.34, 1.57, 1.6 and 1.84 all collapse into `normal` | Running text is the one that moves: the raster has it at 1.57 (37px of pitch at 23.5px) and the system now sets 1.2, so paragraphs read tighter than the PDF does. It is a deliberate simplification of the system, not a measurement — stated here because the pages will look denser than the source | ||||
| D43 | Tracking | Two steps only: `normal` 0 and `wide` 0.1em. The 0.02 measured on the button label and the 0.04 on the feature labels both become 0; 0.1 stays for the page-title band, the one place the design spaces its letters | A feature label loses about 1.5px per character of air. The band keeps its character | ||||
| D44 | The three widths | `layout.content` 1584 → 1366, `layout.content-header` 1334 → 1366 (the header now shares the content column exactly), `layout.page` 3168 → 3500 (no longer derived as twice the content width). Authored, not measured | Three collapse thresholds had been set against the old column and had to follow it, or the design folded: `.pair` 680 → 560 per column (two columns plus the 96px gap need 1216px, and 680 each made every pair one column), `layout.grid-min-wide` 460 → 400 (three across a 1366px band are 418px once the gutter and the gaps are out, so the measured 460 collapsed the product row to two), `layout.sidebar-narrow` 304 → 288 — the design measures both, and at a 667px card a 304px picture leaves the text less than the half-card the sidebar primitive insists on, so the picture stacked. With all three followed, mean drift against the PDF goes 3.9% → 3.5% | ||||
| D45 | Elements the design does not have | h4, h5 and h6 are removed from the spec. The design has one heading size and three levels; the three extra levels were defined speculatively at 45, 37 and 30px. The shared preview now shows only the heading levels the spec sizes, and names the missing ones in the block's caption instead of drawing a specimen for each | A page that needs a fourth level is a question for the client, not a size to invent. 104 styles → 101. The core change improves every project: htc, which also sizes only h1–h3, stops showing three "not defined by the spec" specimens | ||||
| D46 | The Elements slider | The viewport slider is now shared, and the Elements block has one too. It works differently there by necessity: the element specimens take their sizes from the project's own stylesheet, so the slider redefines the fluid tokens on the specimen scope rather than the elements — the same arithmetic the `clamp()` does | One control, two modes (`samples`, `vars`), one script. Reaches every project; the other three were re-rendered and all still produce 10/10 sections | ||||
| D47 | The heading scale | h1 = `font.size.lg` (30px), weight regular, tracking wide — the title band's own voice, with `<strong>` for the word or two that are bold. h2 = `4xl` (52px), the largest type in the system. h3 = `3xl` (45px), one step below. `.page-title__text` is deleted: it said exactly this and said it twice | The h1 is authored, not measured: the band measures 40px (`2xl`) in the PDF, and the system now sets 30. The band is a fixed 113px, so nothing moves — the sentence simply sits smaller inside it than the source shows. Say the word and it goes back to `2xl`: one token reference | ||||
| D48 | The current menu item | It was already marked (`.is-current`, set by the composer from the page's slug); it is now told apart from a hover — bold over a 12px yellow rule, where a hover draws the same rule at 8px and no bold — and the composer adds `aria-current="page"` | Authored: the PDF's menu marks nothing, so this is an addition in the same family as hover and focus (D23), not a measurement. The attribute is what a screen reader announces; the class is only for the eye | ||||
| D49 | Specimens that were prose | Six styles were being drawn in the preview as a paragraph of sample text — `.masthead__logo-link`, `.btn:hover .btn__leaf`, the four `.decor--*` colours and `.value__label`. The shared preview's rule was "every property is typographic ⇒ it is a text component", and `color` alone or `line-height` alone passed it. A style now has to name the text (`font-size`, `font-weight`, `font-family`, `text-transform`, …) to count | Fixed in `core/`, so every project gets it. Each of the six also gained a real example: the logo inside its link, the hovered button with its ink leaf, the four leaves each on the surface it is meant for, and the value label beside its pictogram | ||||
| D50 | `space.3xl+`, `4xl+`, `5xl+`, `6xl+` | Renamed. The ladder is continuous: `2xs` 4 · `xs` 8 · `s` 12 · `m` 16 · `l` 20 · `xl` 24 · `2xl` 32 · `3xl` 40 · `4xl` 48 · `5xl` 56 · `6xl` 64 · `7xl` 72 · `8xl` 88 · `9xl` 96 · `10xl` 112 · `11xl` 128 · `12xl` 192`. Seventeen measured values, seventeen names, no `+` | They were not decoration: two of them were broken. `+` is not allowed in a CSS identifier, so `--space-4xl+: 4rem` was an invalid declaration the browser dropped — and the compiler's own reference pattern does not match a name with a `+` either, so `{space.4xl+}` was copied into the stylesheet as text. `.section--pad-s` and `.section--pad-l` therefore had no padding at all and fell back to the default band. `3xl+` and `5xl+` were used by nothing | ||||
| D51 | The site menu, reverted | D48 is withdrawn. The current page keeps the single yellow rule it shared with a hover; no bold, no thicker rule. `aria-current` stays, because it is not a style: it is what a screen reader announces, and nothing on screen changes | The request was about the preview's own fixed bar, not the site's menu. That bar now marks the section being read — bold, full-strength label, the accent rule — in both navigations, the pills in the hero and the sticky bar. It is a `core/` change, so every project's preview has it | ||||
| D52 | One logo size | `.masthead__logo` is always 296px (`layout.logo`). The compact state no longer resizes it | The PDF shows the mark at 416px on the home and at 296 on the inner pages — where the capture simply caught the header already scrolled. A logo that changes size while you read is an effect, not a rule. The home's header is therefore 46px shorter than the source, which is most of the drift that moved below | ||||
| D53 | Only the menu is pinned | The header stays in the flow and scrolls away with the page. On scroll, only the menu band leaves it and fixes to the top of the window. A new element, `.masthead__navband`, is the full-width band that pins; `.masthead__bar` stays the 1366px column inside it, so the white runs edge to edge while the menu keeps its place | Measured, not guessed: at rest the header is 182px and the first section starts at 182; after scrolling 700px the band is fixed at 0, 55px tall with its hairline below, and the first section still starts at 182 — the padding under the header replaces exactly what the band took with it, so nothing jumps. The hairline moves from above the menu to below it, since at the top of the window a rule above the menu separates nothing | ||||
| D54 | `layout.logo` | `clamp(150px, 14vw, 210px)`, stated as CSS. The mark grows with the window, stops at 210px from 1500px up, and never falls below 150 | It is not the system's fluid shape: a fluid token interpolates between the project's two viewports (390 → 1920) and would have compiled to `clamp(150px, 134.7px + 3.92vw, 210px)`, which is a different curve. So the token says the value outright, and `core/` gained the one rule that allows it: a dimension written as a string travels verbatim. Measured widths: 150 at 390 and 900, 179 at 1280, 210 from 1500 up | ||||
| D55 | No fixed heights in the header | `.masthead__bar` loses `height`, `.masthead` loses its padding, and each row carries its own: `xs` (8px) on `.masthead__top`, `s` (12px) on `.masthead__navband`. `layout.nav-bar` is deleted — a height that is never set needs no token | The menu band lands at 54px at 1920 — the measured value, now arrived at rather than asserted (30px of menu + 2×12), and free to grow with a longer menu or a larger text size. The header is 147px at 1920, 132 at 1280, 184 at 390 | ||||
| D56 | Where the header width is stated | `.masthead` carries `max-width: {layout.content-header}` and centres itself. `.masthead__top` drops its own copy of that width, and the two rows simply inherit it | The width was stated twice and could have drifted apart. The header is now 1366px wide and centred; the menu band inside it is 1366 at rest and goes to the full window only when it pins, which is exactly the one moment it has to | ||||
| D57 | The hairline | Moved from `.masthead__bar` to `.masthead__navband` | The rule opens the band, so it belongs to the band — and that is what keeps it spanning the whole header when the band pins and goes full width. On the column it would have stopped at the column's edges. When pinned the top rule is made transparent and a bottom one takes over: at the top of the window a rule above the menu separates nothing | ||||
| D58 | The menu's type | `.site-nav` is `font.size.xs` (20px) and bold | Authored: the raster measures the menu at 21.5px regular. 20px bold reads as a navigation instead of as running text that happens to sit in the header, and it holds up at the smaller sizes the menu reaches on a narrow window | ||||
| D59 | The header's first row | `.masthead__top` aligns its three parts to the bottom of the row | The contact lines and the social marks now sit on the foot of the logo, whatever height the logo's clamp gives it | ||||
| D60 | The language switch | `.site-nav__lang` is xs and bold, like the menu (D58) | It shares the line with the menu and now shares its voice | ||||
| D61 | The gutter | `max(32px, 7vw)`, and it is now a minimum margin instead of a tax on the column: `.section` is `max-width: calc(content + 2 × gutter)` with the gutter as padding, and `.section--wide` pads by `max(gutter, (100% − content) / 2)` | Two things had to change together, and the second is the interesting one. `vw`, not `%`: the same token pads a plain section (whose box is the column) and a full-bleed band (whose box is the page), and a percentage resolves against each in turn — the two columns would stop aligning, which is the one thing component rule 9 forbids. Minimum, not addition: the old formula subtracted the gutter from the column, invisible at 24px but 268px at 1920 once the gutter grew. The column fell to 1097, the pair and the product grid dropped below their thresholds, and two pages grew by a fifth before the formula was corrected | ||||
| D62 | When the band pins | `.is-pinned`, set by the script when the menu band's top reaches the top of the window — not on the first pixel of scroll. The old `.is-scrolled` stays at `scrollY > 8` for the projects that use it | Measured on Azienda: at 0 the band sits at 93, at 50 it has scrolled naturally to 43, at 92 it fixes at 0 and stays. The header scrolls away with the page, the band arrives at the top, and there is nothing to see in between | ||||
| D63 | The current menu item | The underline is replaced by the leaf: accent yellow, one em tall, to the left of the label. A hover keeps its underline, so the two signals are different in kind, not in degree | The design's own motif does the marking. It is a `::before` over the same mask every other leaf in the system uses — no extra markup, and the composer still sets nothing but `.is-current` and `aria-current`. Measured at 1920: label 20px, leaf 19.98 × 19.98, `#EAA900`, `text-decoration: none`. One more portability note (a pseudo-element with `content`), which the system already carries for `.tab-row` and `.leaf-list` | ||||
| D64 | The title band's height | No height. The band is as tall as what it holds plus `space.2xl` above and below. `layout.title-band` (113px) is deleted — a height that is never set needs no token | It is the leaf that sets the height, not the text. At 1920 the band is 116px = max(text 30, leaf 52) + 2×32, against the 113 measured in the PDF — three pixels, by a route that now survives a longer title: at 390 the sentence wraps to two lines, the text becomes 57px and the band opens to 121 instead of clipping. If you want the band to follow the text literally, the leaf has to come down to the text's height (1em, as in D63), and the band would land at 94px — further from the raster, so I left the leaf at its measured 52 | ||||
| D65 | Hover | The leaf marks the hover too. The underline is gone from the menu entirely | The leaf sits in the line box of every item, transparent until it is wanted, so hovering colours a shape that is already there. Measured: the label's left edge is at 410px at rest and at 410px hovered — the pointer never pushes what it is pointing at. Hover and the current page now look the same; the current one is also announced by `aria-current`, which is the part that was never a style | ||||
| D66 | The menu row | `.site-nav__list` is left-aligned with a 32px column gap (`space.2xl`), and a 16px row gap for when it wraps | It no longer spreads across the header: it starts where the content starts. The 72px gap the raster measures was an artefact of six items spread over 1334px | ||||
| D67 | The title band | Padding `space.m` (16px) instead of `2xl` | The band is 84px at 1024 and above (the 52px leaf plus 2×16; the leaf still sets it, D64), and 89px at 390 where the sentence wraps to two lines. Against the PDF's 113 that is 29px per page, on all six | ||||
| D68 | The gutter | `max(2rem, 4vw)` | In `rem`, so the margin follows the reader's own text size, not only the window's. Columns, all three kinds on the same one: 326 / 828 / 1178 / 1366 at 390 / 900 / 1280 / 1920 — below the reference width the column is now wider than it was at 7vw (1178 against 1101 at 1280) | ||||
| D69 | The mobile menu | Collapses at 1024px instead of 900 | Measured at the edge: the toggle is in at 1024 and out at 1025. A tablet in portrait now gets the menu behind the button, which is where six uppercase items at 20px bold belong | ||||
| D70 | The menu bar | `.masthead__bar` gets a 48px column gap (`space.4xl`) | 3rem, which is also the gap the compact state wants between the language switch and the menu button — so that distance is stated once | ||||
| D71 | The leaf | Moves to the right of the label and is mirrored on its horizontal axis (`scaleY(-1)`) | The shape is a lens on a diagonal, so mirroring turns its axis the other way, as the two leaves of the title band already do. Hover and current still differ from the rest only by the leaf's colour, and the label still does not move: measured, left edge 410px at rest and 410px hovered | ||||
| D72 | The breakpoint | 1200px (was 1024) | Measured at the edge: compact at 1200, full at 1201 | ||||
| D73 | The open menu | `.site-nav__panel` reads at `font.size.sm` | 17px at 390, 22 at the top of the range, against the 15–20 of the bar. A menu you have opened on purpose is the page's content for that moment | ||||
| D74 | The compact header | Below the breakpoint the header is a different shape, not a squeezed one: `.masthead__top` is hidden — contacts and social marks with it — the logo moves into the menu band on the left, and the language switch and the menu button sit together on the right, 48px apart | Measured at 390: logo 32→180, language 228→266, button 314→358, gap exactly 48px, the whole row inside the 326px column. The pattern carries a second logo link in the band, shown only here; `.is-mobile` now lands on the header as well as on the navigation (a `core/` change), so the header lays itself out from the breakpoint the pattern already declares, with no media query of its own and no `:has()` | ||||
| D75 | The open menu's box | `.site-nav__panel` opens with `space.6xl` (64px) above the list and `space.m` below | The panel's top edge is at 68px and the first item at 132px: the menu does not start under the button that opened it | ||||
| D76 | Hover | The hovered leaf is cream, the current page's stays accent yellow | Measured: `rgb(250, 236, 200)` under the pointer, `rgb(234, 169, 0)` on the page being read. D65 had made the two identical; colour tells them apart again, and the hover is the quieter of the two | ||||
| D77 | The switch below 500px | It leaves the bar and appears at the end of the open menu, under the last item | A stylesheet cannot carry an element from one box to another — the panel is inside the navigation, the bar's copy beside it — so the pattern carries two, and a media query shows one or the other. It needed a `core/` change: a navigation's panel could hold nothing but the list, and now takes the pattern's own nodes after it (`after`), built like any others. Measured: in the panel at 390 and 499, in the bar at 500 and above. The menu button keeps the right-hand end, taking over the auto margin the switch used to carry | ||||
| D78 | The title band | `text-align: center` | At 390 the sentence wraps to three lines and they stack centred, not ragged against the leaf on their left | ||||
| D79 | The header's bottom line | `.masthead__contact` and `.masthead__social` carry `space.xs` of padding below | The row sits on its bottom edge (D59); the padding is what lifts the last contact line and the three marks clear of the logo's foot | ||||
| D80 | The button | No width, no height. Padding `xs` above and below, `m` left, `s` right — less on the side the leaf is on, because the leaf carries its own air. Radius 15px | Measured: 233 × 66 at 1920, 179 × 52 at 390, against the raster's 244 × 76. The radius is authored against the measurement: the curve runs 18–20px in the PDF, and `radius.m` now says 15. Radii coverage falls from 100% to 29% — the one number in the validation report that this decision pays for, and it is the right kind of failure: the system says what it chose, and the report says it differs from the source | ||||
| D81 | The leaf in the button | `2em` instead of 47px | 48px beside a 24px label, 34px beside a 17px one: it grows with what it sits next to. The selector had to become `.btn .btn__leaf`, because `.leaf` sets a width of its own and is declared later — same specificity, and source order was winning | ||||
| D82 | The section under the title band | `.page-title + .section`: 64px above, 40px below | Every page opens the same way, so the rule is a position rather than an option in six pages' content | ||||
| D83 | The seam | Its height is `space.xs` (8px), not `border.underline` (4px) | It is a shape with a size, not a rule under text. It doubles in thickness, and the token it uses now says what it is | ||||
| D84 | The colophon | `font.size.xs` (20px, was `md` 27) | The footer stops competing with the page | ||||
| D85 | The leaf's body | The short axis grows by 30%, the long one does not move. The motif is a lens: two circular arcs between the same two points, and how far they bow apart is the arc's radius. Keeping the chord and widening the bow means a smaller radius, not a bigger one: 57 → 48.09 in the 64-unit box | One change reaches every leaf in the system — the title band's pair, the list bullets, the one in the button, the four decorative sizes, the mark on the menu — because they are all the same mask over `currentColor`. Measured on the rendered pixels, not on the arithmetic: long axis 78.97 → 78.97, short axis 31.90 → 41.41, a ratio of 1.298. The ink goes from 42.5% to 56.4% of its box; the box itself is unchanged, so nothing around a leaf moves | ||||
| D86 | The button | Padding-right `xs`, gap `2xs`, content on the bottom, leaf 1.7em | Measured at 1920: 210 × 59, padding 8/8/8/16, gap 4, leaf 41 × 41. The label keeps the low line the raster shows and the leaf rises beside it, instead of both being centred on a box that no longer has a height of its own | ||||
| D87 | The leaf beside a title | Stated as a height of 2em with no width: `height: 2em`, `width: auto`, `font-size: 1em` | 48px at 1920, 42 at 1280, 34 at 390 — it scales with the type around it instead of holding the 52 it measured at one viewport. The width comes from the square aspect ratio `.leaf` already carries. `width: auto` is the working part: `.leaf` sets a width, and leaving one out of `.leaf--mark` would have left that one standing | ||||
| D88 | The seam | Astride the join: half on the band above, half on the band below, by a negative margin of half its height at each end, and raised one layer so the band that follows does not paint over it | Measured on the home: the two bands meet at y 1228 and the seam runs 1224 → 1232. The row it lives in takes no vertical space at all | ||||
| D89 | The footer | `.colophon` carries the page's gutters | Its three lines stop touching the window's edge on a phone | ||||
| D90 | Leaves on a product band | `band-split` takes a list of leaves instead of one on/off switch: each item names its colour, size and placement (`{"variant": ["white", "xl", "inset-right-top"]}`). The home's band declares four | Measured on the PDF's own pixels, in a band 1920 × 942: white 749 × 805 at x 854, y 62 · accent 211 × 211 at x 1390, y 626 · white 471 × 387 at x 140, y 496 · accent 107 × 107 at x 322, y 366. The four sizes the spec already carried — `xl`, `l`, `m`, `s` — turn out to be exactly these four leaves: they were defined at checkpoint 2 and never emitted, the gap noted at D45 | ||||
| D91 | Who sets a band's height | A new option, `height: media | text`. With `text`, the media column stretches and the picture fills it without adding to it | The home's product band goes from 942px in the raster to 559: it is now the text plus its padding, and the machine fits that height instead of setting it. That is the whole of the home's extra drift | |||
| D92 | The split | A new option: the text half takes three fifths of the row and 7rem of air on the left, above and below | Measured at 1920: row 1366, text 845px (61.9%) from x 798, picture half 489. The switcher gives both halves the same basis and lets them grow, so 3 against 2 is 60/40 above the threshold and one column below it. `max-width: none` had to come with it, and the selector had to carry the column's own class: `.measure` is on that column and is declared later, so 54ch was holding it to 47% of the row however hard it was told to grow — the same trap as the leaf in the button (D81) | ||||
| D93 | Whose background the leaves are | The leaves belong to the two halves, not to the band: `leavesText` sits in the text column, `leavesMedia` in the picture's | Each column is a stacking context with no background of its own, so a leaf one layer down is behind that column's words and still above the band. The placements now resolve against the half they are in, which is what makes them read as its background | ||||
| D94 | The picture | Runs a quarter of its column's width past the right edge, over the gap between the halves | Measured: column 489, picture 611, overflow 122px = 25.0% | ||||
| D95 | `.btn` and `.label` | `font.size.sm` | 22px at 1920, 17 at 390 | ||||
| D96 | The size of an inset leaf | A leaf inside a half of a band is a fraction of that half: `height` in per cent, `width: auto`, `aspect-ratio: 1` — the placement class states its size, overriding `--xl`/`--l`/`--m`/`--s`, and is placed from the far edge. The big white leaf is 116% tall, mirrored, offset −12% / −14%; the accent one under the text 30%, mirrored, from the middle; the white leaf under the machine 65%, tilted −20°, 7% below the foot; the small accent one 15% | Measured at 1920 against halves 627 wide and 652–836 tall: 756, 196, 424 (543 as a box once rotated) and 125 across. The composition now holds at 1920, 1280 and 390 instead of being four fixed sizes that only fitted one band | ||||
| D97 | The two halves | Equal width. Both grow at 2.5, and the 7rem of air between them is the row's column gap, not the text's padding | With the air as padding the halves measured 723 against 611 at 1920 — exactly the 112px of padding apart. Flex hands out the *free* space by `flex-grow` and adds an item's padding outside its share, so equal grow cannot give equal boxes while one of them is padded. As a gap the air lands where it belongs, between the halves, and both come out 627 at 1920, 533 at 1280, 326 at 390 | ||||
| D98 | Which half is in front | The picture's half sits a layer above the text's | So a machine that runs past the edge of its column crosses over the text instead of disappearing beneath it | ||||
| D99 | The machine | `width: auto`, anchored by `right: -12%`: the box is the picture, hanging 12% of its column past the right edge | Measured 12.0% at all three widths. Two things had to come with it. The column needs `aspect-ratio: 3 / 4`, because once the switcher has stacked the halves the column is on its own line with nothing in its flow: it measured 326 × 0 at 390 and the machine was gone from the phone. And the picture must load eagerly — a box sized by the picture is 0 × 0 until it loads, a zero-area box never intersects the viewport, so a lazy one would never load at all. The composer now takes `"eager": true` on an image for exactly this case | ||||
| D100 | The composition | Tuned on the rendered band, against the raster: the big white leaf to `left: -35%`, `top: -12%`; the accent leaf under the text to `left: 45%`; the leaf under the machine to `right: 24%`, `bottom: -5%`, `rotate(348deg)`; the small accent leaf to `top: 30%` | Measured at 1920 on halves 627 wide and 652 tall: 756, 196, 424 and 98 across, as before — only the placements moved | ||||
| D101 | The size of the Green Puff badge | The badge is sized against the label above it: it carries `font.size.sm`, the step SCOPRI is set in, and all three of its measures are in em of that step — wordmark `7.75em`, pill `9.39em x 1.97em`. The wordmark is two and a half times the width of SCOPRI, by request | Measured: the label is a constant 3.10em wide at every viewport (68px at 1920, 64 at 1500, 62 at 1280), so 2.5 x 3.10 = 7.75em holds the ratio wherever the type goes. The wordmark drops 212 -> 170px at 1920 and the ratio reads 2.500 at 1920, 1700, 1500 and 900, 2.48–2.49 where the fluid font size lands on a fraction. In pixels the badge was the only thing in the band that did not breathe: 257 x 54 from 1920 down to 1280 while the label shrank under it. `.badge--lg` follows in em and is pixel-identical at 1920 (408 x 88, wordmark 334) | ||||
| D102 | What sizes the leaves of the text half | The two leaves behind the text are a fraction of the half's width, not its height: big white `120.6%`, accent `31.3%`, `height: auto`, `aspect-ratio: 1`. The big one is anchored on the centre of its half and offset by a fraction of itself — `top: 50%; transform: translateY(-53.44%)` — so nothing in the composition is measured against a dimension the text does not answer to | This revises D96 for the text half only. D96's rule — height in per cent — is right for the machine's half, where the machine is itself 100% of the column and leaves and product scale together. It is wrong here: the text is as wide as the column and only as tall as the type makes it. Sized by height the leaf lost 756 -> 718px between 1920 and 1500 while the text kept all of its 627, and the second line of the heading crossed out of the white onto the cream. Measured after: the leaf is 756 x 756 at 1920, 1700 and 1500 alike, and the text sits in it the same way at all three (the heading's longest line clears the leaf's box by the same 90px, 14.4% of the half, which stays 14.3% at 1366 and 1280 where the column itself narrows). Below the threshold the leaf and the text shrink together | ||||
| D103 | What it cost | Mean drift 8.7%, unchanged; the home -10.0%, 10px lower than before | The band is 10px shorter because the badge is 11px shorter. Validation is unchanged: integrity 0, colours 100%, font sizes 100%, spacing 96%, layout 0 issues in 18 | ||||
| D104 | Where the band changes | 1200px — the width the header already changes shape at (D72, D74) — and 977px, where the switcher stacks the two halves | 977 is not a number anyone chose: the row is `0.92 × viewport` once the gutter is `4vw`, and `layout.threshold` is 900px, so the switcher flips at the first width whose row is under 900. Measured: 978 side by side, 977 stacked | ||||
| D105 | The row below 977 | A column, stated: `flex-direction: column`, `nowrap`, `align-items: stretch`, and the children's basis back to `auto` | The switcher already reached this arrangement by wrapping, but wrapping leaves the row's machinery in place: the basis `calc((900px - 100%) * 999)` would resolve against the height in a column, and `--middle`'s centring would shrink each half to its content. Saying column means the three things that only make sense across a row stop applying instead of being worked around | ||||
| D106 | The big white leaf, stacked | 135% of its half's height, width auto, square — height-driven again, like every other inset leaf (D96), and centred on its half both ways | 456px at 977, 432 at 768, 389 at 390, against the 1573 / 1232 / 571 that covering the words cost. It reads as a leaf again | ||||
| D107 | Where the band turns | 978px, not 977 | 977 was never chosen: it is where the switcher's own arithmetic flips, the first width whose row falls under `layout.threshold` once the gutter is `4vw`. Since D105 the media query states the column outright, so the media query is the breakpoint and the switcher no longer decides. Measured: 979 side by side with the picture out of the flow, 978 stacked with it back in | ||||
| D108 | The product photograph | `puffing-machine.png` replaces `press-green-puff.png` | A cleaner cut-out of the same press, 400 x 701 against 452 x 800, and without the grey shadow the old one carried under its wheels — which on the cream band read as dirt rather than as ground. The band is unchanged at 1920 (769px, the text sets the height, D91) and shorter where the picture sets it: 1322 → 1223 at 978, 1049 → 1044 at 390. Drift unmoved at 8.7% | ||||
| D109 | The white leaf of the picture's half, stacked | 45% of its half's height, anchored to the half's left edge (`left: 0`, `right: auto`) instead of measured in from the right | At 65% and `right: 24%` it sat squarely behind a picture that now fills its half, and read as a white smear under the machine rather than as a mark. Measured at 978: the leaf is clear of the machine's left edge and is drawn as a leaf; at 390 it stays behind the picture's transparent margin, which is all the room a phone has | ||||
| D110 | The dead and the overridden leaf classes (proposal 4 of the D101-D109 review, applied) | `.decor--left-bottom`, `.decor--xl` and `.decor--s` are dropped from `styles.json`, and the four leaves of the product band stop naming a size in `pages/index.json` | Counted on the composed pages, by `class` attribute and not by the inlined stylesheet: `--left-bottom` 0 uses, `--xl` 1, `--s` 1, `--l` 3, `--m` 3. The single use of `--xl` and of `--s` is the product band, where the `--inset-*` placement sets width *and* height and throws the declared size away — so once the content stops naming it, both classes have no user left. 120 → 117 styles. `--l` and `--m` stay: two of their three uses come from the patterns (`moulds` declares `decor--l`, the feature grid `decor--m`) with an edge placement that sets coordinates only, so there the size is the only thing that gives the leaf a box | ||||
| D111 | The home's product band (proposal 1 of the D101-D109 review, approved) | Lifted out of `band-split` into its own pattern, `product-band`, with no options | It was the only user of `split: text` and `height: text`; the four leaf slots, nine of the styles and seven of the media queries served it and nothing else, while eleven other `band-split` sections used none of it. The composition is now the pattern: cream surface, short padding, picture first, text half inset in its white leaf, picture half as tall as the text. 16 patterns instead of 15, and the composed markup is byte-identical — the options only ever baked classes into the tree, so baking them in by hand changes the spec and not the page | ||||
| D112 | The top padding of the product band | 144px (`space.12xl`, a new step) while the halves are side by side; 96px (`space.9xl`) once they stack | `.section--pad-s` gives 64, and the band is the one section where that is wrong: the white leaf the text is written on runs past the top of the band, so the words have to start lower than the band does or they sit on the leaf's edge. Measured against the source on the text half only — the half where the words are, because the machine's dark panels are not type: the PDF puts the first letter of the heading 271px below the band's top edge; at 64 the render put it at 184, at 144 it puts it at 264. Seven pixels from the raster | ||||
| D113 | The two cards of `card-row` | They are one row, not two blocks: the taller card sets the height, both pictures fill it, both buttons sit on the same line at the bottom | The grid already made the two cells equally tall; nothing used it. The picture now takes `width: 100%; height: 100%; object-fit: cover` inside `.card-row__media`, and the button's cluster carries `l-push`, the stack primitive's own "push me to the bottom" (`margin-top: auto`) — no new rule for the button at all. Measured at 1920: both pictures 288 x 408, both buttons' baseline at the same y. Before: 288x408 and 288x316, buttons 90px apart | ||||
| D114 | The button of a card | At the right end of its column, with `space.2xl` of air above it that it will not give up | It was at the left, and `l-push` had left it flush against the paragraph whenever the two columns were nearly the same height — 5px of air on the taller card. `justify-content: flex-end` and a `padding-top` that the auto margin cannot eat. The source puts both buttons at the right of their column, ending within 7px of the text's right edge | ||||
| D115 | The button of a card, again | No padding and no margin above it: it follows the paragraph like any other line of the stack | D113 had pushed it to the floor of the card with `l-push` and D114 had added 32px above it. Both are gone. The cost, stated plainly: the two buttons no longer end on the same line — 2472 and 2533 at 1920 — because the two paragraphs are not the same length and nothing is left to absorb the difference. The pictures are still the same height, 288 x 408 | ||||
| D116 | Where a product row folds | 978px, `layout.break-mid`, not 1024 | `layout.break-product-row` is deleted the day after it was added: the site already had a breakpoint there and did not need a second one 46px away. Back to 94 tokens. One column below `break-narrow` stays | ||||
| D117 | The vertical gutter of both grids | +2rem: 32 → 64 (`space.6xl`), in the card row and in the product rows | The gutter was 64 across and 32 down. It is now 64 both ways: two rows of squares need as much air between them as the squares have beside them, and a stacked card row needs the same | ||||
| D118 | When the picture is a square | Only in state B, when the sidebar is stacked | `aspect-ratio: 1` with `align-self: flex-start` is the square; `align-self: stretch` in the two windows where the picture sits beside the text undoes it. Those windows are `min-width: {layout.break-card}` and `max-width: {layout.break-mid}`, and the state in between is exactly the one where the sidebar wraps. D117 had made it square everywhere, which is wrong beside the text | ||||
| D119 | The picture itself | `width: auto; height: auto; aspect-ratio: 1`, the client's own three lines | The square moves from the box to the picture. Beside the text the flex box still stretches it to the card (288 x 403 at 1920, the ratio ignored, which is right there); stacked, nothing stretches it and it is square to the pixel — 612, 557, 428 | ||||
| D120 | `.card-row__media > img` | No `object-fit`, as asked | The picture is drawn into whatever box it is given, which is `fill`. Flagged, not argued: at 1280 the box is a 557 square and the mixer is 288 x 408, so it is painted 42% wider than it is. At 1920 the box is 288 x 403 against a natural 288 x 408 and nothing shows. One line puts `contain` back if the stretch is not wanted | ||||
| D121 | `.heading-rule::after` | `max-width: 100%` beside its declared 334px | The 334 is a width, not a promise. Measured: in the home cards the heading column is 331 at 1920 and the rule was sticking out by three pixels; at 390 the column is 326 and it stuck out by eight. It now stops at the heading. Where the column is wider — 579 on a single-column card at 978 — the full 334 still draws, so the measured width is untouched wherever it fits | ||||
| D122 | `.tab` | 20rem wide, label 2rem in from the left, nothing on the right | `size.rule.tab` goes 481 → 320. Chosen against the source, which measures the tab 481 wide with the label 86 in; the tab is now a third narrower and the label sits where the padding scale puts it. Measured: 320 x 53 at 1920, and `min(…, 100%)` still holds it inside a 280px window at 216 | ||||
| D123 | `.product-row > img` below 978 | `width: auto` beside the `aspect-ratio: auto` of D120 | The picture is drawn at its own size instead of being blown up to the cell: on two rows a galletta is 280 x 180, not 418 x 418. Anything wider than its cell is still held by the `max-width: 100%` every image on the site carries — at 978 two of the three gallette hit it and sit at 418 | ||||
| D124 | `.product-row` | 10% of padding left and right | The products stop against the tab's own edges and move into the middle of the column, which is how the source draws them. Measured at 1920: the row is 1366 wide, so 137 each side, and the first galletta starts at x 414 instead of 277; three cells of 322 instead of 413. Below 978 the cells become narrower than the pictures' own size, so `max-width: 100%` takes over again and a galletta goes from 280 to 222 at 700 | ||||
| D125 | What was wrong | The four blocks ran the full width of the band and the leaves ran under them | The source holds them in the middle of the page and gives the margins to the leaves. The render had 635px columns from edge to edge and two cream leaves 420 wide sitting behind the words | ||||
| D126 | `.pair` | 55% of the band, 75% below `break-narrow` | A width now, not a cap: 751px at 1920 against the ~825 the source draws, and 75% on a phone so the text is not a ribbon. `max-width: {layout.content-narrow}` goes, it could never bind under 55% | ||||
| D127 | `.tab--auto` | `width: unset`, 3rem of padding on each side | It hugs its text again, which is what it did before D126 pinned it to `min(20rem, 100%)`. Measured: 450 x 53 at 1920 with the label on one line and its middle on the box's middle; 362 at 978, 307 at 390. The wrap D126 flagged is gone | ||||
| D128 | The six leaves | Two containers, `.decor-cluster--left` and `--right`, one per side | Each group is a box with a width and a ratio — 20.9% of the band at 318/448 on the left, 20% at 304/382 on the right — and the three leaves inside it are placed in percentages of that box, not of the section. The group is one drawing that scales; the arrangement cannot come apart | ||||
| D129 | `.decor-cluster--left` | Taller — 318 / 510 — one gutter in from the edge, `top: 14%` | It no longer hangs off the left of the band: the group stands inside the page's own margin at every width. Measured left edge: 77, 48, 39, 32, 32 as the window goes 1920, 1200, 978, 700, 390 | ||||
| D130 | The two groups below `break-mid` | To the far ends of the band — left 5% from the top, right 5% from the bottom — and `max(330px, 40%)` wide | Once the four blocks stack the band is tall and empty at the edges, and a group holding 20% of a phone is a smudge. Measured: 391 wide at 978, 330 at 700 and at 390, against 318 and 251 above the breakpoint. The arrangement inside each group is untouched — still `-10,22 100x59 | 14,0 38x24 | 31,80 29x18` and `0,17 100x83 | 60,2 40x32 | 40,0 20x16` at every width |
| D131 | `.pair--values` | 75% of the band, 6rem between the columns, 0 between the rows | Each value carries its own white rule underneath, and a gap on top of a rule would read as two separate bands of two. Measured 1025 at 1920, 675 at 978, 245 at 390 | ||||
| D132 | `.value--divided` | No padding, no background: the line is an `::after` bar, 75% of the cell, held to its right edge | It was a `linear-gradient` painted 108px in from the left at a fixed 334px. Now it is a shape with a width, so it follows the cell: 348 of a 464px cell at 1920, and its left end lands just past the pictogram, which is where the source puts it | ||||
| D133 | The last row of values | No separator under it | A separator that runs under the last row is an underline, and the source draws none there. Two columns, so the rule is `:nth-last-child(-n+2)`; one column below `break-mid`, where the second-to-last cell gets its line back and only `:last-child` goes without. Measured: rules on 1 and 2 at 1920, on 1, 2 and 3 at 978 and 390 | ||||
| D134 | `.value--divided::after` | Half the cell, set 3em in from the left, hanging 0.6rem below it; `right` dropped | 3em of the value's own type is 120px at 1920, which is where the word starts, so the bar sits under the word and never under the pictogram — the source's alignment, now stated as a rule instead of as a 108px offset in a background image. Measured 232 of a 464px cell at 1920, 337 at 978, 122 on a phone | ||||
| D135 | A portrait | Square, `max(150px, 35%)` of the row, `object-fit: cover` | 204 at 1920, 194 at 978, 150 on a phone. Cropped rather than fitted: a head and shoulders survives a crop, and the two photographs in the source are not the same shape | ||||
| D136 | `.pair--people` | `width: auto` at every width, and 5rem above it while the two are side by side | Against the auto side margins `.pair` carries, `auto` means *as wide as the two of them, centred*: 1315 at 1920, where 90% was 1229. The 5rem is in a `min-width` query, so a stacked page does not inherit it | ||||
| D137 | `.person:first-child` | `justify-content: flex-end` only at `break-mid` and above | Once the grid is a single column there is no second person to close towards, so the first one stays at the left like everything else on the page. Measured offset of the portrait inside its cell: 85px at 1920, 19 at 979, 0 at 390 | ||||
| D138 | What the source actually draws | An ear of wheat on each edge — a stalk, two yellow leaves and a black one, growing upwards — not two black leaves thrown at opposite corners | The render had `decor--ink decor--m decor--left-top` at the top left and `--right-bottom` at the bottom right: two marks of the same size on a diagonal, which is a different drawing | ||||
| D139 | The three leaves of an ear | `rotate(90deg)` each | They were growing up and to the right; turned, they open up and to the left on the left ear, which is the way the source draws them, and the `scaleX(-1)` on the group keeps the right one its mirror. The boxes do not move: a square box rotated a quarter turn is the same square | ||||
| D140 | The section | `section--wide`: full bleed | The ears now reach the edge of the page, not of the band — x 0 and x 1738 at 1920, where before they were pinned to 200 and 1575. That is where the source draws them, and it is what the band was missing. The ear also grows with the section: 182 x 667 at 1920 against 144 x 528, because 9.5% of 1920 is more than 9.5% of 1520 | ||||
| D141 | `.badge--lg` | 14 x 2.5em, wordmark 10em across | Down from 18.55 x 4 with a 15.18em wordmark. Chosen against the raster, which is where those three figures came from. Still in em of the badge's own type, so the three move together: 308 x 55 at 1920, 265 x 47 at 978, 238 x 42 on a phone. It now reads as a label over the features rather than as a second headline | ||||
| D142 | `.pair--features` | The same declarations as `.pair--values` | 75% of the band, 6rem across, 1rem down, the whole width once it stacks. It was 72% with the default 4rem/5rem gutters. Measured: 1025 wide at 1920, cells of 464 | ||||
| D143 | `.pair--features` | `grid-auto-rows: 1fr` and `width: auto` | Every cell now measures 314 x 133 at 1920, where they were 464 x 89 and 464 x 133 by turns: *Struttura e pannellatura completa* sets on three lines and the other five take its height, which is what puts the separators of the two columns on the same line. `auto` against `.pair`'s auto side margins makes the block as wide as its six labels and centred — 725 of the 1920 band, where 75% was 1025 | ||||
| D144 | `.feature--divided::after` | 100% of the cell, still 2.5em in from its left | It runs 2.5em past the cell's right edge, into the 6rem column gap: 314 long on a 314 cell at 1920. The values band keeps its half-width bar — its cells are much wider than their single words, and this grid's are not | ||||
| D145 | A cream `band-split` | The `alt` surface now names the row as well: `band-row--alt` | An option that only painted a background now also says which row it is, so the one section built this way can carry its own figures without a selector reaching in from the section. `product-band` left `band-split` in D111, so nothing else answers to it | ||||
| D146 | `.decor--inset-right-top` | `height: 135%` of the text column, square, 4rem to its left and centred on it — instead of 120.6% of its width at a hand-fitted `translateY(-53.44%)` | A width-based figure could not serve two columns of different shapes. Now both are covered: 866 x 866 behind the home's two paragraphs, 727 x 727 behind Presse's four, where before it was 708 and left the last lines outside it. The 72px D145 flagged are closed, and the home is unchanged in composition — only bigger, and still clipped by the band's right edge, as the source clips it | ||||
| D147 | `.band-row--alt` | `flex-wrap: nowrap` at `break-mid` and above | `l-switcher` breaks on its own threshold, which the 6rem gutter and the 3rem padding of D145 had pulled above the breakpoint: the row was stacking while the rest of the page was still side by side. Measured: two halves of 410 at 1100 and 354 at 979, where before they had already gone one under the other | ||||
| D148 | The row | Centred both ways: `align-items: center` on the row, each picture centred in its own cell | The three cells were top-aligned, so a 332px plate and a 102px block of figures started on the same line and ended nowhere near each other. The source centres them on one another | ||||
| D149 | Where the air above a row lives | On the picture, not on the row: `.mould` loses its top padding, `.mould__media` gains 1.5rem | The rule is a border on the media cell, so padding above it on the row pushed the rule down and left the picture tight against it. Now the 1.5rem sits under the rule, where it belongs | ||||
| D150 | `.mould__spec` | 2.5rem above, 1rem below | It still stretches to the row, less those margins: 300 tall in a 356 row, so the yellow block of an even row stands clear of the two rules instead of running into them | ||||
| D151 | `.mould` | A declared grid: three columns at `break-mid` and above, two below, with the figures spanning the whole of the second row | `l-grid--3` and its intrinsic arithmetic are gone — the same move as D115 and D118, and the end of the `--l-grid-gap` trap D150 ran into. Measured: three cells of 423 at 1920 and 268 at 979; at 978 and 390 the two pictures hold the first row and the figures take 900 and 326 of the second | ||||
| D152 | Below `break-mid` | Every row's figures look the same | The yellow fill of the even rows is switched off: stacked, the figures are a caption under two pictures, and a block of brand yellow spanning the full width would read as a different kind of row rather than as decoration. Measured: three filled and three empty at 1920, six empty at 978 and 390 | ||||
| D153 | The figures | The label of each line in capitals, the value as written: `FORMATO: Ø 90 mm`, `CAPACITÀ: fino a 2700 pz/h` | `text-transform: uppercase` was shouting the measurements too, and `Ø 90 MM` and `PZ/H` are not how units are written. The label is now marked as a label in the content — `<b>Formato:</b> Ø 90 mm` — and the capitals are a rule in the stylesheet rather than a spelling in the data. Eighteen labels across six rows | ||||
| D154 | Three columns | `1fr 1.5fr 1fr`: the figures are half as wide again as a picture | Measured 363 / 544 / 363 at 1920 and 230 / 345 / 230 at 979, where the three were equal. The yellow block of an even row has the width its three lines want, and the two pictures stop floating in cells wider than they are | ||||
| D155 | Under `break-tiny` | The figures go back between the two pictures | `grid-column` and `grid-row` are returned to `auto`, so the placement is dropped and the markup's own order — plate, figures, product — is the order on the page. Measured at 360: plate at y 2573, figures at 2912, product at 3003. The row no longer needs a rule for where its parts go, because in one column there is nothing to decide | ||||
| D156 | `.mould__media` | `width: 100%` | `l-center` carries auto side margins, and auto margins on a grid item stop the stretch, so the cell was shrinking to its picture: 360 and 300 inside columns of 363, with the rule under each one short by that much and the two rules of a row different lengths. Now both measure 363 at 1920, 230 at 979, 450 at 978 — the column, every time. The same mechanism as `.pair` in D135, met for the third time | ||||
| D157 | The five machine pictures | Replaced with the client's: 600 x 600 JPEG on white, against cut-outs of 692 x 496, 496 x 452, 368 x 520, 632 x 372 and 467 x 469 | `machine-aromatizzatore`, `-essiccatore`, `-mixer`, `-nastro` and `-pulitore`. Four were PNGs with transparency and are now JPEGs, so the references in `pages/macchinari-accessori.json` and the `band-split` sample move from `.png` to `.jpg` | ||||
| D158 | `font.size.base` | `clamp(1rem, 0.9235rem + 0.3137vw, 1.3rem)` — fluid 16 → 20.8px, against 17 → 24 | Measured: 20.8 at 1920, 18.8 at 1280, 16 on a phone. Everything written in em follows it, because the body is where em starts: the badge, the tabs, a value's 2em pictogram, the 2.5em offset of a separator. Chosen against a source that measures 23.5 and 24 twice | ||||
| D159 | `.decor--inset-right-top` | Restored to what D145 left it: 120.6% of the column's width, `left: -35%`, `translateY(-53.44%)`, and 190% at `top: 30%` once stacked | That figure was measured against the raster for the home's band, and D146 overwrote it to make one class serve two sections. The home is back to its own shape: 756 x 756 at 1920, the lens tip clear of the band's right edge instead of clipped flat against it | ||||
| D160 | `.mould:first-of-type` below `break-mid` | No top border | Above the breakpoint the rule lives on the media cells and the first row's cells already went without one (D148); stacked, the rule moved onto the row itself (D153) and the exemption did not move with it, so the table opened with a line under nothing. Measured, six rows: `0 4 4 4 4 4` on the rows at 978 and 390 and on the media cells at 1920, the other collection `0` throughout | ||||
| D161 | `.band-row--alt` → `.band-row--wide`, reached by a new `gutters` option | 6rem between the halves, 1rem by 3rem around them, no wrap while side by side, picture first and text centred once stacked — all of it now a choice any section can make | It was tied to `surface: alt`, which is a colour. *Miscelatori per cereali* is a white band and wanted the same spacing, so the option moved out of the surface and onto its own name. Two sections carry it: the cream band on Presse and the mixers on Macchinari. Measured on the mixers: row 1366 x 632, gutter 96, padding 16/48 | ||||
| D162 | The two halves of a wide band row | Equal: `flex-basis: 0`, `flex-grow: 1`, and `max-inline-size: none` | 587 and 587 at 1920, where they were 554 and 620. Three things had to go for that: `.measure` caps the text at 54ch, which is 554px at the running size, and a half that is capped is not a half; a flex item will not shrink under its own content, so the 600px photograph held its side wider; and the heading's rule is 334px wide, which on a 230px phone column kept the text from narrowing — `min-inline-size: 0` at every width settles the last two | ||||
| D163 | `font.size.sm` and `font.size.xs` | Brought under the running text: sm 17–22 → 15–19, xs 15–20 → 13–17 | D158 took `base` down to 16–20.8 and left `sm` above it, which the validator called what it was. Both ends are ordered now — 13 < 15 < 16 on a phone, 17 < 19 < 20.8 at 1920 — and since every size is linear between the same two viewports, ordering the ends orders the whole range. Integrity back to 0 errors, the first clean run since D158 | ||||
| D164 | The running text on *Macchinari accessori* | Seven paragraphs lose their `<br>` | They were carrying the line breaks of the 1920px mock-up into a column that is 587px wide, so a line ended in the middle of a sentence and the next began under it: *"…di produzione di gallette / di cereali soffiati…"*. The text now wraps where its column ends | ||||
| D165 | The leaf showing through the machine | The five pictures get their white background knocked out and go back to PNG with alpha | D161 had blended the picture over the leaf because a JPEG carries an opaque white rectangle; multiply does hide the white, but it also tints everything light in the machine, so the leaf read as lying on it. The background is removed by flooding from the four edges through pixels whose darkest channel is 238 or more, so only the connected background goes and the machine's own whites stay: 58–73% of each picture removed, no holes. `mix-blend-mode` is deleted — 143 styles | ||||
| D166 | `.decor--inset-left-bottom` | 80% of its column's height, up from 65 | Measured: 557 x 557 behind the mixer on Macchinari, in a column of 587. Two sections carry the class, so the home's product band grew with it — its white leaf goes 398 to 582 in a 613 column. It still reads as the leaf the press stands on, and the cream still shows around it, but the change was not asked for and is stated here: a placement of its own would keep the two apart, the way D159 did for the leaf behind a text column | ||||
| D167 | The stacked text column of a wide row | 3rem under it | Measured 48px at 390 and 0 above the breakpoint. Stacked, the text ended against the next band; now it has the same air under it that the band split puts between its own parts | ||||
| D168 | `.band-row--tight` | 2rem between its lines, as between its columns | Measured 32 / 32 at 1920 and at 390, against the wide row's 96 / 0 | ||||
| D169 | *Essiccatori* and *Nastri trasportatori* | Formatted like *Miscelatori per cereali*: `pad: 0` on the section and a cream leaf behind the machine | Both already had `gutters: wide` from D165, so the two things missing were the padding and the leaf. Measured: padding 0 top and bottom on all three, leaf 557 x 557 in each | ||||
| D170 | The five pictures | Replaced with the client's own cut-outs | PNGs with alpha, cropped to the machine: 593 x 398, 524 x 454, 402 x 551, 564 x 341, 435 x 470, against the 600 x 600 squares D165 produced by flooding the white. The file names already ended in `.png`, so nothing in the pages or the spec changed — only the bytes, in the capture and in the preview alike | ||||
| D171 | `.decor--inset-left-bottom` | 110% of the column's height, held to its right edge, upright | It was 80%, inset 24% and tilted 12 degrees. Measured now: 606 behind the mixer, 433 behind the flavourer | ||||
| D172 | `.decor--inset-left-bottom` upright | 90% of the column's height, 23% in from its right edge | Measured 496 behind the mixer and 355 behind the flavourer, where 110% and `right: 0` gave 606 and 433 | ||||
| D173 | The upright leaf below `break-mid` | `left: auto` beside the `right: auto` it already had | With neither offset stated it falls back to the static position, where the flow would have left it. Measured at 390: 189 in a column of 230 behind the mixer, 93 behind the flavourer | ||||
| D174 | The picture of a wide band row below `break-mid` | `width: auto; height: 400px`, as asked | Where the column is wide enough it is exact: at 978 the mixer measures 292 x 400 at its own ratio, at 600 the cleaner 370 x 400 at its own | ||||
| D175 | The home's leaf, stacked | `left: 0`, where the five on Macchinari fall back to the flow | `.band-row--inset .decor--inset-left-bottom`: the product band is a composition of its own since D111, and its leaf was placed against the raster long before the machines had one. The row's own name is what tells them apart, so neither needs a variant of its own. Measured at 390: the home's leaf at 0 of its column, Macchinari's at −5 | ||||
| D176 | `.l-switcher--middle` below `break-mid` | Its text column ranges its lines on the middle and lays its own children on that axis; the rule under a heading leaves the flow and centres itself | A row whose halves are centred on each other should centre their contents once they stack: a full-width text column ranged left leaves the yellow rule starting at an edge the eye has no reason to look at. Measured at 390: `text-align: center`, `align-items: center`, and the rule 230 x 8 at `left: 115` — the middle of its column | ||||
| D177 | The rule added in D176 | Reverted | `.l-switcher--middle` no longer centres its stacked text column, and the rule under a heading stays in the flow. Measured at 390: `text-align: start`, `align-items: normal`, the rule `position: static` at 230 x 8 under its heading, and the heading back to two lines at 230 x 68 where centring had given 157 x 56. 149 → 148 styles, portability notes 12 → 11 | ||||
| D178 | The rule withdrawn in D177 | Back, narrowed: `.l-switcher--middle:not(.band-row--tight)` | Stacked, a middle row centres its text column and lets the rule under a heading leave the flow — every row except the tight one. Measured at 390 across the seven rows of Macchinari: `center` and `absolute` on six, `start` and `static` on the tight one. At 1920 all seven read `start` and `static`, as before | ||||
| D179 | `.section > .section__content.band-row--inset` below `break-mid` | 1rem between its halves, against the 5rem a section's content carries everywhere else | The two halves are one picture and one block of words about it; stacked they read as one thing. Measured: 16px at 390, 80 at 1920. The selector names the home's row because `section__content` is shared by four patterns and the figure belongs to this one | ||||
| D180 | `.heading-rule` | The heading hugs its words (`width: fit-content`, `position: relative`) and the rule is 100% of them, out of the flow | It was a declared 334px capped at the column — a figure that fitted the raster at one width and nothing else. Measured: 292 under *Sistemi di aspirazione fumi* at 1920, 157 at 390, each exactly its heading. `size.rule.heading` has no reader left and is deleted: 95 tokens | ||||
| D181 | `.heading-rule::after` | 4px, `border.underline` | The thinnest rule in the system, which is what a line under words has been since D135 | ||||
| D182 | `.heading-rule + p` | 75% of its column, down from 90 | |||||
| D183 | `.leaf-list` | A stack of its own: flex, column, 0.5rem between items; each item a 2rem column for its leaf and its words ranged left | Measured: `flex`, 8px, `32px 1fr`, `left` at 1920 and at 500. The left alignment is the point — D178 centres everything in a stacked middle row, and a list that centres has no left edge for the eye to follow | ||||
| D184 | The leaf on *Pulitori / setacciatori* | Translucent white, `#ffffff70` | On a brand band a cream leaf is a second colour; a white one at 44% is the band itself, lightened. Declared as `color.surface.veil`, a mix of white at 0.44 — 94 tokens, and colour coverage stays 100% because it is a token and not a hex in a rule | ||||
| D185 | The four machine bands on Macchinari | 2rem above and below, at every width, under a name that says so | They were on `pad: 0`, which stopped being true the moment D183 gave them 3em when stacked. The modifier scale makes room instead of stretching a name: the old `2xs` (24) becomes `3xs`, and `2xs` is the new 32, so the figures still climb — 24, 32, 40, 64. Measured at 1920, 500 and 399: 32 on the four bands, 24 on the three brand strips between them | ||||
| D186 | `.pair--people` | 3rem above it, down from 5 | Measured 48px at every width | ||||
| D187 | The three bands | `gutters: wide`: halves of equal width, 6rem between them, 1rem by 3rem around them | Measured at 1920: 587 / 587 on all three | ||||
| D188 | The three leaves on Servizi | `behind-text-left` and `behind-text-right`, mirror images about the vertical | Measured against the source: the leaf behind *Consulenza* had its point down-left, the source puts it down-right. The two bands now answer each other across the page — left bleeds off the left with its point down-right, right bleeds off the right with its point down-left. Both are 708 x 708, and the home keeps `inset-right-top` with its own mirror untouched | ||||
| D189 | The three bands on Servizi | The same height, the tallest setting it | Sections are siblings, so equal heights need a parent that says so: the page's `main` becomes a grid whose last three rows are `1fr`, and `1fr` rows in a grid of auto height all take the height of the tallest. Measured: 614 each at 1920, 740 at 1200, 739 at 978, where they were 373 / 506 / 614 and 348 / 589 / 740 | ||||
| D190 | Each band on Servizi | A grid of one row, so the row fills the section's content box and the two halves centre in the whole of it | Measured at 1920: sections of 582, rows of 438, 502 and 438 — the content box exactly. The `l-switcher--middle` centring was already there; what it lacked was something to centre in | ||||
| D191 | The running text | Eight paragraphs lose their `<br>` — the home's product band, Azienda's people, and all six on Servizi | The same reason as D164: they were the mock-up's line endings carried into a column of a different width. Titles keep theirs, and so do three kinds of content that only look like text — a fair's dates and city, a Green Puff feature label, and a mould's figures, each of which is two or three data on their own lines rather than a sentence that wrapped | ||||
| D192 | The stacked text column of a Servizi band | At least 21rem tall, its lines centred in that height | Measured at 978 and 390: 336px under *Consulenza*, which asks for less, and 393 / 456 and 441 / 519 for the two that ask for more. Side by side nothing changes — 172, 394, 438 at 1920, `min-height: auto`. The three bands keep a family resemblance on a phone even when the shortest of them is four lines long | ||||
| D193 | `+39 (0)11 4249644.` on Servizi | `.nowrap` on the `<strong>` that already held it | The one utility class in the system, declared in D95 for exactly this — a registration number, a share capital, a telephone number. Measured at 1920, 978, 390 and 320: the number draws one line box at each, 165 to 127px wide. The colophon's three numbers have carried the class since the first build; the one in the body of a page had been missed | ||||
| D194 | Its paragraph | Its five hand-set lines restored | D191 took the `<br>` out of eight paragraphs, this one among them. Here the breaks are the composition: the words are written on a white leaf and the leaf was fitted to that shape, so the two are one drawing and the text is not running copy that happens to wrap | ||||
| D195 | A stacked picture in a wide row | `max-height: 400px` and `object-fit: contain`, with `height` gone | The distortion D174 flagged and D182 only half closed is over. Measured at 390, each ratio against its own: mixer 0.730 / 0.730, dryer 1.154 / 1.154, conveyor 1.654 / 1.654 where it had been 0.575, flavourer 1.490 / 1.490, the trasporto photograph 2.003 / 2.003. Nothing is stretched to reach the figure; what cannot be 400 tall is simply shorter | ||||
| D196 | `.card-row__media > img` | 300px wide | Flagged: its column is 288 wide — `layout.sidebar-narrow` — and `max-width: 100%` caps the picture there, so at 1920 it draws 288, not 300. The figure only takes effect once the card stacks, where the column is wider: 300 at 451. If 300 is wanted at every width, `sidebar-narrow` has to grow with it | ||||
| D197 | The list of fairs | A grid of three columns, each fair taking two | Odd fairs hold the first two, even ones the last two, so the empty column walks from one side of the page to the other. Measured at 1920: a band of 1366 in three tracks of 413, every fair 889 wide, the odd ones at x 0 and the even at x 477 | ||||
| D198 | `.card-row__media` | No `aspect-ratio`: the picture carries the square now, not its box | Measured at 1280: the picture 300 x 300 inside a column 557 wide, where before the box was a 557 square and the picture filled it. Beside the text the flex still stretches the picture — 288 x 395 at 1920 | ||||
| D199 | Each picture | A frame of its own ratio, the picture fitted inside: 4 by 5 for the first, 2 by 1 for the second | Measured at 1920: 413 x 516 and 413 x 206, and 261 x 326 and 261 x 130 at 390. A stand photograph and a square logotype can now sit one under the other and still line up. Two things were needed to make the ratio win — `width: 100%`, because the media column centres its children rather than stretching them and a frame was as wide as its picture (600 against a column of 413), and `min-height: 0`, because a flex item will not go under its own content and the picture held the frame at 392 where the ratio asked for 206 | ||||
| D200 | The fairs' pictures | `fiera-snackex.jpg` alone, one to a fair | Six events, six pictures, all the same file in the 4 by 5 frame: 413 x 516 at 1920 and 261 x 326 at 390. Four of the six had two — a stand photograph and the fair's logotype — and two had one | ||||
| D201 | The grid | Five columns, 80% of the band, centred — at every width, not only stacked | Measured at 1920: 1093 of 1366 in five tracks of 167 | ||||
| D202 | A fair below `break-mid` | Two columns, 1fr and 2fr: the picture narrow, the name and dates wide | Measured at 978: 224 and 448 in a grid of 720 | ||||
| D203 | The grid below `break-mid` | The whole band, with 4rem between the fairs | 80% of a phone left too little for a picture and a name side by side. Measured: 900 of 900 at 978, 326 of 326 at 390 |
Primitives define the palette; components only use the semantic names. Italic notes show which site values each token absorbs.
Raw values. Named for what they are, never for where they are used.
Meaning. Each one points at a foundation value and is what components refer to.
WCAG 2.2, computed from the tokens: 4.5:1 for text, 3:1 for components; decor pairs are allowed only as decoration.
Fluid sizes grow between 390 and 1920px and stay in rem, so they follow browser zoom. Each step is shown in every weight, in sentence case and in capitals; the slider moves the fluid steps the way the clamp would at that viewport width.
Design system
Design system
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
A design system turns decisions made once into named choices that every page can reuse, so the next page starts from the brand instead of from a blank screen.
A design system turns decisions made once into named choices that every page can reuse, so the next page starts from the brand instead of from a blank screen.
SECTION LABEL
SECTION LABEL
Live examples rendered with the project's own element and component styles (spec/styles.json).
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next. With a link in the text, some strong words and small print. Press Tab to see the focus ring on the link.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
The quick brown fox jumps over the lazy dog, and a design system keeps every line, every step and every colour in step with the next.
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).

Ciclo di lavoro rapido
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
InnovazionePart of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Know-how
Giovanni Lavista
Direttore generale
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Interpack
Processing & packaging
7-13 maggio 2026
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
Part of a larger component or pattern: shown with the patterns (phase 6).
| Class | Purpose | Key properties |
|---|---|---|
.main | Page ceiling: twice the content width, no padding. Bands are adjacent and touch — measured on all six pages, there is no gap between sections. | maxWidth, marginLeft, marginRight |
.main:has(.media-column--fit-cover) | Servizi, and only Servizi: its three bands are **the same height** (D189). Sections are siblings, so equal heights need a parent that says so — the page's `main` becomes a grid whose last three rows are `1fr`, and `1fr` rows in a grid of auto height all take the height of the tallest. The hook is `:has(.media-column--fit-cover)`, which is the one thing only this page carries; a class on `main` would be cleaner and the composer does not write one. The row list states the page's shape — hero, title, three services — so a fourth service needs a fourth `1fr`. Its children take `width: 100%`: a section carries auto side margins, and auto margins on a grid item stop the stretch — without it the five of them shrank to their content, 554 to 1320 wide instead of the band. Each band is a grid of one row as well (D190), so its row fills the section's content box: the two halves are centred in the whole of it and the photograph is as tall as the band less its padding. The row gives up its own 1rem above and below, so the photograph is the band's content box and nothing less: 470 and not 438. Once the halves stack the three bands go back to their own heights (D191) — equal bands are a figure of the page side by side, and stacked they would only leave one of them half empty — and each row puts 2rem between its picture and its words. Each stacked text column stands at least 21rem tall with its lines centred in that height (D192), so the three bands keep a family resemblance even when the shortest of them is four lines long. | display, gridTemplateRows |
.section | The content column, {layout.content} wide, with the gutter **outside** it: the box is the column plus its two gutters, so the column keeps its full width wherever the window allows it and gives way only when the window is narrower (D61). Position is relative because a section may carry decor (D24). The air above and below is **3em** — 72px, the body's 24px three times over, `space.7xl` (D114). One figure for every white section on the site: a white section no longer states its padding, it inherits it. A coloured surface still overrides, and those overrides are the only ones left. | position, maxWidth, marginLeft, marginRight, paddingTop, paddingBottom, paddingLeft, paddingRight |
.section--wide | A full-bleed coloured band that keeps its content on the same column as a plain section (component rule 9): the padding is whichever is larger, the gutter or half of what the band has over the column — which lands on exactly the column a plain section lands on, at every width (D61). The clip keeps the large decorative leaves inside the band. | maxWidth, overflow, paddingLeft, paddingRight |
.section--alt | Cream band. | background |
.section--brand | Yellow band. Text on it is white at 2.07:1 — a brand choice taken with the number in hand (D11), recorded in DESIGN.md. | background, color |
.section--pad-3xs | The tightest band of all: on Macchinari accessori a yellow band is only as tall as the photograph inside it, with 24px of air measured above and below. | paddingTop, paddingBottom |
.section--pad-2xs | **2rem** above and below, at every width (D185). It is what the four machine bands on Macchinari asked for when `pad: 0` stopped meaning what its name said: a band whose halves stand on its own edges still needs to be told apart from the one before it. | paddingTop, paddingBottom |
.section--pad-xs | The tightest band: the alternating bands of Macchinari accessori, where the photograph sets the height and the padding only keeps it off the seam. | paddingTop, paddingBottom |
.section--pad-s | Tighter band. | paddingTop, paddingBottom |
.section--pad-0 | No padding: the band is exactly as tall as what it holds, as the title band is. | paddingTop, paddingBottom |
.section:not(.section--alt):not(.section--brand):not(.section--pad-0) + .section:not(.section--alt):not(.section--brand):not(.section--pad-0),
.section:not(.section--alt):not(.section--brand):not(.section--pad-0) + .section--pad-0 + .section:not(.section--alt):not(.section--brand):not(.section--pad-0) | Two white sections one after the other are **one field, not two** (D114): the second drops its top padding, so the pair is separated by 3em and not by 6. The second selector steps over a `.seam`, which is a section of zero height between two bands and must not count as one of them. A coloured band breaks the run, which is the whole point of a coloured band. | paddingTop |
.hero | Full-bleed photograph under the header, a `frame` with a measured height. The image is positioned against the band rather than flowing inside it: that way the band keeps the page column like every other section, and the photograph still runs edge to edge without the negative margins the page model forbids. | position, height, overflow |
.hero--home | The home hero is 69px taller than the inner pages. Measured, not a rounding. | height |
.masthead__bar | The bar that holds the menu and the language switch. **No height**: it is as tall as the menu plus the band's padding (D55), which lands within a pixel or two of the measured 54 and survives a longer menu, a second line, a bigger text size. It is the column; the band around it is what spans the page and what pins (D53). | position, display, alignItems, justifyContent, columnGap |
.masthead__navband | The band the menu bar sits in, and the owner of the hairline that separates the menu from the row above (D57): the rule belongs to the band it opens, not to the column inside it — which is also why it still spans the header when the band pins and goes full width. The band exists so that the menu has something to pin **as a band**: a pinned column would leave the page showing through on either side of it (D53). | background, paddingTop, paddingBottom, borderTop |
.media-column | The media side of a band. It centres its pictures instead of stretching them: a cut-out machine has a size of its own and must keep it, which a plain stack would not allow. It sits a layer **above** the text half (D98), so a machine that runs past the edge of its own column crosses over the text instead of disappearing under it. | display, flexDirection, alignItems, gap, position, zIndex |
.media-column--grid | Three photographs of one installation, read as one picture (D180): the first runs the **whole width**, the two under it share it **two to one**. A column of three stacked pictures made the band twice as tall as its text and said nothing the grid does not. Each photograph fills its cell and is cropped to it (D181), so the three make one rectangle instead of three of different heights. Under `break-narrow` the two to one goes and the three photographs stand one under the other (D186): at a third of a phone's width the smaller of the pair is a thumbnail. | display, gridTemplateColumns, gap |
.text-column | The text half of a band, and the box its own leaves are placed against (D93). The layer is what lets them sit behind the words: it makes the column a stacking context, so a leaf one layer down is still above the band's background and below everything written here. | position, zIndex |
.l-switcher--middle:not(.band-row--tight) | A row whose halves are centred on each other **centres their contents too once they stack** (D176, narrowed in D178): the text column ranges its lines on the middle and lays its own children on that axis, and the yellow rule under a heading leaves the flow to sit on it. Stacked, a text column is the full width of the card and ranging it left would leave the rule starting at an edge the eye has no reason to look at. The **tight** row is excluded by name: it keeps no padding of its own, so its text already starts at the band's edge and has nothing to centre in. | |
.text-column.text-column--inset | The text half of the home's product band: **half** the row, with 7rem of air above and below (D92, D97). It grows at 2.5 and so does the picture's half, which is what makes the two containers the same width (D97) — the composition is carried by the leaves and by the machine hanging off its column, not by an uneven split. The air on the **left** is the row's gap and not this column's padding: flex adds an item's padding outside the share it grows into, so a padded half is always wider than an unpadded one by exactly that padding (measured: 723 against 611 at 1920). `max-width: none` has to come with it, and the selector has to carry the column's own class: `.measure` is on this column and is declared later, so 54ch would otherwise hold it to 47% of the row however hard it is told to grow — the same trap as the leaf in the button (D81). Once the halves are stacked **both** paddings go and the column becomes `fit-content`, centred in the row (D105–D107): it is then as wide as its widest line, and everything in it — heading, paragraph, label, badge — stretches to that one width and starts on the same left edge, which centring each block on its own did not do: the 7rem were there to centre the text against a picture beside it, and what is above the text now is the picture itself, with the column's own row gap between them. The column is then exactly as tall as its words, which is what the leaf behind it is measured from. | flexGrow, maxWidth, paddingTop, paddingBottom |
.band-row--inset | The row of a product band whose halves are inset: **7rem** between the machine and the text (D97). Stating it here instead of as padding on the text keeps the two halves the same width, and the air lands where it is meant to be — between them — with the machine free to lean into it. Below **1200px** the gap stops being a fixed 7rem and becomes **7% of the row** (D104): 112px is a tenth of the row at 1920 and a quarter of it at 1000, and a quarter of the row spent on nothing is what squeezed the two halves. At **978px** it stops being a row at all and becomes a **column** (D105, D107) rather than a wrapped row: the switcher would reach the same arrangement by wrapping, at 977, but a column states it — and states **where** (D107), which is why the band turns at 978 and not at a width the gutter arithmetic happened to produce — and the three things that only make sense across a row — the basis that drives the wrap, the centring, the gap between columns — stop applying instead of being worked around. | columnGap |
.section > .section__content.band-row--inset | The home's product band closes up once its halves stack (D179): **1rem between them**, against the 5rem a section's content carries everywhere else. The two halves of this band are one picture and one block of words about it, and stacked they read as one thing. | |
.section > .section__content.band-row--wide | A band split that **stands inside its band**: 6rem between its halves, **which are equal** (D162), 1rem by 3rem of padding around them, nothing between its rows, and it never wraps while the two are side by side — the switcher's own threshold would break the row before the breakpoint that governs the rest of the page does. Once the switcher is a single column the photograph comes first and stands **up to** 400px tall (D174, D195): a ceiling and not a height, so nothing is stretched to reach it, and the text column centres itself under it, its lines ranged left as everywhere else (D166). A choice, not a colour (D161): any section can ask for it with `gutters: wide`. The selector carries both of the row's other names because `.section > .section__content` declares a row gap of its own and is written later in the sheet, and the halves carry `min-inline-size: 0` because a flex item will not shrink below its content and a 600px photograph would otherwise keep its half wider than the other. The halves also drop their `max-inline-size`: `.measure` caps the text at 54ch, which at the running size is 554px, and a half that is capped is not a half. Both halves carry `min-inline-size: 0` at every width: the heading's yellow rule is 334px wide, and a flex item will not go under its own content, so a column of 230px on a phone was refusing to be narrower than that rule. A column that takes its height from the text puts its picture back in the flow when the halves stack (D188), full width and its own height — `max-width: none` is what lets it fill a half, and nothing caps it once the half is the page. Stacked it gives up its 3rem at the sides as well (D198): the band's own gutter is all the margin a phone has room for. | columnGap, padding, rowGap |
.section > .section__content.band-row--tight | A wide band row with the **switcher's own spacing** (D167): no padding and 2rem between the halves. It keeps everything else `band-row--wide` brings — halves of equal width, no wrap while they are side by side, picture first and text centred once they stack — because `gutters: tight` declares both classes. The spacing is the only thing it takes back. 2rem between its lines as well (D168), and its text column drops the auto margins the wide row gives it: with no padding around the row there is nothing to centre the column **in**, so it starts where the band starts. Its photograph keeps its own proportions at every width (D182): full width of the column and the height that follows, instead of the 400px a wide row gives a stacked picture. | padding, columnGap, rowGap |
.section > .section__content.band-row--wide > .media-column--grid > img | A gallery decides for its own pictures (D182). The row's rule for a stacked photograph — 400px tall, width to follow — has the same weight as the gallery's, so this one is written after it and says so plainly: inside a grid, each picture fills its cell and is cropped to it, at every width. | width, height, objectFit |
.section--alt > .section__content.band-row--wide | The cream band on Presse puts **2rem between its lines** (D175), where a white one puts none: it is the only wide row whose two halves are a block of text and a photograph of equal weight, and stacked they need telling apart. | rowGap |
.media-column--inset | The picture's half of that row: the same 2.5 as the text, so the two halves measure the same (D97). | flexGrow |
.media-column--fit | A media column that takes the height the text gives it instead of setting one (D91). **Only while the two halves are side by side** (D104): at 978px and below the row is a column, the column is alone on its line with nothing in its flow, and a picture that has left the flow leaves it 0 tall — which is how the phone lost the machine and the two leaves behind it. Below that width the picture is **in the flow** again, at its own size, and the column is as tall as it. The picture leaves the flow and fills the column, keeping its proportions — the same device the hero uses (D35) — and hangs **3% of the column past its right edge** (D94, D100). It is anchored by that right edge and takes its width from its own proportions, so the machine keeps its shape and the overhang stays the same fraction of the half at every width. Without it a tall product photograph decides how tall the band is, and the text floats in the middle of whatever is left. | alignSelf, minHeight, position |
.media-column--fit.media-column--fit-cover | A photograph that takes the height of the text beside it (D188): it fills the half and is cropped to it, where the product band's machine is fitted inside without cropping. Both leave the flow so the **text** sets the row's height, and both come back into it once the halves stack. | |
.card-row | The two home cards: **two columns above 978px, one below** (D118), declared and not left to auto-fit, so the rule that releases the button meets the same breakpoint exactly. 64px between the columns and 64 down (D117). | gridTemplateColumns, columnGap, rowGap |
.card-row > .l-sidebar | A stacked card is a **grid of two rows**, not a wrapped sidebar (D119): the picture's row is `auto` and the text's is `1fr`, so every pixel the grid adds to the shorter card goes **inside the text** and none of it above it. Wrapped, a flex container shares that space equally between its lines, which pushed the two headings off each other's line by twenty-odd pixels. The two windows where the picture sits beside the text put the sidebar back on flex, where it belongs. | display, gridTemplateColumns, gridTemplateRows |
.card-row__media | The picture beside a card's text. The **picture** carries the square now (D198), not its box: 300px wide, square, and the column takes whatever height that gives it. Beside the text the flex stretches the picture to the card; stacked, nothing does and the square holds. | display, alignSelf |
.l-stack > .card-row__action | The button of a card: at the **right end** of its column, and on the **floor** of the card — `l-push`, with nothing above it. Once the grid is a single column there is no neighbour to line up with, so the push is released and the button follows its paragraph (D118). The selector carries `.l-stack` because that is where `l-push` declares its auto margin, and a single class would lose to it. | justifyContent |
.band | A section that carries decorative leaves: it becomes their positioning context and clips whatever runs past its edge. | position, overflow |
.band--inset | The home's product band is the one section whose top padding is not the one its padding option gives it (D112): **144px** while its halves are side by side, against the 64 of `.section--pad-s`. The white leaf the text is written on runs past the top of the band, so the words start lower than the band does, and the 64 that is right for a plain cream band leaves the heading crowded against the edge. Once the halves stack the picture is on top and the leaf is behind it, so the band goes back to 96 (D104). | paddingTop |
.masthead__top | The header's first row: contacts, logo, social marks. Its three parts sit on the **bottom** of the row (D59), so the contact lines and the social marks line up with the foot of the logo however large the logo's clamp makes it. As tall as they are plus its own padding (D55), and with no width of its own: the header states it once, for both rows (D56). | display, alignItems, justifyContent, paddingTop, paddingBottom |
.pair | Two columns of equal width, **narrow and centred in the band** (D125): the four blocks of the approach hold the middle of the page and leave the margins to the leaves, which is how the source draws them. **55% of the band**, 75% on a phone (D126). Declared and not auto-fit, like every other grid on the site since D115, and a single column below `break-mid`. 4rem between the columns, 5rem between the rows. | display, gridTemplateColumns, width, marginLeft, marginRight, columnGap, rowGap |
.pair--values | The four values on the yellow band take **75%** of it, with 6rem between the columns and **1rem between the rows** (D134): the separator hangs into that gap instead of sitting on the join. Below `break-mid` it takes the whole band (D135): once there is one column left, holding it back from the edges only makes the line shorter for no reason. | width, columnGap, rowGap |
.pair--people | The two people take **as much of the band as they need** (D136): `width: auto` against the auto side margins `.pair` carries means as wide as the two of them, centred. 3rem above them at every width (D186), so the pair sits apart from the paragraph that introduces it. | width, marginTop |
.pair--features | The six Green Puff features take the same grid as the four values on Azienda (D142), with two figures of their own (D143): **`width: auto`**, so against `.pair`'s auto side margins the block is as wide as its six labels and centred, and **`grid-auto-rows: 1fr`**, so all three rows are the height of the tallest — *Struttura e pannellatura completa* sets on three lines and the other five keep its height, which is what puts the separators of the two columns on the same line. | width, gridAutoRows, columnGap, rowGap |
.nowrap | A phrase that must not be broken across lines: a registration number, a share capital, a telephone number, the name of a policy (D95). It is the one utility class in the system, and it exists because the alternative — non-breaking spaces inside the content — hides a typographic rule inside the words. | whiteSpace |
.order-first | Puts a column first without changing the document order: the patterns alternate which side the media sits on, and an option may only add a class, never reorder the markup. | order |
.measure | Reading width in characters of the text's own font (component rule 14). | maxWidth |
.measure--narrow | The narrow text column. | maxWidth |
.measure--wide | 89ch, far past comfortable reading. It holds one or two centred sentences and is kept as measured rather than silently corrected. | maxWidth |
.product-row | A row of cut-out products under a tab. An explicit grid and not an intrinsic one (D115): the number of columns is declared, because the client declared it — the declared count above 978px, two at or below it, one on a phone (D116). Each picture takes the full width of its cell. While the row is **one row** it sits in a square of its own — `aspect-ratio: 1` with `object-fit: contain` — so a round galletta and a flat one occupy the same box and line up on their centres. Once the row folds (D120) the square goes and so does the stretch: below 978 each picture is drawn at its own size (D123), centred in its cell both ways, so two rows of products read as products and not as a wall of boxes. The gutter is 64 both ways (D117): it was 64 across and 32 down, and two rows of squares need as much air between them as the squares have beside them. The modifier carries two classes, so the two queries have to carry them too or they lose on specificity and nothing moves. The row is inset **10% on each side** (D124): the products sit in the middle of the column and not against the tab's own edges, which is how the source draws them — the picture ends well before the rule above it does. | display, gridTemplateColumns, paddingLeft, paddingRight, columnGap, rowGap, alignItems, justifyItems |
.product-families | The families of one section, one under the other inside the column that holds the first tab (D115). 3em between them, the same air a white section puts above and below itself (D114), so the two families are spaced like sections without being sections. | rowGap |
.mould-rows | The rows of the mould table, one against the next (D149): the gap between them is zero, because each row carries its own air under its pictures and the rule is the only thing that separates them. | rowGap |
.events-grid | The fairs: a grid of **five columns** holding 80% of the band, centred, in which every fair takes four (D201). Odd fairs take the first four and even ones the last four, so one column is empty on the right and then on the left and the list walks down the page. One column below `break-mid`. It puts 4rem between the fairs once stacked, and takes the whole band only under `break-narrow` (D204), where 80% of 326 is not enough for a photograph. | display, gridTemplateColumns, width, margin, columnGap |
| Part | Tokens and rules |
|---|---|
| consent | owner: platform consent plugin (not a component); surface: {color.surface.default}; text: {color.text.default}; accept: btn; reject: btn; border: {color.border.control}; radius: {radius.pill}; focus: {color.accent.default}; note: The captured pages carry no consent banner; these are the tokens it must use when the client chooses a plugin. |
Section patterns with their options and repeatable lists, shown with sample content.

Idealmac è in grado di supportare i propri clienti durante tutte le fasi del processo produttivo alimentare.
I miscelatori sono utilizzati in tutte le linee di produzione di gallette di cereali soffiati.


Gallette/ciclo: 6 (4 - 10 g)
Capacità: fino a 2700 pz/h

Conosciamo le dinamiche produttive perché provengono dall'esperienza diretta nel settore alimentare.
InnovazioneIdealmac è guidata da un team che unisce competenze tecniche e conoscenza del processo produttivo.

Giovanni Lavista
Direttore generale

Interpack
Processing & packaging
7-13 maggio 2026
Dusseldorf-Germania
Each page is composed only from spec/patterns.json, spec/styles.json and spec/tokens.json, with the original copy (pages/*.json). Header, footer and cookie banner are left to the platform.
| Page | Sections | Open | Screenshots |
|---|---|---|---|
| Azienda — Idealmac | hero → page-title → intro → approach-grid → values-band → people | rebuilt page | original · rebuilt (1280px) |
| Fiere ed eventi — Idealmac | hero → page-title → events-list | rebuilt page | original · rebuilt (1280px) |
| Idealmac — Tecnologie per l'industria alimentare | hero → page-title → intro → seam → product-band → seam → card-row → product-family | rebuilt page | original · rebuilt (1280px) |
| Macchinari accessori — Idealmac | hero → page-title → band-split → band-split → band-split → band-split → band-split → band-split → band-split | rebuilt page | original · rebuilt (1280px) |
| Presse per gallette e snack non fritti — Idealmac | hero → page-title → feature-grid → seam → band-split → seam → mould-table | rebuilt page | original · rebuilt (1280px) |
| Servizi — Idealmac | hero → page-title → band-split → band-split → band-split | rebuilt page | original · rebuilt (1280px) |
Overall: FAIL — full report in analysis/report.md.
Values farther than ΔE 8 or ±4px from any token: merged by a decision, replaced by the flow model, or written inside a component (decorative overlays).
| Kind | Site value | Uses | Nearest token | Difference |
|---|---|---|---|---|
| Spacing | 86px | 3 | space.8xl | -6px |
| Spacing | 90px | 2 | space.9xl | +6px |
| Spacing | 103px | 1 | space.9xl | -7px |
| Spacing | 107px | 1 | space.10xl | +5px |
| Spacing | 117px | 1 | space.10xl | -5px |
| Spacing | 138px | 1 | space.12xl | +6px |
| Spacing | 170px | 1 | space.13xl | +22px |
| Radius | 20px | 5 | radius.m | -5px |
| Page | Viewport | Height | Overflow | Misaligned sections |
|---|---|---|---|---|
| index | 390 | 6554 | 0 | 0 |
| index | 1280 | 5313 | 0 | 0 |
| index | 2400 | 4877 | 0 | 0 |
| azienda | 390 | 4482 | 0 | 0 |
| azienda | 1280 | 4302 | 0 | 0 |
| azienda | 2400 | 3414 | 0 | 0 |
| presse-per-gallette | 390 | 8147 | 0 | 0 |
| presse-per-gallette | 1280 | 5839 | 0 | 0 |
| presse-per-gallette | 2400 | 5634 | 0 | 0 |
| macchinari-accessori | 390 | 6992 | 0 | 0 |
| macchinari-accessori | 1280 | 5697 | 0 | 0 |
| macchinari-accessori | 2400 | 6133 | 0 | 0 |
| servizi | 390 | 3524 | 0 | 0 |
| servizi | 1280 | 2927 | 0 | 0 |
| servizi | 2400 | 3338 | 0 | 0 |
| fiere-ed-eventi | 390 | 5919 | 0 | 0 |
| fiere-ed-eventi | 1280 | 6014 | 0 | 0 |
| fiere-ed-eventi | 2400 | 6189 | 0 | 0 |












| Where | Note |
|---|---|
*, ::before, ::after | @media (prefers-reduced-motion): check support in the target's media-query UI |
*, ::before, ::after | animation: needs the @keyframes shipped separately |
a | @media (prefers-reduced-motion): check support in the target's media-query UI |
:focus-visible | :focus-visible: element-level global style; check the target accepts pseudo-class selectors |
.main:has(.media-column--fit-cover) | :has(): a relational selector; GenerateBlocks has no UI for it, ship it as CSS |
.l-switcher--middle:not(.band-row--tight) | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.site-nav | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.heading-rule | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.btn | @media (prefers-reduced-motion): check support in the target's media-query UI |
.tab-row | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.leaf-list | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.feature--divided | pseudo-element (::before/::after): supported as a nested selector; check the content property |
.value--divided | pseudo-element (::before/::after): supported as a nested selector; check the content property |