@layer tokens, base, components, layouts, screens;

@layer tokens {
  @font-face {
    font-family: "Geist";
    src: url("geist.woff2") format("woff2");
    font-weight: 100 900;
    font-display: swap;
  }

  :root {
    /* `light-dark()` takes its value from `color-scheme`: the phone, or Theme in Settings. */
    color-scheme: light dark;
    --bg: light-dark(#f4f3f1, #0f0f0e); /* page */
    --surface: light-dark(#ffffff, #191918); /* rows, header, sheets */
    --surface-2: light-dark(#eeedea, #232321); /* raised: a field, a track or a row set on --surface */
    --text: light-dark(#0a0a0a, #ffffff);
    --muted: light-dark(#5f5f5f, #b4b4b4); /* second text, idle icons */
    --line: light-dark(#e6e5e2, #232321); /* borders and separators */
    --control: light-dark(#d4d3cf, #3d3d3a); /* the border of an input or an outline button */
    /* Colour roles. An app sets --primary and --done to its brand in its own tokens layer. */
    --primary: light-dark(#141414, #ffffff); /* the one main action of a view */
    --on-primary: light-dark(#ffffff, #0a0a0a); /* text on --primary */
    --done: light-dark(#1f7a4d, #5fc491); /* progress and the checked state */
    --on-done: light-dark(#ffffff, #0c1a12); /* text on --done */
    --done-soft: light-dark(#e2f2ea, #1d3327); /* a soft --done ground: swipe action, flash */
    --ink: light-dark(#141414, #ffffff); /* a selected fill: pressed chip or toggle */
    --on-ink: light-dark(#ffffff, #0a0a0a); /* text on --ink */
    --secondary: light-dark(#e5e3de, #2a2a28); /* a tonal fill: the current nav item */
    /* Deprecated aliases, for one release. No shared component reads them. */
    --accent: var(--primary); /* deprecated: use --primary */
    --accent-text: var(--on-primary); /* deprecated: use --on-primary */
    --accent-soft: var(--done-soft); /* deprecated: use --done-soft */
    --danger: light-dark(#b3261e, #f08a9b);
    --warning: light-dark(#9a5b00, #f2b65c);
    --toast-bg: light-dark(var(--surface), var(--surface-2)); /* raised: white in light, a step up from the rows in dark */
    --toast-text: var(--text);
    --knob: #ffffff; /* the knob of a switch, in both themes */
    --scrim: rgb(0 0 0 / 0.45); /* veil behind a sheet */

    --font: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: ui-monospace, "Cascadia Mono", Menlo, Consolas, monospace; /* keys of the walkthrough */
    --text-xs: 0.75rem; /* labels, badges, captions */
    --text-s: 0.8125rem; /* second text: 13px */
    --text-label: 0.875rem; /* group and section heads, chips, segments: 14px */
    --text-m: 0.9375rem; /* body: 15px */
    --text-h: 1.25rem; /* the title of a sheet or of an empty state */
    --text-l: 1.5rem; /* screen title */
    --text-xl: 2rem; /* the + of the add button */
    --weight-normal: 400;
    --weight-medium: 600; /* titles, selected labels */
    --weight-strong: 700;

    --space-half: 2px;
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-7: 32px;

    --radius-s: 4px;
    --radius-control: 6px; /* inputs, chips */
    --radius-m: 8px; /* buttons */
    --radius-l: 8px; /* groups, banner, add button */
    --radius-xl: 14px; /* top of a sheet */
    --radius-pill: 999px;
    --radius-round: 50%;

    --tap: 48px; /* least size of a tap target */
    --tap-l: 56px; /* rows, add button */
    --icon: 24px;
    --icon-s: 20px;
    --arrow: 6px; /* the arrow of a select */
    --nav-height: 64px;
    --dock-height: 56px; /* the bar of the main action above the bottom bar */
    /* The adaptive layout (LAY-1): the rail from 600px, the two panes from 840px, the frame and
       its sidebar from 1024px. --rail-width is the start margin, rail included; --frame-side is the
       end margin of the frame. */
    --rail-width: 80px;
    --frame-side: 0px;
    --frame-max: 1280px;
    --sidebar-width: 232px;
    --side-sheet: 400px; /* a sheet with a head from 840px */
    --content-max: 640px;
    --page-max: var(--content-max); /* the widest content column; a layout sets it on body */
    --gutter: var(--space-4); /* the least side padding of the header and main from 600px */
    --header-height: 0px; /* the height of the header; the app writes it */
    --pane-list: 360px;
    --art-width: 240px; /* the drawings of the walkthrough */
    --logo: 96px; /* the app icon at the top of the walkthrough */

    --shadow-s: 0 1px 3px rgb(0 0 0 / 0.3);
    --shadow-l: 0 8px 24px rgb(0 0 0 / 0.14);

    --motion-fast: 150ms;
    --motion: 250ms;
    --motion-slow: 1.2s;
    --ease: cubic-bezier(0.2, 0.8, 0.2, 1); /* the curve of every move */
  }

  @media (min-width: 840px) {
    :root {
      --gutter: var(--space-6);
    }
  }

  @media (min-width: 1024px) {
    :root {
      --frame-side: max(0px, (100vw - var(--frame-max)) / 2);
      --rail-width: calc(var(--frame-side) + var(--sidebar-width));
    }
  }

  @media (prefers-reduced-motion: reduce) {
    :root {
      --motion-fast: 0ms;
      --motion: 0ms;
      --motion-slow: 0ms;
    }
  }

  /* The compact density: shorter bars and add button. --tap does not change. */
  :root[data-density="compact"] {
    --tap-l: 48px;
    --nav-height: 56px;
    --dock-height: 48px;
  }

  :root[data-theme="light"] {
    color-scheme: light;
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
  }
}

@layer base {
  * {
    box-sizing: border-box;
  }

  html,
  body {
    height: 100%;
  }

  button {
    min-height: var(--tap);
    border: 0;
    border-radius: var(--radius-m);
    background: none;
    cursor: pointer;
  }

  button:disabled {
    opacity: 0.4;
    cursor: default;
  }

  [hidden] {
    display: none !important;
  }

  :focus-visible {
    outline: 2px solid var(--text);
    outline-offset: 2px;
  }

  /* @component main
     @summary The scrolling area of a section, between the header and the bottom bar.
     @state enter-next      The content comes in from the right, after a change of section.
     @state enter-previous  The content comes in from the left.
     @example <p>The main element holds the sections; it has no example of its own.</p> */
  main {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* A horizontal slide changes the section, so the browser must not take it. */
    touch-action: pan-y;
    overflow-x: hidden;
    padding-bottom: calc(var(--tap-l) + var(--space-4) * 2);
  }

  /* The dock stands above the bottom bar below 600px, so the content clears it as well. */
  body:has(.dock:not([hidden])) main {
    padding-bottom: calc(var(--tap-l) + var(--space-4) * 2 + var(--dock-height));
  }

  /* Below 600px the add button of a visible dock replaces the floating one, so the content clears
     the dock only. */
  body:has(.dock:not([hidden]) > .dock-add:not([hidden])) main {
    padding-bottom: calc(var(--dock-height) + var(--space-4));
  }

  /* From 600px (LAY-1) the header and main share one side padding, so the title lines up with the
     edge of the content: a column of at most --page-max, centred, at least --gutter from the rail
     and from the end of the frame. */
  @media (min-width: 600px) {
    main {
      padding-inline: max(var(--gutter), (100% - var(--page-max)) / 2);
    }
  }

  /* No dock from 600px: the floating button is back, and the content clears it. */
  @media (min-width: 600px) {
    body:has(.dock:not([hidden])) main {
      padding-bottom: calc(var(--tap-l) + var(--space-4) * 2);
    }

    body:has(.dock:not([hidden]) > .dock-add:not([hidden])) main {
      padding-bottom: calc(var(--tap-l) + var(--space-4) * 2);
    }
  }

  /* The content of a new section comes in from its side. `main` stays in its place, so its scroll
     and the bottom padding do not move. */
  main.enter-next > *,
  main.enter-previous > * {
    animation: enter var(--motion) var(--ease);
  }

  main.enter-next {
    --enter-from: 48px;
  }

  main.enter-previous {
    --enter-from: -48px;
  }

  @keyframes enter {
    from {
      opacity: 0;
      transform: translateX(var(--enter-from));
    }
  }
}

@layer components {
  /* @component icon
     @summary A line icon of icons.svg. It takes the colour of the text around it.
     @variant small  The size --icon-s, for an icon beside a label.
     @example <svg class="icon" aria-hidden="true"><use href="icons.svg#note"/></svg>
     @example <svg class="icon small" aria-hidden="true"><use href="icons.svg#download"/></svg> */
  .icon {
    flex: none;
    width: var(--icon);
    height: var(--icon);
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  .icon.small {
    width: var(--icon-s);
    height: var(--icon-s);
  }

  /* @component button
     @summary Tap target for one action. Min height --tap. The base look has no fill.
     @variant primary  The one main action of a view or a sheet, in --primary. One for each view.
     @variant tonal    Every other action, in the --secondary fill. The button of a sheet head is tonal.
     @variant outline  An action of a row, or a destructive action when combined with danger. A --control border.
     @variant quiet    Muted text, for an action that steps back, such as Saltar.
     @variant accent   --primary text and no fill, for an action that is not the main one.
     @variant danger   Removes data. Only combined with outline.
     @variant strong   Strong text, for the action of a toast.
     @variant icon     Square, icon or one sign only. Needs an aria-label.
     @variant toggle   An icon button that is on with aria-pressed="true": the icon takes --on-ink over an --ink shape.
     @variant wide     Takes the full width of its box.
     @example <button class="button primary" type="button">Add</button>
     @example <button class="button tonal" type="button">Clear menu</button>
     @example <button class="button outline" type="button">Edit</button>
     @example <button class="button outline danger" type="button">Delete</button>
     @example <button class="button outline quiet" type="button">Skip</button>
     @example <button class="button accent" type="button">Update</button>
     @example <button class="button primary icon" type="button" aria-label="Send"><svg class="icon" aria-hidden="true"><use href="icons.svg#download"/></svg></button>
     @example <button class="button icon toggle" type="button" aria-pressed="true" aria-label="Always"><svg class="icon" aria-hidden="true"><use href="icons.svg#refresh"/></svg></button>
     @example <button class="button icon" type="button" aria-label="More">⋯</button>
     @example <button class="button primary wide" type="button">Save the note</button> */
  .button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--tap);
    padding: 0 var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius-m);
    background: none;
    color: inherit;
    text-align: center;
    text-decoration: none;
  }

  .button.primary {
    background: var(--primary);
    color: var(--on-primary);
    font-weight: var(--weight-medium);
  }

  .button.tonal {
    background: var(--secondary);
    color: var(--text);
    border: 0;
  }

  .button.outline {
    border-color: var(--control);
  }

  .button.quiet {
    color: var(--muted);
  }

  .button.accent {
    color: var(--primary);
    font-weight: var(--weight-medium);
  }

  .button.danger {
    color: var(--danger);
  }

  .button.strong {
    font-weight: var(--weight-medium);
  }

  .button.icon {
    flex: none;
    width: var(--tap);
    height: var(--tap);
    padding: 0;
    font-size: var(--text-l);
  }

  .button.icon:where(:not(.primary, .accent)) {
    color: var(--muted);
  }

  .button.toggle {
    color: var(--muted);
  }

  .button.toggle[aria-pressed="true"] {
    color: var(--on-ink);
    background: var(--ink);
  }

  .button.wide {
    width: 100%;
  }

  /* @component chip
     @summary A choice among several, such as a tag or a filter. Selected with aria-pressed="true": an --ink fill.
     @variant surface  Kept for older markup: the chip takes --surface, as the base does.
     @part count  A number after the label, in muted text.
     @example <button class="chip" type="button">Home</button>
     @example <button class="chip" type="button" aria-pressed="true">Work</button>
     @example <div class="cluster" style="background: var(--bg)"><button class="chip surface" type="button" aria-pressed="true"><span>All</span><span class="count">4</span></button><button class="chip surface" type="button"><span>Home</span><span class="count">1</span></button></div> */
  .chip {
    min-height: var(--tap);
    padding: 0 var(--space-4);
    border: 1px solid var(--control);
    border-radius: var(--radius-control);
    background: var(--surface);
    font-size: var(--text-label);
  }

  .chip.surface {
    flex: none;
    background: var(--surface);
  }

  .chip[aria-pressed="true"] {
    border-color: var(--ink);
    background: var(--ink);
    color: var(--on-ink);
    font-weight: var(--weight-medium);
  }

  .chip .count {
    margin-left: var(--space-2);
    color: var(--muted);
    font-weight: var(--weight-normal);
  }

  /* @component pill
     @summary A small tag beside a name, in --done on --done-soft. It is below the tap size: a button pill is a second way to an action.
     @variant ghost  A dashed outline, for a state that is off.
     @example <span class="pill">Done today</span>
     @example <button class="pill ghost" type="button">+ Later</button> */
  .pill {
    flex: none;
    min-height: 0;
    padding: var(--space-1) var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    background: var(--done-soft);
    color: var(--done);
    font-size: var(--text-xs);
    white-space: nowrap;
  }

  .pill:is(button) {
    min-height: calc(var(--tap) - var(--space-3));
  }

  .pill.ghost {
    border: 1px dashed var(--muted);
    background: none;
    color: var(--muted);
  }

  /* @component segmented
     @summary One choice of two or three, side by side, drawn as a switch: a --surface-2 track, and a --surface knob that slides to the chosen segment. The chosen segment has aria-pressed="true".
     @part segment  One choice.
     @example <div class="segmented"><button class="segment" type="button" aria-pressed="true">For today</button><button class="segment" type="button" aria-pressed="false">For later</button></div> */
  .segmented {
    --segment-count: 2;
    --segment-index: 0;
    position: relative;
    isolation: isolate;
    display: flex;
    margin: var(--space-3) 0 var(--space-1);
    padding: var(--space-1);
    border-radius: var(--radius-pill);
    background: var(--surface-2);
  }

  .segmented:has(> .segment:nth-child(3)) {
    --segment-count: 3;
  }

  .segmented:has(> .segment:nth-child(2)[aria-pressed="true"]) {
    --segment-index: 1;
  }

  .segmented:has(> .segment:nth-child(3)[aria-pressed="true"]) {
    --segment-index: 2;
  }

  /* The knob: one segment wide, moved by whole segments. It sits under the labels. */
  .segmented::before {
    content: '';
    position: absolute;
    top: var(--space-1);
    bottom: var(--space-1);
    left: var(--space-1);
    z-index: -1;
    width: calc((100% - 2 * var(--space-1)) / var(--segment-count));
    border-radius: var(--radius-pill);
    background: var(--surface);
    box-shadow: var(--shadow-s);
    transform: translateX(calc(var(--segment-index) * 100%));
    transition: transform var(--motion);
  }

  .segmented:not(:has(> .segment[aria-pressed="true"]))::before {
    opacity: 0;
  }

  .segmented .segment {
    flex: 1;
    min-height: var(--tap);
    border-radius: var(--radius-pill);
    font-size: var(--text-label);
    transition: color var(--motion);
  }

  .segmented .segment[aria-pressed="true"] {
    color: var(--text);
    font-weight: var(--weight-strong);
  }

  /* @component switch
     @summary A setting that is on or off: its name, then the switch. The track is --done when on.
     @part switch-input  The checkbox with role="switch".
     @example <label class="switch"><span>Later</span><input class="switch-input" type="checkbox" role="switch" checked></label> */
  .switch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: var(--tap-l);
    padding: 0 var(--space-4);
  }

  .switch + .switch {
    border-top: 1px solid var(--line);
  }

  /* The body of a sheet pads its content, so a switch there lines up with the fields above it. */
  .sheet-body > .switch {
    padding-inline: 0;
  }

  .switch-input {
    appearance: none;
    position: relative;
    flex: none;
    width: 44px;
    height: 26px;
    margin: 0;
    border-radius: var(--radius-l);
    background: var(--muted);
    cursor: pointer;
    transition: background var(--motion);
  }

  .switch-input::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: var(--space-1);
    margin: auto 0;
    width: 20px;
    height: 20px;
    border-radius: var(--radius-round);
    background: var(--knob);
    box-shadow: var(--shadow-s);
    transition: transform var(--motion);
  }

  .switch-input:checked {
    background: var(--done);
  }

  .switch-input:checked::after {
    transform: translateX(16px);
  }

  /* @component input
     @summary A one-line text box. Height --tap, on --surface, with a --control border that takes --text on focus.
     @variant raised  Kept for older markup: the box takes --surface, as the base does.
     @variant strong  The main box of a sheet, with a --text border.
     @example <input class="input" type="text" placeholder="New tag">
     @example <input class="input raised" type="search" placeholder="Search">
     @example <input class="input strong" type="text" placeholder="Añadir artículo a la lista">
     @state invalid  aria-invalid="true": the border takes --danger, also on focus.
     @part input-help  A line of help or error below the box.
     @variant danger  On input-help: the error text in --danger.
     @example <div><input class="input" type="text" value="Notes" aria-invalid="true"><p class="input-help danger">Enter a name</p></div> */
  .input {
    width: 100%;
    min-width: 0;
    height: var(--tap);
    padding: 0 var(--space-3);
    border: 1px solid var(--control);
    border-radius: var(--radius-control);
    background: var(--surface);
  }

  .input.raised {
    background: var(--surface);
  }

  .input:focus-visible {
    outline: none;
    border-color: var(--text);
  }

  .input::-webkit-search-cancel-button {
    appearance: none;
  }

  .input[aria-invalid="true"] {
    border-color: var(--danger);
  }

  .input-help {
    margin: var(--space-1) 0 0;
    color: var(--muted);
    font-size: var(--text-s);
  }

  .input-help.danger {
    color: var(--danger);
  }

  .input.strong {
    border: 1px solid var(--text);
  }

  /* The border grows by 1px on each side, so the padding shrinks by 1px and the text stays still. */
  .input.strong:focus-visible {
    border-width: 2px;
    padding-inline: calc(var(--space-3) - 1px);
  }

  /* An invalid box keeps the danger border on focus, strong and in a field. */
  .input[aria-invalid="true"]:focus-visible {
    border-color: var(--danger);
  }

  .input.strong[aria-invalid="true"] {
    border-color: var(--danger);
  }

  .input.strong[aria-invalid="true"]:focus-visible {
    border-color: var(--danger);
  }

  .field-input[aria-invalid="true"]:focus {
    border-color: var(--danger);
  }

  /* @component field
     @summary An input with its name as a label above the box. The label follows the input in the
       markup; the field reverses its column. The placeholder only marks the empty box.
     @part field-input  The input, with the classes input field-input and a placeholder.
     @part field-label  The name, after the input.
     @example <label class="field"><input class="input field-input" type="text" placeholder="Text" value="Call Ana"><span class="field-label">Name</span></label>
     @example <label class="field"><input class="input field-input" type="text" placeholder="Cantidad"><span class="field-label">Amount</span></label> */
  .field {
    display: flex;
    flex-direction: column-reverse;
    margin-top: var(--space-4);
  }

  .field-input {
    border: 2px solid var(--control);
  }

  .field-input:focus {
    border-color: var(--text);
  }

  .field-input::placeholder {
    color: transparent;
  }

  .field-label {
    margin-bottom: var(--space-2);
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
  }

  /* @component select
     @summary A choice from a list, full width, with its own arrow.
     @example <select class="select" aria-label="Language"><option>English</option></select> */
  .select {
    appearance: none;
    display: block;
    width: 100%;
    min-height: var(--tap-l);
    padding: 0 var(--tap) 0 var(--space-4);
    border: 0;
    background:
      linear-gradient(45deg, transparent 50%, var(--muted) 50%) right calc(var(--space-4) + var(--space-half) + var(--arrow)) center / var(--arrow) var(--arrow) no-repeat,
      linear-gradient(135deg, var(--muted) 50%, transparent 50%) right calc(var(--space-4) + var(--space-half)) center / var(--arrow) var(--arrow) no-repeat;
    color: inherit;
    font: inherit;
  }

  /* List */

  /* @component app-header
     @summary The top of each section: the ring, the title and its summary, and up to three tool buttons.
     @part app-header-text      The title and the summary, which take the free width.
     @part app-header-title     The name of the section.
     @part app-header-subtitle  The summary of the section.
     @part header-add  The add of the view, a "button primary" at the end, from 600px; while the header has one, the fab does not show from 600px.
     @example <header class="app-header" style="border: 0"><div class="ring" style="--pct: 33%"><span class="ring-text">2/6</span></div><div class="app-header-text"><h1 class="app-header-title">Today</h1><p class="app-header-subtitle">4 notes left</p></div><button class="button icon" type="button" aria-label="Search"><svg class="icon" aria-hidden="true"><use href="icons.svg#help"/></svg></button></header> */
  .app-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: calc(var(--space-4) + env(safe-area-inset-top)) var(--space-4) var(--space-3);
    position: relative;
    border-bottom: 1px solid var(--line);
    touch-action: pan-y;
  }

  .app-header-text {
    flex: 1;
    min-width: 0;
  }

  @media (min-width: 600px) {
    .app-header {
      padding-inline: max(var(--gutter), (100% - var(--page-max)) / 2);
    }
  }

  .header-add {
    display: none;
  }

  @media (min-width: 600px) {
    .app-header > .header-add:not([hidden]) {
      display: inline-flex;
      align-items: center;
      gap: var(--space-2);
      align-self: center;
    }
  }

  /* The tools stand closer to each other than to the title, so that three leave room for it. */
  .app-header .button.icon:not([hidden]) ~ .button.icon:not([hidden]) {
    margin-left: calc(var(--space-2) * -1);
  }

  .app-header .button.icon[aria-expanded="true"] {
    color: var(--text);
  }

  .app-header-title {
    margin: 0;
    font-size: var(--text-l);
    font-weight: var(--weight-strong);
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .app-header-subtitle {
    margin: var(--space-half) 0 0;
    color: var(--muted);
    font-size: var(--text-s);
  }

  /* @component ring
     @summary The progress of a section, as a ring around a short text. --pct sets the --done arc.
     @part ring-text  The text in the middle.
     @example <div class="ring" style="--pct: 66%"><span class="ring-text">4/6</span></div> */
  .ring {
    --pct: 0%;
    flex: none;
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    border-radius: var(--radius-round);
    background: conic-gradient(var(--done) var(--pct), var(--line) 0);
  }

  .ring-text {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-round);
    background: var(--bg);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-strong);
  }

  /* @component progress
     @summary The progress of a section, as a thin bar the full width of its box. --pct sets the --done part.
     @example <div class="progress" style="--pct: 20%" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div> */
  .progress {
    --pct: 0%;
    width: 100%;
    height: 4px;
    border-radius: var(--radius-pill);
    background: linear-gradient(to right, var(--done) var(--pct), var(--line) 0);
  }

  /* @component nav-bar
     @summary The bottom bar below 600px, a rail at the start side from 600px with either hand, and from 1024px a labelled sidebar at the start of the frame (the app pads its body on the rail side): one item for each section that is on.
     @part nav-item  A section: its icon and its name. The current one has aria-current="page".
     @part nav-brand  The mark (an img of 28px) and the name of the app at the top of the sidebar, from 1024px only.
     @part nav-count  A count at the end of an item, from 1024px only. It is not part of the name for a screen reader.
     @part nav-foot   A line at the foot of the rail: its icon from 600px, its icon and its text from 1024px.
     @part nav-foot-text  The text of nav-foot, shown from 1024px.
     @example <nav class="nav-bar" style="position: static; width: 232px; min-height: 280px"><div class="nav-brand"><span>App</span></div><button class="nav-item" type="button" aria-current="page"><svg class="icon" aria-hidden="true"><use href="icons.svg#note"/></svg><span>Notes</span><span class="nav-count">4</span></button><button class="nav-item" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#settings"/></svg><span>Settings</span></button><p class="nav-foot"><svg class="icon small" aria-hidden="true"><use href="icons.svg#settings"/></svg><span class="nav-foot-text">On this device</span></p></nav>
     @example <nav class="nav-bar" style="width: 100%"><button class="nav-item" type="button" aria-current="page"><svg class="icon" aria-hidden="true"><use href="icons.svg#note"/></svg><span>Notes</span></button><button class="nav-item" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#settings"/></svg><span>Later</span><em class="badge">2</em></button></nav> */
  .nav-bar {
    display: flex;
    flex: none;
    padding-bottom: env(safe-area-inset-bottom);
    border-top: 1px solid var(--line);
    background: var(--bg);
  }

  .nav-item {
    position: relative;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-half);
    height: var(--nav-height);
    border-radius: 0;
    color: var(--muted);
    font-size: var(--text-xs);
  }

  /* The current section is a --secondary pill behind its icon; its icon and label take --text. */
  .nav-item[aria-current="page"] {
    color: var(--text);
    font-weight: var(--weight-medium);
  }

  .nav-item[aria-current="page"] .icon {
    box-sizing: content-box;
    padding: var(--space-1) var(--space-5);
    border-radius: var(--radius-pill);
    background: var(--secondary);
    color: var(--text);
  }

  /* From 600px the bar is a rail at the start side, so a tablet or a phone on its side keeps its
     height for the content (LAY-2). */
  @media (min-width: 600px) {
    .nav-bar {
      position: fixed;
      inset-block: 0;
      inset-inline-start: 0;
      z-index: 10;
      flex-direction: column;
      justify-content: center;
      width: calc(var(--rail-width) + env(safe-area-inset-left));
      padding-bottom: 0;
      padding-inline-start: env(safe-area-inset-left);
      border-top: 0;
      border-inline-end: 1px solid var(--line);
    }

    .nav-item {
      flex: none;
    }
  }

  .nav-brand,
  .nav-count,
  .nav-foot {
    display: none;
  }

  @media (min-width: 600px) {
    .nav-bar > .nav-foot:not([hidden]) {
      position: absolute;
      bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
      inset-inline: 0;
      display: flex;
      justify-content: center;
      margin: 0;
      color: var(--muted);
    }

    .nav-foot-text {
      display: none;
    }
  }

  /* From 1024px (LAY-1) the rail is a sidebar of --sidebar-width at the start of the frame: the brand
     on top, then one row for each section, the current one on a --secondary pill. */
  @media (min-width: 1024px) {
    .nav-bar {
      inset-inline-start: var(--frame-side);
      width: var(--sidebar-width);
      justify-content: flex-start;
      gap: var(--space-half);
      padding: var(--space-6) var(--space-3);
      padding-inline-start: calc(var(--space-3) + env(safe-area-inset-left));
      border: 0;
    }

    .nav-brand {
      display: flex;
      align-items: center;
      gap: var(--space-3);
      padding: 0 var(--space-2) var(--space-5);
      font-size: var(--text-h);
      font-weight: var(--weight-strong);
    }

    .nav-brand > img {
      border-radius: var(--radius-m);
    }

    .nav-item {
      flex-direction: row;
      justify-content: flex-start;
      gap: var(--space-3);
      height: var(--tap);
      padding-inline: var(--space-3);
      border-radius: var(--radius-m);
      font-size: var(--text-m);
    }

    .nav-item:hover {
      background: var(--surface-2);
      color: var(--text);
    }

    .nav-item[aria-current="page"] {
      background: var(--secondary);
    }

    .nav-item[aria-current="page"] .icon {
      padding: 0;
      background: none;
    }

    .nav-bar > .nav-foot:not([hidden]) {
      justify-content: flex-start;
      gap: var(--space-2);
      padding-inline: var(--space-3);
      font-size: var(--text-s);
    }

    .nav-foot-text {
      display: inline;
    }

    .nav-count {
      display: block;
      margin-inline-start: auto;
      color: var(--muted);
      font-size: var(--text-s);
      font-weight: var(--weight-normal);
      font-variant-numeric: tabular-nums;
    }
  }

  /* @component badge
     @summary A count on a nav item, in --primary. It is hidden while it is empty.
     @example <span style="position: relative; display: inline-block; width: 48px; height: 32px"><em class="badge">2</em></span> */
  .badge {
    position: absolute;
    top: var(--space-2);
    left: calc(50% + var(--space-2));
    padding: 0 var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--primary);
    color: var(--on-primary);
    font-size: var(--text-xs);
    font-style: normal;
    font-weight: var(--weight-strong);
  }

  .badge:empty {
    display: none;
  }

  /* @component sheet
     @summary A panel that comes up from the bottom over a veil; from 840px a sheet with a head is a side sheet. The layer is hidden to close it.
     @part sheet-layer  The fixed layer that holds the veil and the sheet.
     @part sheet-veil   The dark veil; a tap on it closes the sheet.
     @part sheet-grab   The handle at the top. A drag on it closes the sheet.
     @part sheet-head   The bar under the handle: a title and a Done or close button.
     @part sheet-body   The part that scrolls, between the head and the foot.
     @part sheet-foot   The bar that stays at the bottom: the actions of the sheet.
     @part sheet-saved  A short note in the foot, such as Saved.
     @state on          On sheet-saved: the note shows.
     @part sheet-title  A title of the sheet or of one of its parts.
     @variant main      On sheet-title: the title of the head, one line.
     @variant part      On sheet-title: a title below a line that separates it from the part above.
     @part entry        A choice of the sheet: a name, with its effect below it.
     @part entry-note   The effect of an entry.
     @example <div class="sheet" style="border-radius: var(--radius-xl)"><div class="sheet-grab"></div><h2 class="sheet-title">Group by</h2><button class="entry" type="button"><span>Finish</span><small class="entry-note">Done notes leave the list</small></button><h2 class="sheet-title part">Tags</h2></div>
     @example <div class="sheet" style="border-radius: var(--radius-xl); position: static"><div class="sheet-grab"></div><header class="sheet-head"><h2 class="sheet-title main">Note</h2><button class="button" type="button">Done</button></header><div class="sheet-body">Text of the note.</div><footer class="sheet-foot"><button class="button outline danger" type="button">Delete</button><span class="sheet-saved on">Saved</span></footer></div> */
  .sheet-layer {
    position: fixed;
    inset: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    transition: display var(--motion) allow-discrete;
  }

  /* A sheet that leaves stays on the screen for the transition, and takes no tap. */
  .sheet-layer[hidden] {
    pointer-events: none;
  }

  .sheet-veil {
    position: absolute;
    inset: 0;
    background: var(--scrim);
    transition: opacity var(--motion) var(--ease);
  }

  .sheet-layer[hidden] .sheet-veil {
    opacity: 0;
  }

  .sheet-layer[hidden] .sheet {
    transform: translateY(100%);
  }

  @starting-style {
    .sheet-veil {
      opacity: 0;
    }

    .sheet {
      transform: translateY(100%);
    }
  }

  .sheet {
    position: relative;
    max-height: 85%;
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    background: var(--surface);
    transition: transform var(--motion) var(--ease);
  }

  /* A sheet without a head scrolls as a whole, as before. */
  .sheet:not(:has(.sheet-head)) {
    overflow-y: auto;
    padding: var(--space-3) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom));
  }

  /* A sheet with a head keeps its padding in the parts, and only the body scrolls. */
  .sheet:has(.sheet-head) {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
  }

  /* The handle. A sheet without a head keeps the small bar of 0.2.1. */
  .sheet-grab {
    width: 40px;
    height: 4px;
    margin: 0 auto var(--space-3);
    border-radius: var(--radius-s);
    background: var(--line);
    touch-action: none;
  }

  /* With a head, the handle is a tap tall and draws its bar inside. */
  .sheet:has(.sheet-head) .sheet-grab {
    position: relative;
    flex: none;
    width: auto;
    height: var(--tap);
    margin: 0;
    background: none;
  }

  .sheet:has(.sheet-head) .sheet-grab::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 40px;
    height: 4px;
    border-radius: var(--radius-s);
    background: var(--muted);
    transform: translate(-50%, -50%);
  }

  .sheet-head {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--space-3);
    padding: 0 var(--space-4) var(--space-2);
    touch-action: none;
  }

  .sheet-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--space-4);
  }

  /* A headed sheet without a foot ends at the screen edge, so its body clears the bottom inset. */
  .sheet:has(.sheet-head):not(:has(.sheet-foot)) .sheet-body {
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom));
  }

  .sheet-foot {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
  }

  .sheet-saved {
    color: var(--muted);
    font-size: var(--text-s);
    opacity: 0;
    transition: opacity var(--motion);
  }

  .sheet-saved.on {
    opacity: 1;
  }

  [data-hand="left"] .sheet-head,
  [data-hand="left"] .sheet-foot {
    flex-direction: row-reverse;
  }

  /* The title of a part of a sheet is a head in --text, as the label of a field. */
  .sheet-title {
    margin: var(--space-2) 0 0;
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
  }

  /* The title of the head: it takes the free width and keeps to one line. */
  .sheet-title.main {
    flex: 1;
    min-width: 0;
    margin: 0;
    overflow: hidden;
    color: var(--text);
    font-size: var(--text-h);
    font-weight: var(--weight-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* A title below a line that separates its part from the part above. */
  .sheet-title.part {
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--line);
  }

  /* From 600px a sheet keeps the width of the column and stands beside the rail, so a tablet does
     not show it as wide as the screen; its veil still covers the rail. From 840px a sheet with a head
     is a card at the end of the frame with either hand: --side-sheet wide, from below the header, as
     high as its content, --gutter from the end, over the veil. A sheet without a head keeps the
     bottom sheet. */
  @media (min-width: 600px) {
    .sheet-layer {
      padding-left: calc(var(--rail-width) + env(safe-area-inset-left));
      padding-right: calc(var(--frame-side) + env(safe-area-inset-right));
    }

    .sheet {
      width: 100%;
      max-width: var(--content-max);
      margin-inline: auto;
    }
  }

  @media (min-width: 840px) {
    .sheet-layer:has(.sheet-head) {
      justify-content: flex-start;
      align-items: flex-end;
      padding: calc(var(--header-height) + var(--space-4)) calc(var(--frame-side) + var(--gutter) + env(safe-area-inset-right)) calc(var(--space-4) + env(safe-area-inset-bottom)) 0;
    }

    .sheet-layer:has(.sheet-head) .sheet {
      width: var(--side-sheet);
      height: auto;
      max-height: 100%;
      margin: 0;
      border-radius: var(--radius-l);
      transition: transform var(--motion) var(--ease), opacity var(--motion) var(--ease);
    }

    .sheet-layer:has(.sheet-head)[hidden] .sheet {
      opacity: 0;
      transform: translateX(var(--space-7));
    }

    @starting-style {
      .sheet-layer:has(.sheet-head) .sheet {
        opacity: 0;
        transform: translateX(var(--space-7));
      }
    }

    .sheet:has(.sheet-head) .sheet-grab {
      display: none;
    }

    /* With no grab and a gap above it, the head needs no room for the top inset. */
    .sheet-layer:has(.sheet-head) .sheet-head {
      padding-top: var(--space-3);
    }
  }

  .entry {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--space-half);
    width: 100%;
    min-height: 56px;
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-3);
    background: var(--bg);
    text-align: left;
  }

  .entry-note {
    color: var(--muted);
    font-size: var(--text-xs);
  }

  /* @component toast
     @summary A short message at the bottom, with an optional action. With a sheet open it goes to the top.
     @part toast-text  The message.
     @example <div class="toast" role="status" style="position: static"><span class="toast-text">Note deleted</span><button class="button strong" type="button">Undo</button></div> */
  .toast {
    position: fixed;
    /* Above the add button, in reach of the thumb. */
    bottom: calc(var(--nav-height) + env(safe-area-inset-bottom) + var(--tap-l) + var(--space-6));
    left: calc(var(--space-3) + env(safe-area-inset-left));
    right: calc(var(--space-3) + env(safe-area-inset-right));
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 48px;
    padding: var(--space-1) var(--space-1) var(--space-1) var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-m);
    background: var(--toast-bg);
    color: var(--toast-text);
    box-shadow: var(--shadow-l);
    transition: opacity var(--motion) var(--ease), transform var(--motion) var(--ease), display var(--motion) allow-discrete;
  }

  /* The toast leaves to this state, and enters from the same one. */
  .toast[hidden] {
    opacity: 0;
    transform: translateY(12px);
  }

  @starting-style {
    .toast {
      opacity: 0;
      transform: translateY(12px);
    }
  }

  /* Below 600px a visible dock stands under the toast, so the toast clears it. */
  body:has(.dock:not([hidden])) .toast {
    bottom: calc(var(--nav-height) + env(safe-area-inset-bottom) + var(--dock-height) + var(--tap-l) + var(--space-6));
  }

  .toast-text {
    flex: 1;
  }

  /* No bar at the bottom from 600px: the toast goes down, clears the rail at the start and the
     frame's end margin at the end. */
  @media (min-width: 600px) {
    .toast {
      bottom: calc(env(safe-area-inset-bottom) + var(--tap-l) + var(--space-6));
      left: calc(var(--rail-width) + env(safe-area-inset-left) + var(--space-3));
      right: calc(var(--frame-side) + env(safe-area-inset-right) + var(--space-3));
    }

    body:has(.dock:not([hidden])) .toast {
      bottom: calc(env(safe-area-inset-bottom) + var(--tap-l) + var(--space-6));
    }
  }

  /* Last, so it wins over the dock lift above. An open sheet has the bottom of the screen, so the toast goes to the top. */
  body:has(.sheet-layer:not([hidden])) .toast {
    top: calc(var(--space-2) + env(safe-area-inset-top));
    bottom: auto;
  }

  /* @component banner
     @summary A message in the flow of a list, on --surface-2 with the --text colour, with its action below it. A button tonal in it stands on --surface.
     @variant done  A message about work that is done, on --done-soft.
     @part banner-text  The text below the title.
     @example <section class="banner"><strong>Version 2 is ready</strong><p class="banner-text">Open it again to use it.</p><button class="button tonal wide" type="button">Update</button></section>
     @example <section class="banner done"><strong>Done?</strong><p class="banner-text">All done.</p><button class="button tonal wide" type="button">Finish</button></section> */
  .banner {
    margin: var(--space-4) var(--space-3);
    padding: var(--space-4) var(--space-4);
    border-radius: var(--radius-l);
    background: var(--surface-2);
    color: var(--text);
  }

  .banner.done {
    background: var(--done-soft);
  }

  /* The --secondary fill is close to the ground of a banner, so its tonal button stands on --surface. */
  .banner .button.tonal {
    background: var(--surface);
  }

  .banner-text {
    margin: var(--space-1) 0 var(--space-3);
    font-size: var(--text-s);
  }

  /* @component empty-state
     @summary The text of a list or a part with nothing in it. Holds a button, except inline (STATE-1). A second action is a button tonal below the primary.
     @variant inline  In a sheet: aligned with the content, with less space.
     @variant low     At the bottom of its area, in reach of the thumb. The app gives the list area a height, as the percent needs one.
     @part empty-state-title  The short title above the text.
     @example <div class="empty-state"><p>The list is empty.</p><button class="button primary" type="button">Bring from Later</button></div>
     @example <div class="empty-state low" style="height: 240px"><h2 class="empty-state-title">Nothing here</h2><p>Add the first note.</p><button class="button primary" type="button">Add a note</button><button class="button tonal" type="button">Import notes</button></div>
     @example <p class="empty-state inline">No note matches.</p> */
  .empty-state {
    margin: calc(var(--space-6) * 2) var(--space-6);
    color: var(--muted);
    text-align: center;
  }

  .empty-state .button {
    margin-top: var(--space-2);
  }

  /* Kept for the markup of 0.6.0, where the second action was an outline button: on --bg, with art
     behind it, it is a --surface box with the text colour. A new second action is tonal (STATE-1). */
  .empty-state .button.outline {
    background: var(--surface);
    color: var(--text);
  }

  .empty-state.inline {
    margin: var(--space-3) 0;
    text-align: left;
  }

  .empty-state.low {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
    min-height: 50%;
    margin: 0 var(--space-5) var(--space-6);
  }

  .empty-state.low .button {
    width: 100%;
    min-height: var(--tap-l);
    margin-top: 0;
  }

  .empty-state-title {
    margin: 0;
    color: var(--text);
    font-size: var(--text-h);
    font-weight: var(--weight-medium);
  }

  /* @component item-list
     @summary The list of a section: item rows, section heads and group heads.
     @example <ul class="item-list"><li class="item-row"><div class="item-row-body"><button class="item-row-name" type="button"><span>Call Ana</span></button></div></li></ul> */
  .item-list {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* A row closes before it leaves the list, and a group head before the list becomes flat. `collapse` in motion.js sets the height. */
  .item-list > .leaving {
    overflow: hidden;
    min-height: 0;
    opacity: 0;
    transition: height var(--motion) var(--ease), opacity var(--motion) var(--ease);
  }

  /* @component item-row
     @summary One entry of a list, as a row. Its body slides over its actions. With a check, its divider starts at the text.
     @part item-row-swipe         The box that clips the sliding body.
     @part item-row-body          The row itself: check, name, then buttons.
     @part item-row-check         The square checkbox of an entry that can be done. --done when checked.
     @part item-row-name          The name button, with its quantity and its meta line.
     @part item-row-qty           The quantity after the name.
     @part item-row-label         The text of the name. It keeps two lines, then ends with an ellipsis.
     @part item-row-meta          A muted line below the name, such as its tags.
     @part item-row-end           The fixed slot at the end, two buttons wide. It holds up to two button icon and
                                  mirrors as a whole for the left hand.
     @part item-row-actions       The swipe actions behind the right end. Hidden while the row is closed, so a
                                  fractional pixel ratio shows no edge of them. The drag code adds open on settle.
     @part item-row-backdrop      The action under a slide to the right: --done-soft, then --done once armed.
     @state done      A ticked entry: its name is struck out.
     @state flash     A row that has just come back: its body flashes in --done-soft.
     @state gone      A row that the motion code hides at once.
     @state lifted    A row that the drag code lifts.
     @state leaving   A row that closes before it leaves.
     @state open      On item-row-swipe: the actions show.
     @state armed     On item-row-swipe: a slide far enough to run its action.
     @state dragging  On item-row-body: the body follows the finger, with no transition.
     @state tick      On item-row-body: the whole body is the tick target. The check is a mark only.
     @example <ul class="item-list" style="width: 100%"><li class="item-row"><div class="item-row-swipe"><div class="item-row-body"><input class="item-row-check" type="checkbox" aria-label="Call Ana"><button class="item-row-name" type="button"><span class="item-row-label">Ideas <span class="item-row-qty">1 h</span></span><small class="item-row-meta">Work</small></button><button class="button icon toggle" type="button" aria-pressed="false" aria-label="Repeat"><svg class="icon" aria-hidden="true"><use href="icons.svg#refresh"/></svg></button></div></div></li><li class="item-row done"><div class="item-row-body"><input class="item-row-check" type="checkbox" checked aria-label="Read book"><button class="item-row-name" type="button"><span>Read book</span></button><button class="pill ghost" type="button">+ Later</button></div></li></ul>
     @example <ul class="item-list" style="width: 100%"><li class="item-row"><div class="item-row-body tick"><input class="item-row-check" type="checkbox" aria-label="Call Ana"><button class="item-row-name" type="button"><span class="item-row-label">Call Ana about the long list of notes for the next meeting at the office</span></button><div class="item-row-end"><button class="button icon" type="button" aria-label="Edit"><svg class="icon" aria-hidden="true"><use href="icons.svg#settings"/></svg></button><button class="button icon" type="button" aria-label="Delete"><svg class="icon" aria-hidden="true"><use href="icons.svg#trash"/></svg></button></div></div></li></ul>
     @example <ul class="item-list" style="width: 100%"><li class="item-row"><div class="item-row-swipe open"><div class="item-row-actions"><button class="swipe-action" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#settings"/></svg><span class="swipe-action-label">Move</span></button><button class="swipe-action danger" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#trash"/></svg><span class="swipe-action-label">Delete</span></button></div><div class="item-row-body" style="transform: translateX(-144px)"><button class="item-row-name" type="button"><span>Call Ana</span></button></div></div></li></ul> */
  .item-row {
    border-bottom: 1px solid var(--line);
    background: var(--surface);
    /* The rows of a scope open a gap for a lifted row. `placeLifted` in drag.js sets the transform. */
    transition: transform var(--motion-fast) var(--ease);
  }

  /* With a check, the divider starts where the text starts: after the body padding, the check and
     its margins, the gap and the name padding. It runs to the end of the row. */
  .item-row:has(.item-row-check) {
    border-image: linear-gradient(to right, transparent calc(var(--space-2) * 3 + var(--space-1) * 2 + var(--icon)), var(--line) 0) 0 0 1 0 / 0 0 1px 0;
  }

  .item-row.lifted {
    position: relative;
    z-index: 2;
    box-shadow: var(--shadow-l);
    transition: none;
  }

  .item-row.flash .item-row-body {
    animation: flash var(--motion-slow) var(--ease);
  }

  .item-row.gone {
    display: none;
  }

  @keyframes flash {
    from {
      background: var(--done-soft);
    }
  }

  .item-row-swipe {
    position: relative;
    overflow: hidden;
  }

  .item-row-actions {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    display: flex;
  }

  .item-row-swipe:not(.open) .item-row-actions {
    visibility: hidden;
  }

  /* The slide to the right shows its action below the row. The actions cover it. */
  .item-row-backdrop {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    padding-left: var(--space-5);
    background: var(--done-soft);
    color: var(--done);
    transition: background var(--motion-fast), color var(--motion-fast);
  }

  .item-row-swipe.armed .item-row-backdrop {
    background: var(--done);
    color: var(--on-done);
  }

  .item-row-body {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--tap-l);
    padding: 0 var(--space-1) 0 var(--space-2);
    background: var(--surface);
  }

  .item-row-swipe .item-row-body {
    padding-right: var(--space-3);
    touch-action: pan-y;
    user-select: none;
    transition: transform var(--motion-fast) var(--ease);
  }

  .item-row-swipe .item-row-body.dragging {
    transition: none;
  }

  .item-row-check {
    flex: none;
    display: grid;
    place-items: center;
    width: var(--icon);
    height: var(--icon);
    margin: var(--space-3) var(--space-1) var(--space-3) var(--space-2);
    border: 2px solid var(--muted);
    border-radius: var(--radius-s);
    background: none;
    appearance: none;
    cursor: pointer;
  }

  .item-row-check:checked {
    border-color: var(--done);
    background: var(--done);
  }

  /* The tick: two sides of a small box, turned. */
  .item-row-check:checked::after {
    content: "";
    width: 10px;
    height: 5px;
    margin-top: calc(var(--space-half) * -1);
    border: solid var(--on-done);
    border-width: 0 0 2px 2px;
    transform: rotate(-45deg);
  }

  /* The slot at the end: as wide as two buttons, so the text ends at the same place on every row. */
  .item-row-end {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    width: calc(var(--tap) * 2);
  }

  /* With tick, the body takes the tap and the check only shows the state. */
  .item-row-body.tick {
    cursor: pointer;
  }

  .item-row-body.tick .item-row-check {
    pointer-events: none;
  }

  .item-row-name {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    min-width: 0;
    min-height: var(--tap-l);
    padding: 0 var(--space-1);
    border-radius: 0;
    overflow-wrap: anywhere;
    text-align: left;
  }

  /* A row with no checkbox: its name keeps the place of one. */
  .item-row-body > .item-row-name:first-child {
    padding-left: var(--space-3);
  }

  .item-row.done .item-row-name {
    color: var(--muted);
    text-decoration: line-through;
  }

  /* The text of a name keeps two lines. The meta line below it is not clamped. */
  .item-row-label {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
  }

  .item-row-meta {
    color: var(--muted);
    font-size: var(--text-xs);
  }

  .item-row-qty {
    color: var(--muted);
    font-size: var(--text-s);
  }

  /* @component swipe-action
     @summary An action behind the right end of a row, shown by a slide to the left. In --done on --done-soft by default.
     @variant neutral  A plain action, such as edit, in the surface colour.
     @variant danger   Removes the item.
     @part swipe-action-label  The word below the icon.
     @example <div style="display: flex; height: 64px"><button class="swipe-action neutral" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#settings"/></svg><span class="swipe-action-label">Edit</span></button><button class="swipe-action" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#refresh"/></svg><span class="swipe-action-label">Repeat</span></button><button class="swipe-action danger" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#trash"/></svg><span class="swipe-action-label">Delete</span></button></div> */
  .swipe-action {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-half);
    width: calc(var(--tap) + var(--space-6));
    height: 100%;
    border-radius: 0;
    font-size: var(--text-xs);
    background: var(--done-soft);
    color: var(--done);
  }

  .swipe-action + .swipe-action {
    border-left: 1px solid var(--surface);
  }

  .swipe-action.neutral {
    background: var(--surface-2);
    color: var(--text);
  }

  .swipe-action.danger {
    background: var(--danger);
    color: var(--surface);
  }

  /* @component section-head
     @summary The row that opens and closes a part of a list, such as the done entries.
     @part section-head-button  The full-width button, with aria-expanded.
     @part chevron              The open or closed sign at its end, in --muted: a glyph or an icon.
     @example <ul class="item-list" style="width: 100%"><li class="section-head"><button class="section-head-button" type="button" aria-expanded="false"><span>2 done</span><span class="chevron">▸</span></button></li></ul> */
  .section-head-button {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    min-height: var(--tap);
    padding: 0 var(--space-4);
    border-radius: 0;
    border-bottom: 1px solid var(--line);
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
    letter-spacing: normal;
    text-transform: none;
  }

  .chevron {
    font-size: var(--text-m);
  }

  .section-head-button .chevron {
    color: var(--muted);
  }

  /* @component group-head
     @summary The title of a group of a list, such as a tag, with its count: a sentence-case band that sticks to the top of the scroll area on a --bg ground.
     @example <ul class="item-list" style="width: 100%"><li class="group-head"><span>Work</span><span>2</span></li></ul> */
  .group-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    min-height: var(--tap);
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 0 var(--space-4);
    border-bottom: 1px solid var(--line);
    background: var(--bg);
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
    letter-spacing: normal;
    text-transform: none;
  }

  /* @component list-group
     @summary A full-width block of rows on a --bg page, as in Settings.
     @variant pair   Two rows side by side.
     @variant plain  No box: rows split by lines, as the suggestions of a search.
     @part list-row       A row: an icon and a text, or a control.
     @part list-row-text  The main text of a row that has two.
     @part list-row-meta  A muted text at the end of a row.
     @variant accent      On list-row: the row that makes a new thing, in --primary text.
     @variant card        An inset box with a border and rounded corners, without a shadow.
     @part list-group-head      The title above a group, in --text.
     @part settings-row         A tall row of a group: a label, a value and a chevron.
     @part settings-row-label   The text of a settings row.
     @part settings-row-value   The muted value at the end of a settings row.
     @part chevron              The muted end mark of a settings row (see section-head).
     @variant danger      On settings-row: the row of a risky action, in the danger colour.
     @example <div class="list-group" style="width: 100%"><button class="list-row" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#help"/></svg><span>Show the introduction</span></button><select class="select" aria-label="Language"><option>English</option></select></div>
     @example <div class="list-group pair" style="width: 100%"><button class="list-row" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#export"/></svg><span>Export</span></button><button class="list-row" type="button"><svg class="icon" aria-hidden="true"><use href="icons.svg#import"/></svg><span>Import</span></button></div>
     @example <ul class="list-group plain" style="width: 100%"><li><button class="list-row" type="button"><span class="list-row-text">Buy milk</span><span class="list-row-meta">In Notes</span></button></li><li><button class="list-row accent" type="button">Create «Buy»</button></li></ul>
     @example <h3 class="list-group-head">Data</h3><div class="list-group card" style="width: 100%"><button class="settings-row" type="button"><span class="settings-row-label">Language</span><span class="settings-row-value">English</span><span class="chevron">›</span></button><button class="settings-row danger" type="button"><span class="settings-row-label">Delete all</span></button></div> */
  .list-group {
    margin: 0;
    border: 1px solid var(--line);
    border-inline: 0;
    border-radius: 0;
    background: var(--surface);
    overflow: hidden;
  }

  .list-group.pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  /* The group clips its rows, so a row draws its focus ring inside itself. In a card, an end row takes
     the corners of the group (the radius less the 1px border), so any ring of the row bends with them,
     also one an app draws on the row for a control inside it. */
  .list-group > :focus-visible {
    outline-offset: -2px;
  }

  .list-group.card:not(.plain, .pair) > :is(.list-row, .settings-row):first-child {
    border-start-start-radius: calc(var(--radius-l) - 1px);
    border-start-end-radius: calc(var(--radius-l) - 1px);
  }

  .list-group.card:not(.plain, .pair) > :is(.list-row, .settings-row):last-child {
    border-end-start-radius: calc(var(--radius-l) - 1px);
    border-end-end-radius: calc(var(--radius-l) - 1px);
  }

  .list-group.card.pair > :is(.list-row, .settings-row):first-child {
    border-start-start-radius: calc(var(--radius-l) - 1px);
    border-end-start-radius: calc(var(--radius-l) - 1px);
  }

  .list-group.card.pair > :is(.list-row, .settings-row):last-child {
    border-start-end-radius: calc(var(--radius-l) - 1px);
    border-end-end-radius: calc(var(--radius-l) - 1px);
  }

  .list-group.plain {
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    list-style: none;
  }

  .list-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-height: var(--tap-l);
    padding: 0 var(--space-4);
    border-radius: 0;
    text-align: left;
  }

  .list-row + .list-row {
    border-top: 1px solid var(--line);
  }

  .list-group.pair > .list-row + .list-row {
    border-top: 0;
    border-left: 1px solid var(--line);
  }

  .list-group.plain .list-row {
    min-height: var(--tap);
    padding: 0 var(--space-half);
    border-top: 1px solid var(--line);
  }

  .list-row .icon {
    color: var(--muted);
  }

  .list-row.accent {
    color: var(--primary);
    font-weight: var(--weight-strong);
  }

  .list-row-text {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  .list-row-meta {
    flex: none;
    color: var(--muted);
    font-size: var(--text-xs);
  }

  .list-group.card {
    margin: 0 var(--space-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    box-shadow: none;
  }

  .list-group-head {
    margin: var(--space-5) var(--space-4) var(--space-2);
    color: var(--text);
    font-size: var(--text-label);
    font-weight: var(--weight-medium);
  }

  .settings-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-height: var(--tap-l);
    padding: 0 var(--space-3) 0 var(--space-4);
    border-radius: 0;
    color: var(--text);
    font-size: var(--text-m);
    text-align: start;
  }

  .settings-row + .settings-row {
    border-top: 1px solid var(--line);
  }

  .settings-row-label {
    flex: 1;
    min-width: 0;
  }

  .settings-row-value {
    color: var(--muted);
  }

  .settings-row .chevron {
    flex: none;
    color: var(--muted);
  }

  .settings-row.danger {
    color: var(--danger);
    font-weight: var(--weight-medium);
  }

  /* @component fab
     @summary The add button, in --primary, fixed above the bottom bar at the side of the hand.
     @state collapsed  A square of the tap size with the icon only, for example while a list scrolls.
     @example <button class="fab" type="button" style="position: static">+</button>
     @example <button class="fab collapsed" type="button" style="position: static">+</button> */
  .fab {
    position: fixed;
    right: calc(var(--space-4) + env(safe-area-inset-right));
    bottom: calc(var(--nav-height) + env(safe-area-inset-bottom) + var(--space-4));
    z-index: 5;
    width: 56px;
    height: 56px;
    border-radius: var(--radius-l);
    background: var(--primary);
    color: var(--on-primary);
    font-size: var(--text-xl);
    line-height: 1;
    box-shadow: var(--shadow-l);
    transition: width var(--motion-fast) var(--ease), height var(--motion-fast) var(--ease);
  }

  .fab.collapsed {
    width: var(--tap);
    height: var(--tap);
  }

  /* Below 600px a dock stands between the button and the bottom bar. */
  body:has(.dock:not([hidden])) .fab {
    bottom: calc(var(--nav-height) + env(safe-area-inset-bottom) + var(--dock-height) + var(--space-4));
  }

  /* Below 600px the add button of a visible dock replaces the floating one. */
  body:has(.dock:not([hidden]) > .dock-add:not([hidden])) .fab {
    display: none;
  }

  /* @component action-bar
     @summary A row of buttons stuck to the bottom of a scroll area, over a fade.
     @example <div class="action-bar" style="position: static"><button class="button" type="button">Cancel</button><button class="button primary" type="button">Save</button></div> */
  .action-bar {
    position: sticky;
    bottom: 0;
    display: flex;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    background: linear-gradient(transparent, var(--bg) var(--space-4));
  }

  .action-bar .button {
    flex: 1;
    min-height: var(--tap-l);
    white-space: nowrap;
  }

  /* Left hand: the controls change side. Text stays aligned to the left, and a row slides as before. */

  [data-hand="left"] .app-header {
    flex-direction: row-reverse;
  }

  [data-hand="left"] .app-header .button.icon:not([hidden]) ~ .button.icon:not([hidden]) {
    margin-right: calc(var(--space-2) * -1);
    margin-left: 0;
  }

  /* The text of a row starts at --space-4 with either hand. Only the check and the trailing slot change side. */
  [data-hand="left"] .item-row-body {
    padding-left: calc(var(--space-4) - var(--space-1));
  }

  [data-hand="left"] .item-row-body > .item-row-name:first-child {
    padding-left: var(--space-1);
  }

  [data-hand="left"] .item-row-check {
    order: 1;
    margin: var(--space-3) var(--space-2) var(--space-3) var(--space-1);
  }

  /* The check is at the end, so the divider starts at the text, at --space-4. */
  [data-hand="left"] .item-row:has(.item-row-check) {
    border-image: linear-gradient(to right, transparent var(--space-4), var(--line) 0) 0 0 1 0 / 0 0 1px 0;
  }

  /* A trailing slot comes first: the text starts after the body padding, the slot, the gap and the
     name padding. */
  [data-hand="left"] .item-row:has(.item-row-check):has(.item-row-end) {
    border-image: linear-gradient(to right, transparent calc(var(--space-4) + var(--space-2) + var(--tap) * 2), var(--line) 0) 0 0 1 0 / 0 0 1px 0;
  }

  [data-hand="left"] .item-row-end {
    order: -1;
    flex-direction: row-reverse;
  }

  [data-hand="left"] .fab {
    right: auto;
    left: calc(var(--space-4) + env(safe-area-inset-left));
  }

  /* No bar at the bottom from 600px; on the left side the button clears the rail, on the right side
     the frame's end margin. */
  @media (min-width: 600px) {
    .fab {
      right: calc(var(--frame-side) + env(safe-area-inset-right) + var(--space-4));
      bottom: calc(env(safe-area-inset-bottom) + var(--space-4));
    }

    [data-hand="left"] .fab {
      left: calc(var(--rail-width) + env(safe-area-inset-left) + var(--space-4));
    }

    body:has(.dock:not([hidden])) .fab {
      bottom: calc(env(safe-area-inset-bottom) + var(--space-4));
    }

    /* The dock does not show from 600px, so the floating button is the add button again. */
    body:has(.dock:not([hidden]) > .dock-add:not([hidden])) .fab {
      display: block;
    }

    /* A header-add is the add button from 600px (LAY-2). Last, and the second selector as specific as
       the rule above, so it wins beside a dock too. */
    body:has(.app-header > .header-add) .fab,
    body:has(.app-header > .header-add):has(.dock:not([hidden]) > .dock-add:not([hidden])) .fab {
      display: none;
    }
  }

  /* @component dock
     @summary The filter and the search of a list, in thumb reach below 600px. From 600px the app shows them in the header.
     @part dock-field  A search box in the dock: an input and a clear button. A --control border that takes --text on focus.
     @part dock-input  The input in the field. The field draws the border.
     @part dock-clear  The button that empties the field.
     @part dock-add  The add button of the view, a "button primary icon", the last direct child of the dock. While it shows, the fab does not show below 600px.
     @example <div class="dock" style="position: static"><button class="chip" type="button" aria-pressed="true">All 4</button><button class="chip" type="button">Filter</button><label class="dock-field"><input class="dock-input" type="search" aria-label="Filter"><button class="dock-clear" type="button" aria-label="Clear">x</button></label></div> */
  .dock {
    position: fixed;
    right: 0;
    bottom: calc(var(--nav-height) + env(safe-area-inset-bottom));
    left: 0;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: var(--dock-height);
    padding-inline: calc(var(--space-4) + env(safe-area-inset-left)) calc(var(--space-4) + env(safe-area-inset-right));
    border-top: 1px solid var(--line);
    background: var(--bg);
  }

  .dock .chip {
    flex: 1;
    justify-content: center;
  }

  .dock-field {
    flex: 2;
    display: flex;
    align-items: center;
    min-width: 0;
    min-height: 0;
    height: calc(var(--dock-height) - var(--space-2));
    border: 2px solid var(--control);
    border-radius: var(--radius-pill);
    background: var(--surface-2);
  }

  .dock-field:focus-within {
    border-color: var(--text);
  }

  .dock-input {
    flex: 1;
    align-self: stretch;
    min-width: 0;
    min-height: 0;
    padding: 0 var(--space-4);
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
  }

  /* The field draws the focus, so the input has no outline and no native clear. */
  .dock-input:focus-visible {
    outline: none;
  }

  .dock-input::-webkit-search-cancel-button {
    appearance: none;
  }

  .dock-clear {
    flex: none;
    display: grid;
    place-items: center;
    width: var(--tap);
    height: var(--tap);
    border-radius: var(--radius-round);
    color: var(--muted);
  }

  .dock-add {
    width: var(--tap);
    height: var(--tap);
    border-radius: var(--radius-m);
    margin-inline-start: auto;
  }

  [data-hand="left"] .dock {
    flex-direction: row-reverse;
  }

  /* The row is reversed, so the auto margin changes side to keep the add button at the far left. */
  [data-hand="left"] .dock-add {
    margin-inline-start: 0;
    margin-inline-end: auto;
  }

  /* No bar at the bottom from 600px, and the app shows the filter in the header. */
  @media (min-width: 600px) {
    .dock {
      display: none;
    }
  }
}

@layer layouts {
  /* @component cluster
     @summary A row of parts that wraps, with a --space-2 gap.
     @variant scroll  One line that scrolls on its own, with no scroll bar.
     @example <div class="cluster"><button class="chip" type="button">Home</button><button class="chip" type="button" aria-pressed="true">Work</button></div> */
  .cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-3) 0;
  }

  .cluster:empty {
    display: none;
  }

  .cluster.scroll {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding-inline-end: var(--space-6);
    mask-image: linear-gradient(to right, black calc(100% - var(--space-7)), transparent);
  }

  /* @component input-row
     @summary An input that grows, and the button that submits it. With the left hand the button is on the left.
       The button has no fill while it is disabled, so an empty box reads as not ready.
     @variant pinned  Sticks to the bottom of a sheet body, on --surface.
     @example <div class="input-row"><input class="input" type="text" placeholder="New tag"><button class="button primary" type="button">Add</button></div>
     @example <div class="input-row pinned" style="position: static"><input class="input" type="text" placeholder="New tag"><button class="button primary" type="button" disabled>Add</button></div> */
  .input-row {
    display: flex;
    gap: var(--space-2);
  }

  .input-row > .input {
    flex: 1;
  }

  .input-row > .button:disabled {
    background: var(--surface-2);
    color: var(--muted);
    opacity: 1;
  }

  .input-row.pinned {
    position: sticky;
    bottom: 0;
    padding-block: var(--space-2);
    background: var(--surface);
  }

  /* The last child of a sheet body without a foot stands at the screen edge: it clears the inset. */
  .input-row.pinned:last-child {
    padding-bottom: calc(var(--space-2) + env(safe-area-inset-bottom));
  }

  [data-hand="left"] .input-row {
    flex-direction: row-reverse;
  }

  /* @component split
     @summary Two equal columns. Each child takes the full width of its column.
     @part hand-side  The child that stands at the side of the hand: right, or left with the left hand.
     @example <div class="split" style="width: 100%"><button class="button primary hand-side" type="button">Fill</button><button class="button outline" type="button">Clear all</button></div> */
  .split {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }

  .split > * {
    width: 100%;
  }

  .split > .hand-side {
    grid-row: 1;
    grid-column: 2;
  }

  [data-hand="left"] .split > .hand-side {
    grid-column: 1;
  }

  /* @component pane
     @summary A list and the detail of one entry. Below 840px one of them shows at a time, in one centred column; from 840px they stand side by side, --gutter apart, and fill the content area: the detail is a card on --surface.
     @part pane-list  The list. Hidden below 840px while a detail is open.
     @part pane-detail  The detail. Hidden below 840px while no entry is open.
     @part pane-back  The button of the detail that goes back to the list. Hidden from 840px, where the list stays in sight.
     @state data-detail  An entry is open.
     @example <div class="pane" data-detail style="width: 100%"><div class="pane-list"><p>List</p></div><div class="pane-detail"><button class="button quiet pane-back" type="button">Back</button><p>Detail</p></div></div> */
  .pane {
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
  }

  .pane-detail {
    display: none;
    padding: var(--space-4);
  }

  .pane[data-detail] > .pane-list {
    display: none;
  }

  .pane[data-detail] > .pane-detail {
    display: block;
  }

  @media (min-width: 840px) {
    body:has(main > .pane:not([hidden])) {
      --page-max: 100%;
    }

    .pane {
      display: grid;
      grid-template-columns: var(--pane-list) minmax(0, 1fr);
      align-items: start;
      column-gap: var(--gutter);
      max-width: none;
    }

    .pane > .pane-list,
    .pane[data-detail] > .pane-list,
    .pane > .pane-detail {
      display: block;
    }

    /* The detail is a card, --space-4 below the header, that stays in sight while the list scrolls. */
    .pane > .pane-detail {
      position: sticky;
      top: var(--space-4);
      margin-block: var(--space-4);
      padding: var(--space-4) var(--space-5);
      border-radius: var(--radius-l);
      background: var(--surface);
    }

    .pane-back {
      display: none;
    }
  }
}
