/* packages/ui/components.css — the ONE stylesheet of the rythem-works components.
   Every color is a token from tokens.css. Dimensions are the values hardcoded in the Figma components
   unless a dimension token exists (page-margin-*, small). */

.zz-button {
  /* ── anatomy: horizontal auto-layout, hug, radius 0, 1px inside border ── */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  margin: 0;
  border: 1px solid;
  border-radius: 0;
  background-clip: padding-box;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  text-decoration: none;
  white-space: nowrap;

  /* ── type: the label carries the text-style class (label-medium: large, medium | label-extra-small: small, extra-small), see components.js ── */

  /* ── per-instance slots, set by hierarchy + state below ── */
  background-color: var(--zz-btn-bg);
  border-color: var(--zz-btn-border);
  color: var(--zz-btn-fg);
}

.zz-button__label { display: inline-flex; align-items: center; }
.zz-button__icon { display: inline-flex; flex: none; }
.zz-button__icon > svg { width: 100%; height: 100%; display: block; }
.zz-button__icon svg [fill]:not([fill="none"]) { fill: var(--zz-btn-icon); }
.zz-button__icon svg [stroke]:not([stroke="none"]) { stroke: var(--zz-btn-icon); }

/* ── size (Figma: _button-sizes) ──
   Figma padding as is: _button-states has "strokes included in layout", so the 1px border adds
   to the box in Figma too. Heights: 46 / 38 / 32 / 24 (decision 0014). */
.zz-button[data-size="large"] {
  padding: 12px;
  gap: 8px;
}
.zz-button[data-size="large"] .zz-button__label { gap: 4px; }
.zz-button[data-size="large"] .zz-button__icon { width: 20px; height: 20px; }

.zz-button[data-size="medium"] {
  padding: 8px;
  gap: 8px;
}
.zz-button[data-size="medium"] .zz-button__label { gap: 4px; }
.zz-button[data-size="medium"] .zz-button__icon { width: 20px; height: 20px; }

.zz-button[data-size="small"] {
  padding: 8px;
  gap: 8px;
}
.zz-button[data-size="small"] .zz-button__label { gap: 4px; }
.zz-button[data-size="small"] .zz-button__icon { width: 14px; height: 14px; }

.zz-button[data-size="extra-small"] {
  padding: 4px;
  gap: 2px;
}
.zz-button[data-size="extra-small"] .zz-button__label { gap: 2px; }
.zz-button[data-size="extra-small"] .zz-button__icon { width: 14px; height: 14px; }

/* ── hierarchy × state (Figma: _button-states) ──
   [data-state] exists only so documentation can pin a state; in the product
   the browser pseudo-classes drive it. */

/* primary */
.zz-button[data-hierarchy="primary"] {
  --zz-btn-bg: var(--action-primary-background-default);
  --zz-btn-border: var(--action-primary-border-default);
  --zz-btn-fg: var(--text-strong-invert);
  --zz-btn-icon: var(--icon-strong-invert);
}
.zz-button[data-hierarchy="primary"][data-state="hover"] {
  --zz-btn-bg: var(--action-primary-background-hover);
  --zz-btn-border: var(--action-primary-border-hover);
}
@media (hover: hover) {   /* touch screens keep :hover after a tap (decision 0043) */
  .zz-button[data-hierarchy="primary"]:hover {
    --zz-btn-bg: var(--action-primary-background-hover);
    --zz-btn-border: var(--action-primary-border-hover);
  }
}
.zz-button[data-hierarchy="primary"]:active,
.zz-button[data-hierarchy="primary"][data-state="pressed"] {
  --zz-btn-bg: var(--action-primary-background-pressed);
  --zz-btn-border: var(--action-primary-border-pressed);
}
.zz-button[data-hierarchy="primary"]:disabled,
.zz-button[data-hierarchy="primary"][data-state="disabled"] {
  --zz-btn-bg: var(--action-primary-background-disabled);
  --zz-btn-border: var(--action-primary-border-disabled);
  --zz-btn-fg: var(--text-strong);
  --zz-btn-icon: var(--icon-strong);
}

/* secondary */
.zz-button[data-hierarchy="secondary"] {
  --zz-btn-bg: var(--action-secondary-background-default);
  --zz-btn-border: var(--action-secondary-border-default);
  --zz-btn-fg: var(--text-strong);
  --zz-btn-icon: var(--icon-strong);
}
.zz-button[data-hierarchy="secondary"][data-state="hover"] {
  --zz-btn-bg: var(--action-secondary-background-hover);
  --zz-btn-border: var(--action-secondary-border-hover);
}
@media (hover: hover) {
  .zz-button[data-hierarchy="secondary"]:hover {
    --zz-btn-bg: var(--action-secondary-background-hover);
    --zz-btn-border: var(--action-secondary-border-hover);
  }
}
.zz-button[data-hierarchy="secondary"]:active,
.zz-button[data-hierarchy="secondary"][data-state="pressed"] {
  --zz-btn-bg: var(--action-secondary-background-pressed);
  --zz-btn-border: var(--action-secondary-border-pressed);
}
.zz-button[data-hierarchy="secondary"]:disabled,
.zz-button[data-hierarchy="secondary"][data-state="disabled"] {
  --zz-btn-bg: var(--action-secondary-background-disabled);
  --zz-btn-border: var(--action-secondary-border-disabled);
}

/* active = the current page (Figma state=active, 2026-11-10; decision 0045). Label text/strong on both hierarchies;
   no hover / pressed look and no pointer: it does not go anywhere new. Comes after hover/pressed so it wins. */
.zz-button[data-hierarchy="primary"][data-state="active"],
.zz-button[data-hierarchy="primary"][aria-current="page"] {
  --zz-btn-bg: var(--action-primary-background-active);
  --zz-btn-border: var(--action-primary-border-active);
  --zz-btn-fg: var(--text-strong);
  --zz-btn-icon: var(--icon-strong);
}
.zz-button[data-hierarchy="secondary"][data-state="active"],
.zz-button[data-hierarchy="secondary"][aria-current="page"] {
  --zz-btn-bg: var(--action-secondary-background-active);
  --zz-btn-border: var(--action-secondary-border-active);
}
.zz-button[data-state="active"], .zz-button[aria-current="page"] { cursor: default; }

/* secondary border: same logic as Tab — Figma 1px inside stroke, solid (default, disabled), dashPattern [4,4] (hover),
   [2,2] (pressed). Drawn as four 1px repeating gradients (see Tab below for the @property registration),
   so solid → dashed animates. The colour is --zz-btn-border (action-secondary-border-*). */
.zz-button[data-hierarchy="secondary"] {
  --zz-dash-on: 4px;
  --zz-dash-off: 0px;                         /* Figma: solid */
  border-color: transparent;
  background-origin: border-box;
  background-clip: border-box;
  background-repeat: no-repeat;
  background-image:
    repeating-linear-gradient(90deg,  var(--zz-btn-border) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(90deg,  var(--zz-btn-border) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(180deg, var(--zz-btn-border) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(180deg, var(--zz-btn-border) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off)));
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
  /* TODO motion: placeholder, same as Tab (no motion tokens in Figma yet) */
  transition: --zz-dash-on 150ms ease-out, --zz-dash-off 150ms ease-out, background-color 150ms ease-out;
}
.zz-button[data-hierarchy="secondary"][data-state="hover"] { --zz-dash-on: 4px; --zz-dash-off: 4px; }   /* Figma: [4,4] */
@media (hover: hover) { .zz-button[data-hierarchy="secondary"]:hover { --zz-dash-on: 4px; --zz-dash-off: 4px; } }
.zz-button[data-hierarchy="secondary"]:active,
.zz-button[data-hierarchy="secondary"][data-state="pressed"] { --zz-dash-on: 2px; --zz-dash-off: 2px; } /* Figma: [2,2] */
.zz-button[data-hierarchy="secondary"]:disabled,
.zz-button[data-hierarchy="secondary"][data-state="disabled"] { --zz-dash-on: 4px; --zz-dash-off: 0px; } /* Figma: solid */
.zz-button[data-hierarchy="secondary"][data-state="active"],
.zz-button[data-hierarchy="secondary"][aria-current="page"] { --zz-dash-on: 4px; --zz-dash-off: 0px; } /* Figma: solid, also on hover */
@media (prefers-reduced-motion: reduce) { .zz-button[data-hierarchy="secondary"] { transition: none; } }

/* disabled: label struck through (Figma: textDecoration STRIKETHROUGH on both hierarchies) */
.zz-button:disabled,
.zz-button[data-state="disabled"] { cursor: not-allowed; }
.zz-button:disabled .zz-button__label,
.zz-button[data-state="disabled"] .zz-button__label { text-decoration: line-through; }

/* focus: not yet defined in Figma — intentionally no style here (TODO: border/focus token) */

/* ═══════════════════════════════════════════════════════════════════════
   Tabs (Figma: tab group) · Tab (Figma: tab → _tab-states → _tab-sizes)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-tabs {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;                 /* Figma: slot container itemSpacing */
  margin: 0;
  padding: 0;
}

.zz-tab,
a.zz-tab {
  /* same anatomy as .zz-button medium: 1px inside border, radius 0, padding 8, gap 8 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  margin: 0;
  padding: 8px;
  gap: 8px;
  border: 1px solid;
  border-radius: 0;
  background-origin: border-box;   /* the border is drawn by gradients inside the border box (see below) */
  background-clip: border-box;
  background-repeat: no-repeat;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  white-space: nowrap;

  /* type: label carries the class label-medium */
  color: var(--text-strong);
  text-decoration: none;
  background-color: var(--tab-background-default);
  border-color: transparent;     /* the visible border is drawn by the gradients below */
}
.zz-tab__label { display: inline-flex; align-items: center; gap: 4px; }
.zz-tab__icon { display: inline-flex; flex: none; width: 20px; height: 20px; color: var(--icon-strong); }
.zz-tab__icon > svg { width: 100%; height: 100%; display: block; }

/* border: Figma draws a 1px inside stroke — solid (default, active), dashPattern [4,4] (hover), [2,2] (pressed).
   CSS `dashed` can neither set the pattern nor animate, so the four sides are always drawn as repeating
   gradients, 1px thick: --zz-dash-on = dash length, --zz-dash-off = gap. Solid = gap 0.
   Both lengths are registered (@property) so they interpolate: solid → dashed opens the gaps smoothly. */
@property --zz-dash-on  { syntax: "<length>"; inherits: false; initial-value: 4px; }
@property --zz-dash-off { syntax: "<length>"; inherits: false; initial-value: 0px; }

.zz-tab {
  --zz-dash-color: var(--tab-border-default);
  --zz-dash-on: 4px;
  --zz-dash-off: 0px;                           /* Figma: solid */
  background-image:
    repeating-linear-gradient(90deg,  var(--zz-dash-color) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(90deg,  var(--zz-dash-color) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(180deg, var(--zz-dash-color) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off))),
    repeating-linear-gradient(180deg, var(--zz-dash-color) 0 var(--zz-dash-on), transparent var(--zz-dash-on) calc(var(--zz-dash-on) + var(--zz-dash-off)));
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
  /* TODO motion: no duration/easing in Figma yet — 150ms ease-out is a placeholder (candidate token motion/duration/fast) */
  transition: --zz-dash-on 150ms ease-out, --zz-dash-off 150ms ease-out, background-color 150ms ease-out;
}
@media (prefers-reduced-motion: reduce) { .zz-tab { transition: none; } }

.zz-tab[data-state="hover"] {
  background-color: var(--tab-background-hover);
  --zz-dash-color: var(--tab-border-hover);
  --zz-dash-on: 4px;  --zz-dash-off: 4px;      /* Figma: dashPattern [4,4] */
}
@media (hover: hover) {   /* decision 0043 */
  .zz-tab:hover {
    background-color: var(--tab-background-hover);
    --zz-dash-color: var(--tab-border-hover);
    --zz-dash-on: 4px;  --zz-dash-off: 4px;
  }
}
.zz-tab:active,
.zz-tab[data-state="pressed"] {
  background-color: var(--tab-background-pressed);
  --zz-dash-color: var(--tab-border-pressed);
  --zz-dash-on: 2px;  --zz-dash-off: 2px;      /* Figma: dashPattern [2,2] */
}
.zz-tab[aria-selected="true"],
.zz-tab[data-selected="true"],
.zz-tab[data-state="active"] {
  background-color: var(--tab-background-active);
  --zz-dash-color: var(--tab-border-active);
  --zz-dash-on: 4px;  --zz-dash-off: 0px;      /* Figma: solid */
  cursor: default;
}

/* focus: not yet defined in Figma — no style (TODO: border/focus token) */

/* ═══════════════════════════════════════════════════════════════════════
   Header (Figma: header · device=desktop | mobile)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-header {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 8px 0 0;            /* Figma: paddingTop 8 */
  margin: 0;
  width: 100%;
  box-sizing: border-box;
  font-family: var(--font-sans);
  color: var(--text-strong);
  background: var(--surface-accent);   /* Figma: header fill → surface/accent (both devices) */
}
/* width: always fill (Figma: layout sizing FILL). The mobile variant is drawn at 375 in Figma; the parent sets the width. */

.zz-header__row {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;     /* Figma: counterAxis MIN */
}

.zz-header__leading,
.zz-header__trailing {
  display: flex;
  flex-direction: row;
  gap: 8px;
}
.zz-header__leading { align-items: flex-start; min-height: 46px; }   /* Figma: min height = store name 38 + 8, so without the logo (0094) the name keeps 8px above the bottom rule (Gio, 2026-11-15) */
.zz-header__trailing { align-items: center; }
.zz-header__tabs { display: flex; }

.zz-header__logo { flex: none; width: 64px; height: 64px; overflow: hidden; }
.zz-header[data-device="mobile"] .zz-header__logo { width: 48px; height: 48px; }
/* Figma `header` boolean `logo` (default on). The storefront turns it off when the row cannot hold everything
   (apps/storefront/scripts/site.js → fitHeaderLogo): the store name always stays. */
.zz-header[data-logo="inside"] .zz-header__logo,
.zz-header[data-logo="hidden"] .zz-header__logo { display: none; }
/* …and it moves inside the store name (Figma `store name` logo=true): logo 36x36 against the left border,
   padding 0 8 0 0, gap 8 → still 38px tall like the plain name. */
.zz-header__name-logo { display: none; flex: none; width: 36px; height: 36px; overflow: hidden; }
.zz-header__name-logo > img,
.zz-header__name-logo > svg { display: block; width: 100%; height: 100%; object-fit: cover; }
.zz-header[data-logo="inside"] .zz-header__name-logo { display: block; }
.zz-header[data-logo="inside"] .zz-header__name { padding: 0 8px 0 0; }
/* data-logo="hidden": not even the small one fits (e.g. 320px): only the name */
.zz-header__logo > img,
.zz-header__logo > svg { display: block; width: 100%; height: 100%; object-fit: cover; }

/* store name: 1px border divider/strong, padding 8, label/medium */
.zz-header__name {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  padding: 8px;
  gap: 8px;
  /* Figma: 1px inside stroke, dashPattern [8,8]. CSS `dashed` cannot set the pattern,
     so the four sides are drawn as 8px-on / 8px-off repeating gradients, 1px thick, in the border box. */
  border: 1px solid transparent;
  background-origin: border-box;
  background-clip: border-box;
  background-repeat: no-repeat;
  background-image:
    repeating-linear-gradient(90deg,  var(--zz-name-border, var(--border-divider-strong)) 0 8px, transparent 8px 16px),
    repeating-linear-gradient(90deg,  var(--zz-name-border, var(--border-divider-strong)) 0 8px, transparent 8px 16px),
    repeating-linear-gradient(180deg, var(--zz-name-border, var(--border-divider-strong)) 0 8px, transparent 8px 16px),
    repeating-linear-gradient(180deg, var(--zz-name-border, var(--border-divider-strong)) 0 8px, transparent 8px 16px);
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
  border-radius: 0;
  color: var(--text-strong);
  text-decoration: none;
  /* type: class label-medium on the element */
  white-space: nowrap;
}

/* Figma `store name` state=active (2026-11-14): the home page lights the store name like the tab of the current
   section (tab/background/active, tab/border/active); the dashed border stays */
.zz-header__name[data-state="active"] { --zz-name-border: var(--tab-border-active); background-color: var(--tab-background-active); }
.zz-header__name[aria-current="page"] { cursor: default; }

/* bottom rule — Figma has it hardcoded #000000; the token it should bind to is border divider/strong */
.zz-header__rule { height: 1px; width: 100%; background: var(--border-divider-strong); }

/* responsive header (device omitted): one of menu / tabs is shown.
   Breakpoint: `--mobile` from tokens.json → breakpoint (expanded at build time by media.js). */
/* search (Figma boolean `search`, 2026-11-12): the field on desktop (280 wide, Gio 2026-11-12), the magnifier Button on mobile */
.zz-header__search { width: 280px; }
.zz-header[data-device="responsive"] .zz-header__search-link { display: none; }
.zz-header[data-device="responsive"] .zz-header__menu { display: none; }
@media (max-width: 959px) {
  .zz-header[data-device="responsive"] .zz-header__search { display: none; }
  .zz-header[data-device="responsive"] .zz-header__search-link { display: inline-flex; }
  .zz-header[data-device="responsive"] .zz-header__menu { display: inline-flex; }
  .zz-header[data-device="responsive"] .zz-header__tabs { display: none; }
  .zz-header[data-device="responsive"] .zz-header__account { display: none; }   /* Figma mobile: only Bag */
  .zz-header[data-device="responsive"] .zz-header__logo { width: 48px; height: 48px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Breadcrumbs (Figma: breadcrumbs)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-breadcrumbs { display: flex; flex-direction: column; width: 100%; }
.zz-breadcrumbs__list {
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;            /* Figma: trail on one line, clips → scrolls sideways here */
  overflow-x: auto;
  white-space: nowrap;
  gap: 4px;                     /* Figma: slot container itemSpacing */
  margin: 0;
  padding: var(--small);        /* Figma: breadcrumbs wrapper padding → dimension `small` */
  list-style: none;
  /* type: class body-medium on the list */
  color: var(--text-strong);
}
.zz-breadcrumbs__item a { color: inherit; text-decoration: none; }
.zz-breadcrumbs__sep, .zz-breadcrumbs__item { flex: none; }
.zz-breadcrumbs__rule { height: 1px; width: 100%; background: var(--border-divider-strong); }

/* ═══════════════════════════════════════════════════════════════════════
   ActiveFilter (Figma: active filter, device=desktop|mobile — only the chip size differs, set by the consumer)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-active-filter { display: flex; flex-direction: column; width: 100%; }
.zz-active-filter[hidden] { display: none; }
.zz-active-filter__row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: var(--small); }   /* Figma: row padding `small`, counter-axis center */
.zz-active-filter__rule { height: 1px; width: 100%; background: var(--border-divider-strong); }   /* Figma: border, fill border divider/strong */
/* Figma 2026-11-12: on desktop the medium chip's instance has padding-top/bottom 8 → 40px tall (24 + 8 + 8).
   Override in `active filter` only: the medium chip inside the admin EntityField stays 24 so the field keeps 38. */
.zz-active-filter .zz-entity-chip:not([data-size="large"]) { height: 40px; }

/* ═══════════════════════════════════════════════════════════════════════
   Tag (Figma: tag item) · TagGroup (Figma: tag group)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-tag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 0;
  background: var(--surface-support-1);
  color: var(--text-strong);
  white-space: nowrap;
}
a.zz-tag { text-decoration: none; }   /* a tag with href: same look, no interaction states yet (open.md) */
/* type: the label carries body-extra-small (extra-small, small) | body-small (medium) | body-medium (large) */
.zz-tag[data-size="extra-small"] { padding: 2px 4px; }
.zz-tag[data-size="small"]       { padding: 4px 8px; }
.zz-tag[data-size="medium"]      { padding: 4px 8px; }
.zz-tag[data-size="large"]       { padding: 4px 8px; }

.zz-tag-group {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 4px;                 /* Figma: tags slot itemSpacing */
  margin: 0;
  padding: 0;
  list-style: none;
  width: 100%;
}
.zz-tag-group__item { display: contents; }

/* ═══════════════════════════════════════════════════════════════════════
   PlayerButton (Figma: player button · state default | hover | pressed | disabled)
   + nested `_player button-icon` (playing). Icon and label share action-floating-foreground-{state};
   the button itself fills with action-floating-background-{state} (a tint only on hover/pressed).
   ═══════════════════════════════════════════════════════════════════════ */

.zz-player-button {
  display: inline-flex;
  align-items: center;
  gap: 0;                   /* Figma: itemSpacing 0 */
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--action-floating-background-default);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  color: var(--action-floating-foreground-default);
  /* type: label carries body-medium */
  white-space: nowrap;
}
.zz-player-button[data-state="hover"]    { color: var(--action-floating-foreground-hover);    background: var(--action-floating-background-hover); }
@media (hover: hover) { .zz-player-button:hover { color: var(--action-floating-foreground-hover); background: var(--action-floating-background-hover); } }   /* 0043 */
.zz-player-button:active,
.zz-player-button[data-state="pressed"]  { color: var(--action-floating-foreground-pressed);  background: var(--action-floating-background-pressed); }
.zz-player-button:disabled,
.zz-player-button[data-state="disabled"] { color: var(--action-floating-foreground-disabled); background: var(--action-floating-background-disabled); cursor: not-allowed; }

/* ── IconButton (Figma: icon button) — 24 × 24, the icon fills the box; colours as PlayerButton ── */
.zz-icon-button {
  display: inline-flex;
  flex: none;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--action-floating-background-default);
  color: var(--action-floating-foreground-default);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}
.zz-icon-button[data-state="hover"]    { color: var(--action-floating-foreground-hover);    background: var(--action-floating-background-hover); }
@media (hover: hover) { .zz-icon-button:hover { color: var(--action-floating-foreground-hover); background: var(--action-floating-background-hover); } }   /* 0043 */
.zz-icon-button:active,
.zz-icon-button[data-state="pressed"]  { color: var(--action-floating-foreground-pressed);  background: var(--action-floating-background-pressed); }
.zz-icon-button:disabled,
.zz-icon-button[data-state="disabled"] { color: var(--action-floating-foreground-disabled); background: var(--action-floating-background-disabled); cursor: not-allowed; }
.zz-icon-button__icon { display: inline-flex; width: 100%; height: 100%; color: inherit; }
.zz-icon-button__icon svg { width: 100%; height: 100%; display: block; }

/* _player button-icon: 24px, fill = the button colour (Figma overrides icon/strong with action/floating/foreground/*) */
.zz-player-button__icon { display: inline-flex; flex: none; width: 24px; height: 24px; color: inherit; }
.zz-player-button__icon svg { width: 100%; height: 100%; display: block; }

/* play ↔ pause: both icons are in the markup, the nearest [data-playing] decides (PlayerButton, Player) */
.zz-icon-play, .zz-icon-pause { display: contents; }
[data-playing="true"] .zz-icon-play,
[data-playing="false"] .zz-icon-pause { display: none; }

/* focus: not yet defined in Figma — no style (TODO: border/focus token) */

/* ═══════════════════════════════════════════════════════════════════════
   Player (Figma: player · device=desktop | mobile, + _player controls)
   Docking, slide in/out and playback belong to the site (apps/storefront), not here.
   ═══════════════════════════════════════════════════════════════════════ */

.zz-player {
  box-sizing: border-box;
  display: grid;
  gap: var(--gutter-small);                       /* Figma desktop: itemSpacing gutter/small */
  padding: 8px var(--page-margin-desktop);        /* Figma: 8 / page-margin */
  background: var(--surface-accent);                /* Figma fill #73ff61, unbound → surface-accent (same value) */
  border-top: 1px solid var(--border-divider-strong);   /* Figma top stroke #000, unbound */
  color: var(--text-strong);
  /* desktop: one row, top-aligned (Figma: horizontal, align MIN). Figma groups actions+now+controls and the native
     player as two FILL siblings (equal halves of the row minus close and gaps), native capped at max-width 800 */
  grid-template-columns: auto minmax(0, 1fr) 136px min(800px, calc((100% - 24px - 2 * var(--gutter-small)) / 2)) 24px;
  grid-template-areas: "actions now controls native close";
  align-items: start;
}
.zz-player__now      { grid-area: now; display: flex; gap: 8px; min-width: 0; }
.zz-player__actions  { grid-area: actions; display: flex; gap: 4px; }
.zz-player__controls { grid-area: controls; display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.zz-player__native   { grid-area: native; }
.zz-player__close    { grid-area: close; }

/* now playing: image 48 + title row (track · sample / product), gap 8 */
.zz-player__image { flex: none; width: 48px; height: 48px; overflow: hidden; }
.zz-player__image > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.zz-player__title { display: flex; gap: 8px; min-width: 0; margin: 0; }
.zz-player__number { flex: none; }
.zz-player__names { display: flex; flex-direction: column; min-width: 0; }
.zz-player__sample, .zz-player__product { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* _player controls: position + transport (gap 8), fixed 136, right-aligned */
.zz-player__position { white-space: nowrap; }
.zz-player__transport { display: flex; gap: 8px; }

/* native player slot: the browser's audio controls, 32 tall, full width of the slot (Figma placeholder) */
.zz-player__audio { display: block; width: 100%; height: 32px; }

/* mobile (Figma device=mobile): now + close / actions · controls (space-between, centred) / native (padding-y 8) */
.zz-player[data-device="mobile"] {
  padding: 8px var(--page-margin-mobile);
  grid-template-columns: auto minmax(0, 1fr) 24px;
  grid-template-areas: "now now close" "actions controls controls" "native native native";
  column-gap: 8px;
  row-gap: 8px;
}
.zz-player[data-device="mobile"] .zz-player__actions,
.zz-player[data-device="mobile"] .zz-player__controls { align-self: center; }
.zz-player[data-device="mobile"] .zz-player__native { padding-bottom: 8px; }
@media (max-width: 959px) {
  .zz-player[data-device="responsive"] {
    padding: 8px var(--page-margin-mobile);
    grid-template-columns: auto minmax(0, 1fr) 24px;
    grid-template-areas: "now now close" "actions controls controls" "native native native";
    column-gap: 8px;
    row-gap: 8px;
  }
  .zz-player[data-device="responsive"] .zz-player__actions,
  .zz-player[data-device="responsive"] .zz-player__controls { align-self: center; }
  .zz-player[data-device="responsive"] .zz-player__native { padding-bottom: 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ProductInfo (Figma: product info · device=desktop | mobile)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-product-info {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));   /* Figma: 3 FLEX columns, no gaps */
  gap: 0;
  width: 100%;
  padding-bottom: 64px;   /* Figma: device=desktop paddingBottom 64 (literal: no spacing token, open.md) */
  color: var(--text-strong);
}
.zz-product-info[data-device="mobile"] { padding-bottom: 32px; }   /* Figma: device=mobile paddingBottom 32 */
@media (max-width: 959px) { .zz-product-info[data-device="responsive"] { padding-bottom: 32px; } }
/* type: every text node carries its text-style class (components.js); here only the UA margins are reset */
.zz-product-info p, .zz-product-info :is(h1, h2, h3, h4, h5, h6) { margin: 0; }   /* the title level is a prop (headingLevel) */

.zz-product-info__details { grid-column: 1 / -1; display: flex; flex-direction: column; }
.zz-product-info__title-block { display: flex; flex-direction: column; padding-bottom: 16px; }
.zz-product-info__title-row { display: flex; flex-direction: row; align-items: flex-start; gap: var(--gutter-small); }
.zz-product-info__name { flex: 1 1 auto; min-width: 0; }
.zz-product-info__price { flex: none; }
.zz-product-info__tags { display: flex; flex-direction: column; gap: 8px; padding-bottom: 16px; }
.zz-product-info__description-block { display: flex; flex-direction: column; gap: 4px; padding-bottom: 16px; }

/* samples: desktop = middle column, vertical, gap 8 · mobile = full width, horizontal, wrap, gap 16 (rows and columns) (Figma `slot container` 2018:10164); the bottom space is the component's own padding */
.zz-product-info__samples { grid-column: 2 / 4; display: flex; flex-direction: column; gap: 8px; }
.zz-product-info__samples .zz-player-button { width: 100%; justify-content: flex-start; }

.zz-product-info[data-device="mobile"] .zz-product-info__samples { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; align-content: flex-start; gap: 16px; }
.zz-product-info[data-device="mobile"] .zz-product-info__samples .zz-player-button { width: auto; }

@media (max-width: 959px) {
  .zz-product-info[data-device="responsive"] .zz-product-info__samples { grid-column: 1 / -1; flex-direction: row; flex-wrap: wrap; align-content: flex-start; gap: 16px; }
  .zz-product-info[data-device="responsive"] .zz-product-info__samples .zz-player-button { width: auto; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ProductCard (Figma: PDP `product card`, device=desktop | mobile)
   One markup, CSS grid places the blocks (in Figma the description lives in a different frame per variant).
   desktop: 9 cols, gap gutter/small · image cols 1–2 · info cols 3–5 (gap 16) · purchase cols 6–9 (gap 8),
            description below the purchase block (row gap = purchase gap 8). Rows `auto 1fr`: the image's extra height
            goes to the last row, never between blocks.
   mobile:  gap gutter/small · image 4 of 12 cols, max 96px (0039) · info + purchase (hug, right-aligned) share the rest,
            details gap 8 = gutter/small today (Figma: literal 8).
   ═══════════════════════════════════════════════════════════════════════ */

.zz-product-card {
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
  grid-template-rows: auto 1fr;
  gap: var(--gutter-small);
  align-items: start;
  color: var(--text-strong);   /* Figma: text fills unbound #000 → text/strong (open.md) */
}
/* Hover (Figma `product card` state=hover, decision 0088): the whole card takes surface/hover. Pointer devices only,
   so a tap on touch does not leave the card tinted. No padding change: the tint is the card box as it is. */
.zz-product-card[data-state="hover"] { background: var(--surface-hover); }
@media (hover: hover) {
  .zz-product-card:hover { background: var(--surface-hover); }
}
.zz-product-card p, .zz-product-card h1, .zz-product-card h2, .zz-product-card h3, .zz-product-card h4 { margin: 0; }
.zz-product-card__image {
  grid-column: 1 / 3; grid-row: 1 / -1;
  aspect-ratio: 1;   /* Figma: product image, proportions locked 1:1 */
  overflow: hidden;
  background: var(--surface-support-1);   /* Figma: product image fill (shown when there is no cover) */
}
a.zz-product-card__image { display: block; color: inherit; }
.zz-product-card__image > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.zz-product-card__info        { grid-column: 3 / 6; grid-row: 1 / -1; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.zz-product-card__title-block { display: flex; flex-direction: column; min-width: 0; }
.zz-product-card__samples     { display: flex; flex-wrap: wrap; gap: 8px; }
.zz-product-card__purchase    { grid-column: 6 / 10; grid-row: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; min-width: 0; }
.zz-product-card__add         { width: 100%; }   /* Figma: button layout=fill (the parent decides the width) */
.zz-product-card__description { grid-column: 6 / 10; grid-row: 2; display: flex; flex-direction: column; gap: 4px; }

/* responsive: both buttons are in the markup, one shows per breakpoint */
.zz-product-card[data-device="responsive"] .zz-product-card__add[data-device="mobile"] { display: none; }

.zz-product-card[data-device="mobile"] {
  grid-template-columns: min(calc((100% - 11 * var(--gutter-small)) / 3 + 3 * var(--gutter-small)), 96px) minmax(0, 1fr) auto;   /* image = 4 of 12 cols, max 96 (Figma: image track fixed 96, decision 0039); info fill; purchase hug */
  grid-template-rows: repeat(4, auto) minmax(16px, 1fr);   /* title, description, tags, samples; last row = Figma details padding-bottom 16 */
  row-gap: 0;   /* info spacing is per block (margin), so a missing block leaves no gap */
}
.zz-product-card[data-device="mobile"] .zz-product-card__image       { grid-column: 1; grid-row: 1 / -1; }
.zz-product-card[data-device="mobile"] .zz-product-card__info        { display: contents; }
.zz-product-card[data-device="mobile"] .zz-product-card__title-block { grid-column: 2; grid-row: 1; }
.zz-product-card[data-device="mobile"] .zz-product-card__description { grid-column: 2; grid-row: 2; margin-top: 8px; }
.zz-product-card[data-device="mobile"] .zz-product-card__tags        { grid-column: 2; grid-row: 3; margin-top: 8px; }
.zz-product-card[data-device="mobile"] .zz-product-card__samples     { grid-column: 2; grid-row: 4; margin-top: 8px; padding-top: 8px; }   /* Figma: info gap 8 + samples padding-top 8 */
.zz-product-card[data-device="mobile"] .zz-product-card__purchase    { grid-column: 3; grid-row: 1 / -1; align-items: flex-end; }   /* Figma: hug, counter-axis MAX */
.zz-product-card[data-device="mobile"] .zz-product-card__add         { width: auto; }   /* Figma: button layout=hug */

@media (max-width: 959px) {
  .zz-product-card[data-device="responsive"] {
    grid-template-columns: min(calc((100% - 11 * var(--gutter-small)) / 3 + 3 * var(--gutter-small)), 96px) minmax(0, 1fr) auto;   /* image = 4 of 12 cols, max 96 (Figma: image track fixed 96, decision 0039); info fill; purchase hug */
    grid-template-rows: repeat(4, auto) minmax(16px, 1fr);   /* title, description, tags, samples; last row = Figma details padding-bottom 16 */
    row-gap: 0;   /* info spacing is per block (margin), so a missing block leaves no gap */
  }
  .zz-product-card[data-device="responsive"] .zz-product-card__image       { grid-column: 1; grid-row: 1 / -1; }
  .zz-product-card[data-device="responsive"] .zz-product-card__info        { display: contents; }
  .zz-product-card[data-device="responsive"] .zz-product-card__title-block { grid-column: 2; grid-row: 1; }
  .zz-product-card[data-device="responsive"] .zz-product-card__description { grid-column: 2; grid-row: 2; margin-top: 8px; }
  .zz-product-card[data-device="responsive"] .zz-product-card__tags        { grid-column: 2; grid-row: 3; margin-top: 8px; }
  .zz-product-card[data-device="responsive"] .zz-product-card__samples     { grid-column: 2; grid-row: 4; margin-top: 8px; padding-top: 8px; }   /* Figma: info gap 8 + samples padding-top 8 */
  .zz-product-card[data-device="responsive"] .zz-product-card__purchase    { grid-column: 3; grid-row: 1 / -1; align-items: flex-end; }   /* Figma: hug, counter-axis MAX */
  .zz-product-card[data-device="responsive"] .zz-product-card__add         { width: auto; }   /* Figma: button layout=hug */
  .zz-product-card[data-device="responsive"] .zz-product-card__add[data-device="desktop"] { display: none; }
  .zz-product-card[data-device="responsive"] .zz-product-card__add[data-device="mobile"]  { display: inline-flex; }
}

/* ═══════════════════════════════════════════════════════════════════════
   BagItem (Figma: page Bag → `bag item`, device=desktop | mobile × status=available | unavailable; decision 0044)
   Row: cover 96 · info (gap 8) · purchase (right-aligned, gap 8); row gap gutter/small, as ProductCard (0074). Type classes come from components.js.
   ═══════════════════════════════════════════════════════════════════════ */
.zz-bag-item {
  display: flex;
  align-items: flex-start;
  gap: var(--gutter-small);                   /* Figma: itemSpacing → gutter/small, same as ProductCard columns (0074) */
  color: var(--text-strong);
  font-family: var(--font-sans);
}
.zz-bag-item p, .zz-bag-item h1, .zz-bag-item h2, .zz-bag-item h3, .zz-bag-item h4 { margin: 0; }
.zz-bag-item__image { flex: none; width: 96px; height: 96px; display: block; }   /* Figma: product image 96 × 96 */
.zz-bag-item__image > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.zz-bag-item__info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--small); }
.zz-bag-item__title-block { display: flex; flex-direction: column; min-width: 0; }
.zz-bag-item__link { color: inherit; text-decoration: none; }
@media (hover: hover) { .zz-bag-item__link:hover { text-decoration: underline; } }   /* as .site-inline-link (decision 0043) */
.zz-bag-item__description { display: flex; flex-direction: column; gap: 4px; }
.zz-bag-item__samples { display: flex; flex-wrap: wrap; gap: 8px; }
.zz-bag-item__purchase { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: var(--small); }
/* sold (decision 0064) and reserved (0065): name plain (decision 0078); "SOLD OUT" / "RESERVED" in an inline Alert beside the struck price (Figma `price row`, gap small) */
.zz-bag-item:is([data-status="sold"], [data-status="reserved"]) .zz-bag-item__price { display: flex; align-items: center; gap: var(--small); white-space: nowrap; }
/* mobile: the word below the struck price (Figma mobile `price row` vertical: price, then word; align right, gap small; Gio 2026-11-14). column-reverse keeps the DOM / screen reader order "SOLD OUT, not in your order, 12€" */
.zz-bag-item[data-device="mobile"]:is([data-status="sold"], [data-status="reserved"]) .zz-bag-item__price { flex-direction: column-reverse; align-items: flex-end; }
@media (max-width: 959px) { .zz-bag-item[data-device="responsive"]:is([data-status="sold"], [data-status="reserved"]) .zz-bag-item__price { flex-direction: column-reverse; align-items: flex-end; } }
/* mobile: samples padding-top 8 (Figma: copied from the mobile product card) */
.zz-bag-item[data-device="mobile"] .zz-bag-item__samples { padding-top: 8px; }
@media (max-width: 959px) { .zz-bag-item[data-device="responsive"] .zz-bag-item__samples { padding-top: 8px; } }

/* ═══════════════════════════════════════════════════════════════════════
   BagSummary (Figma: page Bag → `bag summary`; decision 0044)
   Column gap 8: divider · rows (label | value, space-between) · Checkout (primary large, fill) · note.
   ═══════════════════════════════════════════════════════════════════════ */
.zz-bag-summary { display: flex; flex-direction: column; gap: var(--small); color: var(--text-strong); font-family: var(--font-sans); }
.zz-bag-summary__ship-to > * { width: 100%; }
.zz-bag-summary__row[hidden] { display: none; }
.zz-bag-summary__rows { display: flex; flex-direction: column; gap: var(--small); margin: 0; }
.zz-bag-summary__row { display: flex; justify-content: space-between; gap: var(--small); }
.zz-bag-summary__row dt, .zz-bag-summary__row dd { margin: 0; }
.zz-bag-summary__row dd { text-align: right; }
.zz-bag-summary__checkout { width: 100%; }   /* Figma: button layout=fill */
.zz-bag-summary__note { margin: 0; }

/* ═══════════════════════════════════════════════════════════════════════
   Alert (Figma: page Bag → `alert`; decision 0077)
   A blocking message in a coloured box: padding 8 (small), surface-alert, body/small in text-on-alert, full width.
   ═══════════════════════════════════════════════════════════════════════ */
.zz-alert { margin: 0; padding: var(--small); background: var(--surface-alert); color: var(--text-on-alert); font-family: var(--font-sans); }
.zz-alert[data-layout="inline"] { display: inline-flex; padding: 2px 4px; }   /* Figma layout=inline: as Tag extra-small */
.zz-alert[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════════
   RelatedProducts (Figma: PDP `you may also like`, device=desktop | mobile — same layout, only the cards differ)
   top stroke 1px border divider/strong (strokes included in layout → border + full padding, 0014),
   padding 8/0, title body/medium, list of cards gap 8. Figma padding/gap are literal 8 (open.md).
   ═══════════════════════════════════════════════════════════════════════ */

.zz-related-products {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0;
  border-top: 1px solid var(--border-divider-strong);
  box-sizing: border-box;
  color: var(--text-strong);
  min-width: 0;
}
.zz-related-products__title { margin: 0; }
.zz-related-products__list  { display: flex; flex-direction: column; gap: 8px; }

/* ═══════════════════════════════════════════════════════════════════════
   Gallery (Figma: PDP - desktop `gallery` 2138:1176 · PDP - mobile `mobile gallery`)
   desktop: both images full width, stacked, gap gutter/small (Figma itemSpacing bound to gutter/small); the box scrolls vertically on its own
            once it is taller than --zz-gallery-max-height (set by the page; Figma: clip + overflow vertical)
   mobile:  horizontal swipe, one full-width square per screen, gap 1px (as in Figma)
   ═══════════════════════════════════════════════════════════════════════ */

.zz-gallery {
  display: flex;
  flex-direction: column;
  gap: var(--gutter-small);       /* Figma: gallery itemSpacing → gutter/small */
  width: 100%;
  max-height: var(--zz-gallery-max-height, none);
  overflow-y: auto;
  scrollbar-width: none;          /* no scrollbar in Figma (open.md): wheel, touch and keyboard (tabindex) still scroll */
}
.zz-gallery::-webkit-scrollbar { display: none; }
.zz-gallery:focus-visible { outline: auto; }   /* browser ring: no focus token yet */
.zz-gallery__item { flex: 0 0 auto; aspect-ratio: 1 / 1; overflow: hidden; }
.zz-gallery__item > img { display: block; width: 100%; height: 100%; object-fit: cover; }

.zz-gallery[data-device="mobile"] {
  display: flex;
  flex-direction: row;
  gap: 1px;                       /* Figma: mobile gallery itemSpacing 1 */
  max-height: none;
  overflow-y: hidden;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.zz-gallery[data-device="mobile"]::-webkit-scrollbar { display: none; }
/* --zz-gallery-bleed: how far the scroller extends past its column on BOTH sides (the page sets it to the page margin).
   The scroller spans the full viewport, padding + scroll-padding put the snap point back on the page margin:
   image 1 rests at the left margin with image 2 peeking at the right edge; swiping, image 1 exits on the left
   and image 2 stops at the margin, keeping the right margin. Items stay as wide as the column (100% of the content box).
   width auto: with width 100% the negative margins would only shift the box, not widen it. */
.zz-gallery[data-device="mobile"] {
  width: auto;
  margin-inline: calc(-1 * var(--zz-gallery-bleed, 0px));
  padding-inline: var(--zz-gallery-bleed, 0px);
  scroll-padding-inline: var(--zz-gallery-bleed, 0px);
}
.zz-gallery[data-device="mobile"] .zz-gallery__item { flex: 0 0 100%; scroll-snap-align: start; }

@media (max-width: 959px) {
  .zz-gallery[data-device="responsive"] {
    display: flex;
    flex-direction: row;
    gap: 1px;
    max-height: none;
    overflow-y: hidden;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .zz-gallery[data-device="responsive"]::-webkit-scrollbar { display: none; }
  .zz-gallery[data-device="responsive"] {
    width: auto;
    margin-inline: calc(-1 * var(--zz-gallery-bleed, 0px));
    padding-inline: var(--zz-gallery-bleed, 0px);
    scroll-padding-inline: var(--zz-gallery-bleed, 0px);
  }
  .zz-gallery[data-device="responsive"] .zz-gallery__item { flex: 0 0 100%; scroll-snap-align: start; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Admin forms (Figma page BACKEND → Create). Render inside [data-theme="backend"]:
   surface-accent is black-a4 there (Figma mode `backend`, 2026-11-10): translucent.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Input (Figma: input + _input-master) ──
   Column, gap 4: top content (label · Optional) / field / support text.
   Field: 1px border-divider-strong, surface-accent, padding 8 (compact 4), gap 8 (compact 4), strokes included in
   layout (0014) → 38px (compact 30px). Text: label + Optional + support body-small, value + affixes body-medium. */
.zz-input {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  color: var(--text-strong);
}
.zz-input__top { display: flex; align-items: flex-start; gap: 8px; }
.zz-input__label { flex: 1 1 0; min-width: 0; color: var(--text-strong); }
.zz-input__optional { flex: none; color: var(--text-moderate); }

.zz-input__field {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px;
  box-sizing: border-box;
  border: 1px solid var(--border-divider-strong);
  border-radius: 0;
  background: var(--surface-accent);
  cursor: text;
}
.zz-input[data-size="compact"] .zz-input__field { gap: 4px; padding: 4px; }
/* text area (Figma `_input-master` size=multiline, proposal 2026-11-12): field at least 120 tall, text from the top */
.zz-input[data-variant="text-area"] .zz-input__field { align-items: flex-start; min-height: 120px; }
.zz-input[data-variant="text-area"] .zz-input__control { min-height: 102px; resize: vertical; }

.zz-input__control {
  flex: 1 1 0;
  min-width: 0;
  height: 20px;            /* body-medium line height: the field is 38 = 1 + 8 + 20 + 8 + 1 */
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text-strong);
  font: inherit;           /* overridden by the body-medium class on the element */
  appearance: none;
  -webkit-appearance: none;
}
/* focus (0063): the ring goes around the whole field, not the bare control inside it. Still the browser ring
   (no border/focus token yet, design/open.md). Without :has the control keeps its own ring: never no ring. */
@supports selector(:has(*)) {
  .zz-input__control:focus-visible { outline: none; }
  .zz-input__field:has(.zz-input__control:focus-visible) { outline: auto; }
}
.zz-input__control::placeholder { color: var(--text-moderate); opacity: 1; }
.zz-input__control::-webkit-search-decoration,
.zz-input__control::-webkit-search-cancel-button { -webkit-appearance: none; }

/* select: native element, chevron drawn by the trailing icon on top of it; "Select" placeholder = the empty option */
.zz-input[data-variant="select"] .zz-input__field { cursor: pointer; }
.zz-input[data-variant="select"] .zz-input__control { cursor: inherit; padding-right: 28px; margin-right: -28px; }   /* the 20px chevron + 8px gap stay clickable */
.zz-input[data-variant="select"] .zz-input__control:has(option[value=""]:checked) { color: var(--text-moderate); }
.zz-input[data-variant="select"][data-size="compact"] .zz-input__control { padding-right: 24px; margin-right: -24px; }

.zz-input__affix { flex: none; color: var(--text-strong); white-space: nowrap; }
.zz-input__icon { display: inline-flex; flex: none; width: 20px; height: 20px; color: var(--icon-strong); }
.zz-input__icon svg { width: 100%; height: 100%; display: block; }
.zz-input__icon--trailing { pointer-events: none; }
/* search actions (Figma `input` type=search, 2026-11-12): × clear (leading, only with text) and magnifier submit
   (trailing). Same 20px icon slot as the decorative icons; the button adds no box of its own. */
.zz-input__action {
  display: inline-flex; flex: none; width: 20px; height: 20px; padding: 0; margin: 0;
  border: 0; background: none; color: var(--icon-strong); cursor: pointer;
}
.zz-input__action svg { width: 100%; height: 100%; display: block; }
.zz-input__clear:has(+ .zz-input__control:placeholder-shown),
.zz-input:has(.zz-input__control:placeholder-shown) .zz-input__clear { display: none; }

.zz-input__support { margin: 0; color: var(--text-strong); }

/* state=error: field border + support text in the alert colour (PROVISIONAL tokens, see tokens.json / open.md) */
.zz-input[data-invalid="true"] .zz-input__field { border-color: var(--border-alert); }
.zz-input[data-invalid="true"] .zz-input__support { color: var(--text-alert); }

/* state=locked (readOnly): surface-subtle, no stroke (Figma) → field 36px (compact 28px); value in text-moderate as drawn */
.zz-input[data-locked="true"] .zz-input__field { border: 0; padding: 8px; background: var(--surface-subtle); cursor: default; }
.zz-input[data-locked="true"][data-size="compact"] .zz-input__field { padding: 4px; }
.zz-input[data-locked="true"] .zz-input__control { color: var(--text-moderate); }

/* ── DropZone (Figma: `uploads` → `drop zone`) ──
   surface-subtle, 1px dashed [16,16] border-divider-strong (four gradients, as the outlined controls in 0005),
   the button centred → 88px with a medium Button. No drag-over state in Figma. */
.zz-drop-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 25px 1px;       /* Figma padding 24 0 + the 1px stroke, included in layout (0014): the dashes are a background → 88px */
  box-sizing: border-box;
  background-color: var(--surface-subtle);
  background-repeat: no-repeat;
  background-image:
    repeating-linear-gradient(90deg,  var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(90deg,  var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(180deg, var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(180deg, var(--border-divider-strong) 0 16px, transparent 16px 32px);
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
}

/* ── FileList / FileItem (Figma: `uploads` → `file item`; the list is a layout frame) ──
   List gap 8. Row: gap 16 between handle · content · remove; content gap 8, top-aligned (image rows),
   centred with a play button (audio rows). Thumbnail 40 × 40, surface-support-1 when empty. */
.zz-file-list { position: relative; display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; }   /* relative: rows' offsetTop is measured from the list */
.zz-file-list__item { display: block; }
/* live reorder: rows slide to their new place (FLIP, set in JS).
   TODO motion: no duration/easing in Figma yet — 150ms ease-out is the same placeholder as Button/Tab.
   The dragged row itself follows the pointer (no browser drag image), its number showing the slot it will land in,
   and the list shows an empty slot of its size there (Gio, 2026-11-10). The dragged row is on surface-base (opaque white, Gio 2026-11-10); the slot has no style. */
.zz-file-list__item { transition: transform 150ms ease-out; }
@media (prefers-reduced-motion: reduce) { .zz-file-list__item { transition: none; } }
.zz-file-list__item[data-dragged] { position: relative; z-index: 1; transition: none; background: var(--surface-base); }   /* opaque, so it covers the rows it passes (Gio, 2026-11-10) */
.zz-file-list__status { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }

.zz-file-item { display: inline-flex; align-items: flex-start; gap: 16px; color: var(--text-strong); }
.zz-file-item[data-body] { display: flex; }
.zz-file-item__body { display: flex; flex-direction: column; gap: 16px; flex: 1; min-width: 0; }
.zz-file-item__row { display: flex; align-items: flex-start; gap: 16px; }
.zz-file-item__content { display: flex; align-items: flex-start; gap: 8px; min-width: 0; }
.zz-file-item[data-media="audio"] .zz-file-item__content { align-items: center; }
.zz-file-item__position,
.zz-file-item__featured { flex: none; white-space: nowrap; }
.zz-file-item__name { min-width: 0; overflow-wrap: anywhere; }

.zz-file-item__thumb {
  display: block;
  flex: none;
  width: 40px;
  height: 40px;
  background: var(--surface-support-1);
  overflow: hidden;
}
.zz-file-item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* drag handle: 24 × 24 icon, chromeless (no Figma states: it reuses the floating colours of IconButton) */
.zz-file-item__handle {
  display: inline-flex;
  flex: none;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--action-floating-background-default);
  color: var(--action-floating-foreground-default);
  cursor: grab;
  appearance: none;
  -webkit-appearance: none;
}
@media (hover: hover) { .zz-file-item__handle:hover { background: var(--action-floating-background-hover); } }   /* 0043 */
.zz-file-item__handle:active { background: var(--action-floating-background-pressed); cursor: grabbing; }
.zz-file-item__handle-icon { display: inline-flex; width: 100%; height: 100%; }
.zz-file-item__handle-icon svg { width: 100%; height: 100%; display: block; }

/* ── Checkbox (Figma: controls → checkbox, state default | active) ─────────
   row gap 8, icon 24, label body/medium text/strong. The native input is visually hidden and drives the drawn box. */
.zz-checkbox { display: inline-flex; align-items: flex-start; gap: 8px; color: var(--text-strong); cursor: pointer; position: relative; }
.zz-checkbox__control { position: absolute; width: 24px; height: 24px; margin: 0; opacity: 0; cursor: inherit; }
.zz-checkbox__box { display: inline-flex; width: 24px; height: 24px; flex: none; color: var(--icon-strong); }
.zz-checkbox__box svg { width: 100%; height: 100%; display: block; }
/* one of three icons: empty (default) · tick (:checked) · minus (:indeterminate wins, Figma state=indeterminate) */
.zz-checkbox__box svg:nth-child(n+2),
.zz-checkbox__control:checked + .zz-checkbox__box svg:first-child { display: none; }
.zz-checkbox__control:checked + .zz-checkbox__box svg:nth-child(2) { display: block; }
.zz-checkbox__control:is(:indeterminate, [data-indeterminate]) + .zz-checkbox__box svg { display: none; }
.zz-checkbox__control:is(:indeterminate, [data-indeterminate]) + .zz-checkbox__box svg:nth-child(3) { display: block; }
/* Figma label=false: the label stays for assistive tech (e.g. "Select <record>") */
.zz-checkbox--hide-label .zz-checkbox__label {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.zz-checkbox__control:focus-visible + .zz-checkbox__box { outline: auto; }   /* browser ring: no border/focus token yet */
/* disabled: not designed (design/open.md) */

/* ── Dialog (Figma: page dialog → dialog, 2026-11-10) ─────────────────── */
.zz-dialog {
  width: min(400px, calc(100vw - 32px)); box-sizing: border-box;
  margin: auto; padding: 16px;
  display: flex; flex-direction: column; gap: 24px;
  background: var(--surface-base);                 /* opaque in every mode: never surface-accent (0023) */
  color: var(--text-strong);
  font-family: var(--font-sans);                   /* a modal sits in the top layer: never inherit the page font (a field inside reads it) */
  border: 1px solid var(--border-divider-strong);
}
.zz-dialog:not([open]) { display: none; }
.zz-dialog::backdrop { background: var(--surface-scrim); }
.zz-dialog__content { display: flex; flex-direction: column; gap: 8px; }
.zz-dialog__title { margin: 0; text-transform: uppercase; }
.zz-dialog__body { margin: 0; }
.zz-dialog__body p { margin: 0; }
.zz-dialog__actions { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 8px; }

/* ═══════════════════════════════════════════════════════════════════════
   EntityField + EntityChip (Figma: BACKEND → Create, "DRAFT · entity chips" 2106:676; decision 0020)
   It IS the Figma `input` with the `chips` property on (`_input chips` building block before the value) and the
   field padding overridden to 6, so a row of 24px chips keeps the 38px of a text field (1 + 6 + 24 + 6 + 1).
   The menu hangs 4px below the field.
   ═══════════════════════════════════════════════════════════════════════ */
.zz-entity-field__field { flex-wrap: wrap; row-gap: 8px; padding: 6px; }
.zz-entity-field__chips { display: contents; list-style: none; margin: 0; padding: 0; }
.zz-entity-field__chips > li { display: contents; }
.zz-entity-field__control { flex: 1 1 96px; height: 24px; }   /* a chip tall: the empty field is 38 too */

/* entity chip = tag item medium (surface-support-1, body-small, padding-left 8) + a 24×24 remove target (WCAG 2.5.8) */
.zz-entity-chip {
  display: inline-flex;
  align-items: center;
  height: 24px;
  padding-left: 8px;
  box-sizing: border-box;
  background: var(--surface-support-1);
  color: var(--text-strong);
  white-space: nowrap;
}
.zz-entity-chip__remove {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none;
  margin: 0; padding: 0; border: 0; border-radius: 0;
  background: transparent; color: var(--icon-strong); cursor: pointer;
}
.zz-entity-chip__remove svg { width: 16px; height: 16px; display: block; }
.zz-entity-chip[data-state="hover"] .zz-entity-chip__remove { background: var(--surface-subtle); }
@media (hover: hover) { .zz-entity-chip__remove:hover { background: var(--surface-subtle); } }   /* 0043 */
/* focus: 2px text-strong outside, as drawn (TODO: no focus token yet, design/open.md) */
.zz-entity-chip:has(.zz-entity-chip__remove:focus-visible),
.zz-entity-chip[data-state="focus"] { outline: 2px solid var(--text-strong); outline-offset: 0; }
.zz-entity-chip__remove:focus-visible { outline: none; }

/* menu (listbox) + entity option (kind entity | alias | create; active = aria-selected) */
.zz-entity-field__menu {
  position: absolute; z-index: 10;
  top: calc(100% + 4px); left: -1px; right: -1px;
  margin: 0; padding: 0; list-style: none;
  max-height: 288px; overflow-y: auto;
  background: var(--surface-base);   /* floats over the form: opaque in every mode (0023; Gio 2026-11-10) */
  border: 1px solid var(--border-divider-strong);
}
.zz-entity-field__menu[hidden] { display: none; }
.zz-entity-option {
  display: flex; align-items: center; gap: 8px;
  padding: 8px;
  background: var(--surface-base);
  color: var(--text-strong);
  cursor: pointer;
}
.zz-entity-option[aria-selected="true"] { background: var(--surface-subtle); }
.zz-entity-option__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.zz-entity-option__official { flex: none; color: var(--text-moderate); }
.zz-entity-option__icon { display: inline-flex; width: 20px; height: 20px; flex: none; color: var(--icon-strong); }
.zz-entity-option__icon svg { width: 100%; height: 100%; display: block; }

/* ── Divider (Figma: `divider` → `divider`) ── 1px border/divider/strong, full width. */
.zz-divider { display: block; height: 1px; margin: 0; border: 0; background: var(--border-divider-strong); }

/* ── ActionPanel (Figma: `listing form` → `action panel`) ── surface/subtle, padding 16, Buttons fill, gap 8. */
.zz-action-panel { display: flex; flex-direction: column; gap: 8px; padding: 16px; background: var(--surface-subtle); }
.zz-action-panel > .zz-button { width: 100%; }

/* ── ListingLine (Figma: `listing form` → `listing line`) ──
   line: fields + sample, gap 8. fields: 8-column grid, column gap 16, row gap 8 (Figma grid auto layout, 0012).
   removable=false: artist 1–4, title 5–8. removable=true: artist 1–3, title 4–7, remove × in 8.
   Row 2: spelling under the artist, credit under the title. Sample: gap 16 (Figma `sample`). */
.zz-listing-line { display: flex; flex-direction: column; gap: 8px; }
.zz-listing-line__fields { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 8px 16px; align-items: start; }
.zz-listing-line__fields > * { display: flex; flex-direction: column; min-width: 0; }   /* cells blockify their field, as direct grid items would */
.zz-listing-line__artist   { grid-row: 1; grid-column: 1 / span 4; }
.zz-listing-line__title    { grid-row: 1; grid-column: 5 / span 4; }
.zz-listing-line__spelling { grid-row: 2; grid-column: 1 / span 4; }
.zz-listing-line__credit   { grid-row: 2; grid-column: 5 / span 4; }
.zz-listing-line[data-removable="true"] .zz-listing-line__artist   { grid-column: 1 / span 3; }
.zz-listing-line[data-removable="true"] .zz-listing-line__title    { grid-column: 4 / span 4; }
.zz-listing-line[data-removable="true"] .zz-listing-line__spelling { grid-column: 1 / span 3; }
.zz-listing-line[data-removable="true"] .zz-listing-line__credit   { grid-column: 4 / span 4; }
.zz-listing-line__remove   { grid-row: 1; grid-column: 8; align-items: flex-start; }
.zz-listing-line__sample { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 959px) {   /* not drawn: one column in source order, as the other admin rows */
  .zz-listing-line__fields { grid-template-columns: minmax(0, 1fr); }
  .zz-listing-line[data-removable] .zz-listing-line__fields > * { grid-row: auto; grid-column: auto; }
}

/* ── shared: visually hidden, still read by assistive tech ───────────────── */
.zz-visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── Tab suffix (Figma: _tab-sizes `suffix`, label container gap 4, same text style) ── */
.zz-tab__suffix { display: inline; }

/* ── Dialog choices (Figma: dialog `choices`, under the body, content gap 8) ── */
.zz-dialog__choices { display: flex; flex-direction: column; }

/* ── Radio (Figma: controls → radio, 2026-11-10) ───────────────────────────
   control 24 with the 18px icon centred, gap 8, label body/medium text/strong. Same mechanics as the checkbox. */
.zz-radio { display: inline-flex; align-items: flex-start; gap: 8px; color: var(--text-strong); cursor: pointer; position: relative; }
.zz-radio__control { position: absolute; width: 24px; height: 24px; margin: 0; opacity: 0; cursor: inherit; }
.zz-radio__box { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; flex: none; color: var(--icon-strong); }
.zz-radio__box svg { width: 18px; height: 18px; display: block; }
.zz-radio__box svg:nth-child(2),
.zz-radio__control:checked + .zz-radio__box svg:first-child { display: none; }
.zz-radio__control:checked + .zz-radio__box svg:nth-child(2) { display: block; }
.zz-radio__control:focus-visible + .zz-radio__box { outline: auto; }   /* browser ring: no border/focus token yet */
.zz-radio--hide-label .zz-radio__label {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.zz-radio-group { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; border: 0; min-width: 0; }
.zz-radio-group__legend { padding: 0; margin: 0 0 8px; }

/* ── Filter / FilterBlock / Filters (Figma: page filters, 2026-11-10) ──────
   filter: box 16, gap 8, label body/small text/strong, count body/small text/moderate (4.6:1 on acid green, 0067).
   filter block: heading label/extra small, gap 16, options gap 8. filters: blocks gap 24, padding 0 8. */
.zz-filter { display: inline-flex; align-items: center; gap: 8px; color: var(--text-strong); cursor: pointer; position: relative; }
.zz-filter__control { position: absolute; left: 0; top: 50%; width: 16px; height: 16px; margin: -8px 0 0; opacity: 0; cursor: inherit; }
.zz-filter__box { display: inline-flex; width: 16px; height: 16px; flex: none; color: var(--icon-strong); }
.zz-filter__box svg { width: 100%; height: 100%; display: block; }
.zz-filter__box svg:nth-child(2),
.zz-filter__control:checked + .zz-filter__box svg:first-child { display: none; }
.zz-filter__control:checked + .zz-filter__box svg:nth-child(2) { display: block; }
.zz-filter__control:focus-visible + .zz-filter__box { outline: auto; }   /* browser ring: no border/focus token yet */
.zz-filter__count { color: var(--text-moderate); }
.zz-filter-block { display: flex; flex-direction: column; gap: 16px; margin: 0; padding: 0; border: 0; min-width: 0; }
.zz-filter-block__label { padding: 0; margin: 0; float: left; width: 100%; }   /* float: lets the legend take part in the flex gap */
.zz-filter-block__options { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; clear: both; }
.zz-filters { display: flex; flex-direction: column; gap: 24px; padding: 0 8px; }

/* filter device=mobile (Figma 2026-11-10, Gio): box 24, label + count body-medium (class body-medium--mobile),
   padding-y small → 40px row (Gio accepts < 44, design/open.md). The row is the <label>: all of it is the target. */
@media (max-width: 959px) {
  .zz-filter[data-content="label"] { display: flex; width: 100%; box-sizing: border-box; padding: var(--small) 0; }
  .zz-filter__box { width: 24px; height: 24px; }
  .zz-filter__control { width: 24px; height: 24px; margin-top: -12px; }
}

/* ── Button suffix (Figma `_button-sizes` → suffix): same text style, label container gap 4 (0029) ── */
.zz-button__suffix, .zz-button__prefix { display: inline; }

/* ── IconButton: the hidden attribute wins over display:inline-flex ── */
.zz-icon-button[hidden] { display: none; }

/* ── EntityChip size=large (Figma 2026-11-10, mobile): 44px, padding-left medium-small, label body-medium,
   remove target 44 × 44 with a 24px icon ── */
.zz-entity-chip[data-size="large"] { height: 44px; padding-left: var(--medium-small); }
.zz-entity-chip[data-size="large"] .zz-entity-chip__remove { width: 44px; height: 44px; }
.zz-entity-chip[data-size="large"] .zz-entity-chip__remove svg { width: 24px; height: 24px; }

/* ── FilterFacet (Figma `filter facet`): a row of the main level, opens the facet's level. Padding-y medium-small,
   gap small, 1px border-divider-strong at the bottom; label label-medium (uppercase), value body-small text-moderate
   (one line, ellipsis), chevron 24. Hover / pressed / focus not designed (design/open.md): browser focus ring. ── */
.zz-filter-facet {
  display: flex; align-items: center; gap: var(--small);
  width: 100%; box-sizing: border-box; margin: 0;
  padding: var(--medium-small) 0;
  border: 0; border-bottom: 1px solid var(--border-divider-strong); border-radius: 0;
  background: transparent; color: var(--text-strong); font: inherit; text-align: left; cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
.zz-filter-facet__label { flex: 1 1 auto; min-width: 0; text-transform: uppercase; }
.zz-filter-facet__value { flex: 0 1 auto; min-width: 0; color: var(--text-moderate); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.zz-filter-facet__chevron { display: inline-flex; flex: none; width: 24px; height: 24px; color: var(--icon-strong); }
.zz-filter-facet__chevron svg { width: 100%; height: 100%; display: block; }

/* ── FiltersLayer (Figma `filters layer`, level main | facet): a full-height column on surface-accent.
   Bar: padding medium-small × page-margin-mobile, gap small, bottom border. Body: scrolls on its own, padding
   medium-small (top small on level=facet), gap medium-small. Actions: sticky at the bottom (they sit outside the
   scrolling body), padding small × page-margin-mobile, gap small, top border, surface-accent (Gio). ── */
.zz-filters-layer { display: flex; flex-direction: column; height: 100%; box-sizing: border-box; background: var(--surface-accent); color: var(--text-strong); }
.zz-filters-layer [hidden] { display: none !important; }
.zz-filters-layer__bar {
  display: flex; align-items: center; gap: var(--small); flex: none;
  padding: var(--medium-small) var(--page-margin-mobile);
  border-bottom: 1px solid var(--border-divider-strong);
}
.zz-filters-layer__title { flex: 1 1 auto; min-width: 0; margin: 0; text-transform: uppercase; }
.zz-filters-layer__body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: var(--medium-small);
  padding: var(--medium-small) var(--page-margin-mobile);
}
.zz-filters-layer[data-level="facet"] .zz-filters-layer__body { padding-top: var(--small); }
.zz-filters-layer__applied { display: flex; flex-direction: column; gap: var(--small); }
.zz-filters-layer__applied-label { margin: 0; text-transform: uppercase; }
.zz-filters-layer__chips { display: flex; flex-wrap: wrap; gap: 4px; }   /* Figma: 4, as tag group (no token) */
.zz-filters-layer__actions {
  display: flex; gap: var(--small); flex: none;
  padding: var(--small) var(--page-margin-mobile);
  border-top: 1px solid var(--border-divider-strong);
  background: var(--surface-accent);
}
.zz-filters-layer__apply { flex: 1 1 auto; }
/* the facet level shows one FilterBlock: its heading is the bar title, options gap 0 (rows carry the padding) */
.zz-filters-layer .zz-filter-block__options { gap: 0; align-items: stretch; }

/* ── MenuLayer / MenuGroup / MenuItem (Figma page `menu`, proposal 2026-11-12): the mobile menu. Same frame as
   FiltersLayer (bar, body scrolling on its own) without the actions. ── */
.zz-menu-layer { display: flex; flex-direction: column; height: 100%; box-sizing: border-box; background: var(--surface-accent); color: var(--text-strong); }
.zz-menu-layer__bar {
  display: flex; align-items: center; gap: var(--small); flex: none;
  padding: var(--medium-small) var(--page-margin-mobile);
  border-bottom: 1px solid var(--border-divider-strong);
}
.zz-menu-layer__title { flex: 1 1 auto; min-width: 0; margin: 0; text-transform: uppercase; }
.zz-menu-layer__body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 24px;   /* Figma: 24 literal, no token (design/open.md) */
  padding: var(--small) var(--page-margin-mobile) var(--medium-small);
}
.zz-menu-group { display: flex; flex-direction: column; }
.zz-menu-item {
  display: flex; align-items: center; gap: var(--small);
  padding: var(--medium-small) var(--small);
  border-bottom: 1px solid var(--border-divider-strong);
  color: var(--text-strong); text-decoration: none;
}
.zz-menu-item[data-state="current"] { background-color: var(--tab-background-active); }
button.zz-menu-item { width: 100%; margin: 0; border-width: 0 0 1px; border-style: solid; border-color: var(--border-divider-strong); background: none; font: inherit; text-align: left; cursor: pointer; }   /* an action row (0089) */
.zz-menu-item__label { flex: 1 1 auto; min-width: 0; text-transform: uppercase; }
.zz-menu-item__detail { flex: 0 1 auto; min-width: 0; color: var(--text-moderate); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.zz-menu-layer__legal { display: flex; flex-wrap: wrap; gap: var(--small) var(--medium-small); padding: 0 var(--small); }
.zz-menu-layer__legal a { color: var(--text-strong); text-decoration: none; }
.zz-menu-layer__legal a:hover { text-decoration: underline; }

/* ── Footer (Figma page `footer`, decision 0077): border-top = the header border (1px border-divider-strong);
   main = newsletter + link columns, padding-y medium-small, gap medium-small; bottom = copyright + legal, padding-bottom
   medium-small. Desktop: side by side, flex 1 each (Figma FILL), so without the newsletter the links take the whole row.
   Mobile: stacked, links in two columns (Figma: wrap, (374 - 16) / 2), legal above the copyright. ── */
.zz-footer { background: var(--surface-accent); color: var(--text-strong); border-top: 1px solid var(--border-divider-strong); }
.zz-footer__main { display: flex; gap: var(--medium-small); padding: var(--medium-small) 0; }
.zz-footer__main > * { flex: 1 1 0; min-width: 0; }
.zz-footer__links { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--medium-small); align-items: start; }
.zz-footer__bottom { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--small) var(--medium-small); padding-bottom: var(--medium-small); }
.zz-footer__copyright { margin: 0; }
.zz-footer__legal { display: flex; flex-wrap: wrap; gap: var(--small) var(--medium-small); }
/* links: as the menu legal row (text-strong, underline on hover). 16px lines 24px apart (gap small) = WCAG 2.5.8 by spacing */
.zz-footer a { color: var(--text-strong); text-decoration: none; }
@media (hover: hover) { .zz-footer a:hover { text-decoration: underline; } }
.zz-footer-links__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--small); }
.zz-footer-newsletter { display: flex; flex-direction: column; gap: var(--small); margin: 0; }
.zz-footer-newsletter__row { display: flex; align-items: flex-end; gap: var(--small); }
.zz-footer-newsletter__input { flex: 1 1 auto; min-width: 0; }
.zz-footer-newsletter__note { margin: 0; }
/* a link inside the note is told apart from the text by the underline, not by colour only (WCAG 1.4.1) */
.zz-footer .zz-footer-newsletter__note a { text-decoration: underline; }
@media (max-width: 959px) {
  .zz-footer__main { flex-direction: column; }
  .zz-footer__links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .zz-footer__bottom { flex-direction: column-reverse; }
}

/* ── PlpHeader (Figma `PLP header`, device=mobile): filters trigger and sort, space-between, padding-y small,
   1px border-divider-strong under ── */
.zz-plp-header { display: flex; flex-direction: column; }
.zz-plp-header__controls { display: flex; align-items: center; justify-content: space-between; gap: 4px; padding: var(--small) 0; }
.zz-plp-header__rule { height: 1px; background: var(--border-divider-strong); }

/* ── SelectButton: a native select invisible over a Button look (Figma: button + trailing chevron-down) ── */
.zz-select-button { position: relative; }
.zz-select-button[data-layout="fill"] { display: flex; width: 100%; }   /* Figma button layout=fill (desktop sort) */
.zz-select-button__control {
  position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer;
  font-size: 16px;   /* iOS zooms into a focused select under 16px */
  appearance: none; -webkit-appearance: none;
}
.zz-select-button:has(.zz-select-button__control:focus-visible) { outline: auto; }   /* no border/focus token yet */
/* 0081: a select matches :focus-visible after a mouse pick too; the page marks a pointer focus and the ring hides
   until a key is pressed. Focus stays on the select. */
.zz-select-button[data-focus-source="pointer"]:has(.zz-select-button__control:focus-visible) { outline: none; }

/* ═══════════════════════════════════════════════════════════════════════
   Toast + ToastRegion (Figma: page `toast` → `toast` 2177:439; decision 0049)
   The look: surface-base (opaque, 0023), 1px border-divider-strong dashed [16,16] (0053), padding and gap medium-small,
   message body/medium text-strong, no radius, no shadow. Action = Button secondary small, dismiss = IconButton ×.
   Region: fixed at the top of the viewport (Gio, decision 0071; was the bottom in 0049), gutter-small below whatever
   is docked there (--zz-toast-offset: the shop sets it to the visible part of its sticky header, so the toast follows
   the header as it slides away and back), or below the safe area (notch) when that is larger. Desktop: right-aligned
   at the page margin, 360 wide (Figma). Mobile: full width minus the page margins.
   ═══════════════════════════════════════════════════════════════════════ */
.zz-toast-region {
  position: fixed; z-index: 30;
  right: var(--page-margin-desktop);
  top: calc(max(var(--zz-toast-offset, 0px), env(safe-area-inset-top, 0px)) + var(--gutter-small));
  width: min(360px, calc(100vw - 2 * var(--page-margin-desktop)));
  pointer-events: none;   /* the empty region never blocks the page; the toast itself takes the pointer */
}
@media (max-width: 959px) {
  .zz-toast-region { left: var(--page-margin-mobile); right: var(--page-margin-mobile); width: auto; }
}
.zz-toast-region__slot:empty { display: none; }
.zz-toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--medium-small);
  padding: calc(var(--medium-small) + 1px); box-sizing: border-box; width: 100%;   /* + the 1px stroke, in layout (0014) */
  color: var(--text-strong);
  font-family: var(--font-sans);
  /* border dashed [16,16] (Gio, decision 0053), the static dash of the drop zone, drawn as four 1px gradients because
     CSS `dashed` cannot set the pattern. Not [4,4] / [2,2]: those are hover / pressed of controls (0005) */
  background-color: var(--surface-base);
  background-repeat: no-repeat;
  background-image:
    repeating-linear-gradient(90deg,  var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(90deg,  var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(180deg, var(--border-divider-strong) 0 16px, transparent 16px 32px),
    repeating-linear-gradient(180deg, var(--border-divider-strong) 0 16px, transparent 16px 32px);
  background-size: 100% 1px, 100% 1px, 1px 100%, 1px 100%;
  background-position: 0 0, 0 100%, 0 0, 100% 0;
}
.zz-toast__message { flex: 1 1 auto; min-width: 0; margin: 0; }
.zz-toast__action, .zz-toast__dismiss { flex: none; }
/* enter: fade + drop from above (the region is at the top, 0071). TODO motion: 150ms ease-out is the DS placeholder (no motion tokens); none with reduced motion */
.zz-toast-region .zz-toast { animation: zz-toast-in 150ms ease-out; }
@keyframes zz-toast-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .zz-toast-region .zz-toast { animation: none; } }

/* ── Trade (Figma page `Trade`, proposal 2026-11-12) ── */
.zz-trade-note { display: flex; flex-direction: column; gap: var(--small); color: var(--text-strong); }
.zz-trade-note__title, .zz-trade-note__signature { margin: 0; }
.zz-trade-note__body p { margin: 0; }
.zz-trade-note__signature { color: var(--text-moderate); }
.zz-discogs-card { display: flex; flex-direction: column; gap: 4px; padding: var(--small); border: 1px solid var(--border-divider-strong); color: var(--text-strong); }
.zz-discogs-card p { margin: 0; }
.zz-discogs-card__label { color: var(--text-moderate); }
.zz-discogs-card__link { color: inherit; text-decoration: underline; align-self: flex-start; }
.zz-trade-step { display: flex; gap: var(--medium-small); color: var(--text-strong); }
.zz-trade-step__number { flex: none; width: 24px; }
.zz-trade-step__text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.zz-trade-step__title, .zz-trade-step__body { margin: 0; }

/* Discogs release picker (Figma page Trade: release option, release list, chosen release) */
.zz-release-list { background: var(--surface-base); border: 1px solid var(--border-divider-strong); color: var(--text-strong); }
.zz-release-list__options { margin: 0; padding: 0; list-style: none; }
.zz-release-list__message, .zz-release-list__source { margin: 0; padding: var(--small); }
.zz-release-list__source { color: var(--text-moderate); }
.zz-release-option, .zz-chosen-release { display: flex; align-items: flex-start; gap: var(--small); padding: var(--small); color: var(--text-strong); }
.zz-release-option { cursor: pointer; background: var(--surface-base); }
.zz-release-option[data-highlighted="true"], .zz-release-option:hover { background: var(--surface-subtle); }
.zz-release-option__thumb { flex: none; display: block; width: 40px; height: 40px; object-fit: cover; background: var(--surface-support-1); }
.zz-release-option__text { display: flex; flex-direction: column; flex: 1 1 0; min-width: 0; }
.zz-release-option__meta { color: var(--text-moderate); }
.zz-chosen-release { border: 1px solid var(--border-divider-strong); }
.zz-chosen-release__link { color: inherit; text-decoration: underline; align-self: flex-start; }
.zz-chosen-release__change { flex: none; background: none; border: 0; padding: 0; color: inherit; text-decoration: underline; cursor: pointer; font: inherit; }
/* Memo item (Figma BACKEND → Memo › `memo item`, decision 0088) */
/* ProgressBar (Figma `progress bar`, 0091): 4px track, value grows with a short ease */
.zz-progress-bar { position: relative; height: 4px; background: var(--surface-subtle); overflow: hidden; }
.zz-progress-bar__value { height: 100%; background: var(--action-primary-background-default); transition: width 0.3s ease-out; }
@media (prefers-reduced-motion: reduce) { .zz-progress-bar__value { transition: none; } }
.zz-memo-item { display: flex; align-items: center; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--border-divider-strong); color: var(--text-strong); }
.zz-memo-item__text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.zz-memo-item__text > p { margin: 0; overflow-wrap: anywhere; }
.zz-memo-item__meta { color: var(--text-moderate); }
.zz-memo-item__link { color: inherit; text-decoration: underline; align-self: flex-start; }
.zz-memo-item__actions { flex: none; display: flex; flex-wrap: wrap; gap: 8px; }
@media (max-width: 959px) { .zz-memo-item { flex-direction: column; align-items: stretch; gap: 8px; } }

/* ── AdminBar (Figma page `admin` → `admin bar`, decision 0089): desktop = section buttons + account, mobile = menu · title · create ── */
.zz-admin-bar__desktop { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; }
.zz-admin-bar__nav, .zz-admin-bar__account { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.zz-admin-bar__email { color: var(--text-moderate); }
.zz-admin-bar__mobile { display: none; }
@media (max-width: 959px) {
  .zz-admin-bar__desktop { display: none; }
  .zz-admin-bar__mobile {
    display: flex; align-items: center; gap: var(--small);
    padding: var(--small) var(--page-margin-mobile);   /* Figma: 8 all round, like the shop mobile header */
    background: var(--surface-accent); border-bottom: 1px solid var(--border-divider-strong);
  }
  .zz-admin-bar__title { flex: 1 1 auto; min-width: 0; margin: 0; text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ── ListingRow (Figma page `admin` → `listing row`, decision 0089): an admin list row on mobile, MemoItem grammar ── */
.zz-listing-row { display: flex; align-items: flex-start; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--border-divider-strong); color: var(--text-strong); }
.zz-listing-row__select { flex: none; display: flex; }
.zz-listing-row__text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.zz-listing-row__title { color: inherit; overflow-wrap: anywhere; }
.zz-listing-row__meta, .zz-listing-row__price { margin: 0; }
.zz-listing-row__meta { color: var(--text-moderate); }
.zz-listing-row__price { flex: none; text-align: right; font-variant-numeric: tabular-nums; }

/* ── ActionBar (Figma page `admin` → `action bar`, decision 0089): form actions stuck to the bottom of the viewport ──
   surface/base, not the ActionPanel's surface/subtle: that one is translucent in the backend mode and the form
   scrolls under the bar (Gio 2026-11-15). */
.zz-action-bar {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; flex-direction: column; gap: 8px;
  padding: var(--medium-small);
  background: var(--surface-base); border-top: 1px solid var(--border-divider-strong);
}
.zz-action-bar__actions { display: flex; gap: 8px; }
.zz-action-bar__actions > .zz-button { flex: 1 1 0; min-width: 0; }
.zz-action-bar__status:empty { display: none; }

/* Touch targets (decision 0089, WCAG 2.5.5): on a coarse pointer, small Buttons (32) and checkboxes (24) get an
   invisible tap area of 44, without changing how they look. */
@media (pointer: coarse) {
  .zz-button[data-size="small"] { position: relative; }
  .zz-button[data-size="small"]::after { content: ""; position: absolute; inset: -6px 0; }
  .zz-checkbox::after { content: ""; position: absolute; inset: -10px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   Home (Figma page `Home`, 2026-11-14): section head, genre links, home about
   ═══════════════════════════════════════════════════════════════════════ */
.zz-section-head { display: flex; flex-direction: column; gap: var(--medium-small); }
.zz-section-head__row { display: flex; align-items: center; justify-content: space-between; gap: var(--small); }
.zz-section-head__title { margin: 0; color: var(--text-strong); }
.zz-section-head__actions { display: flex; gap: var(--small); flex: none; }

.zz-genre-links { display: flex; flex-direction: column; gap: var(--medium-small); }
.zz-genre-links__list { display: flex; flex-wrap: wrap; gap: var(--small); margin: 0; padding: 0; list-style: none; }

.zz-home-about__link { color: var(--text-strong); text-decoration: underline; }

.zz-home-about { display: flex; flex-direction: column; align-items: stretch; gap: var(--medium-small); color: var(--text-strong); }
.zz-home-about__bio { margin: 0; }
.zz-home-about__link { align-self: flex-start; }
