/* ==========================================================================
   Datum, the house application design language.
   This file IS the distributable. An app copies it, swaps the :root palette,
   and keeps every pattern. Nothing below hardcodes a hex outside :root.

   Roles are documented in datum-design/references/palette.md
   Patterns are documented in datum-design/references/patterns.md

   Promotion round 2026-09-12, extracted from Personal OS after each pattern
   proved itself on a real surface: the section label (pattern 11), inline
   disclosure (pattern 12), the identity badge, and the leaving row, which is
   Datum's first decided motion pair (pattern 13).
   Follow-up 2026-09-12 (Personal OS rounds 10 and 11): the empty line
   (pattern 14), a one-line empty state for a bucket inside a grouped list.
   Follow-up 2026-09-12 (Personal OS round 12): the segmented code entry
   (pattern 15), the first Datum component whose behaviour ships as a helper
   in `datum.js` rather than as CSS alone.
   Follow-up 2026-09-13 (Personal OS redesign): the app shell rail
   (pattern 16). The rail's revealed panel starts at the card's top edge,
   never above it, fills with canvas so it reads as the header continuing
   down, and throws its shadow to the right only. Taken from Gmail.
   Follow-up 2026-09-13: the dark theme (section 1b). A second value set for
   every role under :root[data-theme="dark"], opt-in by attribute, resolved
   for "match system" by `datumTheme` in `datum.js`. No component rule is
   re-declared for it, because every component already reads roles.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS. The only place a color is written down.
   To rebrand: change values here, keep every name.
   -------------------------------------------------------------------------- */
:root{
  color-scheme:light;

  /* surfaces */
  --dt-canvas:#F7F9FB;
  --dt-surface:#FFFFFF;
  --dt-surface-border:#E5E7EB;
  --dt-divider:#E9EEF3;

  /* ink. --dt-ink is body text AND the primary action, same value on purpose */
  --dt-ink:#0B2839;
  --dt-ink-strong:#061A26;
  --dt-ink-muted:#5A7A89;        /* 4.59:1, the floor for small text */
  --dt-ink-subtle:#8A9DA8;       /* 2.81:1, DECORATIVE ONLY, never text */
  --dt-ink-placeholder:#ADC0C9;  /* 1.88:1, placeholder only */
  --dt-ink-on-primary:#FFFFFF;

  /* selection, never a tone: a preference is not a status */
  --dt-select-tint:#E6EAEE;
  --dt-select-edge:#C7D2DA;

  /* tones. each is line + fill + ink. ink only ever sits on its own fill. */
  --dt-positive-line:#52B788; --dt-positive-fill:#E0F5EC; --dt-positive-ink:#2E7D32;
  --dt-negative-line:#C0392B; --dt-negative-fill:#FDECEA; --dt-negative-ink:#A5291D;
  --dt-neutral-line:#D5DEE3;  --dt-neutral-fill:#EEF2F4;  --dt-neutral-ink:#1D3A47;
  --dt-info-line:#7C3AED;     --dt-info-fill:#EDE9FE;     --dt-info-ink:#5B21B6;
  --dt-accent-line:#E8735A;   --dt-accent-fill:#FBEAE6;   --dt-accent-ink:#A33F28;
  /* warning: attention without failure. Added 2026-09-11. NOT a category mark. */
  --dt-warning-line:#D99A1C;  --dt-warning-fill:#FDF4E3;  --dt-warning-ink:#8A5A12;

  --dt-focus-ring:rgba(11,40,57,.22);
  --dt-scrim:rgba(11,40,57,.5);

  /* geometry */
  --dt-r-chip:6px; --dt-r-control:8px; --dt-r-modal:14px; --dt-r-card:16px;
  --dt-shadow-card:0 20px 45px -20px rgba(15,35,48,.18),
                   0 2px 6px -1px rgba(15,35,48,.08);
  --dt-shadow-modal:0 20px 60px rgba(0,0,0,.15);
  --dt-shadow-pop:0 12px 32px rgba(11,40,57,.14);

  /* density. compact is a founding constraint, not a preference. */
  --dt-control-h:32px;
  --dt-row-h:30px;
  --dt-pad-card:16px;

  /* shell, pattern 16. The app frame: a transparent header, an icon rail,
     one card. `shell-gap` is the space between the header and the card AND
     the revealed rail's top offset: one role, so the rail can never start
     above the card again. `rail-w-open` is a LABEL BUDGET, re-measured
     against the app's longest label at 13px/800, never eyeballed. */
  --dt-shell-header-h:56px;
  --dt-shell-gap:8px;
  --dt-rail-w:56px;
  --dt-rail-w-open:216px;
  --dt-shadow-rail:1px 0 0 var(--dt-surface-border),
                   8px 0 18px -8px rgba(11,40,57,.18);

  /* motion SCALE. Still PROPOSED, see the open question in the reference. */
  --dt-dur-instant:120ms;
  --dt-dur-fast:180ms;
  --dt-dur-base:250ms;
  --dt-dur-slow:400ms;
  --dt-ease:cubic-bezier(.4,0,.2,1);
  --dt-ease-out:cubic-bezier(0,0,.2,1);
  --dt-ease-spring:cubic-bezier(.34,1.4,.64,1);

  /* motion ROLES. DECIDED 2026-09-12, the first and so far the only ones:
     the leaving-row pair, pattern 13. `hold` is how long a row wears its
     final tone before it starts to collapse, so the wash is actually seen;
     `leave` is the collapse itself. Both are bound to the scale above so a
     re-tuned scale moves them together. Script reads them from CSS
     (patterns.md shows how); an app never carries a second literal. The
     rest of motion (character, transitions, when the scale applies) stays
     open. */
  --dt-motion-hold:var(--dt-dur-instant);
  --dt-motion-leave:var(--dt-dur-base);
  --dt-ease-leave:var(--dt-ease);

  --dt-font:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,
            "Segoe UI",Roboto,sans-serif;
}

/* --------------------------------------------------------------------------
   1b. DARK THEME. @marker datum-dark-theme
   The same roles, a second value set, and nothing else. Every component
   reads roles, so no component rule is re-declared here; if a component
   needs a dark override, it has a light-only literal and that is the bug.

   OPT-IN BY ATTRIBUTE, never by `prefers-color-scheme` alone: an app whose
   own CSS still carries light-only literals must not be flipped dark by the
   OS before it has been audited. "Match system" is resolved in script by
   `datumTheme` (datum.js), which stamps data-theme="dark" or "light".

   Measured (palette.md, "Dark"): ink/surface 12.84, ink-muted/surface 6.77,
   ink-muted/canvas 7.34, every tone ink on its fill >= 7.58, danger text
   4.90, ink-subtle 4.05 and still decorative only. The 9% wash is kept: it
   reads at least as strongly on this surface as on white.
   -------------------------------------------------------------------------- */
:root[data-theme="dark"]{
  color-scheme:dark;

  --dt-canvas:#0D171E;
  --dt-surface:#141F27;
  --dt-surface-border:#26343E;
  --dt-divider:#1D2A33;

  --dt-ink:#D9E3E9;
  --dt-ink-strong:#F2F6F8;
  --dt-ink-muted:#93A8B4;        /* 6.77:1 on surface, 7.34:1 on canvas */
  --dt-ink-subtle:#6A808C;       /* 4.05:1, STILL decorative only */
  --dt-ink-placeholder:#55697A;  /* 2.94:1, placeholder only */
  --dt-ink-on-primary:#0D171E;   /* primary is light here, so its text is dark */

  --dt-select-tint:#23323C;
  --dt-select-edge:#35495A;

  --dt-positive-line:#46BE85; --dt-positive-fill:#11302A; --dt-positive-ink:#79DDAA;
  --dt-negative-line:#E0584F; --dt-negative-fill:#3A1D1E; --dt-negative-ink:#FF9C92;
  --dt-neutral-line:#3A4D58;  --dt-neutral-fill:#1C2A33;  --dt-neutral-ink:#C4D2DA;
  --dt-info-line:#9B7CF6;     --dt-info-fill:#282147;     --dt-info-ink:#C6B6FF;
  --dt-accent-line:#EC8A6E;   --dt-accent-fill:#38241E;   --dt-accent-ink:#FFB29C;
  --dt-warning-line:#DDA73C;  --dt-warning-fill:#352A18;  --dt-warning-ink:#F0C56E;

  --dt-focus-ring:rgba(217,227,233,.28);
  --dt-scrim:rgba(0,0,0,.6);

  /* shadows deepen: a navy shadow at light-theme alpha vanishes on navy */
  --dt-shadow-card:0 20px 45px -20px rgba(0,0,0,.55),
                   0 2px 6px -1px rgba(0,0,0,.35);
  --dt-shadow-modal:0 20px 60px rgba(0,0,0,.5);
  --dt-shadow-pop:0 12px 32px rgba(0,0,0,.5);
  --dt-shadow-rail:1px 0 0 var(--dt-surface-border),
                   8px 0 18px -8px rgba(0,0,0,.55);
}
/* The flip itself does not animate. datumTheme adds this class for one frame
   so every colour transition in the page lands at once instead of smearing
   through a half-light, half-dark state (which also misreports computed
   colours to anyone measuring mid-flight). */
html.dt-theme-swap *,html.dt-theme-swap *::before,html.dt-theme-swap *::after{
  transition:none !important;
}

/* Anyone who asked their OS not to animate gets no animation. Non-negotiable. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important;
  }
}

/* --------------------------------------------------------------------------
   2. BASE
   -------------------------------------------------------------------------- */
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
/* The UA's [hidden] rule is a plain display:none at (0,1,0) and loses to ANY
   author `display` on the same element, so a block styled display:grid or
   display:flex renders while hidden and nothing errors. One rule, ahead of
   every display below, so `el.hidden = true` always means gone. Pattern 12
   depends on it. Measured in Personal OS (round 4) before it was written. */
[hidden]{display:none !important;}
body{
  background:var(--dt-canvas); color:var(--dt-ink);
  font-family:var(--dt-font); font-size:14px; line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0; color:var(--dt-ink-strong); font-weight:800;
            letter-spacing:-.01em;}
h1{font-size:26px;} h2{font-size:19px;} h3{font-size:15px;} h4{font-size:13px;}
p{margin:0 0 8px;}
a{color:var(--dt-ink); text-underline-offset:2px;}
code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px;
     background:var(--dt-neutral-fill); padding:1px 5px;
     border-radius:4px; color:var(--dt-ink-strong);}

/* The signature type treatment. Micro labels ALWAYS use ink-muted. */
.dt-micro{font-size:10.5px; font-weight:800; letter-spacing:.1em;
          text-transform:uppercase; color:var(--dt-ink-muted);}

:where(button,a,input,select,textarea,[tabindex]):focus-visible{
  outline:none; box-shadow:0 0 0 3px var(--dt-focus-ring);
}
/* inset variant: overflow-x:auto forces overflow-y:auto, which clips an
   outset ring's bottom edge. Use inside any scroll container. */
.dt-focus-inset:focus-visible{
  outline:none; box-shadow:inset 0 0 0 2px var(--dt-focus-ring);
}

/* --------------------------------------------------------------------------
   3. CARD, pattern 9. The border is structural: surface on canvas is 1.06:1.
   -------------------------------------------------------------------------- */
.dt-card{
  background:var(--dt-surface); border:1px solid var(--dt-surface-border);
  border-radius:var(--dt-r-card); box-shadow:var(--dt-shadow-card);
  overflow:hidden;
}
.dt-card-head{padding:14px 18px;}
.dt-card-body{padding:18px; border-top:1px solid var(--dt-divider);}
.dt-section + .dt-section{border-top:1px solid var(--dt-divider);}

/* --------------------------------------------------------------------------
   4. STATUS ROW, pattern 1. The signature.
   9% alpha, dying at 42%, from the reading edge.
   -------------------------------------------------------------------------- */
.dt-row{
  padding:14px 18px; border-bottom:1px solid var(--dt-divider);
  display:flex; gap:14px; align-items:flex-start;
}
.dt-row:last-child{border-bottom:0;}
.dt-row.is-positive{
  background:linear-gradient(90deg, rgba(82,183,136,.09), transparent 42%);
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--dt-positive-line) 9%, transparent), transparent 42%);}
.dt-row.is-negative{
  background:linear-gradient(90deg, rgba(192,57,43,.09), transparent 42%);
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--dt-negative-line) 9%, transparent), transparent 42%);}
.dt-row.is-info{
  background:linear-gradient(90deg, rgba(124,58,237,.09), transparent 42%);
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--dt-info-line) 9%, transparent), transparent 42%);}
.dt-row.is-warning{
  background:linear-gradient(90deg, rgba(217,154,28,.09), transparent 42%);
  background:linear-gradient(90deg,
    color-mix(in srgb, var(--dt-warning-line) 9%, transparent), transparent 42%);}
/* neutral gets NO tone. An absence of state must not be given a color. */
.dt-row.is-neutral{opacity:.62;}

/* --------------------------------------------------------------------------
   5. INDEX BADGE, pattern 2. Fixed width or the column goes ragged.
   -------------------------------------------------------------------------- */
.dt-idx{
  flex:0 0 30px; height:22px; border-radius:var(--dt-r-chip);
  background:var(--dt-neutral-fill); color:var(--dt-ink-muted);
  font-size:11px; font-weight:800;
  display:flex; align-items:center; justify-content:center;
  margin-top:1px; /* optical centring against a 14px first line */
  transition:background var(--dt-dur-fast) var(--dt-ease),
             color var(--dt-dur-fast) var(--dt-ease);
}
.dt-row.is-positive .dt-idx{background:var(--dt-positive-fill); color:var(--dt-positive-ink);}
.dt-row.is-negative .dt-idx{background:var(--dt-negative-fill); color:var(--dt-negative-ink);}
.dt-row.is-info     .dt-idx{background:var(--dt-info-fill);     color:var(--dt-info-ink);}
.dt-row.is-warning  .dt-idx{background:var(--dt-warning-fill);  color:var(--dt-warning-ink);}

/* --------------------------------------------------------------------------
   6. BUTTONS
   -------------------------------------------------------------------------- */
.dt-btn{
  font:inherit; font-size:13px; font-weight:700; line-height:1.2;
  height:var(--dt-control-h); padding:0 12px; cursor:pointer;
  border-radius:var(--dt-r-control); border:1px solid var(--dt-surface-border);
  background:var(--dt-surface); color:var(--dt-ink);
  display:inline-flex; align-items:center; justify-content:center; gap:6px;
  transition:background var(--dt-dur-fast) var(--dt-ease),
             border-color var(--dt-dur-fast) var(--dt-ease),
             color var(--dt-dur-fast) var(--dt-ease);
}
.dt-btn:hover:not(:disabled){background:var(--dt-neutral-fill);}
.dt-btn:active:not(:disabled){transform:translateY(.5px);}
.dt-btn:disabled{opacity:.45; cursor:not-allowed;}

.dt-btn--primary{background:var(--dt-ink); border-color:var(--dt-ink);
                 color:var(--dt-ink-on-primary);}
.dt-btn--primary:hover:not(:disabled){background:var(--dt-ink-strong);
                 border-color:var(--dt-ink-strong);}
.dt-btn--danger{background:var(--dt-negative-line); border-color:var(--dt-negative-line);
                color:var(--dt-ink-on-primary);}   /* light 5.44:1 white on red; dark 4.90:1 */
.dt-btn--danger:hover:not(:disabled){background:var(--dt-negative-ink);
                border-color:var(--dt-negative-ink);}
.dt-btn--ghost{background:transparent; border-color:transparent;
               color:var(--dt-ink-muted);}
.dt-btn--ghost:hover:not(:disabled){background:var(--dt-neutral-fill);
               color:var(--dt-ink-strong);}
.dt-btn--sm{height:26px; padding:0 9px; font-size:12px;}
.dt-btn--lg{height:38px; padding:0 16px; font-size:14px;}

/* --------------------------------------------------------------------------
   7. TRI-STATE CONTROL GROUP, pattern 3.
   Selected adopts its own tone: fill AND border AND ink, all three.
   -------------------------------------------------------------------------- */
.dt-acts{display:flex; flex-direction:column; gap:6px; flex:0 0 92px;}
.dt-act{
  font:inherit; font-size:11px; font-weight:800; letter-spacing:.04em;
  cursor:pointer; padding:6px 8px; border-radius:var(--dt-r-chip);
  line-height:1.2; text-align:center;
  border:1px solid var(--dt-surface-border);
  background:var(--dt-surface); color:var(--dt-ink-muted);
  transition:background var(--dt-dur-fast) var(--dt-ease),
             border-color var(--dt-dur-fast) var(--dt-ease),
             color var(--dt-dur-fast) var(--dt-ease);
}
.dt-act:hover{background:var(--dt-neutral-fill); color:var(--dt-ink-strong);}
.dt-act[aria-pressed="true"][data-tone="positive"]{
  background:var(--dt-positive-fill); border-color:var(--dt-positive-line);
  color:var(--dt-positive-ink);}
.dt-act[aria-pressed="true"][data-tone="negative"]{
  background:var(--dt-negative-fill); border-color:var(--dt-negative-line);
  color:var(--dt-negative-ink);}
.dt-act[aria-pressed="true"][data-tone="warning"]{
  background:var(--dt-warning-fill); border-color:var(--dt-warning-line);
  color:var(--dt-warning-ink);}
.dt-act[aria-pressed="true"][data-tone="neutral"]{
  background:var(--dt-neutral-fill); border-color:var(--dt-neutral-line);
  color:var(--dt-ink-strong);}
@media (max-width:640px){
  .dt-row{flex-wrap:wrap;}
  .dt-acts{flex-direction:row; flex:1 1 100%;}
  .dt-act{flex:1;}
}

/* --------------------------------------------------------------------------
   8. SEGMENTED METER, pattern 4. One bar, not three.
   -------------------------------------------------------------------------- */
.dt-track{height:8px; border-radius:99px; background:var(--dt-neutral-fill);
          overflow:hidden; display:flex;}
.dt-track > i{display:block; height:100%;
              transition:width var(--dt-dur-base) var(--dt-ease);}
.dt-seg-positive{background:var(--dt-positive-line);}
.dt-seg-negative{background:var(--dt-negative-line);}
.dt-seg-neutral{background:var(--dt-ink-placeholder);}
.dt-seg-info{background:var(--dt-info-line);}
.dt-seg-warning{background:var(--dt-warning-line);}

/* --------------------------------------------------------------------------
   9. MICRO-LABEL ROW, pattern 5. inline-block + min-width = invisible column.
   -------------------------------------------------------------------------- */
.dt-meta{margin:0 0 6px; font-size:13px; color:var(--dt-ink-muted);}
.dt-meta > b{
  font-size:10.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--dt-ink-muted);
  display:inline-block; min-width:74px;
}

/* --------------------------------------------------------------------------
   10. CHIP, pattern 6. Selection = tint + WEIGHT. Never color alone.
   -------------------------------------------------------------------------- */
.dt-chip{
  font:inherit; font-size:12px; font-weight:700; cursor:pointer; line-height:1.2;
  padding:6px 11px; border-radius:99px;
  border:1px solid var(--dt-surface-border);
  background:var(--dt-surface); color:var(--dt-ink-muted);
  transition:background var(--dt-dur-fast) var(--dt-ease);
}
.dt-chip:hover{background:var(--dt-neutral-fill); color:var(--dt-ink-strong);}
.dt-chip[aria-pressed="true"]{
  background:var(--dt-select-tint); border-color:var(--dt-select-edge);
  color:var(--dt-ink-strong); font-weight:800;
}

/* --------------------------------------------------------------------------
   11. BADGE. Static status marker. Tone fill + tone ink, never a line fill.
   -------------------------------------------------------------------------- */
.dt-badge{
  display:inline-flex; align-items:center; gap:5px;
  font-size:10px; font-weight:800; letter-spacing:.08em; text-transform:uppercase;
  padding:3px 8px; border-radius:5px;
  background:var(--dt-neutral-fill); color:var(--dt-ink-muted);
}
.dt-badge--positive{background:var(--dt-positive-fill); color:var(--dt-positive-ink);}
.dt-badge--negative{background:var(--dt-negative-fill); color:var(--dt-negative-ink);}
.dt-badge--info{background:var(--dt-info-fill); color:var(--dt-info-ink);}
.dt-badge--accent{background:var(--dt-accent-fill); color:var(--dt-accent-ink);}
.dt-badge--warning{background:var(--dt-warning-fill); color:var(--dt-warning-ink);}
/* IDENTITY BADGE. A badge that names a proper noun (a person, a company, a
   project) rather than a status word keeps its own casing: "Acme Concrete",
   not "ACME CONCRETE". Same box, same tone pairs, so nothing is re-measured.
   It takes a CATEGORY tone (info, accent) or none at all; never positive or
   negative, because a name is not good or bad news. A qualifier inside it
   ("Primary", "low") prints as a WORD in the badge's own ink, never as a
   different shade of the tone. Promoted 2026-09-12 from Personal OS, where
   two ad hoc overrides of .dt-badge had grown for the same reason. */
.dt-badge--identity{text-transform:none; letter-spacing:0; font-size:11px; font-weight:700;}
.dt-badge--identity .dt-micro{color:inherit;}

/* --------------------------------------------------------------------------
   12. FIELDS
   -------------------------------------------------------------------------- */
.dt-field{display:flex; flex-direction:column; gap:5px;}
.dt-label{font-size:12px; font-weight:700; color:var(--dt-ink-muted);}
.dt-input,.dt-select,.dt-textarea{
  font:inherit; font-size:13px; color:var(--dt-ink);
  background:var(--dt-surface); border:1px solid var(--dt-surface-border);
  border-radius:var(--dt-r-control); padding:0 10px; height:var(--dt-control-h);
  width:100%; transition:border-color var(--dt-dur-fast) var(--dt-ease);
}
.dt-textarea{height:auto; min-height:72px; padding:8px 10px; resize:vertical;}
.dt-input::placeholder,.dt-textarea::placeholder{color:var(--dt-ink-placeholder);}
.dt-input:hover,.dt-select:hover,.dt-textarea:hover{border-color:var(--dt-neutral-line);}
.dt-input[aria-invalid="true"]{border-color:var(--dt-negative-line);}
.dt-hint{font-size:12px; font-weight:500; color:var(--dt-ink-muted);}
.dt-error{font-size:12px; font-weight:600; color:var(--dt-negative-ink);}

/* ---- 12b. CODE ENTRY, pattern 15. Six boxes, one code. --------------------
   A FORM CONTROL, not a status surface: no tone, no wash, no index badge.
   Each box is a `dt-input` carrying `dt-code-box`, so a palette swap moves
   the code entry with every other field and nothing here writes a colour.
   The box is deliberately TALLER and larger-typed than the 32px control
   height: it holds one glyph read at arm's length, and 20px clears iOS's
   16px auto-zoom floor. The boxes shrink before the row wraps, because a
   6-digit code broken across two lines stops reading as one code.
   BEHAVIOUR IS NOT OPTIONAL and is not in this file: `datumCodeEntry` in
   `datum.js` wires paste-splitting, auto-advance, backspace-retreat and the
   complete callback. See patterns.md pattern 15. */
.dt-code-entry{display:flex; justify-content:center; gap:8px; margin:10px 0;}
.dt-code-entry .dt-code-box{
  flex:0 1 44px; width:44px; min-width:32px; height:48px; padding:0;
  text-align:center; font-size:20px; font-weight:600;
}

/* --------------------------------------------------------------------------
   13. TABLE. Compact. Scrolls inside its own box, never the page.
   -------------------------------------------------------------------------- */
.dt-table-wrap{overflow-x:auto;}
.dt-table{width:100%; border-collapse:collapse; font-size:13px;}
.dt-table th{
  text-align:left; font-size:10.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--dt-ink-muted);
  background:var(--dt-neutral-fill);
  padding:8px 12px; white-space:nowrap;
}
.dt-table td{padding:0 12px; height:var(--dt-row-h);
             border-top:1px solid var(--dt-divider); color:var(--dt-ink);}
.dt-table tbody tr:hover{background:var(--dt-neutral-fill);}
.dt-num{text-align:right; font-variant-numeric:tabular-nums;}
/* A negative number is red AND parenthesised: never color alone.
   ! IS REQUIRED AND IS THE SANCTIONED REMEDY, not laziness. This is a SEMANTIC
   override that must outrank whatever ink its host element chose. Unprefixed,
   `.dt-neg` is specificity (0,1,0) and loses to `.dt-table td` at (0,1,1), so
   the number silently renders in body ink and nothing errors. Caught by
   measuring computed style in a real browser, which is the only check that
   finds this class of failure. Same remedy, same reason, as Kwotemark's
   negativeInk() helper. */
.dt-neg{color:var(--dt-negative-ink) !important;}

/* A value a human typed over one the system computed gets a dotted underline,
   never a color change. Extracted from Kwotemark's worksheet (round 5T),
   where an overridden cost used to share the SAME red as negative money and
   a reader could not tell "this lost money" from "a person overrode this".
   The type keeps whatever ink its cell already had and is NEVER restyled by
   this class; only the decoration LINE uses ink-subtle - decorative use is
   exactly what that role is for, see palette.md. (Measured in the sample
   below the ink is the table's body ink; in Kwotemark's worksheet it is
   ink-muted at 4.59:1. Either way the ban on ink-subtle as TEXT is not
   engaged, because nothing here sets a text color.)
   `text-decoration` and `color` are different properties, so this class needs
   no `!important` to sit beside .dt-neg on the same node; both signals paint
   at once on a negative overridden cell. */
.dt-overridden{
  text-decoration:underline dotted var(--dt-ink-subtle);
  text-underline-offset:3px;
}

/* --------------------------------------------------------------------------
   14. TABS. Bar + weight + ink. Fill is pure hover, answered by every tab.
   -------------------------------------------------------------------------- */
.dt-tabs{display:flex; gap:4px; overflow-x:auto; /* scroll, never wrap */}
.dt-tab{
  font:inherit; font-size:13px; font-weight:700; cursor:pointer;
  padding:9px 12px; white-space:nowrap;
  background:transparent; border:0; border-bottom:2px solid transparent;
  color:var(--dt-ink-muted);
}
.dt-tab:hover{background:var(--dt-neutral-fill);}
.dt-tab[aria-selected="true"]{
  border-bottom-color:var(--dt-ink); color:var(--dt-ink-strong); font-weight:800;
}

/* --------------------------------------------------------------------------
   15. STICKY BAR. Translucent so content reads as passing underneath.
   -------------------------------------------------------------------------- */
.dt-bar{
  position:sticky; top:0; z-index:20;
  background:rgba(247,249,251,.92);
  background:color-mix(in srgb, var(--dt-canvas) 92%, transparent);
  backdrop-filter:saturate(180%) blur(8px);
  border-bottom:1px solid var(--dt-divider); padding:12px 0;
}

/* --------------------------------------------------------------------------
   16. EMPTY STATE
   -------------------------------------------------------------------------- */
.dt-empty{padding:40px 20px; text-align:center;}
.dt-empty svg{color:var(--dt-ink-subtle); margin-bottom:10px;}
.dt-empty h3{margin-bottom:4px;}
.dt-empty p{color:var(--dt-ink-muted); font-size:13px; max-width:40ch;
            margin-left:auto; margin-right:auto;}

/* --------------------------------------------------------------------------
   17. LOADING. Skeleton is the default: it reserves the real box.
   -------------------------------------------------------------------------- */
.dt-skel{
  background:linear-gradient(90deg,
    var(--dt-neutral-fill) 25%, #F6F9FA 37%, var(--dt-neutral-fill) 63%);
  background:linear-gradient(90deg,
    var(--dt-neutral-fill) 25%,
    color-mix(in srgb, var(--dt-neutral-fill) 55%, var(--dt-surface)) 37%,
    var(--dt-neutral-fill) 63%);
  background-size:400% 100%;
  animation:dt-shimmer 1.4s ease infinite;
  border-radius:var(--dt-r-chip);
}
@keyframes dt-shimmer{0%{background-position:100% 0}100%{background-position:0 0}}
.dt-skel--text{height:12px; margin:6px 0;}
.dt-skel--line{height:12px; width:100%;}

.dt-spinner{
  width:16px; height:16px; border-radius:50%;
  border:2px solid var(--dt-neutral-line);
  border-top-color:var(--dt-ink);
  animation:dt-spin .7s linear infinite; display:inline-block;
}
@keyframes dt-spin{to{transform:rotate(360deg)}}

/* indeterminate top-of-page bar, for route changes */
.dt-toploader{position:relative; height:2px; background:var(--dt-neutral-fill);
              overflow:hidden; border-radius:99px;}
.dt-toploader::after{
  content:''; position:absolute; inset:0 auto 0 0; width:40%;
  background:var(--dt-ink); border-radius:99px;
  animation:dt-slide 1.1s var(--dt-ease) infinite;
}
@keyframes dt-slide{
  0%{transform:translateX(-100%)} 100%{transform:translateX(350%)}
}

/* --------------------------------------------------------------------------
   18. MOTION PRIMITIVES
   -------------------------------------------------------------------------- */
@keyframes dt-fade{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@keyframes dt-pop{from{opacity:0; transform:scale(.97)} to{opacity:1; transform:none}}
@keyframes dt-slide-in{from{transform:translateX(-100%)} to{transform:translateX(0)}}
.dt-anim-fade{animation:dt-fade var(--dt-dur-base) var(--dt-ease-out);}
.dt-anim-pop{animation:dt-pop var(--dt-dur-fast) var(--dt-ease);}
.dt-anim-slide{animation:dt-slide-in var(--dt-dur-fast) var(--dt-ease);}

/* --------------------------------------------------------------------------
   19. TOAST + MODAL
   -------------------------------------------------------------------------- */
.dt-toast{
  background:var(--dt-surface); border:1px solid var(--dt-positive-line);
  border-radius:var(--dt-r-control); box-shadow:var(--dt-shadow-pop);
  padding:10px 14px; font-size:13px; color:var(--dt-ink);
  display:inline-flex; align-items:center; gap:9px;
}
.dt-scrim{background:var(--dt-scrim);}
.dt-modal{
  background:var(--dt-surface); border-radius:var(--dt-r-modal);
  box-shadow:var(--dt-shadow-modal); overflow:hidden;
}

/* --------------------------------------------------------------------------
   21. SECTION LABEL, pattern 11. A micro label between clusters of rows.
   Not a tab, not a collapsible header (pattern 7), not a row with a state.
   It is a LABEL, so it may carry a tone as TEXT ONLY: the three-signal trio
   is for rows, and a label has no badge or control to coordinate with.
   Tone ink on surface is measured in palette.md (positive 5.13:1 is the
   floor; every other tone clears 5.9:1). The label sits INSIDE the card:
   ink-muted on canvas is 4.34:1 and fails, so never float one onto canvas.
   -------------------------------------------------------------------------- */
.dt-group{
  padding:12px 18px 6px; border-top:1px solid var(--dt-divider);
  font-size:10.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:var(--dt-ink-muted);
}
.dt-group:first-child{border-top:0; padding-top:14px;}
.dt-row + .dt-group{border-top:0;}  /* the row's own divider is the rule; never two */
/* The tone family. Also valid on a lone .dt-micro that states a condition
   (a health line under a connector), which is the same idea without the
   section geometry. Neutral gets nothing: the default IS the neutral. */
.dt-group.is-positive-label, .dt-micro.is-positive-label{color:var(--dt-positive-ink);}
.dt-group.is-negative-label, .dt-micro.is-negative-label{color:var(--dt-negative-ink);}
.dt-group.is-warning-label,  .dt-micro.is-warning-label {color:var(--dt-warning-ink);}
.dt-group.is-info-label,     .dt-micro.is-info-label    {color:var(--dt-info-ink);}
.dt-group.is-accent-label,   .dt-micro.is-accent-label  {color:var(--dt-accent-ink);}

/* --------------------------------------------------------------------------
   22. INLINE DISCLOSURE, pattern 12. A micro label made tappable, sitting
   inline in a row's meta line, that reveals a block the row is keeping
   folded (notes, a description, a long address). Reader-initiated, which is
   what separates it from pattern 8 (state-initiated: the field appears
   because a chosen state needs it). Rendered as a borderless, backgroundless
   button so it reads as part of the meta line and not as a control; the
   dotted underline is the whole affordance. It inherits the line's ink, so
   on a toned label it stays in that tone. Works on an <a> too (a "Meeting"
   link that opens the source), same look, same meaning: "more here".
   The folded block carries `hidden` and .dt-disclose-body, so print can
   expand it (section 20 rule: paper has no taps).
   Not pattern 10: that dotted line is ink-subtle on a VALUE in a table and
   means "a human overrode this"; this one is the label's own ink on an
   uppercase micro label and means "tap for more". Different node, different
   ink, different size, and never both on the same element.
   -------------------------------------------------------------------------- */
.dt-disclose{
  font:inherit; font-size:10.5px; font-weight:800; letter-spacing:.1em;
  text-transform:uppercase; color:inherit;
  display:inline; margin-left:6px; padding:0; border:0; background:none;
  cursor:pointer;
  text-decoration:underline dotted; text-underline-offset:2px;
}
.dt-disclose:hover{color:var(--dt-ink-strong);}
.dt-disclose-body{margin:4px 0 0; white-space:pre-wrap;}

/* --------------------------------------------------------------------------
   23. LEAVING ROW, pattern 13. Datum's first decided motion.
   A row that has stopped belonging to its list leaves IN PLACE, in three
   beats: it wears its final tone for --dt-motion-hold (the wash is seen),
   collapses over --dt-motion-leave (height, opacity, padding and the divider
   all go together, or a 1px rule is left behind), then is removed from the
   DOM and the list is renumbered. A section label (pattern 11) whose cluster
   just emptied is pruned at the same moment.
   HEIGHT, NOT MAX-HEIGHT: script pins the measured height, adds this class,
   and sets height to 0 on the next frame, so the collapse is exact for a
   one-line row and a four-line one alike. The reduced-motion block at the
   top flattens both durations to nothing; script must therefore never wait
   on transitionend alone (patterns.md carries the fallback).
   The tone on the way out is the intent's tone (complete: positive, delete:
   negative, a routine move: none). Pattern 1's rule holds: an absence of
   state gets no wash, even while leaving.
   -------------------------------------------------------------------------- */
.dt-row.is-leaving{
  overflow:hidden; height:0; opacity:0; padding-top:0; padding-bottom:0;
  border-bottom-color:transparent; pointer-events:none;
  transition:height var(--dt-motion-leave) var(--dt-ease-leave),
             opacity var(--dt-motion-leave) var(--dt-ease-leave),
             padding-top var(--dt-motion-leave) var(--dt-ease-leave),
             padding-bottom var(--dt-motion-leave) var(--dt-ease-leave);
}

/* --------------------------------------------------------------------------
   24. EMPTY LINE, pattern 14. @marker datum-r12-empty-line
   A one-line, un-iconed, un-headlined empty state for ONE BUCKET inside a
   grouped list, so the bucket's section label (pattern 11) stays and the
   bucket reads as intentionally empty rather than missing. Not section 16:
   .dt-empty is the whole-card treatment (icon, heading, 40px, one action)
   for a list that has nothing at all; this is for a quiet cluster sitting
   between populated ones on the same card, where that treatment would shout.
   NO TONE, ever. An absence of rows is not a state (pattern 1's rule), and a
   toned empty line would claim one. The label above it may carry its tone as
   text; the line itself is muted ink and nothing else.
   INK-MUTED, NOT INK-SUBTLE. This is text, so it takes the small-text floor
   (4.59:1 on surface, palette.md). The app it came from used ink-subtle here
   at 2.81:1, which is the exact hazard the palette records. It lives INSIDE
   the card for the same reason as the label: ink-muted on canvas is 4.34:1.
   NO NEW TOKEN: divider, ink-muted and the row's 18px gutter are all it uses.
   Pattern 13 prunes a label whose LAST row just left; this is the other case,
   a bucket that is a fixed fact of the view (a window, a horizon, a person)
   and must be shown empty rather than dropped. Static buckets get the line,
   derived clusters get pruned. Never both in one list.
   -------------------------------------------------------------------------- */
.dt-empty-line{
  margin:0; padding:10px 18px; border-bottom:1px solid var(--dt-divider);
  font-size:12.5px; color:var(--dt-ink-muted);
}
.dt-empty-line:last-child{border-bottom:0;}
.dt-empty-line + .dt-group{border-top:0;}  /* the line's divider is the rule; never two */

/* --------------------------------------------------------------------------
   25. APP SHELL + RAIL, pattern 16. @marker datum-shell-rail
   One canvas runs through the header, the rail and the gap around the card;
   header and rail are transparent over it. The rail PANEL is absolutely
   positioned over a fixed-width SLOT, so revealing it lays it over the
   content instead of pushing it. Three rules make the reveal read like
   Gmail's, and each one is a separate way to get it wrong:
     1. the panel's top is `--dt-shell-gap`, the card's top edge, not 0,
     2. the revealed fill is `--dt-canvas`, so the panel is the header
        continuing down, never a lighter or darker sheet over it,
     3. the shadow is clipped to the right edge. A box-shadow's blur spreads
        past ALL four sides; without the clip it smudges up into the header.
   The icon lane is x=30 in both states: collapsed 22 (nav) + 8 (item),
   pinned 12 + 18. The header's 18px left pad centres a 40px menu button on
   the same x=38 as the 16px icons. Change one term, re-derive the others.
   -------------------------------------------------------------------------- */
.dt-shell{
  height:100vh; height:100dvh; display:flex; flex-direction:column;
  overflow:hidden; background:var(--dt-canvas);
}
.dt-shell-header{
  height:var(--dt-shell-header-h); flex:none; position:relative; z-index:30;
  display:flex; align-items:center; gap:8px; padding:0 20px 0 18px;
}
.dt-shell-body{flex:1; min-height:0; display:flex;}
.dt-shell-main{
  flex:1; min-width:0; min-height:0; overflow-y:auto;   /* main scrolls, the document never does */
  padding:var(--dt-shell-gap) 20px 16px;
}
.dt-rail-slot{
  position:relative; z-index:20; flex:none; width:var(--dt-rail-w);
  transition:width var(--dt-dur-base) var(--dt-ease);   /* animates on pin only, never on hover */
}
.dt-rail{
  position:absolute; top:var(--dt-shell-gap); bottom:0; left:0;
  width:var(--dt-rail-w); overflow:hidden;
  clip-path:inset(0 -32px 0 0);                         /* rule 3: right edge only */
  transition:width var(--dt-dur-base) var(--dt-ease),
             background-color var(--dt-dur-base) var(--dt-ease),
             box-shadow var(--dt-dur-base) var(--dt-ease);
}
@media (hover:hover){   /* touch fires a sticky hover; keep it out of the reveal */
  .dt-shell:not(.is-pinned) .dt-rail:hover{
    width:var(--dt-rail-w-open); background:var(--dt-canvas); box-shadow:var(--dt-shadow-rail);
  }
}
.dt-shell:not(.is-pinned) .dt-rail:has(:focus-visible){   /* Tab reveals, a click does not stick it open */
  width:var(--dt-rail-w-open); background:var(--dt-canvas); box-shadow:var(--dt-shadow-rail);
}
.dt-shell.is-pinned .dt-rail-slot,
.dt-shell.is-pinned .dt-rail{width:var(--dt-rail-w-open);}
.dt-rail-nav{
  height:100%; box-sizing:border-box; display:flex; flex-direction:column; gap:2px;
  overflow-y:auto; overflow-x:hidden;   /* overflow-y alone computes x to auto too */
  padding:0 2px 12px 22px;
}
.dt-shell.is-pinned .dt-rail-nav{padding:0 12px 12px;}
.dt-rail-item{
  display:flex; align-items:center; gap:12px; padding:6px 8px;
  border-radius:var(--dt-r-control); white-space:nowrap; text-decoration:none; cursor:pointer;
  font-size:13px; font-weight:600; color:var(--dt-ink);
  transition:background var(--dt-dur-fast) var(--dt-ease);
}
.dt-shell.is-pinned .dt-rail-item{padding-left:18px;}
.dt-rail-item > svg{width:16px; height:16px; flex:none;}
.dt-rail-item:hover{background:var(--dt-neutral-fill); color:var(--dt-ink-strong);}
.dt-rail-item[aria-current="page"]{
  background:var(--dt-select-tint); color:var(--dt-ink-strong); font-weight:800;   /* tint + weight, never ink alone */
}
.dt-rail-label,.dt-rail-heading > span,.dt-rail-heading::before{
  transition:opacity var(--dt-dur-base) var(--dt-ease);
}
.dt-rail-heading{position:relative; height:22px; margin:6px 0 2px; flex:none;}
.dt-rail-heading::before{   /* collapsed, the heading has no room: a hairline carries the grouping */
  content:""; position:absolute; left:8px; right:8px; top:50%; height:1px; background:var(--dt-divider);
}
.dt-rail-heading > span{
  position:absolute; left:8px; bottom:2px; white-space:nowrap;
  font-size:10.5px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
  color:var(--dt-ink);   /* ON CANVAS: ink-muted is 4.34:1 here and fails */
}
.dt-shell.is-pinned .dt-rail-heading > span{left:18px;}
.dt-shell:not(.is-pinned) .dt-rail-label,
.dt-shell:not(.is-pinned) .dt-rail-heading > span,
.dt-shell.is-pinned .dt-rail-heading::before{opacity:0;}
@media (hover:hover){
  .dt-shell:not(.is-pinned) .dt-rail:hover .dt-rail-label,
  .dt-shell:not(.is-pinned) .dt-rail:hover .dt-rail-heading > span{opacity:1;}
  .dt-shell:not(.is-pinned) .dt-rail:hover .dt-rail-heading::before{opacity:0;}
}
.dt-shell:not(.is-pinned) .dt-rail:has(:focus-visible) .dt-rail-label,
.dt-shell:not(.is-pinned) .dt-rail:has(:focus-visible) .dt-rail-heading > span{opacity:1;}

/* --------------------------------------------------------------------------
   20. PRINT. Expand what is collapsed, reveal what is hidden.
   -------------------------------------------------------------------------- */
@media print{
  body{background:#fff;}
  .dt-bar,.dt-acts,.dt-no-print,.dt-disclose{display:none !important;}
  .dt-disclose-body[hidden]{display:block !important;}  /* paper has no taps */
  .dt-card{box-shadow:none; break-inside:avoid;}
  .dt-shell-header,.dt-rail-slot{display:none !important;}   /* paper has no navigation */
  .dt-shell,.dt-shell-main{height:auto; overflow:visible;}
}
