---
version: 1.0
name: VinoPrint-design-system
description: A press-room design language for a winery that prints its own labels. Every surface is built as a printed label — an outer field with an inset rule and a deep foot margin, locked to the 1:1.237 label ratio taken from the logo. Barolo burgundy (#662731) is the only brand color and carries every action; foil brass (#b08a46) appears solely where a real print finish is being sold. Corners are square because paper is square, shadows exist only where paper physically lifts, and the type system pairs a geometric display face (the wordmark's voice) with a neutral UI face and one editorial serif reserved for vintages and winemaker copy.

colors:
  # --- Brand ---
  primary: "#662731"
  primary-hover: "#521e27"
  primary-active: "#3c151c"
  primary-subtle: "#f9f1f2"
  primary-subtle-2: "#f3e2e5"
  primary-border: "#e7c6cb"
  primary-muted: "#d39da6"
  primary-bright: "#b8616f"
  primary-mid: "#983e4d"
  primary-deep: "#521e27"
  primary-deepest: "#260d11"
  primary-on-dark: "#d39da6"

  # --- Foil (print finish accent) ---
  foil: "#b08a46"
  foil-light: "#d8b878"
  foil-pale: "#ebd9a8"
  foil-deep: "#8a6a2e"
  foil-subtle: "#faf4e6"
  foil-on-dark: "#d8b878"

  # --- Paper (light theme surfaces) ---
  canvas: "#fbf8f4"
  surface: "#ffffff"
  surface-sunken: "#f2ece3"
  surface-kraft: "#efe6d8"
  surface-raised: "#ffffff"

  # --- Cellar (dark theme surfaces) ---
  canvas-dark: "#1a1013"
  surface-dark: "#241a1e"
  surface-dark-raised: "#2e2126"
  surface-dark-sunken: "#140c0f"

  # --- Ink ---
  ink: "#241a1c"
  ink-secondary: "#4a3d40"
  ink-muted: "#6e625b"
  ink-disabled: "#a1958d"
  on-primary: "#ffffff"
  on-dark: "#f7f2ec"
  on-dark-muted: "#b3a49d"
  on-foil: "#241a1c"

  # --- Rules & borders ---
  hairline: "#e3dacd"
  hairline-strong: "#cdbfad"
  hairline-dark: "#3a2b30"
  hairline-dark-strong: "#4e3b41"

  # --- Semantic ---
  success: "#43694f"
  success-subtle: "#e8f0e9"
  success-on-dark: "#8fbf9e"
  warning: "#b4741c"
  warning-subtle: "#fbf0de"
  warning-on-dark: "#e0a854"
  danger: "#c23a2b"
  danger-subtle: "#fceae7"
  danger-on-dark: "#f08d80"
  info: "#3d5a80"
  info-subtle: "#e9eef5"
  info-on-dark: "#8fabcf"

  # --- Technical (prepress guides only, never UI) ---
  dieline: "#00a3c4"
  bleed-guide: "#ff3d7f"
  safe-guide: "#7a8f3f"

gradients:
  foil-sheen: "linear-gradient(105deg, #8a6a2e 0%, #d8b878 34%, #b08a46 50%, #ebd9a8 66%, #9a7838 100%)"
  cellar-fade: "linear-gradient(180deg, rgba(26,16,19,0) 0%, rgba(26,16,19,0.86) 62%, #1a1013 100%)"
  paper-tint: "linear-gradient(180deg, #ffffff 0%, #f2ece3 100%)"

typography:
  wordmark:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 32px
    fontWeight: 300
    lineHeight: 1.0
    letterSpacing: 0.02em
    textTransform: lowercase
  display-hero:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 72px
    fontWeight: 300
    lineHeight: 1.04
    letterSpacing: -0.015em
  display-xl:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 56px
    fontWeight: 300
    lineHeight: 1.06
    letterSpacing: -0.012em
  display-lg:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 40px
    fontWeight: 400
    lineHeight: 1.12
    letterSpacing: -0.01em
  display-md:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 32px
    fontWeight: 400
    lineHeight: 1.18
    letterSpacing: -0.008em
  display-sm:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 24px
    fontWeight: 500
    lineHeight: 1.25
    letterSpacing: -0.005em
  title:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 20px
    fontWeight: 500
    lineHeight: 1.3
    letterSpacing: 0
  eyebrow:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 12px
    fontWeight: 500
    lineHeight: 1.2
    letterSpacing: 0.2em
    textTransform: uppercase
  nav-link:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.2
    letterSpacing: 0.08em
    textTransform: uppercase
  body-lg:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 18px
    fontWeight: 400
    lineHeight: 1.6
    letterSpacing: 0
  body:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 16px
    fontWeight: 400
    lineHeight: 1.65
    letterSpacing: 0
  body-sm:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.6
    letterSpacing: 0.005em
  caption:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 13px
    fontWeight: 400
    lineHeight: 1.45
    letterSpacing: 0.01em
  micro:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 11px
    fontWeight: 500
    lineHeight: 1.3
    letterSpacing: 0.06em
    textTransform: uppercase
  form-label:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 13px
    fontWeight: 500
    lineHeight: 1.3
    letterSpacing: 0.04em
    textTransform: uppercase
  button:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 14px
    fontWeight: 500
    lineHeight: 1.0
    letterSpacing: 0.06em
    textTransform: uppercase
  button-sm:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 12px
    fontWeight: 500
    lineHeight: 1.0
    letterSpacing: 0.07em
    textTransform: uppercase
  price:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 20px
    fontWeight: 500
    lineHeight: 1.2
    letterSpacing: -0.01em
    fontVariantNumeric: tabular-nums
  price-lg:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 28px
    fontWeight: 500
    lineHeight: 1.15
    letterSpacing: -0.015em
    fontVariantNumeric: tabular-nums
  data:
    fontFamily: "Circular Std, Circular, DM Sans, system-ui, sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.5
    letterSpacing: 0
    fontVariantNumeric: tabular-nums
  editorial-lead:
    fontFamily: "Fraunces, Georgia, serif"
    fontSize: 24px
    fontWeight: 300
    lineHeight: 1.5
    letterSpacing: -0.01em
  editorial-quote:
    fontFamily: "Fraunces, Georgia, serif"
    fontSize: 32px
    fontWeight: 300
    lineHeight: 1.35
    letterSpacing: -0.01em
    fontStyle: italic
  vintage-numeral:
    fontFamily: "Fraunces, Georgia, serif"
    fontSize: 48px
    fontWeight: 400
    lineHeight: 1.0
    letterSpacing: 0.02em
    fontVariantNumeric: tabular-nums

rounded:
  none: 0px
  xs: 2px
  sm: 3px
  die-cut: 4px
  pill: 999px
  full: 50%

spacing:
  xxs: 4px
  xs: 8px
  sm: 12px
  md: 16px
  lg: 24px
  xl: 32px
  xxl: 48px
  xxxl: 64px
  huge: 96px
  section: 128px

frame:
  inset-sm: 12px
  inset-md: 24px
  inset-lg: 40px
  foot-multiplier: 5
  rule-width: 1px
  rule-width-strong: 2px
  field-aspect: 0.809
  lockup-aspect: 0.678

elevation:
  flat: "none"
  rule: "0 0 0 1px {colors.hairline}"
  lift-1: "0 1px 2px rgba(36,26,28,0.06), 0 2px 8px rgba(36,26,28,0.04)"
  lift-2: "0 2px 4px rgba(36,26,28,0.06), 0 12px 28px rgba(36,26,28,0.08)"
  lift-3: "0 24px 64px rgba(36,26,28,0.16)"
  paper-drop: "0 18px 40px -12px rgba(36,26,28,0.35)"
  foil-emboss: "inset 0 1px 0 rgba(255,255,255,0.35), inset 0 -1px 0 rgba(0,0,0,0.18)"

motion:
  duration-fast: 120ms
  duration-base: 200ms
  duration-slow: 320ms
  duration-page: 480ms
  ease-settle: "cubic-bezier(0.2, 0.7, 0.2, 1)"
  ease-exit: "cubic-bezier(0.6, 0, 0.9, 0.4)"
  rise-distance: 8px

breakpoints:
  xs: 380px
  sm: 480px
  md: 640px
  lg: 768px
  xl: 1024px
  xxl: 1280px
  xxxl: 1440px
  wide: 1680px

layout:
  container-max: 1280px
  container-wide: 1440px
  container-prose: 680px
  columns: 12
  gutter: 24px
  page-margin: 24px
  page-margin-lg: 48px

components:
  announcement-bar:
    backgroundColor: "{colors.primary-deep}"
    textColor: "{colors.on-dark}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    height: 36px
  global-nav:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    typography: "{typography.nav-link}"
    rounded: "{rounded.none}"
    height: 72px
    borderBottom: "1px solid {colors.hairline}"
  global-nav-scrolled:
    backgroundColor: "rgba(251,248,244,0.92)"
    textColor: "{colors.ink}"
    typography: "{typography.nav-link}"
    height: 60px
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.button}"
    rounded: "{rounded.none}"
    padding: 16px 32px
    minHeight: 48px
  button-primary-hover:
    backgroundColor: "{colors.primary-hover}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.none}"
  button-primary-active:
    backgroundColor: "{colors.primary-active}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.none}"
  button-primary-disabled:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink-disabled}"
    rounded: "{rounded.none}"
  button-secondary:
    backgroundColor: transparent
    textColor: "{colors.primary}"
    typography: "{typography.button}"
    rounded: "{rounded.none}"
    padding: 15px 31px
    border: "1px solid {colors.primary}"
    minHeight: 48px
  button-secondary-hover:
    backgroundColor: "{colors.primary-subtle}"
    textColor: "{colors.primary-hover}"
    border: "1px solid {colors.primary-hover}"
  button-quiet:
    backgroundColor: transparent
    textColor: "{colors.ink}"
    typography: "{typography.button}"
    rounded: "{rounded.none}"
    padding: 15px 23px
    border: "1px solid {colors.hairline-strong}"
  button-foil:
    backgroundColor: "{gradients.foil-sheen}"
    textColor: "{colors.on-foil}"
    typography: "{typography.button}"
    rounded: "{rounded.none}"
    padding: 16px 32px
    boxShadow: "{elevation.foil-emboss}"
  button-on-dark:
    backgroundColor: "{colors.on-dark}"
    textColor: "{colors.primary}"
    typography: "{typography.button}"
    rounded: "{rounded.none}"
    padding: 16px 32px
  button-sm:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.button-sm}"
    rounded: "{rounded.none}"
    padding: 11px 20px
    minHeight: 40px
  button-icon:
    backgroundColor: transparent
    textColor: "{colors.ink}"
    rounded: "{rounded.none}"
    size: 44px
  text-link:
    backgroundColor: transparent
    textColor: "{colors.primary}"
    typography: "{typography.body}"
    borderBottom: "1px solid {colors.primary-border}"
  text-link-on-dark:
    backgroundColor: transparent
    textColor: "{colors.foil-on-dark}"
    typography: "{typography.body}"
  input-text:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.body}"
    rounded: "{rounded.none}"
    padding: 13px 16px
    border: "1px solid {colors.hairline-strong}"
    minHeight: 48px
  input-text-focus:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    border: "1px solid {colors.primary}"
    boxShadow: "0 0 0 3px {colors.primary-subtle-2}"
  input-text-error:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    border: "1px solid {colors.danger}"
  input-select:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.body}"
    rounded: "{rounded.none}"
    padding: 13px 40px 13px 16px
    border: "1px solid {colors.hairline-strong}"
    minHeight: 48px
  input-search:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 12px 16px 12px 42px
    border: "1px solid transparent"
    minHeight: 44px
  artwork-dropzone:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink-muted}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 48px 24px
    border: "1px dashed {colors.hairline-strong}"
  artwork-dropzone-active:
    backgroundColor: "{colors.primary-subtle}"
    textColor: "{colors.primary}"
    border: "1px dashed {colors.primary}"
  quantity-stepper:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.data}"
    rounded: "{rounded.none}"
    border: "1px solid {colors.hairline-strong}"
    height: 48px
  label-frame:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    rounded: "{rounded.none}"
    padding: 24px
    border: "1px solid {colors.hairline}"
  label-frame-inverted:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.none}"
    padding: 24px
  label-preview-canvas:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink-muted}"
    rounded: "{rounded.none}"
    padding: 40px
    aspectRatio: "{frame.field-aspect}"
  label-artwork:
    backgroundColor: "{colors.surface}"
    rounded: "{rounded.die-cut}"
    boxShadow: "{elevation.paper-drop}"
    aspectRatio: "{frame.field-aspect}"
  product-card:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.title}"
    rounded: "{rounded.none}"
    padding: 20px
    border: "1px solid {colors.hairline}"
  product-card-hover:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    border: "1px solid {colors.primary}"
    boxShadow: "{elevation.lift-1}"
  bottle-card:
    backgroundColor: "{colors.surface-kraft}"
    textColor: "{colors.ink}"
    typography: "{typography.title}"
    rounded: "{rounded.none}"
    padding: 0px
  option-tile:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 16px
    border: "1px solid {colors.hairline-strong}"
    minHeight: 88px
  option-tile-selected:
    backgroundColor: "{colors.primary-subtle}"
    textColor: "{colors.primary}"
    rounded: "{rounded.none}"
    border: "2px solid {colors.primary}"
  option-tile-disabled:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink-disabled}"
    border: "1px solid {colors.hairline}"
  swatch-chip:
    backgroundColor: "{colors.surface}"
    rounded: "{rounded.none}"
    size: 56px
    border: "1px solid {colors.hairline-strong}"
  swatch-chip-foil:
    backgroundColor: "{gradients.foil-sheen}"
    rounded: "{rounded.none}"
    size: 56px
    boxShadow: "{elevation.foil-emboss}"
  step-rail:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink-muted}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    borderTop: "1px solid {colors.hairline}"
  badge-neutral:
    backgroundColor: "{colors.surface-sunken}"
    textColor: "{colors.ink-secondary}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    padding: 5px 10px
  badge-brand:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    padding: 5px 10px
  badge-foil:
    backgroundColor: "{gradients.foil-sheen}"
    textColor: "{colors.on-foil}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    padding: 5px 10px
  badge-outline:
    backgroundColor: transparent
    textColor: "{colors.ink-secondary}"
    typography: "{typography.micro}"
    rounded: "{rounded.none}"
    padding: 4px 9px
    border: "1px solid {colors.hairline-strong}"
  filter-chip:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink-secondary}"
    typography: "{typography.caption}"
    rounded: "{rounded.none}"
    padding: 8px 14px
    border: "1px solid {colors.hairline-strong}"
  filter-chip-active:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.caption}"
    rounded: "{rounded.none}"
    padding: 8px 14px
  spec-table:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.data}"
    rounded: "{rounded.none}"
    border: "1px solid {colors.hairline}"
  price-row:
    backgroundColor: transparent
    textColor: "{colors.ink-secondary}"
    typography: "{typography.data}"
  price-row-total:
    backgroundColor: transparent
    textColor: "{colors.ink}"
    typography: "{typography.price}"
    borderTop: "1px solid {colors.hairline-strong}"
  cart-drawer:
    backgroundColor: "{colors.canvas}"
    textColor: "{colors.ink}"
    rounded: "{rounded.none}"
    padding: 24px
    boxShadow: "{elevation.lift-3}"
    width: 440px
  proof-panel:
    backgroundColor: "{colors.foil-subtle}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 24px
    border: "1px solid {colors.foil}"
  alert-info:
    backgroundColor: "{colors.info-subtle}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 14px 16px
    borderLeft: "3px solid {colors.info}"
  alert-success:
    backgroundColor: "{colors.success-subtle}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 14px 16px
    borderLeft: "3px solid {colors.success}"
  alert-warning:
    backgroundColor: "{colors.warning-subtle}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 14px 16px
    borderLeft: "3px solid {colors.warning}"
  alert-danger:
    backgroundColor: "{colors.danger-subtle}"
    textColor: "{colors.ink}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 14px 16px
    borderLeft: "3px solid {colors.danger}"
  age-gate:
    backgroundColor: "{colors.canvas-dark}"
    textColor: "{colors.on-dark}"
    typography: "{typography.body}"
    rounded: "{rounded.none}"
    padding: 48px
  editorial-band:
    backgroundColor: "{colors.surface-kraft}"
    textColor: "{colors.ink}"
    typography: "{typography.editorial-lead}"
    rounded: "{rounded.none}"
    padding: 96px
  cellar-band:
    backgroundColor: "{colors.canvas-dark}"
    textColor: "{colors.on-dark}"
    typography: "{typography.display-lg}"
    rounded: "{rounded.none}"
    padding: 128px
  footer:
    backgroundColor: "{colors.canvas-dark}"
    textColor: "{colors.on-dark-muted}"
    typography: "{typography.body-sm}"
    rounded: "{rounded.none}"
    padding: 96px
---

## Overview

VinoPrint makes wine and prints the labels that go on it. The design language takes that literally: **the interface is a print shop, and every surface is a label in progress.**

The whole system is derived from one construction found in the logo — an outer field, a rule inset from its edge, and a **foot margin five times deeper than the top margin**, where the wordmark sits. That shape is the page hero, the product card, the label preview, the checkout summary. Repeat it at different scales and the site reads as a printed sheet rather than a website.

Color discipline is extreme. **Barolo Burgundy (`{colors.primary}` — #662731) is the only brand color**, inherited straight from the logo, and it carries every interactive element on the site. There is no secondary brand hue. The one accent — **Foil Brass (`{colors.foil}` — #b08a46)** — is not decoration: it appears *only* where a genuine print finish (hot foil stamping) is being offered, priced, or previewed. If the customer cannot buy it, it must not be gold.

Density sits between a winery's editorial pace and a configurator's working density. Marketing surfaces breathe at `{spacing.section}` (128px); the label configurator and checkout tighten to 16–24px because they are tools, not stories.

**Key Characteristics:**
- **The Frame is the signature.** Outer field + 1px inset rule + deep foot. Used at hero, card, preview, and modal scale. Ratio `{frame.foot-multiplier}` (foot = 5 × top inset) comes directly from the logo geometry.
- **Square corners everywhere.** Radius scale tops out at 3px. Paper is cut, not rounded. The only exception is `{rounded.die-cut}` (4px) on rendered label artwork, simulating a die-cut label, and `{rounded.pill}` on nothing at all — pills are banned.
- **One brand color, one accent with a job.** Burgundy for action, brass for print finishes. Nothing else is decorative.
- **Warm paper neutrals, not gray.** The canvas is `{colors.canvas}` (#fbf8f4) — uncoated stock, not #fafafa. Every neutral carries a warm cast; there is no cool gray in the system.
- **Shadows only where paper lifts.** `{elevation.paper-drop}` under a rendered label or bottle mockup. Chrome, buttons, and nav are flat. Hierarchy comes from rules and surface changes.
- **One interface voice, one editorial voice.** Circular carries everything structural, from the 72px headline to the legal line; Fraunces appears only in winemaker copy and vintage numerals.
- **Uppercase micro-typography.** Eyebrows, buttons, form labels and badges are uppercase with 0.06–0.2em tracking — the vocabulary of a technical spec sheet and a wine back-label at once.
- **Dark mode is a cellar, not a dark gray UI.** `{colors.canvas-dark}` (#1a1013) is burgundy-black, derived by pulling the brand hue down to 6% lightness.

## Brand Foundation

> Source: `assets/brand/vinoprint-lockup-light-bg.svg`, `assets/brand/vinoprint-lockup-dark-bg.svg`, both `180.73 × 266.4`.

### The Logo Construction

The lockup is a portrait field at **1 : 1.474** containing an inset rectangle, the pour mark centered inside it, and the lowercase wordmark set in the deep foot margin below.

| Measurement | Light-background lockup | Dark-background lockup | Token |
|---|---|---|---|
| Side inset | 6.05% of width | 9.38% of width | `{frame.inset-md}` / `{frame.inset-lg}` |
| Top inset | 4.30% of height | 6.64% of height | — |
| Foot margin | 5.10 × top inset | 3.80 × top inset | `{frame.foot-multiplier}` |
| Inner field aspect | 1 : 1.2373 | 1 : 1.2363 | `{frame.field-aspect}` (0.809) |

The inner field aspect is **identical across both variants (1 : 1.237)**. That is not a coincidence — it is the brand's field proportion, and it governs every **brand** surface: The Frame, product thumbnails, the two label faces in the front/back explainer, the default portrait die.

**It does not govern the customer's die.** The product sells five shapes — portrait rectangle (1 : 1.237), landscape rectangle (1.237 : 1), square, circle and oval — and the preview takes its aspect ratio and corner radius from the shape the customer picked, not from the brand. Circle and oval carry a shaped die (a real cost and three extra days, surfaced in the quote). The brand ratio is the default and the house proportion; it is not a constraint imposed on the buyer's label.

### Variants and Usage

- **`vinoprint-dark.svg`** (= `lockup-light-bg`) — **white outer field**, burgundy inner panel, white mark, burgundy wordmark.
- **`vinoprint-light.svg`** (= `lockup-dark-bg`) — **burgundy outer field**, white inner panel, burgundy mark, white wordmark.

**The lockup is applied, not blended.** It behaves like a printed label stuck onto the page: it always opposes the surface under it rather than disappearing into it. So the variant is chosen for **contrast with the background, not for matching it**:

| Surface | Variant | Reads as |
|---|---|---|
| Navigation, on `{colors.canvas}` | `vinoprint-light.svg` (burgundy field) | a burgundy tile pinned to the paper |
| Footer, on `{colors.canvas-dark}` | `vinoprint-dark.svg` (white field) | a white label stuck to the cellar wall |
| Packaging, stationery, photography | whichever opposes the substrate | — |

The outer field is never removed and the lockup is never set on a transparent background: the field *is* the label. Note that the supplied filenames describe the artwork's own colour, not the background it goes on — `vinoprint-dark` is the one with the white field.
- **`mark`** (`assets/brand/vinoprint-mark.svg`) — the pour mark alone, `currentColor`, viewBox `40.28 55.23 100.17 106.43`. Use for the app icon, the loading state, watermarks, and as a bullet glyph in editorial lists at 12–16px.
- **`favicon.svg`** — mark reversed out of a solid burgundy square. Never a transparent favicon.

**Clear space**: minimum one side-inset (6% of the lockup width) on all four sides. **Minimum size**: **76px tall** for the lockup, 20px for the mark alone. The wordmark is only 14.5% of the lockup's height, so at 76px it reads at ~11px — that is the floor, and it is what sets the height of the navigation bar (see `{component.global-nav}`). **The lockup is used as supplied; there is no horizontal variant.** Layouts adapt to the lockup, not the other way round.

**Parent brand.** VinoPrint is a brand of **Vini Salmaso** (Colli Euganei). The two never lock up together: VinoPrint stands alone on every product surface, and the parent is credited in running text — footer, cantina band, legal strip — in `{typography.caption}` or `{typography.micro}`, never as a second logo. This is deliberate: the whole proposition is that the customer's name is the only one on the bottle, and a co-branded lockup would contradict it.

**Never**: recolor the lockup outside #662731/#ffffff · rotate it · place the full portrait lockup inside a 72px nav bar (it is a packaging asset — see Known Gaps for the horizontal lockup) · apply the foil gradient to it · add a shadow to it · stretch it off 1 : 1.474.

## Colors

### Brand
- **Barolo Burgundy** (`{colors.primary}` — #662731): the logo color and the single interactive color. Primary buttons, active states, links, selected options, brand bands. Contrast on `{colors.canvas}` is **10.6 : 1** — it is a text color as much as a fill.
- **Barolo Deep** (`{colors.primary-hover}` — #521e27): hover on every burgundy fill; also the announcement bar, where full-strength burgundy next to the nav would flatten the two.
- **Barolo Dark** (`{colors.primary-active}` — #3c151c): pressed state. Depth is a darkening, never a shadow.
- **Barolo Mid** (`{colors.primary-mid}` — #983e4d, 6.7 : 1 on canvas): the one lighter burgundy safe for body text — use for inline links on `{colors.surface-sunken}` and for secondary metadata that must still read as brand.
- **Rosé** (`{colors.primary-bright}` — #b8616f, 4.2 : 1): **decorative and large-text only.** Never body copy on light. Dividers inside brand bands, chart series, illustration.
- **Blush** (`{colors.primary-subtle}` — #f9f1f2) and **Blush 2** (`{colors.primary-subtle-2}` — #f3e2e5): selected-option fills, focus rings, hover on ghost buttons, table row highlight.
- **Barolo Tint Border** (`{colors.primary-border}` — #e7c6cb): link underlines and the border of brand-tinted panels.
- **Barolo Light on dark** (`{colors.primary-on-dark}` — #d39da6): the only place the pale burgundy is a text color — over `{colors.canvas-dark}`.

### Foil (the print-finish accent)
- **Foil Brass** (`{colors.foil}` — #b08a46): the color of hot-foil stamping. Allowed on: finish swatches, foil badges, the "add foil" upsell button, premium tier markers, proof-approval panels. **Forbidden as a general accent** — a gold detail on a page with no foil product is a lie about the print spec.
- **Foil Light** (`{colors.foil-light}` — #d8b878): text-weight gold. On `{colors.primary}` it reaches **5.7 : 1** — this is the only gold permitted for text on burgundy or on the cellar canvas.
- **Foil Pale / Deep** (`{colors.foil-pale}` #ebd9a8 / `{colors.foil-deep}` #8a6a2e): the highlight and shadow stops of `{gradients.foil-sheen}`. Not standalone colors.
- **Foil Subtle** (`{colors.foil-subtle}` — #faf4e6): the fill of `{component.proof-panel}` — the warm cream that says "this is the print-side of the product, not the shop-side."
- Raw `{colors.foil}` on burgundy is **3.4 : 1** — acceptable for borders, icons, and text at 24px+/bold only. Step to `{colors.foil-light}` for anything smaller.

### Paper (light theme)
- **Canvas** (`{colors.canvas}` — #fbf8f4): the default page. A warm uncoated stock. The site is never pure white at the page level.
- **Surface** (`{colors.surface}` — #ffffff): cards, inputs, the label preview sheet. Pure white here means "this is a printable surface" — it reads as stock lying on the desk.
- **Sunken** (`{colors.surface-sunken}` — #f2ece3): the configurator workspace, search fields, disabled fills, the mat behind a label preview.
- **Kraft** (`{colors.surface-kraft}` — #efe6d8): full-width editorial bands (winemaker story, vineyard, process). The warmest surface — used to change the page's pace without going dark.

### Cellar (dark theme)
- **Cellar Canvas** (`{colors.canvas-dark}` — #1a1013): the brand hue at 6% lightness. Footer, hero over photography, age gate, dark sections.
- **Cellar Surface** (`{colors.surface-dark}` — #241a1e) and **Raised** (`{colors.surface-dark-raised}` — #2e2126): cards and menus on the cellar canvas. Elevation in dark mode is a **lighter** surface, never a shadow.
- **Cellar Sunken** (`{colors.surface-dark-sunken}` — #140c0f): inputs and wells inside dark surfaces.

### Ink
- **Ink** (`{colors.ink}` — #241a1c, 16 : 1): all headlines and body copy on light. Warm near-black, never #000 — pure black next to burgundy reads as a printing error.
- **Ink Secondary** (`{colors.ink-secondary}` — #4a3d40): sub-headings, table labels, badge text.
- **Ink Muted** (`{colors.ink-muted}` — #6e625b, 4.7 : 1): captions, helper text, placeholders, the lowest passing body tone.
- **Ink Disabled** (`{colors.ink-disabled}` — #a1958d): disabled labels only. Never for readable content.
- **On Dark** (`{colors.on-dark}` — #f7f2ec): body on the cellar canvas. Warm off-white, matching the paper cast.
- **On Dark Muted** (`{colors.on-dark-muted}` — #b3a49d): footer links, secondary dark-mode copy.

### Rules & Borders
- **Hairline** (`{colors.hairline}` — #e3dacd): the default 1px rule. Card borders, dividers, table lines. This rule *is* the design — it replaces shadow as the primary separator.
- **Hairline Strong** (`{colors.hairline-strong}` — #cdbfad): input borders, option tiles, anything a user must perceive as an interactive edge.
- **Hairline Dark / Dark Strong** (`{colors.hairline-dark}` #3a2b30 / `{colors.hairline-dark-strong}` #4e3b41): their cellar-mode counterparts.

### Semantic
| Role | Light fill | Text/border | On dark | Note |
|---|---|---|---|---|
| Success | `{colors.success-subtle}` #e8f0e9 | `{colors.success}` #43694f | `{colors.success-on-dark}` #8fbf9e | Vine green — order confirmed, proof approved |
| Warning | `{colors.warning-subtle}` #fbf0de | `{colors.warning}` #b4741c | `{colors.warning-on-dark}` #e0a854 | Mosto amber — low resolution artwork, stock running out |
| Danger | `{colors.danger-subtle}` #fceae7 | `{colors.danger}` #c23a2b | `{colors.danger-on-dark}` #f08d80 | Vermilion — **deliberately brighter and more orange than burgundy** |
| Info | `{colors.info-subtle}` #e9eef5 | `{colors.info}` #3d5a80 | `{colors.info-on-dark}` #8fabcf | Slate — lead times, shipping notes |

**The danger rule.** On a burgundy site, an error state in "a dark red" is invisible as an error. `{colors.danger}` (#c23a2b) is 22° warmer and 18 points lighter than `{colors.primary}` — that gap is mandatory. Never signal an error with a burgundy tint, and never place a destructive button in `{colors.primary}`.

### Technical Guides (prepress only)
`{colors.dieline}` (#00a3c4 cyan), `{colors.bleed-guide}` (#ff3d7f magenta), `{colors.safe-guide}` (#7a8f3f olive). These are overlay guides drawn **on the label preview canvas only** — die line, 3mm bleed, and safe-text area. They are intentionally alien to the palette so they can never be mistaken for design. **Never use these three hexes anywhere else in the UI.**

### Gradients
- **`{gradients.foil-sheen}`** — a five-stop metallic sweep at 105°. The one true gradient in the system. It exists to simulate foil under raking light and appears on `{component.swatch-chip-foil}`, `{component.badge-foil}`, and `{component.button-foil}`. Always pair with `{elevation.foil-emboss}`; a flat gold rectangle does not read as metal.
- **`{gradients.cellar-fade}`** — a bottom-anchored scrim so `{colors.on-dark}` copy stays legible over vineyard photography. Functional, not decorative.
- **`{gradients.paper-tint}`** — a whisper-soft white→sunken vertical wash used on the label preview mat to suggest a lightbox. Optional.
- **No other gradients exist.** Buttons, cards, headers and bands are flat fills.

## Typography

### Font Families and Their Zones

One family carries the interface, one carries the editorial voice. Crossing that single boundary is the fastest way to make this system look generic.

| Family | Role | Allowed in | Banned from |
|---|---|---|---|
| **Circular** (Lineto, licensed) | Everything structural | Headlines, eyebrows, nav, body copy, buttons, forms, prices, specs, legal | — |
| **DM Sans** (Google, variable) | Fallback for Circular | Wherever Circular is unavailable | Being named in the design; it stands in, it does not lead |
| **Fraunces** (Google, variable, `opsz` 144) | Editorial / wine voice | Winemaker copy, pull quotes, vintage numerals, tasting notes | Any UI chrome, any button, any form, any price |

**Circular is commercial and cannot be bundled.** Declare the `@font-face` rules for the licensed files and the stack in `tokens.css` picks them up with no other change. Until then the page renders in DM Sans, the closest open geometric by proportion and x-height. The two are not identical: Circular's counters are rounder and its apertures more open, so the same weight reads slightly lighter. Re-check display tracking after the real font is installed — the negative tracking on `{typography.display-hero}` was set against a substitute.

**One family carries the whole interface.** The old split (a geometric for display, a neutral for UI) is gone: Circular is designed to do both, and a second family in the chrome would only dilute it. The boundary that survives is Fraunces, which is a different voice, not a different size.

The lockup's own wordmark is artwork, not type: set it from the supplied SVG, never re-typeset it. Where the wordmark has to be rendered as live text (a page title, an email signature), Circular Light at `letterSpacing: 0.02em`, lowercase, is the closest match to its geometric, high-x-height, light-stroke construction.

### Hierarchy

| Token | Family | Size | Weight | Line height | Tracking | Use |
|---|---|---|---|---|---|---|
| `{typography.display-hero}` | Circular | 72px | 300 | 1.04 | -0.015em | Homepage hero only, one per page |
| `{typography.display-xl}` | Circular | 56px | 300 | 1.06 | -0.012em | Page titles, band headlines |
| `{typography.display-lg}` | Circular | 40px | 400 | 1.12 | -0.01em | Section headings |
| `{typography.display-md}` | Circular | 32px | 400 | 1.18 | -0.008em | Sub-sections, modal titles |
| `{typography.display-sm}` | Circular | 24px | 500 | 1.25 | -0.005em | Card group headings, drawer titles |
| `{typography.title}` | Circular | 20px | 500 | 1.3 | 0 | Product names, panel titles |
| `{typography.eyebrow}` | Circular | 12px | 500 | 1.2 | 0.2em | Section kickers ("LA COLLEZIONE") |
| `{typography.nav-link}` | Circular | 14px | 400 | 1.2 | 0.08em | Main navigation |
| `{typography.body-lg}` | Circular | 18px | 400 | 1.6 | 0 | Lead paragraphs, product descriptions |
| `{typography.body}` | Circular | 16px | 400 | 1.65 | 0 | Default paragraph |
| `{typography.body-sm}` | Circular | 14px | 400 | 1.6 | 0.005em | Dense UI copy, card text, drawers |
| `{typography.caption}` | Circular | 13px | 400 | 1.45 | 0.01em | Helper text, image captions |
| `{typography.micro}` | Circular | 11px | 500 | 1.3 | 0.06em | Badges, legal, metadata |
| `{typography.form-label}` | Circular | 13px | 500 | 1.3 | 0.04em | Field labels (uppercase) |
| `{typography.button}` | Circular | 14px | 500 | 1.0 | 0.06em | All buttons (uppercase) |
| `{typography.button-sm}` | Circular | 12px | 500 | 1.0 | 0.07em | Compact buttons |
| `{typography.price}` | Circular | 20px | 500 | 1.2 | -0.01em | Prices, tabular numerals |
| `{typography.price-lg}` | Circular | 28px | 500 | 1.15 | -0.015em | PDP and cart total |
| `{typography.data}` | Circular | 14px | 400 | 1.5 | 0 | Spec tables, order lines, tabular |
| `{typography.editorial-lead}` | Fraunces | 24px | 300 | 1.5 | -0.01em | Story intros |
| `{typography.editorial-quote}` | Fraunces | 32px | 300 italic | 1.35 | -0.01em | Pull quotes from the winemaker |
| `{typography.vintage-numeral}` | Fraunces | 48px | 400 | 1.0 | 0.02em | Vintage year ("2021") on a bottle card |

### Principles

- **Light weights carry the brand.** Display sizes at 56px+ run at weight 300, echoing the wordmark's thin stroke. Never set a 72px headline at 600 — it collapses the brand into a generic SaaS hero.
- **Weight rises as size falls.** 300 at 72/56px → 400 at 40/32px → 500 at 24/20px. The ladder is 300/400/500 only. **Weights 600 and 700 do not exist in this system.** Emphasis comes from color, tracking, and case.
- **Tracking is the brand's fingerprint.** Negative at display sizes (-0.008 to -0.015em), zero at body, strongly positive at micro sizes (0.04-0.2em uppercase). The `{typography.eyebrow}` at 0.2em is the loudest single typographic gesture in the system.
- **Eyebrow budget: at most one per three sections.** An eyebrow above every section headline produces the templated rhythm every generated page has. Use one only where it carries information the headline cannot: the branch of a flow, a category the reader cannot infer from position. Everywhere else the headline stands alone. Count them before shipping.
- **No em dashes or en dashes in visible copy.** Use a colon, a comma, parentheses or two sentences. The em dash is the single most recognisable machine-written tell, and this brand's voice is a winemaker's, not an essayist's. Hyphens for compounds and ranges only.
- **All numerals in commerce are tabular.** Prices, quantities, order totals, spec values: `font-variant-numeric: tabular-nums`. Vintage years in Fraunces are the deliberate exception — they are typography, not data.
- **Uppercase is structural, not emphatic.** Buttons, labels, badges and eyebrows are uppercase because a label spec sheet is uppercase. Sentence copy is never uppercased for emphasis.
- **Prose locks at `{layout.container-prose}` (680px).** Roughly 70 characters at 16px. Editorial bands and product descriptions never run wider.
- **Fraunces uses `opsz` 144 and `WONK` 1.** `font-variation-settings: "SOFT" 0, "WONK" 1, "opsz" 144`. The wonk gives the serif the slightly irregular, letterpress quality that keeps it from reading as a default Georgia fallback.

### Font Loading
```
Circular:  300, 400, 500  (licensed, self-hosted woff2)
DM Sans:   300, 400, 500  (variable, fallback)
Fraunces:  300, 400       (variable, opsz 144, italic for quotes)
```
Preload the interface family; **lazy-load Fraunces** — it appears below the fold on most pages and must never block the hero. Stack: `'Circular Std', 'Circular', 'DM Sans', system-ui, sans-serif` · `Fraunces, 'Iowan Old Style', Georgia, serif`.

## Layout

### Spacing System
- **Base unit: 4px.** Everything is a multiple. Structural layout snaps to 8/16/24.
- **Tokens:** `{spacing.xxs}` 4 · `{spacing.xs}` 8 · `{spacing.sm}` 12 · `{spacing.md}` 16 · `{spacing.lg}` 24 · `{spacing.xl}` 32 · `{spacing.xxl}` 48 · `{spacing.xxxl}` 64 · `{spacing.huge}` 96 · `{spacing.section}` 128.
- **Section rhythm:** `{spacing.section}` (128px) between marketing sections on desktop, `{spacing.huge}` (96px) on tablet, `{spacing.xxxl}` (64px) on mobile.
- **Card padding:** `{spacing.lg}` (24px) standard, `{spacing.md}` (16px) in dense grids and the cart drawer.
- **Configurator density:** the label builder drops to a 16px rhythm. It is a workbench and should feel tighter than the shop.

### The Frame (signature construction)

Every framed surface — hero, card, preview, modal, footer block — is built as:

```
outer field
  └─ 1px rule inset by {frame.inset-*} on left, right and top
     └─ content
  └─ foot margin = inset × {frame.foot-multiplier} (5)
```

| Scale | Inset | Foot | Use |
|---|---|---|---|
| `{frame.inset-sm}` | 12px | 60px | Product cards, thumbnails |
| `{frame.inset-md}` | 24px | 120px | Standard sections, preview canvas |
| `{frame.inset-lg}` | 40px | 200px | Hero, full-bleed brand bands |

The foot is where the identifying line lives — product name and price on a card, the CTA on a hero, the wordmark on packaging. **The foot is never filled edge to edge with content**; it holds one short line and a lot of air. That asymmetry is the whole visual signature. On viewports below 640px the multiplier relaxes to 3 (a 5× foot eats a phone screen).

### Grid & Container
- **Max content width:** `{layout.container-max}` 1280px. `{layout.container-wide}` 1440px for full-bleed galleries and the configurator's two-pane layout. `{layout.container-prose}` 680px for all running text.
- **12 columns**, `{layout.gutter}` 24px.
- **Page margins:** 24px mobile/tablet → 48px from 1024px up.
- **Product grid:** 4 columns at ≥1280, 3 at 1024, 2 at 640, 1 below 480. Label design grids go one step denser (5/4/3/2) because the thumbnails are portrait and small.
- **Configurator:** a fixed two-pane split — preview pane 58% (sticky), options pane 42% — collapsing to a stacked layout with a sticky preview header at 1024px.

### Whitespace Philosophy
Space is the mat around a print. Marketing surfaces are generous to the point of sparse: a band headline gets at least 64px above it and 48px below before the first paragraph. The product grid and the configurator deliberately break this — they are working surfaces, and a customer configuring 2,000 labels wants to see the options, not the air. **The rule: storytelling breathes, tooling works.**

## Elevation & Depth

| Level | Treatment | Use |
|---|---|---|
| `{elevation.flat}` | none | Nav, footer, bands, buttons, badges — the default for all chrome |
| `{elevation.rule}` | 1px `{colors.hairline}` | Cards, tables, dividers — the primary separator in the whole system |
| `{elevation.lift-1}` | 0 1px 2px / 0 2px 8px @ 4–6% | Card hover, dropdown menus |
| `{elevation.lift-2}` | 0 2px 4px / 0 12px 28px @ 6–8% | Popovers, sticky configurator summary |
| `{elevation.lift-3}` | 0 24px 64px @ 16% | Cart drawer, modals, age gate |
| `{elevation.paper-drop}` | 0 18px 40px -12px @ 35% | **Signature.** Under a rendered label or bottle mockup only |
| `{elevation.foil-emboss}` | inset highlight + inset shade | Foil swatches, foil badges, the foil CTA |

**Shadow philosophy.** Ink does not cast a shadow; **paper does.** `{elevation.paper-drop}` exists to make a rendered label look like a physical sheet resting on the workspace, and it is the only assertive shadow in the system. Everything else separates with a 1px rule or a surface change. If a component needs emphasis, change its surface or thicken its rule to `{frame.rule-width-strong}` (2px) before reaching for a shadow.

In **dark mode there are no shadows at all** — elevation is `{colors.surface-dark}` → `{colors.surface-dark-raised}`, plus a `{colors.hairline-dark}` rule.

### Decorative Depth
- **Surface alternation** (canvas → kraft → cellar → canvas) is the section divider. Bands touch edge to edge with no gap.
- **The inset rule** creates depth by suggesting a mounted print, not by simulating light.
- **`{gradients.foil-sheen}` + `{elevation.foil-emboss}`** is the only place the system simulates a physical material.

## Shapes

### Border Radius Scale

| Token | Value | Use |
|---|---|---|
| `{rounded.none}` | 0px | **The default.** Buttons, cards, inputs, badges, modals, images, bands — everything |
| `{rounded.xs}` | 2px | Inline code, tiny color swatches inside a table cell |
| `{rounded.sm}` | 3px | Avatars in reviews, the only place a soft edge is allowed in chrome |
| `{rounded.die-cut}` | 4px | **Rendered label artwork only** — simulates the die-cut corner of a real label |
| `{rounded.pill}` | 999px | Reserved. Currently used by nothing. Do not introduce pills |
| `{rounded.full}` | 50% | Step indicators and the rating dot only |

**Square is the rule.** The logo is three nested rectangles with no rounding anywhere. A rounded button in this system reads as a component borrowed from another brand.

### Photography & Image Geometry
- **Label artwork:** always `{frame.field-aspect}` (1 : 1.237 portrait), `{rounded.die-cut}`, `{elevation.paper-drop}`.
- **Bottle shots:** 3:4 portrait, shot on `{colors.surface-kraft}` or `{colors.canvas-dark}`, full-bleed inside the card with no inner padding.
- **Vineyard / process photography:** 16:9 or full-bleed. Warm grade, never cool. Overlay `{gradients.cellar-fade}` before setting `{colors.on-dark}` copy on it.
- **Press / macro shots** (foil, embossing, paper texture): 1:1, used in editorial bands at 48px gutters.
- **No rounded photography, ever.** Images are rectangular and sit flush with their frame's inset rule.
- Serve AVIF → WebP → JPEG, responsive `srcset`; hero eager, everything below the fold lazy.

## Motion

- **Durations:** `{motion.duration-fast}` 120ms (color/border changes) · `{motion.duration-base}` 200ms (component transitions) · `{motion.duration-slow}` 320ms (drawer, modal) · `{motion.duration-page}` 480ms (route transitions).
- **Easing:** `{motion.ease-settle}` `cubic-bezier(0.2, 0.7, 0.2, 1)` for everything entering — fast start, long settle, like a sheet coming to rest. `{motion.ease-exit}` for everything leaving.
- **The signature: "ink settle."** Content enters with `translateY({motion.rise-distance})` → 0 plus opacity 0 → 1. **No scale, no bounce, no spring.** Paper does not bounce.
- **Card interaction is a rule change, not a lift.** On hover, `{component.product-card}` swaps its border to `{colors.primary}` and gains `{elevation.lift-1}` — it does not translate or scale.
- **The label preview never animates on load.** It cross-fades at `{motion.duration-base}` when an option changes, so the customer can see exactly what changed.
- Honour `prefers-reduced-motion: reduce` — drop all translation and keep opacity only.

## Components

### Navigation

**`announcement-bar`** — 36px strip pinned above the nav. Background `{colors.primary-deep}`, text `{colors.on-dark}` in `{typography.micro}`, centered. Carries one message only: lead time, free-shipping threshold, or campaign. Dismissible; never stacked two-deep.

**`global-nav`** — **144px bar** (106px below 640px), background `{colors.canvas}`, bottom border 1px `{colors.hairline}`, **flat, no shadow.** It carries **the lockup and one action**: no links, no search, no account, no cart. The product page is a single funnel, so the only thing worth putting beside the brand is the way into it. Lockup left at 112px tall (78px on mobile), `{component.button-primary}` at small size on the right, same label as every other call to action on the page.

Past 120px of scroll it becomes `{component.global-nav-scrolled}`: height 78px (64px on mobile), lockup down to 56px, background at 92% with `backdrop-filter: blur(12px)`. Driven by an `IntersectionObserver` on a sentinel, never a scroll listener. Sticky offsets elsewhere reference the resting height.

**`mega-menu`** — Full-width panel dropping from the nav. Background `{colors.surface}`, 1px `{colors.hairline}` bottom border, `{elevation.lift-1}`, padding `{spacing.xl}` 32px. Three to four columns of links in `{typography.body-sm}` under `{typography.eyebrow}` headings, plus one promoted `{component.product-card}` on the right. No radius, no arrow pointer.

**`breadcrumb`** — `{typography.caption}` in `{colors.ink-muted}`, separator is a thin `/` at 60% opacity. Current page in `{colors.ink}`, not linked.

### Buttons

The grammar is: **square, uppercase, tracked, 48px tall.** Radius is always `{rounded.none}`.

**`button-primary`** — Background `{colors.primary}`, text `{colors.on-primary}`, `{typography.button}`, padding 16×32, min-height 48px. Hover → `{colors.primary-hover}`. Active → `{colors.primary-active}`. Disabled → `{component.button-primary-disabled}` (`{colors.surface-sunken}` fill, `{colors.ink-disabled}` text, no border). Focus → 2px `{colors.primary}` outline at 2px offset. **One per view.**

**`button-secondary`** — Transparent, 1px `{colors.primary}` border, text `{colors.primary}`, padding 15×31 (compensating the border so it aligns with primary). Hover fills `{colors.primary-subtle}`.

**`button-quiet`** — Transparent with a 1px `{colors.hairline-strong}` border and `{colors.ink}` text. For neutral actions in tooling: "Annulla", "Indietro", "Reimposta".

**`button-foil`** — `{gradients.foil-sheen}` fill, `{colors.on-foil}` text, `{elevation.foil-emboss}`. **Used exclusively to add or upgrade a print finish** ("Aggiungi stampa a caldo oro"). Never a generic premium CTA.

**`button-on-dark`** — `{colors.on-dark}` fill with `{colors.primary}` text. The primary action on `{colors.canvas-dark}` and over photography. Burgundy on cellar-black does not carry enough contrast.

**`button-sm`** — A **size modifier, not a variant**: 40px tall, `{typography.button-sm}`, padding 11×20 (10×19 when the variant carries a border). Combine it with any variant — `button-primary button-sm`, `button-quiet button-sm`. Used in cards, table rows and drawers.

**`button-icon`** — 44×44px transparent hit area, `{colors.ink}` glyph at 20px, square. Hover tints the glyph to `{colors.primary}`; the background stays transparent.

**`text-link`** — `{colors.primary}` with a 1px `{colors.primary-border}` underline that goes full-strength `{colors.primary}` on hover. On dark use `{component.text-link-on-dark}` in `{colors.foil-on-dark}`.

### Inputs & Forms

**`input-text`** — `{colors.surface}` fill, 1px `{colors.hairline-strong}` border, `{typography.body}`, padding 13×16, 48px min-height, square. Label sits above in `{typography.form-label}` (uppercase, 0.04em) — **labels are never placeholders.** Focus → `{component.input-text-focus}`: border `{colors.primary}` plus a 3px `{colors.primary-subtle-2}` ring. Error → `{component.input-text-error}` with the message below in `{typography.caption}` `{colors.danger}`.

**`input-select`** — Same chassis, 40px right padding for a 12px chevron in `{colors.ink-muted}`.

**`input-search`** — `{colors.surface-sunken}` fill, transparent border, 44px, leading 16px search glyph. Focus raises it to `{colors.surface}` with a `{colors.primary}` border.

**`artwork-dropzone`** — The single most important input in the product. `{colors.surface-sunken}` fill, **1px dashed `{colors.hairline-strong}`** (the only dashed border in the system), 48×24 padding, square. Centered stack: mark glyph at 32px in `{colors.ink-disabled}` → instruction in `{typography.body-sm}` → accepted formats and max size in `{typography.caption}` `{colors.ink-muted}` → `{component.button-quiet}` "Sfoglia". Drag-over state is `{component.artwork-dropzone-active}`. After upload it becomes a file row: thumbnail at `{frame.field-aspect}`, filename, size, a resolution check badge (`{component.badge-neutral}` at 300 dpi+, `{component.alert-warning}` below), and a remove icon button.

**`quantity-stepper`** — 48px tall, 1px `{colors.hairline-strong}` border, three cells: −, a tabular `{typography.data}` input, +. Dividers between cells are 1px `{colors.hairline}`. For print quantities the input snaps to the tier steps (250 / 500 / 1.000 / 2.500 / 5.000) and shows the unit price in `{typography.caption}` beneath.

**`checkbox` / `radio`** — 20×20px, 1px `{colors.hairline-strong}` border, square (radio is square too — `{rounded.full}` is reserved). Checked → `{colors.primary}` fill with a white 12px glyph. Label in `{typography.body-sm}`, 12px gap, 44px total touch row.

### The Label System (signature components)

**`label-frame`** — The Frame rendered as a container. `{colors.surface}` background, 1px `{colors.hairline}` inset rule, content top-aligned, deep foot. The base of cards, heroes and panels. `{component.label-frame-inverted}` is the burgundy-field version, mirroring the dark lockup.

**`label-preview-canvas`** — The workbench. `{colors.surface-sunken}` mat at `{spacing.xl}` 40px padding, holding a centered `{component.label-artwork}` at `{frame.field-aspect}`. Toggleable overlays drawn on top: die line in `{colors.dieline}`, 3mm bleed in `{colors.bleed-guide}`, safe-text area in `{colors.safe-guide}` — each a 1px dashed line with a `{typography.micro}` tag in its own color at the top-left corner. Bottom-left shows current dimensions and dpi in `{typography.data}`. **The preview mat never carries brand color** — the customer's artwork is the only color in that rectangle.

**`label-artwork`** — The rendered label itself. `{colors.surface}` base, `{rounded.die-cut}` 4px corners, `{elevation.paper-drop}`. **Brand fonts and brand colors are forbidden inside this element** — it renders customer content. The design system stops at its edge.

**`product-shot`** — A solid `{colors.primary}` field with a `{colors.surface}` sheet inside holding the bottle photograph. It is how the brand color gets **into the layout** instead of surviving only as a 2px border on a selected tile: a page that sells a burgundy-labelled wine and never puts burgundy on screen has a palette on paper only.

Two things it gets wrong if you are not careful. The sheet takes `width: 100%` and a fixed `height`, **never `aspect-ratio` plus a height cap**: with both, it is the width that shrinks to keep the proportion, and the field fills with dead burgundy at the sides. And the field is padded tight (`{spacing.sm}`), because the burgundy is a ground for the product, not a mat around it.

It carries no caption. The wine's name is already the page headline, and the technical line already sits in `{component.spec-accordion}`; repeating either one inside the field is the same fact twice. The deep foot of The Frame lives elsewhere on the page.

**`product-gallery`** — The `{component.product-shot}` field with a row of four thumbnails under the sheet, inside the burgundy. Thumbnails sit at 62% opacity and take a 2px `{colors.foil-light}` ring when active; the sheet cross-fades at `{motion.duration-base}` and the foot caption changes with it, naming the customer whose label is on screen. Same wine, four labels: it shows the product and the proposition in one object. The new image is preloaded before the swap so the sheet never flashes empty.

**`configurator-wizard`** — The product column has two states. At rest it is the **intro**: name, a short promise capped at about 150 characters, a disclosure that opens the longer description, the product tags, the spec accordion, and the primary action. Pressing that action anywhere on the page swaps the column for the **wizard**: three steps (format, quantity, label route), a numbered rail that doubles as navigation, and a close control that returns to the intro.

Two rules make it work. **The price is on screen at every step**, pinned under the panels, because a configurator that hides the running total makes people guess. And **the step panels only ever hold choices** — no prose, no reassurance copy, no secondary links: that material belongs to the intro, which is where someone is still deciding whether to start.

The final step hands off rather than concluding: choosing a route opens the matching panel further down the page. The wizard is the funnel's mouth, not the whole funnel.

**`showcase-rail`** — A horizontal rail of what other customers had made, one card per product: photograph on `{colors.surface-kraft}` at 3:4, customer name in `{typography.title}`, wine and format in `{typography.caption}`, and a text CTA. Built on `scroll-snap-type: x mandatory` with CSS grid auto-columns, no carousel library. The arrows are **hidden, not disabled, when every card fits** — disabled arrows read as broken. The rail bleeds past the container so a partial card is visible at the edge, which is what tells people it scrolls.

**`bottle-mockup`** — The label applied to a 3:4 bottle render on `{colors.surface-kraft}` or `{colors.canvas-dark}`, with `{elevation.paper-drop}`. Paired with the flat preview in a 2-up: flat for accuracy, bottle for desire.

**`finish-swatch`** (`{component.swatch-chip}` / `{component.swatch-chip-foil}`) — 56×56px square, 1px `{colors.hairline-strong}`. Paper stocks show their actual texture crop; foil finishes use `{gradients.foil-sheen}` with `{elevation.foil-emboss}`. Selected state adds a 2px `{colors.primary}` border and a 2px inner white gap. Name in `{typography.caption}` below, price delta in `{typography.micro}` `{colors.ink-muted}`.

**`option-tile`** — The configurator's choice cell. 88px min-height, `{colors.surface}`, 1px `{colors.hairline-strong}`, 16px padding. Contains: option name in `{typography.body-sm}` 500, one-line spec in `{typography.caption}` `{colors.ink-muted}`, price delta right-aligned in `{typography.data}`. Selected → `{component.option-tile-selected}` (2px `{colors.primary}`, `{colors.primary-subtle}` fill). Unavailable → `{component.option-tile-disabled}` with the reason in `{typography.micro}`.

**`step-rail`** — The configurator's progress strip, sticky at the top of the options pane. Steps in `{typography.micro}`, numbered, separated by a 1px `{colors.hairline}` rule. Completed steps show a 6px `{colors.primary}` square; the current step's label goes `{colors.primary}` 500; future steps stay `{colors.ink-muted}`.

**`proof-panel`** — The approval gate before production. `{colors.foil-subtle}` fill, 1px `{colors.foil}` border, 24px padding — the one component that uses gold as a *frame*, marking the moment the order crosses from shop to press. Contains the proof thumbnail, a checklist in `{typography.body-sm}`, and two actions: `{component.button-primary}` "Approva la bozza" and `{component.button-quiet}` "Richiedi modifiche". Above them, a `{component.alert-warning}` stating that approval locks the artwork.

### Commerce

**`product-card`** — Built as The Frame at `{frame.inset-sm}`. `{colors.surface}`, 1px `{colors.hairline}`, 20px padding. Image at `{frame.field-aspect}` (label designs) or 3:4 (bottles). Foot holds: product name in `{typography.title}`, one-line descriptor in `{typography.caption}` `{colors.ink-muted}`, price in `{typography.price}`, and an optional badge row. Hover → `{component.product-card-hover}` (border to `{colors.primary}`, `{elevation.lift-1}`). No CTA inside the card — the whole card is the target.

**`bottle-card`** — Zero padding; the bottle photograph runs full-bleed to the card edge on `{colors.surface-kraft}`, with the text block sitting in a 24px foot beneath it. Vintage in `{typography.vintage-numeral}` (Fraunces) — the one place the serif enters a card.

**`filter-chip`** — Square, 1px `{colors.hairline-strong}`, `{typography.caption}`, 8×14 padding. Active → `{component.filter-chip-active}` (burgundy fill, white text) with an × at 12px. Filters live in a left rail on desktop (sticky, 280px) and a bottom sheet on mobile.

**`spec-table`** — 1px `{colors.hairline}` outer border, rows separated by 1px `{colors.hairline}`, no vertical rules. Left column `{typography.form-label}` in `{colors.ink-secondary}`, right column `{typography.data}` in `{colors.ink}`, both 14px padding. Alternating rows get `{colors.surface-sunken}` at 50%. Used for material specs, print specs, and order lines.

**`price-row` / `price-row-total`** — Order summary lines: label left in `{colors.ink-secondary}`, tabular value right. The total row gains a 1px `{colors.hairline-strong}` top rule, 16px top padding, and `{typography.price}`. VAT and shipping lines always visible before the total — never revealed at the last step.

**`cart-drawer`** — 440px panel sliding from the right at `{motion.duration-slow}` with `{motion.ease-settle}`. `{colors.canvas}` background, `{elevation.lift-3}`, square. Header: "Carrello" in `{typography.display-sm}` + close icon, 1px `{colors.hairline}` under it. Line items show the label thumbnail at `{frame.field-aspect}` 64px wide, name, configured options in `{typography.caption}` `{colors.ink-muted}`, a `{component.quantity-stepper}`, and the line total. Footer is sticky: price rows, then a full-width `{component.button-primary}`.

**`checkout-stepper`** — Horizontal, 4 steps, `{typography.micro}`. The connector is a 1px `{colors.hairline}` line that fills `{colors.primary}` as steps complete. Step markers are 8px squares, not circles.

**`badges`** — All square, all `{typography.micro}`. `{component.badge-brand}` for "Novità" and cart counts · `{component.badge-foil}` for foil/premium finishes only · `{component.badge-outline}` for certifications (DOCG, Bio, Vegan) · `{component.badge-neutral}` for neutral metadata ("300 dpi OK"). Maximum two badges per card.

### Feedback & Content

**`alerts`** — `{component.alert-info}` / `-success` / `-warning` / `-danger`. Square, tinted fill, **3px left border** in the semantic color, 14×16 padding, `{typography.body-sm}`. The left rule is the only "accent bar" pattern in the system.

**`toast`** — Bottom-left, 380px max, `{colors.ink}` fill with `{colors.on-dark}` text, square, `{elevation.lift-2}`. Auto-dismiss at 5s. Never covers the cart or the primary CTA.

**`age-gate`** — Required before first entry. Full-viewport `{colors.canvas-dark}`, centered `{component.label-frame-inverted}` holding the lockup, a `{typography.display-md}` question, the birth-year field, and two actions. Copy sets the legal drinking age and links to the responsible-consumption statement in `{typography.caption}` `{colors.on-dark-muted}`. Remembers the answer; never shown twice in a session.

**`editorial-band`** — Full-width `{colors.surface-kraft}`, 96px vertical padding. Content locked to `{layout.container-prose}` 680px. Opens with an `{typography.eyebrow}`, then `{typography.editorial-lead}` in Fraunces, then `{typography.body}` in Circular. Optional `{typography.editorial-quote}` pull quote with a 2px `{colors.primary}` left rule at 24px padding.

**`cellar-band`** — Full-width `{colors.canvas-dark}`, 128px padding, `{typography.display-lg}` in `{colors.on-dark}`, `{component.button-on-dark}` CTA. Used once per page at most, as the pace change before the footer.

**`footer`** — `{colors.canvas-dark}`, 96px top padding. Four link columns in `{typography.body-sm}` `{colors.on-dark-muted}` under `{typography.eyebrow}` headings in `{colors.foil-on-dark}` — the footer is the one place gold is allowed as pure typographic accent, because it reads as a printer's colophon. `vinoprint-dark.svg` (the white-field lockup) sits left at 84px wide — a white label against the cellar. Bottom strip: 1px `{colors.hairline-dark}` rule, then legal, VAT number, and the responsible-drinking line in `{typography.micro}`.

### Empty & Loading States
- **Empty states** use the mark at 48px in `{colors.hairline-strong}`, a `{typography.display-sm}` line, one `{typography.body-sm}` sentence, and a single `{component.button-secondary}`.
- **Skeletons** are `{colors.surface-sunken}` rectangles with a 1.4s shimmer at 6% opacity — square, matching the real element's footprint exactly, including `{frame.field-aspect}` for label thumbnails.
- **The label preview's loading state** shows the die-line overlay alone on the empty mat — the frame exists before the artwork does.

## Do's and Don'ts

### Do
- Build every container as The Frame: field, inset rule, deep foot.
- Lock every **brand** surface — frames, thumbnails, the label faces — to `{frame.field-aspect}` (1 : 1.237), and let the **customer's** preview follow the die shape they chose.
- Keep `{colors.primary}` as the only interactive color across the entire site.
- Use `{colors.foil}` only where a real foil finish is being previewed, priced or sold.
- Separate with a 1px `{colors.hairline}` rule before considering a shadow.
- Set uppercase micro-type (buttons, labels, eyebrows, badges) with 0.04–0.2em tracking.
- Use tabular numerals for every price, quantity and spec value.
- Keep Fraunces below the fold, in winemaker copy and vintages only.
- Alternate canvas → kraft → cellar to pace a long page.
- Reserve `{elevation.paper-drop}` for rendered labels and bottle mockups.
- Keep the label preview neutral so customer artwork is the only color inside it.

### Don't
- Don't round corners. No `border-radius` above 4px anywhere, and 4px only on rendered label artwork.
- Don't introduce a second brand color, a teal, or a "modern" accent — the palette is burgundy, brass, paper, ink.
- Don't use gold on a page with no foil product; it devalues the one place it means something.
- Don't set error states in a burgundy tint — use `{colors.danger}` #c23a2b and nothing else.
- Don't use `{colors.primary-bright}` (#b8616f) for body text; it fails at 4.2 : 1.
- Don't use weight 600 or 700 anywhere. The ladder is 300/400/500.
- Don't put Fraunces in a button, a label, or anything a user clicks.
- Don't drop shadows on buttons, nav, badges or bands.
- Don't animate with scale or bounce. Paper rises and settles; it never springs.
- Don't use pure #000 or a cool gray — every neutral is warm.
- Don't let brand typography or brand color leak inside `{component.label-artwork}`.
- Don't use `{colors.dieline}`, `{colors.bleed-guide}` or `{colors.safe-guide}` outside the prepress overlay.
- Don't stack two `{typography.eyebrow}` blocks in consecutive sections, and don't exceed one eyebrow per three sections overall.
- Don't use an em dash or an en dash in anything the reader sees.
- Don't lay out three identical columns of equal cards. Give one item the weight it deserves and let the others sit beside each other.

## Responsive Behavior

### Breakpoints

| Name | Width | Key changes |
|---|---|---|
| `{breakpoints.xs}` | ≤ 380px | Hero drops to 32px; frame foot multiplier 3; single column everywhere; page margin 16px |
| `{breakpoints.sm}` | 381–480px | Hero 36px; product grid 1 col; label grid 2 col; filters become a bottom sheet |
| `{breakpoints.md}` | 481–640px | Product grid 2 col; section rhythm 64px; cart drawer goes full-width |
| `{breakpoints.lg}` | 641–768px | Display scale steps up one level; configurator still stacked |
| `{breakpoints.xl}` | 769–1024px | Nav collapses to hamburger below 1024px; product grid 3 col; section rhythm 96px |
| `{breakpoints.xxl}` | 1025–1280px | Full nav; configurator splits into two panes; page margin 48px; product grid 4 col |
| `{breakpoints.xxxl}` | 1281–1440px | Content locks at 1280px; galleries and configurator may use 1440px |
| `{breakpoints.wide}` | ≥ 1441px | Content locked; margins absorb the excess; full-bleed bands still run edge to edge |

The breakpoints that actually matter to an agent: **1280px** (content lock), **1024px** (nav collapse + configurator split), **640px** (grid halves), **480px** (single column, frame foot relaxes).

### Type Scale Collapse
- `{typography.display-hero}` 72 → 56 (1024) → 44 (768) → 36 (480) → 32 (380)
- `{typography.display-xl}` 56 → 44 (1024) → 36 (640)
- `{typography.display-lg}` 40 → 34 (768) → 30 (480)
- Body sizes never shrink. 16px stays 16px on a phone.
- Tracking on `{typography.eyebrow}` relaxes from 0.2em to 0.14em below 480px to prevent wrapping.

### Touch Targets
- Minimum 44×44px. Buttons are 48px tall by default, 40px for `{component.button-sm}` (which is desktop/dense only and gets bumped to 44px on touch).
- `{component.option-tile}` is 88px minimum — comfortably tappable and large enough to show a spec line.
- `{component.swatch-chip}` is 56×56px with an 8px gap: the visual square is the target.
- `{component.quantity-stepper}` cells are 48×48px each.

### Collapsing Strategy
- **Nav:** full link row → hamburger + mark + cart at 1024px. The drawer opens from the right, `{colors.canvas}`, full-height, links in `{typography.display-sm}` rather than nav-link size.
- **Configurator:** two panes → stacked, with the preview pinned as a 240px sticky header and the options scrolling beneath. A sticky bottom bar carries the running total and "Aggiungi al carrello".
- **Filters:** 280px sticky left rail → a "Filtri" button opening a bottom sheet at ≤ 768px.
- **Frame:** foot multiplier 5 → 3 below 640px, insets step `{frame.inset-lg}` → `{frame.inset-md}` → `{frame.inset-sm}`.
- **Editorial bands:** 96px padding → 64px (768) → 48px (480). Prose stays at 680px and simply fills the column.
- **Spec tables:** at ≤ 640px each row becomes a stacked label-over-value pair, keeping the 1px row rules.

## Commerce & Print Patterns

Product-specific rules that a generic e-commerce system would get wrong.

- **Two catalogs, one chassis.** Wine (bottles, vintages, cases) and labels (designs, materials, print runs) share every component. The distinction is carried by image aspect — 3:4 for bottles, `{frame.field-aspect}` for labels — and by which badge set applies.
- **Price is per-unit and per-run.** Always show the tier price in `{typography.price}` and the unit price in `{typography.caption}` beneath ("€0,148 / etichetta"). Quantity changes re-render both at `{motion.duration-fast}`.
- **Lead time is a first-class field.** Every print product shows its production window in a `{component.alert-info}` near the CTA, before the cart, never as a footnote.
- **Artwork state is visible at all times.** From upload to proof approval, the current state (Caricato → In verifica → Bozza pronta → Approvato) sits in the `{component.step-rail}` and repeats in the cart line item.
- **The proof gate is irreversible and must look it.** `{component.proof-panel}` plus a `{component.alert-warning}` above the approve action.
- **Alcohol compliance is design, not legal boilerplate.** `{component.age-gate}` on entry; the responsible-consumption line in the footer in `{typography.micro}`; no imagery implying consumption by minors; shipping-restriction notices surfaced at the address step, not at payment.
- **Currency and units are Italian-first.** `€ 1.250,00`, comma decimals, `,` thousands separator as `.`; dimensions in mm; paper weight in g/m²; always `IVA esclusa`/`IVA inclusa` labelled explicitly on every price row.

## Accessibility

- **Contrast floors:** body text ≥ 4.5 : 1, large text and UI borders ≥ 3 : 1. The audited pairs: `{colors.ink}` on `{colors.canvas}` **16 : 1** · `{colors.primary}` on `{colors.canvas}` **10.6 : 1** · `{colors.ink-muted}` on `{colors.canvas}` **4.7 : 1** · `{colors.primary-mid}` on `{colors.canvas}` **6.7 : 1** · `{colors.foil-light}` on `{colors.primary}` **5.7 : 1** · `{colors.on-primary}` on `{colors.primary}` **10.6 : 1**. Failing pairs to avoid: `{colors.primary-bright}` on light (4.2 : 1, large only), `{colors.foil}` on `{colors.primary}` (3.4 : 1, non-text only).
- **Focus is always visible:** 2px `{colors.primary}` outline at 2px offset on light, 2px `{colors.on-dark}` on cellar surfaces. Never `outline: none` without a replacement.
- **Selection is never color-only.** Selected `{component.option-tile}` and `{component.swatch-chip}` gain a 2px border *and* a check glyph — a burgundy tint alone fails for color-blind users.
- **Uppercase tracked type needs help:** every uppercase string in `{typography.eyebrow}`, `{typography.micro}` or `{typography.button}` that is an abbreviation gets an `aria-label` with the expanded form.
- **The label preview needs a text equivalent:** overlays (die line, bleed, safe area) are decorative; the dimensions, dpi and finish must also exist as text in a `{component.spec-table}` beside the canvas.
- Target size 44px minimum, `prefers-reduced-motion` honoured, form errors announced via `aria-live="polite"` and referenced by `aria-describedby`.

## Iteration Guide

1. Work one component at a time and reference its YAML key directly (`{component.option-tile}`, `{component.label-preview-canvas}`).
2. Variants live as separate entries with a suffix: `-hover`, `-active`, `-selected`, `-disabled`, `-on-dark`.
3. Use `{token.refs}` everywhere. Never inline a hex value in a component spec.
4. When in doubt about emphasis, in order: change the surface → thicken the rule → change the type weight. Reach for a shadow last, and only if the element is literally paper.
5. Before adding a color, prove it cannot be `{colors.primary}`, a paper neutral, or a semantic token. The answer is almost always that it can.
6. Before adding a radius, remember the logo is three nested rectangles.
7. Any new container starts as The Frame. If the deep foot does not work for it, question the layout before abandoning the frame.
8. Gold must earn its place by corresponding to something the customer can buy.

## Known Gaps

- **No horizontal lockup exists, by decision.** The supplied artwork is portrait only and is used as-is; the navigation bar was made tall enough to hold it rather than deriving a lockup the brand owner has not approved. If a horizontal variant is ever wanted — for a narrow header, an email signature, a vehicle — it has to be drawn and signed off, not improvised.
- **Circular is not installed.** The licensed files are not in the repo, so every screen so far has been judged in DM Sans. Weights, tracking and the optical size of the display steps all need a second look once the real font is in place.
- **The wordmark's exact typeface is unidentified.** It is used as supplied artwork, so nothing depends on identifying it, but if the original face turns out to be Circular the lockup and the interface would finally share one voice.
- **Paper stock swatches need photography.** `{component.swatch-chip}` specifies a texture crop per stock; those crops are production assets that do not exist yet.
- **Dark mode is specified structurally but not audited component by component.** The surfaces, inks and rules are defined; individual dark variants of `{component.option-tile}`, `{component.spec-table}` and the configurator are not yet documented.
- **No email or print-collateral tokens.** The system covers the web product only. Invoices, delivery notes and packaging inherit the frame and the palette but need their own spec.
- **Illustration and iconography are undefined.** The icon set should be 1.5px stroke, square-cut terminals, 24px grid — consistent with the rule weight — but no library has been chosen.
- **Motion on the bottle mockup** (rotation, label wrap) is out of scope; only the cross-fade behaviour of the flat preview is specified.
