/* rastrillo/ui — design tokens and the component classes the eight
   shipped partials use.

   rastrillo new writes this file once into your app's static/ directory.
   From then on it is yours: edit it, replace it, delete rules you do not
   want. Nothing regenerates it, and rastrillo.Serve never serves it —
   your app's own static handler does.

   Every class and custom property is prefixed rst- — except the single
   .icon rule, which styles the class rastrillo.Icon emits — so dropping
   this file into an app with existing CSS has exactly one known
   collision surface. There are no imports, no remote assets and no webfont:
   this stylesheet fetches nothing, from anywhere, ever. That is a test,
   not a wish (ui_test.go, TestTokensCSSIsSelfContained) — which is also
   why this comment cannot spell those constructs out literally.

   The look: ink on paper. Neutrals carry a faint violet cast rather than
   the usual cool blue-grey, and the one accent is an iron-gall violet —
   the colour writing ink oxidises to. Semantic status colours are
   functional and never the accent, so a pill can never be mistaken for a
   link. Both themes are authored, never inverted: dark follows the OS,
   and the [data-theme] hooks let an explicit toggle win in both
   directions.

   ── WCAG 2.2 AA, measured ─────────────────────────────────────────────
   Normal text needs 4.5:1; large text and UI component boundaries need
   3:1. Every pair below was computed from these exact hex values. If you
   change a token, recompute its row.

   LIGHT                                     ratio      requirement
     --rst-text        on --rst-surface       17.23      4.5:1  text
     --rst-text        on --rst-bg            15.47      4.5:1  text
     --rst-text        on --rst-surface-2     16.43      4.5:1  text
     --rst-text        on --rst-accent-soft   15.11      4.5:1  text
     --rst-text-muted  on --rst-surface        6.98      4.5:1  text
     --rst-text-muted  on --rst-bg             6.26      4.5:1  text
     --rst-text-muted  on --rst-surface-2      6.65      4.5:1  text
     --rst-text-muted  on --rst-accent-soft    6.12      4.5:1  text
     --rst-text-faint  on --rst-surface        5.21      4.5:1  text
     --rst-text-faint  on --rst-bg             4.67      4.5:1  text
     --rst-text-faint  on --rst-surface-2      4.96      4.5:1  text
     --rst-text-faint  on --rst-accent-soft    4.56      4.5:1  text
     --rst-accent      on --rst-surface        7.72      4.5:1  text + focus ring
     --rst-accent      on --rst-bg             6.93      4.5:1  text + focus ring
     --rst-accent      on --rst-surface-2      7.36      4.5:1  text
     --rst-accent      on --rst-accent-soft    6.77      4.5:1  text
     --rst-on-accent   on --rst-accent         7.72      4.5:1  button label
     --rst-on-accent   on --rst-accent-strong 10.06      4.5:1  button label, hover
     --rst-line-strong on --rst-surface        3.36      3:1    control border
     --rst-line-strong on --rst-bg             3.02      3:1    control border
     --rst-line-strong on --rst-surface-2      3.20      3:1    control border
     pill neutral  fg on its own tint          7.88      4.5:1  text
     pill positive fg on its own tint          6.10      4.5:1  text
     pill warning  fg on its own tint          6.21      4.5:1  text
     pill negative fg on its own tint          6.37      4.5:1  text

   DARK                                      ratio      requirement
     --rst-text        on --rst-surface       14.92      4.5:1  text
     --rst-text        on --rst-bg            16.20      4.5:1  text
     --rst-text        on --rst-surface-2     15.60      4.5:1  text
     --rst-text        on --rst-accent-soft   13.35      4.5:1  text
     --rst-text-muted  on --rst-surface        7.11      4.5:1  text
     --rst-text-muted  on --rst-bg             7.72      4.5:1  text
     --rst-text-muted  on --rst-surface-2      7.43      4.5:1  text
     --rst-text-muted  on --rst-accent-soft    6.36      4.5:1  text
     --rst-text-faint  on --rst-surface        5.69      4.5:1  text
     --rst-text-faint  on --rst-bg             6.17      4.5:1  text
     --rst-text-faint  on --rst-surface-2      5.95      4.5:1  text
     --rst-text-faint  on --rst-accent-soft    5.09      4.5:1  text
     --rst-accent      on --rst-surface        8.08      4.5:1  text + focus ring
     --rst-accent      on --rst-bg             8.77      4.5:1  text + focus ring
     --rst-accent      on --rst-surface-2      8.45      4.5:1  text
     --rst-accent      on --rst-accent-soft    7.23      4.5:1  text
     --rst-on-accent   on --rst-accent         8.15      4.5:1  button label
     --rst-on-accent   on --rst-accent-strong 10.34      4.5:1  button label, hover
     --rst-line-strong on --rst-surface        3.32      3:1    control border
     --rst-line-strong on --rst-bg             3.60      3:1    control border
     --rst-line-strong on --rst-surface-2      3.47      3:1    control border
     pill neutral  fg on its own tint          7.43      4.5:1  text
     pill positive fg on its own tint          8.42      4.5:1  text
     pill warning  fg on its own tint          8.21      4.5:1  text
     pill negative fg on its own tint          6.93      4.5:1  text

   --rst-line is a hairline divider between rows, not a control border or
   a meaningful graphic, so 1.4.11's 3:1 does not apply to it. Control
   borders use --rst-line-strong, which does clear 3:1.

   Pagination chips are the one deliberate exception: they are bordered
   with --rst-line rather than --rst-line-strong, because a page strip
   ringed in hard outlines shouts louder than the list it belongs to.
   1.4.11 is satisfied without that border — each chip's meaning is
   carried by its text label at 4.5:1 or better, and the current page
   adds a filled background, a heavier weight and aria-current on top.
   The border is decoration, so it is allowed to be quiet.

   --rst-text-faint is the tightest pair in the set — 4.67:1 in light on
   --rst-bg, against a 4.5:1 floor. Put it on a surface not listed above
   and you are guessing; recompute first.

   Padding values are load-bearing for SC 2.5.8 Target Size (AA, new in
   WCAG 2.2): pagination chips measure about 30px, row action pills
   about 27px and buttons about 34px on their smaller axis, all clear of
   the 24px minimum. Do not shrink them without re-measuring.

   Type sizes are rem, not px, so the whole scale tracks a reader who has
   raised their browser's default font size. Spacing is rem for the same
   reason. Anything you add here should follow suit.
   ──────────────────────────────────────────────────────────────────── */

/* ── Scale: type, spacing, radius. Theme-independent. ─────────────── */
:root {
  --rst-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --rst-fs-base: 0.875rem;   /* 14px at the browser default */
  --rst-fs-sm: 0.78125rem;   /* 12.5px */
  --rst-fs-xs: 0.71875rem;   /* 11.5px */

  --rst-sp-1: 0.25rem;
  --rst-sp-2: 0.5rem;
  --rst-sp-3: 0.75rem;
  --rst-sp-4: 1rem;
  --rst-sp-5: 1.5rem;
  --rst-sp-6: 2.5rem;

  --rst-radius: 10px;
  --rst-radius-sm: 7px;
  --rst-radius-pill: 999px;
}

/* ── Colour: light. Also the explicit-toggle-to-light case. ───────── */
:root,
:root[data-theme="light"] {
  color-scheme: light;

  --rst-bg: #f3f2f7;
  --rst-surface: #ffffff;
  --rst-surface-2: #faf9fc;
  --rst-line: #e4e2ec;
  --rst-line-strong: #8d89a4;

  --rst-text: #1b1a23;
  --rst-text-muted: #5b5670;
  --rst-text-faint: #6e6987;

  --rst-accent: #5b3fa8;
  --rst-accent-strong: #47308f;
  --rst-accent-soft: #f2eefc;
  --rst-on-accent: #ffffff;

  --rst-tone-neutral-fg: #45415a;
  --rst-tone-neutral-bg: #e8e6ef;
  --rst-tone-positive-fg: #17603f;
  --rst-tone-positive-bg: #d7ecdf;
  --rst-tone-warning-fg: #74490e;
  --rst-tone-warning-bg: #f4e4c9;
  --rst-tone-negative-fg: #93262f;
  --rst-tone-negative-bg: #f9dcdd;
}

/* ── Colour: dark, following the OS. ──────────────────────────────── */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --rst-bg: #0d0c12;
    --rst-surface: #17161f;
    --rst-surface-2: #121119;
    --rst-line: #292733;
    --rst-line-strong: #6b6782;

    --rst-text: #ebe9f2;
    --rst-text-muted: #a5a0b8;
    --rst-text-faint: #928db0;

    --rst-accent: #b6a0ff;
    --rst-accent-strong: #cbbaff;
    --rst-accent-soft: #241c3d;
    --rst-on-accent: #1a1030;

    --rst-tone-neutral-fg: #bcb7cf;
    --rst-tone-neutral-bg: #2a2836;
    --rst-tone-positive-fg: #74dba8;
    --rst-tone-positive-bg: #16301f;
    --rst-tone-warning-fg: #eab86c;
    --rst-tone-warning-bg: #322512;
    --rst-tone-negative-fg: #f58c95;
    --rst-tone-negative-bg: #35191e;
  }
}

/* ── Colour: dark, chosen explicitly. Higher specificity than the
      media query above, so a toggle wins over the OS in both
      directions. ───────────────────────────────────────────────────── */
:root[data-theme="dark"] {
  color-scheme: dark;

  --rst-bg: #0d0c12;
  --rst-surface: #17161f;
  --rst-surface-2: #121119;
  --rst-line: #292733;
  --rst-line-strong: #6b6782;

  --rst-text: #ebe9f2;
  --rst-text-muted: #a5a0b8;
  --rst-text-faint: #928db0;

  --rst-accent: #b6a0ff;
  --rst-accent-strong: #cbbaff;
  --rst-accent-soft: #241c3d;
  --rst-on-accent: #1a1030;

  --rst-tone-neutral-fg: #bcb7cf;
  --rst-tone-neutral-bg: #2a2836;
  --rst-tone-positive-fg: #74dba8;
  --rst-tone-positive-bg: #16301f;
  --rst-tone-warning-fg: #eab86c;
  --rst-tone-warning-bg: #322512;
  --rst-tone-negative-fg: #f58c95;
  --rst-tone-negative-bg: #35191e;
}

/* ── Base. Three rules, so a freshly scaffolded app looks finished
      before it has any CSS of its own. Delete them if your app owns
      its own base styles. ──────────────────────────────────────────── */
body {
  margin: 0;
  background: var(--rst-bg);
  color: var(--rst-text);
  font-family: var(--rst-font);
  font-size: var(--rst-fs-base);
  line-height: 1.5;
}

/* The content column every screen sits in. Your page markup emits this;
   no partial does. */
.rst-page {
  box-sizing: border-box;
  margin: 0 auto;
  max-width: 52rem;
  padding: var(--rst-sp-5) var(--rst-sp-4) var(--rst-sp-6);
}

/* rastrillo.Icon emits class="icon" with no width or height, so the icon
   sizes from here and tracks the text beside it. */
.icon {
  block-size: 1em;
  flex: none;
  inline-size: 1em;
}

/* Focus is visible everywhere inside a rastrillo component. :where()
   contributes no specificity, so any rule below can override it. */
:where(.rst-page-header, .rst-list, .rst-lbar, .rst-search, .rst-empty, .rst-pagination) :focus-visible {
  outline: 2px solid var(--rst-accent);
  outline-offset: 2px;
}

/* Visually hidden, but in the DOM and the tab order — and it un-hides on
   keyboard focus, because a focused control a sighted keyboard user
   cannot see is a trap. */
.rst-sr-only {
  clip-path: inset(50%);
  block-size: 1px;
  inline-size: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}
.rst-sr-only:focus-visible {
  background: var(--rst-accent);
  border: 0;
  border-radius: var(--rst-radius-sm);
  block-size: auto;
  clip-path: none;
  color: var(--rst-on-accent);
  cursor: pointer;
  font: inherit;
  font-size: var(--rst-fs-sm);
  inline-size: auto;
  overflow: visible;
  padding: 0.3rem 0.6rem;
  position: static;
  white-space: normal;
}

/* ── Buttons: page-header's primary action, empty-state's CTA. ────── */
.rst-btn {
  align-items: center;
  background: var(--rst-surface);
  border: 1px solid var(--rst-line-strong);
  border-radius: var(--rst-radius-sm);
  color: var(--rst-text);
  cursor: pointer;
  display: inline-flex;
  font-family: inherit;
  font-size: var(--rst-fs-sm);
  font-weight: 600;
  gap: var(--rst-sp-2);
  line-height: 1.4;
  padding: 0.45rem 0.75rem;
  text-decoration: none;
}
.rst-btn:hover {
  border-color: var(--rst-accent);
  color: var(--rst-accent);
}
.rst-btn--primary {
  background: var(--rst-accent);
  border-color: var(--rst-accent);
  color: var(--rst-on-accent);
}
.rst-btn--primary:hover {
  background: var(--rst-accent-strong);
  border-color: var(--rst-accent-strong);
  color: var(--rst-on-accent);
}

/* ── page-header ──────────────────────────────────────────────────── */
.rst-page-header {
  align-items: baseline;
  border-bottom: 1px solid var(--rst-line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-3);
  justify-content: space-between;
  margin: 0 0 var(--rst-sp-5);
  padding-bottom: var(--rst-sp-3);
  position: relative;
}
/* The rake line: one short accent stroke laid over the header's rule.
   The library's one flourish — the tool it is named for, drawn once per
   screen and nowhere else. Absolutely positioned, so it is not a flex
   item. */
.rst-page-header::after {
  background: var(--rst-accent);
  block-size: 2px;
  content: "";
  inline-size: 2.5rem;
  inset-block-end: -1px;
  inset-inline-start: 0;
  position: absolute;
}
.rst-page-header__titles {
  flex: 1 1 auto;
  min-inline-size: 0;
}
.rst-page-header h1 {
  color: var(--rst-text);
  font-size: 1.375rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.25;
  margin: 0;
}
.rst-page-header__sub {
  color: var(--rst-text-muted);
  font-size: var(--rst-fs-sm);
  margin: var(--rst-sp-1) 0 0;
  max-inline-size: 44rem;
}

/* ── The list card, its toolbar, and its search field ─────────────── */
.rst-list {
  background: var(--rst-surface);
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius);
  overflow: hidden;
}

/* list-bar: the toolbar strip. The search field is capped at 20rem so
   the space to its right stays reserved — that is where the filter and
   sort controls attach when the dropdown slice lands. */
.rst-lbar {
  align-items: center;
  background: var(--rst-surface-2);
  border-bottom: 1px solid var(--rst-line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-3);
  padding: var(--rst-sp-2) var(--rst-sp-3);
}
.rst-lbar > .rst-search {
  flex: 0 1 20rem;
}

/* list-bar-search renders this form, which is the field box itself. Used
   on its own inside a .rst-list card it just needs margin; inside a
   .rst-lbar the strip supplies the padding. */
.rst-search {
  align-items: center;
  background: var(--rst-surface);
  border: 1px solid var(--rst-line-strong);
  border-radius: var(--rst-radius-sm);
  display: flex;
  gap: var(--rst-sp-2);
  margin: 0;
  padding: 0.3rem 0.55rem;
}
.rst-list > .rst-search {
  margin: var(--rst-sp-3);
}
.rst-search .icon {
  color: var(--rst-text-faint);
}
.rst-search input[type="search"] {
  background: transparent;
  border: 0;
  color: var(--rst-text);
  flex: 1;
  font-family: inherit;
  font-size: var(--rst-fs-sm);
  min-inline-size: 0;
  padding: 0.2rem 0;
}
.rst-search input[type="search"]::placeholder {
  color: var(--rst-text-faint);
}
/* The field box carries the focus indicator for the whole control, so
   the input's own default outline would only double it up. */
.rst-search input[type="search"]:focus-visible {
  outline: none;
}
.rst-search:focus-within {
  border-color: var(--rst-accent);
  outline: 2px solid var(--rst-accent);
  outline-offset: 1px;
}

/* ── list-row-action ──────────────────────────────────────────────── */
.rst-row {
  align-items: center;
  border-top: 1px solid var(--rst-line);
  display: flex;
  gap: var(--rst-sp-3);
  padding: var(--rst-sp-3);
  position: relative;
}
.rst-row:first-child {
  border-top: 0;
}
/* The toolbar strip already draws the line under itself. */
.rst-lbar + .rst-row {
  border-top: 0;
}
.rst-row:hover {
  background: var(--rst-surface-2);
}
.rst-row__lead {
  align-items: center;
  background: var(--rst-tone-neutral-bg);
  block-size: 1.9rem;
  border-radius: 50%;
  color: var(--rst-tone-neutral-fg);
  display: flex;
  flex: none;
  font-size: var(--rst-fs-xs);
  font-weight: 600;
  inline-size: 1.9rem;
  justify-content: center;
}
.rst-row__lead[data-lead="positive"] {
  background: var(--rst-tone-positive-bg);
  color: var(--rst-tone-positive-fg);
}
.rst-row__lead[data-lead="warning"] {
  background: var(--rst-tone-warning-bg);
  color: var(--rst-tone-warning-fg);
}
.rst-row__lead[data-lead="negative"] {
  background: var(--rst-tone-negative-bg);
  color: var(--rst-tone-negative-fg);
}
.rst-row__lead[data-lead="accent"] {
  background: var(--rst-accent-soft);
  color: var(--rst-accent);
}
.rst-row__main {
  flex: 1;
  min-inline-size: 0;
}
/* The name link's overlay makes the whole row clickable without nesting
   one anchor inside another. The action pill sits above it. */
.rst-row__main > a {
  color: var(--rst-text);
  font-weight: 600;
  text-decoration: none;
}
.rst-row__main > a::after {
  content: "";
  inset: 0;
  position: absolute;
}
.rst-row__main > a:hover {
  text-decoration: underline;
}
.rst-row__sub {
  color: var(--rst-text-muted);
  display: block;
  font-size: var(--rst-fs-sm);
  margin-block-start: 0.1rem;
}
.rst-row__action {
  background: var(--rst-surface);
  border: 1px solid var(--rst-line-strong);
  border-radius: var(--rst-radius-pill);
  color: var(--rst-text-muted);
  flex: none;
  font-size: var(--rst-fs-sm);
  padding: 0.2rem 0.7rem;
  position: relative;
  text-decoration: none;
  z-index: 1;
}
.rst-row__action:hover {
  border-color: var(--rst-accent);
  color: var(--rst-accent);
}

/* ── status-pill. The dot is decoration; the label is the state. ──── */
.rst-status {
  align-items: center;
  background: var(--rst-tone-neutral-bg);
  border-radius: var(--rst-radius-pill);
  color: var(--rst-tone-neutral-fg);
  display: inline-flex;
  font-size: var(--rst-fs-xs);
  font-weight: 600;
  gap: 0.35rem;
  line-height: 1.6;
  padding: 0.15rem 0.55rem;
}
.rst-status::before {
  background: currentColor;
  block-size: 0.375rem;
  border-radius: 50%;
  content: "";
  flex: none;
  inline-size: 0.375rem;
}
.rst-status[data-tone="positive"] {
  background: var(--rst-tone-positive-bg);
  color: var(--rst-tone-positive-fg);
}
.rst-status[data-tone="warning"] {
  background: var(--rst-tone-warning-bg);
  color: var(--rst-tone-warning-fg);
}
.rst-status[data-tone="negative"] {
  background: var(--rst-tone-negative-bg);
  color: var(--rst-tone-negative-fg);
}

/* ── empty-state ──────────────────────────────────────────────────── */
.rst-empty {
  background: var(--rst-surface);
  border: 1px dashed var(--rst-line-strong);
  border-radius: var(--rst-radius);
  padding: var(--rst-sp-6) var(--rst-sp-4);
  text-align: center;
}
/* Class-based, so it styles the partial's <h2> without inheriting any
   <h2> default: an empty state's heading is a real heading in the
   document outline, not a paragraph dressed up as one. */
.rst-empty__title {
  color: var(--rst-text);
  font-size: 1.0625rem;
  font-weight: 600;
  margin: 0 0 var(--rst-sp-2);
}
.rst-empty__body {
  color: var(--rst-text-muted);
  margin: 0 auto;
  max-inline-size: 34rem;
}
.rst-empty__cta {
  margin: var(--rst-sp-4) 0 0;
}

/* ── pagination. The current page is a filled chip AND aria-current AND
      bolder — three signals, so it never rests on colour alone. ────── */
.rst-pagination {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
  gap: var(--rst-sp-1);
  margin: var(--rst-sp-4) 0;
}
.rst-pagination a,
.rst-pagination span {
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius-sm);
  color: var(--rst-text-muted);
  display: inline-block;
  font-size: var(--rst-fs-sm);
  min-inline-size: 2rem;
  padding: 0.3rem 0.6rem;
  text-align: center;
  text-decoration: none;
}
.rst-pagination a:hover {
  background: var(--rst-accent-soft);
  border-color: var(--rst-accent);
  color: var(--rst-text);
}
.rst-pagination [aria-current="page"] {
  background: var(--rst-accent);
  border-color: var(--rst-accent);
  color: var(--rst-on-accent);
  font-weight: 600;
}
.rst-pagination [aria-disabled="true"] {
  border-style: dashed;
  color: var(--rst-text-faint);
}
.rst-pagination .rst-pagination__gap {
  border-color: transparent;
  min-inline-size: 0;
  padding-inline: var(--rst-sp-1);
}

/* ── Narrow screens ───────────────────────────────────────────────── */
@media (max-width: 34rem) {
  .rst-lbar > .rst-search {
    flex: 1 1 100%;
  }
  .rst-row {
    align-items: flex-start;
    flex-wrap: wrap;
  }
  .rst-page-header h1 {
    font-size: 1.25rem;
  }
}
