gloss: re-vendor at 1b7b145 and sync DESIGN.md
Gloss went borderless: surface contrast now marks bounded surfaces and
hairlines only divide rows within one. The admin UI survives the walk
unchanged — no contract it uses moved, and admin-header's underline
reads as a divider, not a boundary.

Assisted-by: GLM-5.3 via pi
change uuluylmukrxqkooqoklqnnznrlnvrwkl
commit 10831e1172b2d9067e26c1e62b93e42e3ca01516
author Alpha Chen <alpha@kejadlen.dev>
date
parent wvqvmsnl
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index d84a6ca..7f682d8 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -40,9 +40,11 @@ What that inheritance actually means:
   never shows two accents in a view. `-ink` and `-soft` re-derive themselves.
 - `--gl-color-success` and `--gl-color-danger` are **fixed** — they mean
   confirmed and destructive, never decoration, never emphasis.
-- Flat surfaces and hairline borders do the elevation work. At most one
-  `--gl-shadow-float` on screen — in this app that's the dialog or a toast.
-  Border or shadow, never both.
+- Surface contrast does the elevation work: a bounded surface reads by
+  being a step lighter than what surrounds it, never by an outline.
+  Hairlines are dividers inside a surface (a CardRow, a table rule), never
+  a boundary around one. At most one `--gl-shadow-float` on screen — in
+  this app that's the dialog or a toast.
 - Motion is `--gl-dur-fast` on hover and focus, named properties only, never
   `all`. Press is not animated. Nothing enters, nothing bounces.
 - Icons are Lucide at 16–20px, stroke in `currentColor`, never filled. Nothing
@@ -140,5 +142,7 @@ Ask, in order:
 4. Is it reachable from both sides of any relationship it participates in?
 5. Does Gloss already define the component? Use it as documented rather than
    restyling something adjacent.
-6. Is there a literal hex, duration, or new class in the diff? There shouldn't
+6. Does it pass Gloss' refusals checklist, the consolidated list of
+   generated-interface tells the system refuses to produce?
+7. Is there a literal hex, duration, or new class in the diff? There shouldn't
    be. Is there a Save button? There shouldn't be.
diff --git a/public/vendor/gloss/base.css b/public/vendor/gloss/base.css
index dd67260..c5918d5 100644
--- a/public/vendor/gloss/base.css
+++ b/public/vendor/gloss/base.css
@@ -1,4 +1,4 @@
-/* Vendored from kejadlen/gloss @ d1d0aa0abdc4a70d461ff1878b412424be583091, 2026-08-31.
+/* Vendored from kejadlen/gloss @ 1b7b1455f6177993182b2bff7a262537480c53e2, 2026-09-03.
    Gloss ships this as plain CSS meant to be copied into a consuming
    project (see install.md) — there is no build step to run here.
    Refresh with `rake gloss:vendor GLOSS=/path/to/gloss/checkout`. */
diff --git a/public/vendor/gloss/components.css b/public/vendor/gloss/components.css
index 05fd37f..df4ad5b 100644
--- a/public/vendor/gloss/components.css
+++ b/public/vendor/gloss/components.css
@@ -1,12 +1,21 @@
-/* Vendored from kejadlen/gloss @ d1d0aa0abdc4a70d461ff1878b412424be583091, 2026-08-31.
+/* Vendored from kejadlen/gloss @ 1b7b1455f6177993182b2bff7a262537480c53e2, 2026-09-03.
    Gloss ships this as plain CSS meant to be copied into a consuming
    project (see install.md) — there is no build step to run here.
    Refresh with `rake gloss:vendor GLOSS=/path/to/gloss/checkout`. */
 
 /* Button
-   A flat, quiet button that recedes rather than performs — no gradients, no
-   shine. Variants: default, primary, dark, ghost, danger. Sizes: sm, md
-   (default), lg — padding and font step only, weight and radius stay put.
+   No border, no ornament — hierarchy is ink density, not an outline. A
+   STAMP (primary, dark, danger) is a solid block of colour; a QUIET action
+   (default, ghost) carries no fill at rest and picks up a flat tint of the
+   stock on hover, the same gesture as a CardRow. Variants: default, primary,
+   dark, ghost, danger. Sizes: sm, md (default), lg — padding and font step
+   only, weight and radius stay put.
+
+   The default used to pad itself with --gl-space-xs/-s, which are the rungs
+   a layout reaches for between two controls, not inside one: on a wide
+   viewport they grew the default button taller than the large one. Every
+   size now sits one rung further down the same scale — see the block at
+   the bottom.
 
    The contract is the element itself: a bare <button> is styled directly, no
    class required. An <a> that acts like a button (navigates rather than
@@ -18,36 +27,50 @@
 
 button:not(.icon-button),
 a.btn {
-  --_bg: var(--gl-color-surface-card);
+  --_bg: transparent;
   --_fg: var(--gl-color-text-primary);
-  --_border: var(--gl-color-border-default);
-  --_pad: var(--gl-space-xs) var(--gl-space-s);
+  --_pad-y: var(--gl-space-2xs);
+  --_pad-x: var(--gl-space-xs);
   --_font: var(--gl-step--1);
 
   display: inline-flex;
   align-items: center;
   justify-content: center;
   gap: var(--gl-space-2xs);
-  padding: var(--_pad);
-  border: 1px solid var(--_border);
+  padding: var(--_pad-y) var(--_pad-x);
+  border: none;
   border-radius: var(--gl-radius-md);
   background: var(--_bg);
   color: var(--_fg);
-  font-family: var(--gl-font-sans);
+  font-family: var(--gl-font-mono);
   font-size: var(--_font);
-  font-weight: 550;
-  letter-spacing: -0.01em;
-  line-height: 1.2;
+  font-weight: 500;
+  letter-spacing: 0.01em;
+  line-height: 1.15;
   text-align: center;
   text-decoration: none;
   white-space: nowrap;
   cursor: pointer;
-  transition: filter var(--gl-dur-fast) var(--gl-ease),
-              border-color var(--gl-dur-fast) var(--gl-ease);
+  transition: background-color var(--gl-dur-fast) var(--gl-ease),
+              color var(--gl-dur-fast) var(--gl-ease),
+              filter var(--gl-dur-fast) var(--gl-ease);
 }
 
-:is(button:not(.icon-button), a.btn):hover:not(:disabled):not([aria-disabled="true"]) { filter: brightness(0.96); }
-:is(button:not(.icon-button), a.btn):active:not(:disabled):not([aria-disabled="true"]) { filter: brightness(0.92); }
+/* Quiet variants (default, ghost) carry no fill at rest; hovering picks up
+   a flat tint of the stock instead of darkening a border that isn't there. */
+:is(button:not(.icon-button), a.btn):not([data-variant="primary"]):not([data-variant="dark"]):not([data-variant="danger"]):hover:not(:disabled):not([aria-disabled="true"]) {
+  background: var(--gl-color-surface-fill);
+  color: var(--gl-color-text-primary);
+}
+
+/* Stamp variants (primary, dark, danger) are already ink — hovering just
+   darkens the fill in place. */
+:is(button:not(.icon-button), a.btn)[data-variant="primary"]:hover:not(:disabled):not([aria-disabled="true"]),
+:is(button:not(.icon-button), a.btn)[data-variant="dark"]:hover:not(:disabled):not([aria-disabled="true"]),
+:is(button:not(.icon-button), a.btn)[data-variant="danger"]:hover:not(:disabled):not([aria-disabled="true"]) { filter: brightness(0.96); }
+:is(button:not(.icon-button), a.btn)[data-variant="primary"]:active:not(:disabled):not([aria-disabled="true"]),
+:is(button:not(.icon-button), a.btn)[data-variant="dark"]:active:not(:disabled):not([aria-disabled="true"]),
+:is(button:not(.icon-button), a.btn)[data-variant="danger"]:active:not(:disabled):not([aria-disabled="true"]) { filter: brightness(0.92); }
 
 button:not(.icon-button):disabled,
 :is(button:not(.icon-button), a.btn)[aria-disabled="true"] {
@@ -58,7 +81,6 @@ button:not(.icon-button):disabled,
 :is(button:not(.icon-button), a.btn)[data-variant="primary"] {
   --_bg: var(--gl-color-accent);
   --_fg: var(--gl-color-accent-fg);
-  --_border: var(--gl-color-accent);
 }
 
 /* Reserved for the single most-committing action in a view — a final Save,
@@ -66,13 +88,12 @@ button:not(.icon-button):disabled,
 :is(button:not(.icon-button), a.btn)[data-variant="dark"] {
   --_bg: var(--gl-color-text-primary);
   --_fg: var(--gl-color-surface-page);
-  --_border: var(--gl-color-text-primary);
 }
 
+/* Same quiet block as default, one step further back: text-secondary at
+   rest instead of text-primary. */
 :is(button:not(.icon-button), a.btn)[data-variant="ghost"] {
-  --_bg: transparent;
-  --_fg: var(--gl-color-text-primary);
-  --_border: transparent;
+  --_fg: var(--gl-color-text-secondary);
 }
 
 /* The source hardcodes this label white rather than deriving it — danger
@@ -82,16 +103,23 @@ button:not(.icon-button):disabled,
 :is(button:not(.icon-button), a.btn)[data-variant="danger"] {
   --_bg: var(--gl-color-danger);
   --_fg: var(--gl-color-accent-fg);
-  --_border: var(--gl-color-danger);
 }
 
-:is(button:not(.icon-button), a.btn)[data-size="sm"] { --_pad: 0.375rem 0.75rem; --_font: 0.8125rem; }
-:is(button:not(.icon-button), a.btn)[data-size="lg"] { --_pad: 0.75rem 1.375rem; --_font: var(--gl-step-0); }
+/* Each size is one step of the space scale wide and, at the ends, one step
+   of the type scale. Medium and large share a rung vertically and part on
+   type size, which is what keeps them in order at every viewport width: the
+   scale is fluid, so two sizes that differed only by which fluid rung they
+   padded with could — and did — cross over on a wide screen. */
+:is(button:not(.icon-button), a.btn)[data-size="sm"] { --_pad-y: var(--gl-space-3xs); --_pad-x: var(--gl-space-2xs); --_font: 0.8125rem; }
+:is(button:not(.icon-button), a.btn)[data-size="lg"] { --_pad-x: var(--gl-space-s); --_font: var(--gl-step-0); }
 
 :is(button:not(.icon-button), a.btn)[data-block] { width: 100%; }
 
 /* Button group — [role="group"] is already the correct ARIA role for a
-   toolbar of related buttons, so it doubles as the styling hook for free. */
+   toolbar of related buttons, so it doubles as the styling hook for free.
+   A hairline divider stands in for the border the buttons themselves no
+   longer carry — without one, two adjacent quiet buttons would read as a
+   single wide button. */
 [role="group"] {
   display: inline-flex;
   isolation: isolate;
@@ -99,18 +127,25 @@ button:not(.icon-button):disabled,
 [role="group"] > button { border-radius: 0; }
 [role="group"] > button:first-child { border-start-start-radius: var(--gl-radius-md); border-end-start-radius: var(--gl-radius-md); }
 [role="group"] > button:last-child { border-start-end-radius: var(--gl-radius-md); border-end-end-radius: var(--gl-radius-md); }
-[role="group"] > button + button { margin-inline-start: -1px; }
+[role="group"] > button + button { border-inline-start: 1px solid var(--gl-color-border-hairline); }
 [role="group"] > button:hover,
 [role="group"] > button:focus-visible { z-index: 1; }
 
 /* IconButton — a square icon-only button. Always needs an aria-label; it
    becomes both the accessible name and the native tooltip. There is no
    attribute that distinguishes "a button that is only an icon" from a plain
-   Button, so this is the one sanctioned class in this file. */
+   Button, so this is the one sanctioned class in this file.
+
+   The box is not a number of its own: it is written the way Button's height
+   arrives — two rungs of padding around one 1.15 line of the label type it
+   would carry if it had a label. So the two are the same height at every
+   viewport width, and stay that way when the scale is retuned, instead of a
+   fixed square drifting away from a fluid button. */
 .icon-button {
-  --_size: 2.125rem;
+  --_pad: var(--gl-space-2xs);
+  --_line: var(--gl-step--1);
+  --_size: calc(2 * var(--_pad) + 1.15 * var(--_line));
   --_bg: transparent;
-  --_border: transparent;
   --_fg: var(--gl-color-text-secondary);
 
   display: inline-flex;
@@ -120,7 +155,7 @@ button:not(.icon-button):disabled,
   height: var(--_size);
   padding: 0;
   flex: none;
-  border: 1px solid var(--_border);
+  border: none;
   border-radius: var(--gl-radius-md);
   background: var(--_bg);
   color: var(--_fg);
@@ -128,13 +163,16 @@ button:not(.icon-button):disabled,
   transition: background-color var(--gl-dur-fast) var(--gl-ease),
               color var(--gl-dur-fast) var(--gl-ease);
 }
-.icon-button:hover:not(:disabled) { background: var(--gl-color-surface-fill-2); }
+.icon-button:hover:not(:disabled) { background: var(--gl-color-surface-fill); }
 .icon-button:disabled { opacity: 0.5; cursor: default; }
 
-.icon-button[data-size="sm"] { --_size: 1.75rem; }
-.icon-button[data-size="lg"] { --_size: 2.5rem; }
+/* Same two overrides Button's own sm and lg make, so the pairs stay level. */
+.icon-button[data-size="sm"] { --_pad: var(--gl-space-3xs); --_line: 0.8125rem; }
+.icon-button[data-size="lg"] { --_line: var(--gl-step-0); }
 
-.icon-button[data-variant="outline"] { --_bg: var(--gl-color-surface-card); --_border: var(--gl-color-border-default); --_fg: var(--gl-color-text-primary); }
+/* No border on either variant — "outline" keeps its name for API
+   compatibility, but reads as a raised fill instead of a bordered box. */
+.icon-button[data-variant="outline"] { --_bg: var(--gl-color-surface-fill); --_fg: var(--gl-color-text-primary); }
 .icon-button[data-variant="accent"]  { --_bg: var(--gl-color-accent-soft); --_fg: var(--gl-color-accent-ink); }
 /* Field
    The labeled field wrapper — a <label> that wraps its own caption text
@@ -343,37 +381,40 @@ fieldset > legend {
   color: var(--gl-color-text-secondary);
 }
 /* Badge
-   Mono, uppercase, tracked — the catalog-label convention for status and
-   meta values (SAVED, OVERDUE, RECOMMENDED). A label, never a button.
+   A catalog mark, not a tinted pill: a small ink square in the tone colour
+   plus mono uppercase text, on no background. No border, no radius — status
+   reads as a marginal annotation on the row it belongs to, not a control.
 
    A <span> is too generic to style globally — it is used all over running
    prose for unrelated reasons — so Badge gets the one sanctioned class this
    shape needs. Tone is an attribute, not a modifier class. */
 
 .badge {
-  --_bg: var(--gl-color-surface-fill);
   --_fg: var(--gl-color-text-secondary);
-  --_border: var(--gl-color-border-hairline);
 
   display: inline-flex;
   align-items: center;
-  gap: 0.25rem;
-  padding: 0.1875rem 0.5rem;
-  border: 1px solid var(--_border);
-  border-radius: var(--gl-radius-sm);
-  background: var(--_bg);
+  gap: 0.375rem;
   color: var(--_fg);
   font-family: var(--gl-font-mono);
   font-size: var(--gl-step--2);
+  font-weight: 500;
   text-transform: uppercase;
-  letter-spacing: 0.06em;
+  letter-spacing: 0.09em;
   line-height: 1;
   white-space: nowrap;
 }
+.badge::before {
+  content: "";
+  width: 0.375rem;
+  height: 0.375rem;
+  flex: none;
+  background: var(--_fg);
+}
 
-.badge[data-tone="accent"]  { --_bg: var(--gl-color-accent-soft);  --_fg: var(--gl-color-accent-ink);  --_border: transparent; }
-.badge[data-tone="success"] { --_bg: var(--gl-color-success-bg);   --_fg: var(--gl-color-success);      --_border: transparent; }
-.badge[data-tone="danger"]  { --_bg: var(--gl-color-danger-bg);    --_fg: var(--gl-color-danger);       --_border: transparent; }
+.badge[data-tone="accent"]  { --_fg: var(--gl-color-accent-ink); }
+.badge[data-tone="success"] { --_fg: var(--gl-color-success); }
+.badge[data-tone="danger"]  { --_fg: var(--gl-color-danger); }
 
 .badge-set {
   display: flex;
@@ -393,9 +434,9 @@ fieldset > legend {
   align-items: center;
   gap: 0.375rem;
   padding: 0.25rem 0.625rem;
+  border: none;
   border-radius: var(--gl-radius-sm);
   background: var(--gl-color-surface-fill);
-  border: 1px solid var(--gl-color-border-default);
   color: var(--gl-color-text-secondary);
   font-family: var(--gl-font-mono);
   font-size: var(--gl-step--2);
@@ -424,9 +465,11 @@ fieldset > legend {
   gap: 0.375rem;
 }
 /* Card
-   Flat surface, hairline border, radius-lg. `[data-elevated]` is opt-in and
-   reserved for the one focused/primary card in a view — a dialog, the
-   primary capture card — flat is the default everywhere else.
+   Flat surface, no border, radius-lg. `--gl-color-surface-card` is one
+   neutral step lighter than the page around it — that tonal step, not an
+   outline, is what marks the edge. `[data-elevated]` is opt-in and reserved
+   for the one focused/primary card in a view — a dialog, the primary
+   capture card — flat is the default everywhere else.
 
    A <div>/<article> is too generic to select bare, so Card keeps one
    sanctioned class. When Card's job is holding a list of rows — the common
@@ -443,7 +486,7 @@ fieldset > legend {
   padding: 0;
   list-style: none;
   background: var(--gl-color-surface-card);
-  border: 1px solid var(--gl-color-border-hairline);
+  border: none;
   border-radius: var(--gl-radius-lg);
   box-shadow: none;
   overflow: hidden;
@@ -492,10 +535,11 @@ fieldset > legend {
 .card > li > a:only-child:hover { background: var(--gl-color-surface-fill); }
 
 /* Documentation-only extras: a body/footer pair and a responsive grid, used
-   by the prose pages (rationale.md, index.md) to show a Card as a plain
-   content tile rather than a data row. Neither is part of Card's props —
-   they exist because a content tile's inner layout has no element of its
-   own to hang a selector on. */
+   by the prose pages (rationale.md, index.md, refusals.md) to show a Card
+   as a plain content tile rather than a data row. Neither is part of
+   Card's props — they exist because a content tile's inner layout has no
+   element of its own to hang a selector on. .card-body ul is the same idea
+   applied to a checklist tile (refusals.md) instead of a single paragraph. */
 .card-body {
   display: flex;
   flex-direction: column;
@@ -503,7 +547,9 @@ fieldset > legend {
   padding: var(--gl-space-m);
 }
 .card-body h3 { margin: 0; font: 600 var(--gl-step-0)/1.3 var(--gl-font-sans); }
-.card-body p { margin: 0; color: var(--gl-color-text-secondary); font-size: var(--gl-step--1); }
+.card-body p,
+.card-body ul { margin: 0; color: var(--gl-color-text-secondary); font-size: var(--gl-step--1); }
+.card-body ul { padding-left: 1.125em; }
 .card-footer {
   display: flex;
   flex-wrap: wrap;
@@ -515,8 +561,8 @@ fieldset > legend {
   background: var(--gl-color-surface-fill);
 }
 
-.card[data-link] { transition: border-color var(--gl-dur-fast) var(--gl-ease), box-shadow var(--gl-dur-fast) var(--gl-ease); }
-.card[data-link]:hover { border-color: var(--gl-color-border-default); box-shadow: var(--gl-shadow-sm); }
+.card[data-link] { transition: box-shadow var(--gl-dur-fast) var(--gl-ease); }
+.card[data-link]:hover { box-shadow: var(--gl-shadow-sm); }
 .card[data-link]:focus-within { outline: 2px solid var(--gl-color-accent); outline-offset: 2px; }
 
 .card-grid {
diff --git a/public/vendor/gloss/tokens.css b/public/vendor/gloss/tokens.css
index a7711bc..646cd46 100644
--- a/public/vendor/gloss/tokens.css
+++ b/public/vendor/gloss/tokens.css
@@ -1,4 +1,4 @@
-/* Vendored from kejadlen/gloss @ d1d0aa0abdc4a70d461ff1878b412424be583091, 2026-08-31.
+/* Vendored from kejadlen/gloss @ 1b7b1455f6177993182b2bff7a262537480c53e2, 2026-09-03.
    Gloss ships this as plain CSS meant to be copied into a consuming
    project (see install.md) — there is no build step to run here.
    Refresh with `rake gloss:vendor GLOSS=/path/to/gloss/checkout`. */