/* The surfaces: the system's levels, made of one corner and two spaces.
   A core sheet (every tier; it names no effect).

   L0, the base: a component alone. L1, a window: data-surface="window",
   inset by the Window space (16). L2, a panel: data-surface="panel",
   inset by the Panel space (8). L3, above (a pop-up): the bubble and
   the Dropdown's list. L4, the top, is reserved.

   The corners (round 4, owner, 2026-10-10: "We should define only one
   roundness and it should trickle down to all the other components";
   then "make it so I set up corners on the base layer sliders and
   buttons and all the rest is driven by parameters and potentially just
   calculated or baked in"): the base corner, --ds-radius-corner (4 by
   default; the Global card's Corner writes it), is every control's,
   capped at half its height. A surface derives its own outward, from
   what it holds, by the system's one nesting rule (owner, 2026-10-10:
   "There is only one nesting rule in this system"): concentric, its
   corner the corner it holds plus its inset, the two arcs sharing a
   centre. A panel is the base plus its inset; a window holding panels
   adds its inset to the panel's, a window holding controls to the base,
   so every margin makes its own nesting; a list adds its inset to its
   options'. Outward, the law is exact at every space (the inward law of
   2026-10-04 ran out at the space that equalled the corner). A part inside a control (the theme's strip, the bar's
   inside value) takes the control's corner less its inset, at least 0.

   The space inside (owner, 2026-10-10: "spacing should apply to
   elements inside as well"): a surface's space is its inset and the room
   between what it holds. A window's groups stand the Window space apart
   (--group-gap); controls stacked anywhere, in a panel or right in a
   window, the Panel space (--stack-gap). The defaults (16 and 8) are
   the system's around and between.

   A density (data-density="compact") makes the controls in it 24 high
   on 14 / 20 type (the mono 14): the Slider's and the Dropdown's compact
   windows say so in their markup.

   Drawn by tier: Plain a hairline in the stroke role; Flat and Full the
   text colour at 4 % (opaque on a window). A change of scheme or colour
   rides the clock. */
/* a rule: the 1 px line every stroke, hairline and inside ring is
   drawn with (an inset box-shadow, or a box a rule wide: both pages,
   the components and the lab's chrome), as many whole device pixels as
   one CSS pixel holds and never less than one: 1 CSS px at a whole
   ratio, one device pixel at 125 % and 150 % and below 100 %
   (--ds-hair, lib/core.js). A box-shadow is not snapped to the device
   pixel: a 1px one at 125 % drew a full pixel and a second at about a
   quarter of the ink in Chromium, and in WebKit an outline of two
   weights, its sides lighter than its top (measured 2026-10-08). The
   max() is the floor: below 100 % a hair is more than 1px, and 1px
   rounded down to it is 0, which drew no stroke and no ring at 90 % and
   80 % (review, 2026-10-08). An outline cannot take it: Chromium (153)
   computes an outline's width and offset in whole CSS pixels (0.8px is
   1px, an offset of 2.4px is 2px), so a ring drawn as an outline (the
   system's focus ring, 2 out) stays a CSS pixel wide there */
:root {
  --rule: max(var(--ds-hair), round(down, 1px, var(--ds-hair)));
}
@supports not (width: round(down, 1px, 1px)) {
  :root {
    --rule: 1px;
  }
}
/* the focus ring, said once (iteration 24's audit: its width and its
   offset were written as literals seventeen times): 1 px of the focus
   colour, 2 out from the part, only where the keys brought the focus;
   2 of the system's Highlight in forced colours. An outline, not a
   rule: an outline takes the part's whole box and every corner it has,
   and a neighbour never covers it 2 out; Chromium draws it in whole CSS
   pixels, so it cannot take --rule. The one other shape is the ring
   inside a part a neighbour would cover (a group's cell, a Range's end,
   the field inside a bar), drawn with the rule */
:root {
  --focus-ring-w: 1px;
  --focus-ring-gap: 2px;
  --focus-ring-forced-w: 2px;
}
/* the ladder's off for a part that holds a value in the fill: the fill
   at a third of its ink over the inactive ground, so an off control
   still says where its value stands ("An off state must still be
   readable"; round 6, iteration 17's review), said once for the Slider's
   fill and an off radio row's chosen cell (the critique of iteration
   24: the dock's off rows were faded instead, a third dim level) */
:root {
  --off-ink: 32%;
}
/* the room between controls in a stack and between a window's groups:
   the Global tab's Gap and Window spaces, the tokens space.gap and
   space.window (lib/globals.js writes them on the root; 8 and 16 by
   default; round 5: the Gap stands apart from the Panel space, the
   brief's §2 "margins, padding, gap"; its default is the Panel
   space's 8) */
:root {
  --stack-gap: var(--ds-space-gap);
  --group-gap: var(--ds-space-window);
}
/* the strokes' weights (round 5, the brief's §2: "Stroke: colour and
   weight"): the tokens' CSS px rounded to whole device pixels (the
   hair, lib/core.js), so a weight of 1 is one device pixel at 125 %
   (the finding of 2026-10-08: a box-shadow of 1px drew a pixel and a
   quarter there) and a weight of 0 draws none; every stroke of the
   switches' tokens is drawn with these, never with --rule */
/* the hair every colour shown as a colour wears (a painted bar, a
   swatch, a theme's strip, a picker's field and columns): a darkening
   line in either scheme, the text's ink in light and the page's in dark
   at 14 %, so it edges a light colour and is lost on a dark one (round
   6, iteration 17's review: the text at 12 % was a light ring round a
   dark end in dark).

   The strokes' weights (round 5, the brief's §2: "Stroke: colour and
   weight"): the tokens' CSS px rounded to whole device pixels (the
   hair, lib/core.js), so a weight of 1 is one device pixel at 125 %
   (the finding of 2026-10-08: a box-shadow of 1px drew a pixel and a
   quarter there) and a weight of 0 draws none; every stroke of the
   switches' tokens is drawn with these, never with --rule. The rungs'
   edges (iteration 21, behind the ladder's More): the rest edge's
   weight and what the hand, or a hold, adds to it, in whole device
   pixels as the strokes are.

   Written once for the three places that derive them from their own
   tokens (iteration 24's audit: the same formulas stood three times):
   the root; a part with tokens of its own (the lab's Override locally,
   lib/markers.js, writes a token inline on the part, data-local: what
   the root derives is inherited resolved, so the part derives it again
   from its own; iteration 21's truth audit); and the tool's chrome
   (data-plain, lib/chrome.js), whose tokens are the page's as it had
   them (derived tokens inherit computed, so each must be said again
   where its tokens change) */
:root,
[data-local],
[data-plain] {
  --colour-hair: color-mix(
    in srgb,
    light-dark(var(--ds-color-text), var(--ds-color-page)) 14%,
    transparent
  );
  --stroke-inactive-w: round(
    nearest,
    var(--ds-stroke-inactive-width),
    var(--ds-hair)
  );
  --stroke-active-w: round(
    nearest,
    var(--ds-stroke-active-width),
    var(--ds-hair)
  );
  --hover-edge-w: round(
    nearest,
    var(--ds-stroke-inactive-width) + var(--ds-state-hover-edge-width),
    var(--ds-hair)
  );
  --held-edge-w: round(
    nearest,
    var(--ds-stroke-inactive-width) + var(--ds-state-held-edge-width),
    var(--ds-hair)
  );
}
@supports not (width: round(down, 1px, 1px)) {
  :root,
  [data-local],
  [data-plain] {
    --stroke-inactive-w: var(--ds-stroke-inactive-width);
    --stroke-active-w: var(--ds-stroke-active-width);
    --hover-edge-w: calc(
      var(--ds-stroke-inactive-width) + var(--ds-state-hover-edge-width)
    );
    --held-edge-w: calc(
      var(--ds-stroke-inactive-width) + var(--ds-state-held-edge-width)
    );
  }
}
/* the controls' type (round 5, the brief's §2): a regular surface's
   words at the base size on a line of the line height's share, rounded
   to the cell (16 on 24 at the defaults); a compact one's 2 px less on
   the same rule (14 on 20) */
:root {
  --type: var(--ds-type-size);
  --line: round(
    nearest,
    calc(var(--ds-type-size) * var(--ds-type-line)),
    var(--ds-grid-cell)
  );
}
@supports not (width: round(down, 1px, 1px)) {
  :root {
    --line: calc(var(--ds-grid-cell) * 6);
  }
}
/* the icon in its slot (round 6, iteration 24; owner: "make it so
   user can set up how big icons sit in our slot across our entire UI.
   We can have 2 or 3 preset sizes to cover all needs"): the glyph's box
   a share of the slot's words (Small, Medium, Large: the icon tokens),
   on whole pixels (Medium is the words' size exactly, so the slot's
   rule of iteration 6, 1em of its type, holds at any Type size), never
   closer than a cell to the slot's edge. Regular (a 32 slot on 16
   words): 14, 16, 20; compact (24 on 14): 12, 14, 16. Declared on
   every part that holds a slot, not on the root, since a value derived
   at the root inherits resolved and a compact surface's words would
   never reach it; the chrome (lib/chrome.js) keeps Medium, its own */
.ds-button,
.ds-dropdown,
.ds-dialog,
.ds-picker,
.ds-cpick {
  --icon: min(
    round(
      down,
      calc(var(--type, var(--ds-type-size)) * var(--ds-icon-scale)),
      1px
    ),
    calc(var(--control, calc(var(--ds-grid-cell) * 8)) - var(--ds-space-8))
  );
}
@supports not (width: round(down, 1px, 1px)) {
  .ds-button,
  .ds-dropdown,
  .ds-dialog,
  .ds-picker,
  .ds-cpick {
    --icon: min(
      calc(var(--type, var(--ds-type-size)) * var(--ds-icon-scale)),
      calc(var(--control, calc(var(--ds-grid-cell) * 8)) - var(--ds-space-8))
    );
  }
}
/* the preset in force: Medium (no attribute), or the stored Small or
   Large on html (head.js before the first paint, lib/globals.js), the
   Density's way */
html[data-icons="small"] {
  --ds-icon-scale: var(--ds-icon-small);
}
html[data-icons="large"] {
  --ds-icon-scale: var(--ds-icon-large);
}
/* every glyph of the kit at the house stroke, in CSS px whatever its
   box (lib/icons.js draws it non-scaling; the attribute is the fallback
   where this sheet is not loaded); the grip's dots twice it, so a dot
   reads at 16 */
/* at no weight of their own (:where), so a page that draws a glyph
   its way (the Icons page's grid, its hover) is not outranked */
:where(.ds-icon) :where(path, line) {
  stroke-width: var(--ds-icon-weight);
}
:where(.ds-icon[data-glyph="grip"]) :where(path, line) {
  stroke-width: calc(var(--ds-icon-weight) * 2);
}
/* a part with tokens of its own (the lab's Override locally,
   lib/markers.js, writes a token inline on the part, data-local): what
   the root derives from a token is resolved on the root and inherited
   resolved, so the part derives it again from its own (iteration 21's
   truth audit: a local stroke weight, type size or line, gap or space,
   or stroke colour, changed nothing it drew, while its chip said it
   had; its weights and edges with the root's, above); a compact part's
   type 2 px less, as its surface's */
[data-local] {
  --stack-gap: var(--ds-space-gap);
  --group-gap: var(--ds-space-window);
  --ds-stroke-inactive: var(--ds-color-stroke);
  --ds-stroke-active: var(--ds-color-stroke-active);
  --type: var(--ds-type-size);
  --line: round(
    nearest,
    calc(var(--ds-type-size) * var(--ds-type-line)),
    var(--ds-grid-cell)
  );
}
[data-density="compact"][data-local],
[data-density="compact"] [data-local] {
  --type: calc(var(--ds-type-size) - 2px);
  --line: round(
    nearest,
    calc((var(--ds-type-size) - 2px) * var(--ds-type-line)),
    var(--ds-grid-cell)
  );
}
@supports not (width: round(down, 1px, 1px)) {
  [data-local] {
    --line: calc(var(--ds-grid-cell) * 6);
  }
  [data-density="compact"][data-local],
  [data-density="compact"] [data-local] {
    --line: calc(var(--ds-grid-cell) * 5);
  }
}
/* a window stretched by a slider at its edge (components/effects/give.js,
   round 6, iteration 18; owner: "it should also overstretch the dialog
   window itself ... all the components by this side should also
   overstretch to preserve same margin"): the give at either side,
   carried by the window for what inside it has a width of its own
   (components/picker-compact.css); registered, so the way back
   interpolates */
@property --stretch-l {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
@property --stretch-r {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}
/* a block padded to the 16 lattice (lib/lattice.js, round 6, iteration
   18; owner: "Make sure we align all the system windows and major
   components to the grid"): a window on the canvas evenly at its top
   and its foot; its own, never inherited */
@property --lattice-t {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --lattice-b {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
[data-surface] {
  --space: var(--ds-space-16);
  box-sizing: border-box;
  padding: calc(var(--space) + var(--lattice-t)) var(--space)
    calc(var(--space) + var(--lattice-b));
  /* the base corner plus the inset (below for a window holding panels) */
  border-radius: calc(var(--ds-radius-corner) + var(--space));
  transition:
    background-color var(--ds-motion-clock) var(--ds-motion-ease),
    box-shadow var(--ds-motion-clock) var(--ds-motion-ease),
    color var(--ds-motion-clock) var(--ds-motion-ease),
    padding var(--ds-motion-clock) var(--ds-motion-ease),
    border-radius var(--ds-motion-clock) var(--ds-motion-ease);
}
/* the page's global menu writes the Window and Panel spaces on the root
   (space.window, space.panel; lib/globals.js), so every window, panel
   and control on the page follows one setting; a surface with its own
   --space inline keeps it. A dialog is a window: its inset is the
   Window space (owner, 2026-10-05: "They should affect all the
   elements"; its own 24 of 2026-10-08 was the export's alone, the page
   drawing 16, until iteration 22 made the spaces tokens) */
[data-surface="window"] {
  --space: var(--ds-space-window);
  background: var(--ds-color-page);
  color: var(--ds-color-text);
}
[data-surface="panel"] {
  --space: var(--ds-space-panel);
}
/* a window that holds panels: its inset around the panel's corner */
[data-surface="window"]:has(> [data-surface="panel"]) {
  border-radius: calc(
    var(--ds-radius-corner) + var(--ds-space-panel) + var(--space)
  );
}
/* a control's corner: the one corner, never more than half the
   control's height (a compact 24 at 12 is a pill). The Slider reads it
   as its radius; the dropdown's field, the Checkbox's box
   (components/check.css caps it at 4), the picker's columns, hex and
   saved squares as theirs; the dropdown's root holds it for its list */
.ds-button,
.ds-group,
.ds-slider,
.ds-dropdown,
.ds-dropdown__trigger,
.ds-check,
.ds-picker__col,
.ds-picker__hex,
.ds-picker__swatch,
.ds-picker__shade {
  --control-corner: min(
    var(--ds-radius-corner),
    var(--control, calc(var(--ds-grid-cell) * 8)) / 2
  );
}
.ds-slider {
  --ds-slider-radius: var(--control-corner);
}
/* the tiers' drawing; a tool's own chrome (data-plain, the lab's
   lib/chrome.js) keeps the Full drawing whatever the page's Style */
html[data-render="plain"] [data-surface]:not([data-plain], [data-plain] *) {
  box-shadow: inset 0 0 0 var(--rule) var(--ds-color-stroke);
}
:is(html:not([data-render="plain"]), [data-plain]) [data-surface="window"],
[data-plain][data-surface="window"] {
  background: color-mix(in srgb, var(--ds-color-text) 4%, var(--ds-color-page));
}
:is(html:not([data-render="plain"]), [data-plain]) [data-surface="panel"],
[data-plain][data-surface="panel"] {
  background: color-mix(in srgb, var(--ds-color-text) 4%, transparent);
}
/* a density: a compact surface, its controls 24 high on 14 / 20 type
   and its values in the mono 14 (owner, 2026-10-05: "if size gets too
   small we might want to use smaller font too"); regular is 32 on
   16 / 24 and the mono 15. The Slider's and the Dropdown's compact
   windows carry it in the HTML */
[data-density="compact"] {
  --control: calc(var(--ds-grid-cell) * 6);
  --type: calc(var(--ds-type-size) - 2px);
  --mono-type: var(--ds-text-14);
  --line: round(
    nearest,
    calc((var(--ds-type-size) - 2px) * var(--ds-type-line)),
    var(--ds-grid-cell)
  );
  --ds-slider-height: calc(var(--ds-grid-cell) * 6);
}
@supports not (width: round(down, 1px, 1px)) {
  [data-density="compact"] {
    --line: calc(var(--ds-grid-cell) * 5);
  }
}
/* reduced motion: the system's clocks stop (every transition and settle
   on the clock lands at once; what follows the hand follows at once; a
   state changes in one frame), wherever the system is used, not only on
   a page that says so. Every duration token is zeroed here or derives
   from the clock, but the press floor, which is a hold, not a move.
   This sheet comes after the session's edits (lib/edits.js), so an
   edited clock stops too */
@media (prefers-reduced-motion: reduce) {
  :root {
    --ds-motion-clock: 0ms;
    --ds-motion-state: 0ms;
  }
}
/* the page's scheme (round 4, 2026-10-10: "light/dark mode feels weird
   when it applies to only these windows. make sure you apply theme
   across page."): the whole page takes it (the lab's sheet, ds.css, on
   html); every window inherits it; one that names its own (a mockup's
   fixed light or dark) keeps it */
