/* ============================================================================
   Little Reads admin — the rebuilt admin's stylesheet.

   ONE NAMED BLOCK PER COMPONENT. Every treatment for a component lives in its
   own block and nowhere else, so a finding at the deploy gate is a one-place
   change. A block is added ONLY by a translation spec, alongside its Blade
   component and its row in the law's map.

   EVERY BLOCK BELOW IS A VERBATIM TRANSPLANT of its component's own
   injectStyles(...) literal in the design system. It is copied, never
   authored. Re-writing CSS "on the design system's recipes" is what produced
   the scrapped hub. To change a component's look, change the ratified design
   and re-transplant - never edit the block here.

   Rules this file is held to (mechanically enforced by
   tests/Feature/DesignSystemContractTest.php):
     · Every colour is a token. No raw hex, ever.
     · rgba() IS allowed, and must stay allowed: the token set has no
       alpha-composition primitive, so the design system writes every alpha
       composite as a brand colour at reduced opacity - rgba(100,120,165,0.18)
       is the action blue at 18%. ds-tokens.css defines its own shadow tokens
       exactly this way.
     · CTA orange may be DEFINED here - the button's cta role and the cover's
       owned premium crown are ratified CSS - but no admin surface may SELECT
       it. The gate is on the selection, not on the token.
     · Mint is status and "you are here" only - never a click affordance.
   ============================================================================ */

* { box-sizing: border-box; }

/* THE TRANSLATION LAYER'S ONE BASE RULE BEYOND box-sizing. React mounts panels/menus/
   drawers only while open; these translations keep them in the DOM with the `hidden`
   attribute carrying the closed state. An authored `display` on the same element beats
   the UA's [hidden]{display:none} — so hidden must win explicitly, or a transplanted
   `display: flex` renders a "closed" panel permanently (the 12 Aug 2026 DateRangePicker
   walk finding). Never remove; a component wanting a hidden-but-rendered state opts out
   deliberately. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--surface-cell);
  color: var(--colour-text);
  font-family: var(--font-sans);
  font-weight: var(--font-weight-regular);
}

/* --- button --- transplanted VERBATIM from components/core/Button.jsx
   (injectStyles('lr-button', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-family-base);
  font-weight: var(--font-weight-bold);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.lr-btn:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-btn:active { transform: scale(var(--press-scale)); }
.lr-btn[disabled] { cursor: not-allowed; transform: none; }

/* Sizes */
.lr-btn--sm { font-size: 14px; line-height: 20px; padding: 8px 16px; min-height: 36px; }
.lr-btn--md { font-size: 16px; line-height: 24px; padding: 12px 24px; min-height: 48px; }
.lr-btn--lg { font-size: 18px; line-height: 24px; padding: 16px 32px; min-height: 56px; }

/* CTA — commitment threshold (orange, white label) */
.lr-btn--cta { background: var(--colour-cta); color: var(--text-on-cta); }
.lr-btn--cta:hover:not([disabled]) { background: var(--colour-cta-hover); }
.lr-btn--cta[disabled] { background: var(--surface-tint-info); color: var(--text-disabled); }

/* Action — no-commitment action inside the product (blue, white label) */
.lr-btn--action { background: var(--colour-action); color: var(--text-on-action); }
.lr-btn--action:hover:not([disabled]) { background: var(--colour-action-hover); }
.lr-btn--action[disabled] { background: var(--surface-tint-info); color: var(--text-disabled); }

/* Secondary — quiet outline on surface */
.lr-btn--secondary { background: var(--surface-white); color: var(--colour-text); border-color: var(--border-subtle); }
.lr-btn--secondary:hover:not([disabled]) { background: var(--surface-cell); border-color: var(--colour-action); color: var(--colour-action); }
.lr-btn--secondary[disabled] { color: var(--text-disabled); border-color: var(--border-subtle); }

.lr-btn--block { display: flex; width: 100%; }

/* --- icon-button --- transplanted VERBATIM from components/core/IconButton.jsx
   (injectStyles('lr-iconbutton', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--colour-text);
  cursor: pointer;
  transition: background-color var(--motion-hover) var(--ease-out),
              color var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.lr-iconbtn:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-iconbtn:active { transform: scale(var(--press-scale)); }
.lr-iconbtn[disabled] { cursor: not-allowed; color: var(--text-disabled); transform: none; }

.lr-iconbtn--sm { width: 36px; height: 36px; }
.lr-iconbtn--md { width: 44px; height: 44px; }
.lr-iconbtn--lg { width: 48px; height: 48px; }

/* Plain — quiet, on any surface */
.lr-iconbtn--plain:hover:not([disabled]) { background: var(--surface-tint-info); }
/* Filled tint — subtle mint chip */
.lr-iconbtn--tint { background: var(--surface-tint-info); }
.lr-iconbtn--tint:hover:not([disabled]) { background: var(--border-subtle); }
/* Action — blue solid */
.lr-iconbtn--action { background: var(--colour-action); color: var(--surface-white); }
.lr-iconbtn--action:hover:not([disabled]) { background: var(--colour-action-hover); }

/* --- card --- transplanted VERBATIM from components/core/Card.jsx
   (injectStyles('lr-card', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-card {
  background: var(--surface-card);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  border: 1px solid var(--border-subtle);
  color: var(--colour-text);
}
.lr-card--flat { box-shadow: none; }
.lr-card--interactive { cursor: pointer; transition: box-shadow var(--motion-hover) var(--ease-out), transform var(--motion-hover) var(--ease-out); }
.lr-card--interactive:hover { box-shadow: var(--shadow-2); transform: translateY(-2px); }
.lr-card--interactive:active { transform: translateY(0); }
.lr-card--interactive:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* --- tag --- transplanted VERBATIM from components/core/Tag.jsx
   (injectStyles('lr-tag', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-family-base);
  font-weight: var(--font-weight-bold);
  font-size: 14px; line-height: 20px;
  padding: 6px 14px;
  border-radius: var(--radius-md);
  background: var(--surface-tint-info);
  color: var(--colour-text);
  border: 1px solid transparent;
  -webkit-tap-highlight-color: transparent;
}
.lr-tag--button { cursor: pointer; transition: background-color var(--motion-hover) var(--ease-out), border-color var(--motion-hover) var(--ease-out), color var(--motion-hover) var(--ease-out); }
.lr-tag--button:hover { background: var(--border-subtle); }
.lr-tag--button:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-tag--selected { background: var(--colour-informational); color: var(--surface-white); }
/* tone="reading" / tone="recommended" (4 Jul 2026): the additional STATUS colours — the "Reading"
   chip and the "Recommended" marker chip — ALL status chips are mint (FINAL 4 Jul
   evening), differentiated by icon + copy; the tones stay separate for future re-assignment. Each applies
   to the SELECTED fill only; rest state is unchanged. Status only, never a click affordance. */
.lr-tag--selected.lr-tag--reading { background: var(--colour-reading); }
.lr-tag--selected.lr-tag--reading.lr-tag--button:hover { background: var(--colour-reading); filter: brightness(0.95); }
.lr-tag--selected.lr-tag--recommended { background: var(--colour-recommended); }
.lr-tag--selected.lr-tag--recommended.lr-tag--button:hover { background: var(--colour-recommended); filter: brightness(0.95); }
.lr-tag--selected.lr-tag--button:hover { background: var(--colour-informational); filter: brightness(0.95); }
.lr-tag--sm { font-size: 13px; line-height: 18px; padding: 4px 10px; }

/* --- search-field --- transplanted VERBATIM from components/forms/SearchField.jsx
   (injectStyles('lr-searchfield', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-search { display: flex; align-items: center; gap: var(--space-2);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: 0 var(--space-4); min-height: 48px;
  color: var(--colour-text); transition: border-color var(--motion-hover) var(--ease-out), box-shadow var(--motion-hover) var(--ease-out); }
.lr-search:focus-within { border-color: var(--colour-action); box-shadow: 0 0 0 3px rgba(100,120,165,0.18); }
.lr-search__icon { color: var(--text-secondary); flex: none; }
.lr-search__input { flex: 1; border: none; outline: none; background: transparent;
  font-family: var(--font-family-base); font-size: 16px; line-height: 26px; color: var(--colour-text); padding: 11px 0; min-width: 0; }
.lr-search__input::placeholder { color: var(--text-disabled); }
.lr-search__clear { display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border: none; background: var(--surface-tint-info); color: var(--colour-text);
  border-radius: var(--radius-md); cursor: pointer; flex: none; transition: background-color var(--motion-hover) var(--ease-out); }
.lr-search__clear:hover { background: var(--border-subtle); }
.lr-search__clear:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* --- select --- transplanted VERBATIM from components/forms/Select.jsx
   (injectStyles('lr-select', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-select { position: relative; font-family: var(--font-family-base); display: inline-block; }
.lr-select--block { display: block; }
/* buttons don't inherit font — without this the trigger is the one Arial element in the admin
   (walk-found 12 Aug 2026). Declared first so the size/height below still win. */
.lr-select__trigger { font: inherit; display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; box-sizing: border-box; background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: 11px var(--space-4); min-height: 48px; cursor: pointer;
  font-size: 16px; line-height: 24px; color: var(--colour-text); text-align: left;
  transition: border-color var(--motion-hover) var(--ease-out), box-shadow var(--motion-hover) var(--ease-out); }
.lr-select__trigger:hover { border-color: var(--colour-action); }
.lr-select__trigger:focus-visible { outline: none; border-color: var(--colour-action); box-shadow: 0 0 0 3px rgba(100,120,165,0.18); }
.lr-select__trigger[aria-expanded="true"] { border-color: var(--colour-action); }
.lr-select__placeholder { color: var(--text-disabled); }
.lr-select__chev { color: var(--text-secondary); transition: transform var(--motion-hover) var(--ease-out); flex: none; }
.lr-select__trigger[aria-expanded="true"] .lr-select__chev { transform: rotate(180deg); }
.lr-select__menu { position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  box-shadow: var(--shadow-2); padding: var(--space-1); max-height: 280px; overflow: auto; }
.lr-select__opt { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: 10px var(--space-3); border-radius: var(--radius-md); cursor: pointer; font-size: 16px; line-height: 24px; color: var(--colour-text); }
.lr-select__opt:hover { background: var(--surface-cell); }
.lr-select__opt--sel { color: var(--colour-action); font-weight: var(--font-weight-bold); }
.lr-select__opt--sel .lr-select__check { color: var(--colour-action); }

/* --- text-field --- transplanted VERBATIM from components/forms/TextField.jsx
   (injectStyles('lr-textfield', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-field { display: flex; flex-direction: column; gap: var(--space-2); font-family: var(--font-family-base); }
.lr-field__label { font-size: 14px; line-height: 20px; font-weight: var(--font-weight-bold); color: var(--colour-text); }
.lr-field__opt { font-weight: var(--font-weight-regular); color: var(--text-secondary); }
.lr-field__control { font-family: var(--font-family-base); font-size: 16px; line-height: 26px; color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  padding: 11px var(--space-4); outline: none; width: 100%; box-sizing: border-box;
  transition: border-color var(--motion-hover) var(--ease-out), box-shadow var(--motion-hover) var(--ease-out); }
textarea.lr-field__control { min-height: 96px; resize: vertical; }
.lr-field__control::placeholder { color: var(--text-disabled); }
.lr-field__control:focus { border-color: var(--colour-action); box-shadow: 0 0 0 3px rgba(100,120,165,0.18); }
.lr-field__control:disabled { background: var(--surface-cell); color: var(--text-disabled); cursor: not-allowed; }
.lr-field--error .lr-field__control { border-color: var(--colour-error); }
.lr-field--error .lr-field__control:focus { box-shadow: 0 0 0 3px rgba(200,85,61,0.16); }
.lr-field__msg { font-size: 14px; line-height: 20px; }
.lr-field__msg--error { color: var(--colour-error); }
.lr-field__msg--hint { color: var(--text-secondary); }

/* --- book-picker --- transplanted VERBATIM from components/forms/BookPicker.jsx
   (injectStyles('lr-bookpicker', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
/* ── shell: same anatomy as TextField (label · control · message) so it stacks
      in a column of form controls without reading as a different product ── */
.lr-bp { display: flex; flex-direction: column; gap: var(--space-2); font-family: var(--font-family-base); }
.lr-bp__label { font-size: 14px; line-height: 20px; font-weight: var(--font-weight-bold); color: var(--colour-text); }
.lr-bp__opt { font-weight: var(--font-weight-regular); color: var(--text-secondary); }
.lr-bp__anchor { position: relative; }
.lr-bp__msg { font-size: 14px; line-height: 20px; }
.lr-bp__msg--error { color: var(--colour-error); }
.lr-bp__msg--hint { color: var(--text-secondary); }

/* ── the search face (SearchField idiom: leading glyph, clear button, focus glow) ── */
.lr-bp__combo { display: flex; align-items: center; gap: var(--space-2);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: 0 var(--space-4); min-height: 48px; box-sizing: border-box;
  transition: border-color var(--motion-hover) var(--ease-out), box-shadow var(--motion-hover) var(--ease-out); }
.lr-bp__combo:focus-within { border-color: var(--colour-action); box-shadow: 0 0 0 3px rgba(100,120,165,0.18); }
.lr-bp__icon { color: var(--text-secondary); flex: none; }
/* inputs don't inherit the page font either — declare it, per the Select lesson */
.lr-bp__input { flex: 1; min-width: 0; border: none; outline: none; background: transparent;
  font-family: var(--font-family-base); font-size: 16px; line-height: 26px; color: var(--colour-text); padding: 11px 0; }
.lr-bp__input::placeholder { color: var(--text-disabled); }
.lr-bp__clear { display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none; border: none; cursor: pointer;
  background: var(--surface-tint-info); color: var(--colour-text); border-radius: var(--radius-md);
  transition: background-color var(--motion-hover) var(--ease-out); }
.lr-bp__clear:hover { background: var(--border-subtle); }
.lr-bp__clear:active { transform: scale(var(--press-scale)); }
.lr-bp__clear:focus-visible { outline: none; box-shadow: var(--ring-focus); }

.lr-bp--error .lr-bp__combo, .lr-bp--error .lr-bp__chosen { border-color: var(--colour-error); }
.lr-bp--error .lr-bp__combo:focus-within { box-shadow: 0 0 0 3px rgba(200,85,61,0.16); }
.lr-bp--disabled .lr-bp__combo, .lr-bp--disabled .lr-bp__chosen { background: var(--surface-cell); }
.lr-bp--disabled .lr-bp__input { color: var(--text-disabled); cursor: not-allowed; }
.lr-bp--disabled .lr-bp__title, .lr-bp--disabled .lr-bp__author { color: var(--text-disabled); }

/* ── the popover ── */
.lr-bp__pop { position: absolute; z-index: 30; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-2); overflow: hidden; }
.lr-bp__list { list-style: none; margin: 0; padding: var(--space-1);
  max-height: 336px; overflow-y: auto; overscroll-behavior: contain; }
.lr-bp__list:empty { padding: 0; }
.lr-bp__head { font-size: 13px; line-height: 20px; font-weight: var(--font-weight-bold);
  color: var(--text-secondary); padding: var(--space-2) var(--space-3) var(--space-1); }

/* a result row. Rows are NOT fixed-height: the title wraps, because in a series the
   words that tell two books apart come last — exactly where truncation cuts. */
.lr-bp__row { display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-2); border-radius: var(--radius-md); cursor: pointer;
  min-height: var(--touch-min); box-sizing: border-box;
  transition: background-color var(--motion-hover) var(--ease-out); }
.lr-bp__row:hover { background: var(--surface-cell); }
.lr-bp__row[data-active="true"] { background: var(--surface-cell); box-shadow: inset 0 0 0 2px var(--colour-action); }
.lr-bp__cover { flex: none; }
.lr-bp__body { flex: 1; min-width: 0; }
.lr-bp__title { font-size: 16px; line-height: 24px; font-weight: var(--font-weight-bold); color: var(--colour-text);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; overflow-wrap: anywhere; }
.lr-bp__author { font-size: 14px; line-height: 20px; color: var(--text-secondary);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }
.lr-bp__meta { font-size: 13px; line-height: 20px; color: var(--text-secondary); }
.lr-bp__mark { background: var(--surface-tint-info); color: inherit;
  border-radius: var(--radius-md); -webkit-box-decoration-break: clone; box-decoration-break: clone; }
.lr-bp__check { color: var(--colour-action); flex: none; margin-top: var(--space-1); }

/* the honest "there are more" line — never a silent cut-off */
.lr-bp__note { padding: var(--space-3) var(--space-4); font-size: 14px; line-height: 20px; color: var(--text-secondary); }
.lr-bp__note--after { border-top: 1px solid var(--border-subtle); }
.lr-bp__note b { color: var(--colour-text); font-weight: var(--font-weight-bold); }
.lr-bp__retry { font: inherit; font-size: 14px; line-height: 20px; font-weight: var(--font-weight-bold);
  margin-top: var(--space-1); display: inline-block; background: none; border: none; padding: 0;
  color: var(--colour-action); text-decoration: underline; cursor: pointer; }
.lr-bp__retry:hover { color: var(--colour-action-hover); }
.lr-bp__retry:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-md); }

/* searching — the gentle skeleton pulse is the one sanctioned loop */
.lr-bp__sk-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-2); }
.lr-bp__sk { background: var(--surface-cell); border-radius: var(--radius-md);
  animation: lr-bp-pulse 1400ms var(--ease-standard) infinite; }
.lr-bp__sk--cover { width: 40px; height: 60px; flex: none; }
.lr-bp__sk--lines { flex: 1; display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-1); }
.lr-bp__sk--title { height: 16px; }
.lr-bp__sk--author { height: 14px; width: 55%; }
@keyframes lr-bp-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) { .lr-bp__sk { animation: none; } }

/* ── the chosen face: the choice reads as a book, not as text in an input ── */
.lr-bp__chosen { display: flex; align-items: flex-start; gap: var(--space-3);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); padding: var(--space-3); box-sizing: border-box; }
.lr-bp__chosen .lr-bp__body { padding-top: var(--space-1); }
.lr-bp__actions { flex: none; display: flex; flex-direction: column; gap: var(--space-2); align-items: stretch; }
.lr-bp__change { font: inherit; font-size: 14px; line-height: 20px; font-weight: var(--font-weight-bold);
  min-height: var(--touch-min); padding: 0 var(--space-4); cursor: pointer;
  background: var(--surface-white); color: var(--colour-action);
  border: 1px solid var(--colour-action); border-radius: var(--radius-md);
  transition: background-color var(--motion-hover) var(--ease-out), color var(--motion-hover) var(--ease-out); }
.lr-bp__change:hover { background: var(--surface-tint-info); color: var(--colour-action-hover); }
.lr-bp__change:active { transform: scale(var(--press-scale)); }
.lr-bp__change:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-bp__remove { display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--touch-min); min-width: var(--touch-min); cursor: pointer;
  background: none; border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  color: var(--text-secondary); align-self: center;
  transition: background-color var(--motion-hover) var(--ease-out), color var(--motion-hover) var(--ease-out); }
.lr-bp__remove:hover { background: var(--surface-cell); color: var(--colour-text); }
.lr-bp__remove:active { transform: scale(var(--press-scale)); }
.lr-bp__remove:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-bp__actions--disabled { display: none; }

/* from 40rem there is room for the actions to sit side by side and for a taller list */
@media (min-width: 40rem) {
  .lr-bp__list { max-height: 400px; }
  .lr-bp__actions { flex-direction: row; align-items: center; }
  .lr-bp__remove { align-self: auto; }
}

/* --- list-row --- transplanted VERBATIM from components/content/ListRow.jsx
   (injectStyles('lr-listrow', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-row { display: flex; align-items: center; gap: var(--space-3); width: 100%; box-sizing: border-box;
  font-family: var(--font-family-base); text-align: left; background: var(--surface-white);
  border: none; padding: var(--space-3) var(--space-4); color: var(--colour-text); }
.lr-row--btn { cursor: pointer; transition: background-color var(--motion-hover) var(--ease-out); }
.lr-row--btn:hover { background: var(--surface-cell); }
.lr-row--btn:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-md); }
.lr-row__lead { flex: none; display: flex; align-items: center; color: var(--text-secondary); }
.lr-row__body { flex: 1; min-width: 0; }
.lr-row__title { display: block; font-size: 16px; line-height: 24px; font-weight: var(--font-weight-bold);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lr-row__sub { display: block; font-size: 14px; line-height: 20px; color: var(--text-secondary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lr-row__trail { flex: none; display: flex; align-items: center; gap: var(--space-2); color: var(--text-secondary); }
.lr-row__divider { border: none; border-top: 1px solid var(--border-subtle); margin: 0; }

/* --- section-header --- transplanted VERBATIM from components/content/SectionHeader.jsx
   (injectStyles('lr-sectionheader', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-sh { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4);
  font-family: var(--font-family-base); }
.lr-sh__text { min-width: 0; }
/* PHONE (⚖️ James, 17 Aug 2026 — ratified by appearance on Local): mobile-first — the title and
   its action WRAP at the phone default (the action drops under the title) and sit on one row
   from 768px, so a long title beside a wide action never crushes. */
.lr-sh { flex-wrap: wrap; }
@media (min-width: 768px) { .lr-sh { flex-wrap: nowrap; } }
.lr-sh__title { font-size: 18px; line-height: 24px; font-weight: var(--font-weight-bold); color: var(--colour-text); }
.lr-sh__sub { font-size: 14px; line-height: 20px; color: var(--text-secondary); margin-top: 2px; }
.lr-sh__link { display: inline-flex; align-items: center; gap: 4px; flex: none; border: none; background: none;
  cursor: pointer; font-family: var(--font-family-base); font-weight: var(--font-weight-bold);
  font-size: 14px; line-height: 20px; color: var(--colour-action); padding: 4px; border-radius: var(--radius-md);
  transition: color var(--motion-hover) var(--ease-out); }
.lr-sh__link:hover { color: var(--colour-action-hover); }
.lr-sh__link:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* --- empty-state --- transplanted VERBATIM from components/content/EmptyState.jsx
   (injectStyles('lr-emptystate', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-empty { display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--space-3); padding: var(--space-12) var(--space-6); font-family: var(--font-family-base);
  color: var(--colour-text); max-width: 420px; margin: 0 auto; }
.lr-empty__icon { display: flex; align-items: center; justify-content: center; width: 72px; height: 72px;
  border-radius: var(--radius-pill); background: var(--surface-tint-info); color: var(--colour-informational); margin-bottom: var(--space-1); }
.lr-empty__title { font-size: 22px; line-height: 30px; font-weight: var(--font-weight-bold); }
.lr-empty__body { font-size: 16px; line-height: 26px; color: var(--text-secondary); }
.lr-empty__action { margin-top: var(--space-2); }

/* --- skeleton --- transplanted VERBATIM from components/content/Skeleton.jsx
   (injectStyles('lr-skeleton', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
@keyframes lr-skel-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.55; } }
.lr-skel { background: var(--surface-tint-info); border-radius: var(--radius-md); display: block; }
@media (prefers-reduced-motion: no-preference) { .lr-skel { animation: lr-skel-pulse 1.4s var(--ease-standard) infinite; } }
.lr-skel--text { border-radius: var(--radius-pill); }
.lr-skel--cover { aspect-ratio: 2 / 3; border-radius: var(--radius-md); width: 100%; }
.lr-skel--circle { border-radius: var(--radius-pill); }

/* --- book-cover --- transplanted VERBATIM from components/content/BookCover.jsx
   (injectStyles('lr-bookcover', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-cover { display: inline-block; font-family: var(--font-family-base);
  border: none; background: none; padding: 0; text-align: left; container-type: inline-size; }
.lr-cover--btn { cursor: pointer; }
.lr-cover__frame { position: relative; border-radius: var(--radius-md); overflow: hidden;
  box-shadow: var(--shadow-1); background: var(--surface-tint-info); aspect-ratio: 2 / 3; }
.lr-cover--btn:focus-visible .lr-cover__frame { outline: none; box-shadow: var(--ring-focus); }
.lr-cover--btn:hover .lr-cover__frame { box-shadow: var(--shadow-2); }
.lr-cover__frame, .lr-cover__img { transition: box-shadow var(--motion-hover) var(--ease-out), transform var(--motion-hover) var(--ease-out); }
.lr-cover--btn:hover .lr-cover__img { transform: scale(1.03); }
.lr-cover__img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lr-cover__ph { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  padding: var(--space-3); text-align: center; color: var(--colour-text); font-weight: var(--font-weight-bold); font-size: 14px; }
/* Owned premium crown — bottom-right, 34% of the cover width (LAW 3). The crown recipe traces to
   core/Badge.jsx (tone=cta + crown icon); BookCover owns its size + placement so it is identical on
   every cover. Callers never supply, re-position, or re-size it — they pass only `premium`. */
.lr-cover__crown { position: absolute; right: 5%; bottom: 5%; width: 34%; aspect-ratio: 1 / 1;
  display: flex; align-items: center; justify-content: center;
  background: var(--colour-cta); color: var(--surface-white); border-radius: var(--radius-md); }
.lr-cover__crown > svg { width: 62%; height: 62%; }
/* The RECOMMENDED state (Job 4, ratified 4 Jul 2026 — the ONE sanctioned extension to the crown-only
   contract). Chip ABOVE the cover (never on the art): solid Informational mint + white bold label +
   the FILLED signature star. COPY (corrected 4 Jul, James — the full phrase was illegibly small on a
   phone tile): the chip reads "Recommended" — the SAME copy as the rail's heading chip
   (one chip element, one copy, everywhere). Text is WIDTH-PROPORTIONAL
   (10cqw — the shorter copy allows a larger fraction; must fit one line on a 3-column grid tile;
   a derived fraction of tile width, never a scale token). The ghost twin reserves the identical
   height on unbadged tiles so grid rows share one cover baseline. */
.lr-cover__recchip { width: 100%; box-sizing: border-box; display: flex; align-items: center;
  justify-content: center; gap: 0.3em; background: var(--colour-recommended);
  color: var(--surface-white); font-weight: var(--font-weight-bold); font-size: 10cqw;
  line-height: 1.3; padding: 0.34em 0.4em; border-radius: var(--radius-md);
  box-shadow: var(--shadow-1); white-space: nowrap; overflow: hidden;
  margin-bottom: var(--space-2); }
.lr-cover__recchip--ghost { visibility: hidden; background: none; box-shadow: none; }
/* The recommended FRAME — the COVER-level marker treatment (corrected 4 Jul: the RecommendedRail
   row no longer carries a mint frame; the shared signature element is THE CHIP): 2px
   Informational-mint border + md radius + surface-white mat + elevation-2 + a −2px raise (the
   feature-card exception), with a --space-1 gutter to the cover.
   GEOMETRY (corrected 4 Jul, James's walk — "the cover shrank"): the frame draws OUTWARD beyond the
   tile bounds (negative margin = border + mat), so the COVER stays exactly neighbour-sized and the
   frame floats into the grid gutters. Native translations draw the mat/ring outside the art bounds
   with no layout participation. */
.lr-cover__recframe { border: 2px solid var(--colour-recommended); border-radius: var(--radius-md);
  padding: var(--space-1); background: var(--surface-white); box-shadow: var(--shadow-2);
  transform: translateY(-2px); margin: calc(-1 * (var(--space-1) + 2px)); }

/* --- dialog --- transplanted VERBATIM from components/feedback/Dialog.jsx
   (injectStyles('lr-dialog', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
@keyframes lr-dialog-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes lr-overlay-in { from { opacity: 0; } to { opacity: 1; } }
.lr-overlay { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center;
  padding: var(--space-6); background: rgba(0,52,48,0.32); }
@media (prefers-reduced-motion: no-preference) { .lr-overlay { animation: lr-overlay-in var(--motion-overlay) var(--ease-out); } }
.lr-dialog { width: 100%; max-width: 420px; background: var(--surface-white); border-radius: var(--radius-md);
  box-shadow: var(--shadow-3); padding: var(--space-6); font-family: var(--font-family-base); color: var(--colour-text);
  position: relative; }
@media (prefers-reduced-motion: no-preference) { .lr-dialog { animation: lr-dialog-in var(--motion-overlay) var(--ease-out); } }
.lr-dialog__close { position: absolute; top: var(--space-3); right: var(--space-3); }
.lr-dialog__title { font-size: 24px; line-height: 32px; font-weight: var(--font-weight-bold); padding-right: var(--space-8); }
.lr-dialog__body { font-size: 16px; line-height: 26px; color: var(--text-secondary); margin-top: var(--space-3); }
.lr-dialog__actions { display: flex; gap: var(--space-3); margin-top: var(--space-6); }
.lr-dialog__actions > * { flex: 1; }

/* --- toast --- transplanted VERBATIM from components/feedback/Toast.jsx
   (injectStyles('lr-toast', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-toast { display: flex; align-items: flex-start; gap: var(--space-3); font-family: var(--font-family-base);
  background: var(--colour-text); color: var(--surface-white); border-radius: var(--radius-md); box-shadow: var(--shadow-3);
  padding: var(--space-3) var(--space-4); max-width: 440px; }
.lr-toast--info { background: var(--colour-text); }
.lr-toast--success .lr-toast__icon { color: var(--colour-informational); }
.lr-toast--error { background: var(--colour-error); }
.lr-toast__icon { flex: none; margin-top: 1px; }
.lr-toast__body { flex: 1; min-width: 0; font-size: 15px; line-height: 22px; }
.lr-toast__close { flex: none; border: none; background: none; color: inherit; opacity: 0.75; cursor: pointer;
  padding: 2px; border-radius: var(--radius-md); display: inline-flex; transition: opacity var(--motion-hover) var(--ease-out); }
.lr-toast__close:hover { opacity: 1; }
.lr-toast__close:focus-visible { outline: none; box-shadow: 0 0 0 2px rgba(255,255,255,0.7); }

/* Inline error variant — quiet, on-page (not a floating toast) */
.lr-inline-error { display: flex; align-items: flex-start; gap: var(--space-2); font-family: var(--font-family-base);
  color: var(--colour-error); font-size: 14px; line-height: 20px; }
.lr-inline-error > svg { flex: none; margin-top: 1px; }

/* --- tab-bar --- transplanted VERBATIM from components/navigation/TabBar.jsx
   (injectStyles('lr-tabbar', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-tabbar { display: flex; align-items: stretch; background: var(--surface-white);
  border-top: 1px solid var(--border-subtle); padding-bottom: env(safe-area-inset-bottom, 0px);
  font-family: var(--font-family-base); }
.lr-tab { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: none; background: none; cursor: pointer; padding: 8px 4px 10px; min-height: 56px;
  color: var(--text-secondary); transition: color var(--motion-hover) var(--ease-out); }
.lr-tab:hover { color: var(--colour-text); }
.lr-tab:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-md); }
.lr-tab__label { font-size: 11px; line-height: 14px; font-weight: var(--font-weight-bold); }
.lr-tab--active { color: var(--colour-informational); }
.lr-tab--active:hover { color: var(--colour-informational); }

/* --- sub-nav --- transplanted VERBATIM from components/navigation/SubNav.jsx
   (injectStyles('lr-subnav', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-subnav { display: flex; gap: var(--space-2); padding: 0 var(--space-4);
  border-bottom: 1px solid var(--border-subtle); background: var(--surface-white);
  overflow-x: auto; font-family: var(--font-family-base); }
.lr-subnav__item { display: flex; align-items: center; white-space: nowrap;
  padding: var(--space-3); min-height: var(--touch-min);
  font-size: var(--font-body-size); line-height: var(--font-body-line);
  color: var(--colour-text); text-decoration: none;
  transition: color var(--motion-hover) var(--ease-out); }
/* Hover is an AFFORDANCE cue, so it is action blue. */
.lr-subnav__item:hover { color: var(--colour-action); }
.lr-subnav__item:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-md); }
/* Current is "YOU ARE HERE" — a status, so it is mint (FINAL, Hannah + James, 4 Jul 2026).
   TabBar's active item already uses mint for the same concept in the app. */
.lr-subnav__item--current { font-weight: var(--font-weight-bold); color: var(--colour-informational);
  box-shadow: inset 0 -2px 0 var(--colour-informational); }
/* Both states at once: the ring must survive the current-item underline. */
.lr-subnav__item--current:focus-visible { box-shadow: var(--ring-focus), inset 0 -2px 0 var(--colour-informational); }

/* --- admin-nav --- transplanted VERBATIM from components/navigation/AdminNav.jsx
   (injectStyles('lr-adminnav', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
/* Mobile-first: default rules are the phone; min-width queries scale up.
   Breakpoint 768px — the same one the type scale switches at (Marketing §3). */
.lr-adminnav, .lr-adminnav *, .lr-adminnav *::before, .lr-adminnav *::after { box-sizing: border-box; }
.lr-adminnav { position: relative; z-index: 20; background: var(--surface-white);
  border-bottom: 1px solid var(--border-subtle);
  font-family: var(--font-family-base); color: var(--colour-text); }
.lr-adminnav--sticky { position: sticky; top: 0; }
.lr-adminnav__bar { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); min-height: 56px; padding: var(--space-1) var(--space-4); }

/* Logo lock-up — the admin's landing link. Rendered at bar height. */
.lr-adminnav__logo { display: flex; align-items: center; flex: none;
  border-radius: var(--radius-md); color: var(--colour-text); text-decoration: none; }
.lr-adminnav__logo:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-adminnav__logo img, .lr-adminnav__logo svg { display: block; height: 28px; width: auto; }

/* Hamburger — three bars in a small tinted square, the website's phone control.
   44px so it clears the touch minimum. Hover is action blue: an affordance cue. */
.lr-adminnav__toggle { display: flex; align-items: center; justify-content: center; flex: none;
  width: var(--touch-min); height: var(--touch-min); padding: 0; border: 0;
  border-radius: var(--radius-md); background: var(--surface-cell);
  color: var(--colour-text); cursor: pointer;
  transition: color var(--motion-hover) var(--ease-out),
              background-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out); }
.lr-adminnav__toggle:hover { color: var(--colour-action); background: var(--surface-tint-info); }
.lr-adminnav__toggle:active { transform: scale(var(--press-scale)); }
.lr-adminnav__toggle:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* The drawer: a full-width panel under the bar (the website's shape), absolutely
   positioned so opening it never shifts the page underneath. Elevation 2 — it is a menu. */
.lr-adminnav__menu { display: none; position: absolute; top: 100%; left: 0; right: 0;
  flex-direction: column; padding: var(--space-2) var(--space-4) var(--space-4);
  background: var(--surface-white); border-bottom: 1px solid var(--border-subtle);
  box-shadow: var(--shadow-2); }
.lr-adminnav__menu[data-open="true"] { display: flex;
  animation: lr-adminnav-open var(--motion-overlay) var(--ease-out); }
@keyframes lr-adminnav-open { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* Items are real links. Rest = text; hover/focus = action blue; current = mint. */
.lr-adminnav__item { display: flex; align-items: center; min-height: var(--touch-min);
  padding: var(--space-2) 0; border-radius: var(--radius-md);
  font-size: var(--font-body-size); line-height: var(--font-body-line);
  color: var(--colour-text); text-decoration: none; white-space: nowrap;
  transition: color var(--motion-hover) var(--ease-out); }
.lr-adminnav__item:hover { color: var(--colour-action); }
.lr-adminnav__item:focus-visible { outline: none; box-shadow: var(--ring-focus); }
/* "You are here" is a STATUS, so it is mint — never a click cue. Matches SubNav + TabBar. */
.lr-adminnav__item--current { font-weight: var(--font-weight-bold); color: var(--colour-informational); }
/* Log out is last and quieter: secondary text, hairline above it on the phone. */
.lr-adminnav__item--trailing { margin-top: var(--space-2); padding-top: var(--space-2);
  border-top: 1px solid var(--border-subtle); color: var(--text-secondary); }

@media (min-width: 768px) {
  .lr-adminnav__bar { min-height: 64px; padding: var(--space-2) var(--space-6); }
  .lr-adminnav__logo img, .lr-adminnav__logo svg { height: 32px; }
  .lr-adminnav__toggle { display: none; }
  .lr-adminnav__menu, .lr-adminnav__menu[data-open="true"] { display: flex; position: static;
    flex-direction: row; align-items: center; gap: var(--space-6);
    padding: 0; background: transparent; border-bottom: 0; box-shadow: none; animation: none; }
  .lr-adminnav__item { min-height: 0; padding: var(--space-1) 0; }
  .lr-adminnav__item--trailing { margin: 0 0 0 var(--space-2); padding: var(--space-1) 0; border-top: 0; }
}

/* --- data-table --- transplanted VERBATIM from components/data/DataTable.jsx
   (injectStyles('lr-datatable', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-table { font-family: var(--font-family-base); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-1); overflow: hidden; }
.lr-table__scroll { width: 100%; overflow: auto; }
.lr-table__el { width: 100%; border-collapse: separate; border-spacing: 0; }

/* Header: Label 13/20 Bold, sentence case, quiet on the cell surface. */
.lr-table__th { background: var(--surface-cell); color: var(--text-secondary);
  font-size: var(--font-label-size); line-height: var(--font-label-line); font-weight: var(--font-weight-bold);
  text-align: left; white-space: nowrap; vertical-align: middle;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-subtle); }
.lr-table--sticky .lr-table__th { position: sticky; top: 0; z-index: 3; }
.lr-table__th--num { text-align: right; }
.lr-table__th--center { text-align: center; }

.lr-table__sort { display: inline-flex; align-items: center; gap: var(--space-1);
  font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  border-radius: var(--radius-md); transition: color var(--motion-hover) var(--ease-out); }
.lr-table__sort:hover { color: var(--colour-action); }
.lr-table__sort:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-table__sort[aria-sort="ascending"], .lr-table__sort[aria-sort="descending"] { color: var(--colour-text); }
.lr-table__sortmark { opacity: 0; transition: opacity var(--motion-hover) var(--ease-out); }
.lr-table__sort:hover .lr-table__sortmark, .lr-table__sortmark--on { opacity: 1; }

/* Cells: Small 14/20 — the admin density exception, see DataTable.prompt.md */
.lr-table__td { font-size: var(--font-small-size); line-height: var(--font-small-line);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle; }
.lr-table__row:last-child .lr-table__td { border-bottom: 0; }
.lr-table__td--num { text-align: right; font-variant-numeric: tabular-nums; }
.lr-table__td--center { text-align: center; font-variant-numeric: tabular-nums; }
.lr-table__td--strong { font-weight: var(--font-weight-bold); }
.lr-table__td--muted { color: var(--text-secondary); }
/* Thin cell: one flat mint tint, one step, no ramp. */
.lr-table__td--thin { background: var(--surface-tint-info); font-weight: var(--font-weight-bold); }

.lr-table--compact .lr-table__td { padding: var(--space-2) var(--space-3); }
.lr-table--compact .lr-table__th { padding: var(--space-2) var(--space-3); }

.lr-table__row { transition: background-color var(--motion-hover) var(--ease-out); }
.lr-table__row:hover { background: var(--surface-cell); }
.lr-table__row--clickable { cursor: pointer; }
.lr-table__row--clickable:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* Row header (matrix first column) — sticky, so a 7-wide grid keeps its labels. */
.lr-table__rowhead { position: sticky; left: 0; z-index: 2; background: var(--surface-white);
  font-weight: var(--font-weight-bold); border-right: 1px solid var(--border-subtle); }
.lr-table__row:hover .lr-table__rowhead { background: var(--surface-cell); }
.lr-table--sticky .lr-table__th--rowhead { z-index: 4; }
.lr-table__th--rowhead { position: sticky; left: 0; background: var(--surface-cell);
  border-right: 1px solid var(--border-subtle); }

/* Row actions travel with the row, not with the page. Always visible — a
   hover-reveal is invisible on touch and undiscoverable at a glance. */
.lr-table__actions { text-align: right; white-space: nowrap; width: 1%; }
.lr-table__actions-inner { display: flex; align-items: center; justify-content: flex-end; gap: var(--space-1); }

.lr-table__state { padding: var(--space-12) var(--space-6); text-align: center; }
.lr-table__state-title { font-size: var(--font-body-size); line-height: var(--font-body-line); font-weight: var(--font-weight-bold); }
.lr-table__state-body { font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); margin-top: var(--space-1); }
.lr-table__caption { caption-side: top; text-align: left; padding: var(--space-4) var(--space-4) var(--space-2);
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }

/* --- pagination --- transplanted VERBATIM from components/data/Pagination.jsx
   (injectStyles('lr-pagination', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-pag { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  flex-wrap: wrap; font-family: var(--font-family-base); color: var(--colour-text);
  padding: var(--space-3) var(--space-1); }
.lr-pag__summary { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.lr-pag__pages { display: flex; align-items: center; gap: var(--space-1); }

.lr-pag__btn { display: inline-flex; align-items: center; justify-content: center;
  min-width: 36px; height: 36px; padding: 0 var(--space-2); box-sizing: border-box;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--colour-text); font-variant-numeric: tabular-nums; cursor: pointer;
  transition: background-color var(--motion-hover) var(--ease-out),
              color var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out); }
.lr-pag__btn:hover:not([disabled]):not([aria-current="page"]) { background: var(--surface-tint-info); }
.lr-pag__btn:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-pag__btn:active:not([disabled]) { transform: scale(var(--press-scale)); }
.lr-pag__btn[disabled] { color: var(--text-disabled); cursor: not-allowed; }

/* "You are here" is a status, so the current page is mint tint — never a solid
   action fill, which would read as the one thing on the row still to be clicked. */
.lr-pag__btn[aria-current="page"] { background: var(--surface-tint-info);
  font-weight: var(--font-weight-bold); cursor: default; }

.lr-pag__step { border-color: var(--border-subtle); background: var(--surface-white); }
.lr-pag__step:hover:not([disabled]) { border-color: var(--colour-action); color: var(--colour-action); background: var(--surface-white); }
.lr-pag__step[disabled] { border-color: var(--border-subtle); background: var(--surface-white); }

.lr-pag__gap { min-width: 20px; text-align: center; color: var(--text-disabled);
  font-size: var(--font-small-size); line-height: 36px; user-select: none; }
.lr-pag__size { display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }
.lr-pag__size select { font-family: inherit; font-size: var(--font-small-size); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  padding: 6px var(--space-2); height: 36px; cursor: pointer; }
.lr-pag__size select:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* --- stat-tile --- transplanted VERBATIM from components/data/StatTile.jsx
   (injectStyles('lr-stattile', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-stat { display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-3);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-1);
  font-family: var(--font-family-base); color: var(--colour-text);
  text-align: left; width: 100%; box-sizing: border-box; min-width: 0; position: relative; }

/* The whole tile drills, but the info slot must stay independently clickable —
   so the hit area is its own element rather than a button wrapping the content
   (a button inside a button is invalid, and swallows the popover's click). */
.lr-stat__hit { position: absolute; inset: 0; z-index: 1; padding: 0; margin: 0; border: 0;
  background: none; border-radius: var(--radius-md); cursor: pointer; }
.lr-stat__hit:focus-visible { outline: none; box-shadow: var(--ring-focus); }

.lr-stat__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2);
  position: relative; z-index: 2; pointer-events: none; }
/* An open popover must out-paint the NEXT tile's head — sibling heads share z-2 and later-in-DOM
   wins ties, so a disclosure opened on tile 1 painted under tile 2. The head's own z-index caps
   the popover inside this stacking context, so the lift has to happen on the head. */
.lr-stat__head:has([aria-expanded="true"]) { z-index: 30; }
.lr-stat__info { pointer-events: auto; }
.lr-stat__label { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary); }
.lr-stat__value { font-size: 32px; line-height: 40px; font-weight: var(--font-weight-bold);
  font-variant-numeric: tabular-nums; }
.lr-stat__value--empty { color: var(--text-disabled); }

/* BREAKDOWN — the parts of the number, directly under it: "4 new · 6 returning".
   Pulled up by one step so it reads as the number's own line, and the foot below
   stays the separate "how it moved" line. Counts Bold deep teal (the delta's
   weight, so a part reads as a number of the same family, and a zero part is a
   full-strength 0); labels and the dot in secondary text. Small size, so it
   never competes with the 32px value. Wraps BETWEEN parts, never inside one. */
.lr-stat__parts { display: flex; flex-wrap: wrap; column-gap: var(--space-1);
  margin: calc(-1 * var(--space-1)) 0 0; padding: 0; list-style: none;
  font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.lr-stat__part { white-space: nowrap; }
.lr-stat__part:not(:last-child)::after { content: '\00B7'; margin-left: var(--space-1); }
.lr-stat__partval { font-weight: var(--font-weight-bold); color: var(--colour-text); }

/* The last line (foot or as-of chip) sits on the tile's bottom edge. In a row of
   stretched tiles where only some carry a breakdown, numbers align along the top
   and deltas along the bottom; the tile without parts takes the space in between.
   A tile on its own is unchanged — auto is zero when nothing stretches it. */
.lr-stat__foot { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); font-variant-numeric: tabular-nums; min-height: var(--font-small-line);
  margin-top: auto; }
.lr-stat__delta { font-weight: var(--font-weight-bold); color: var(--colour-text); }

/* CENSUS carries a mint as-of chip and never a delta; FLOW carries a delta line.
   The two must be tellable apart on one row without being told. */
.lr-stat__asof { display: inline-flex; align-items: center; align-self: flex-start;
  padding: 2px var(--space-2); border-radius: var(--radius-md);
  background: var(--surface-tint-info); color: var(--colour-informational);
  font-size: var(--font-label-size); line-height: var(--font-label-line); font-weight: var(--font-weight-bold);
  margin-top: auto; }

.lr-stat--clickable { cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: box-shadow var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out); }
.lr-stat--clickable:hover { box-shadow: var(--shadow-2); border-color: var(--colour-action); transform: translateY(-2px); }
.lr-stat--clickable:active { transform: translateY(0); }
/* The hover lift transforms the tile into its own stacking context exactly while the pointer
   is on it — which it is, having just opened the popover — sealing the popover beneath the
   next tile. Suspend the lift while a disclosure inside the tile is open. */
.lr-stat--clickable:hover:has([aria-expanded="true"]) { transform: none; }

/* --- toggle --- transplanted VERBATIM from components/data/Toggle.jsx
   (injectStyles('lr-toggle', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-toggle { display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-family-base); color: var(--colour-text); }
.lr-toggle__btn { position: relative; flex: none; display: inline-flex; align-items: center;
  padding: 0; border: 1px solid var(--border-subtle); background: var(--surface-white);
  border-radius: var(--radius-pill); cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background-color var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out); }
.lr-toggle__btn:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-toggle__btn:active:not([disabled]) { transform: scale(var(--press-scale)); }
.lr-toggle__btn:hover:not([disabled]) { border-color: var(--colour-action); }

.lr-toggle__btn--md { width: 44px; height: 26px; }
.lr-toggle__btn--sm { width: 36px; height: 22px; }

.lr-toggle__knob { position: absolute; top: 50%; left: 2px; transform: translateY(-50%);
  border-radius: var(--radius-pill); background: var(--surface-white);
  box-shadow: var(--shadow-1); border: 1px solid var(--border-subtle);
  transition: left var(--motion-hover) var(--ease-out), border-color var(--motion-hover) var(--ease-out); }
.lr-toggle__btn--md .lr-toggle__knob { width: 20px; height: 20px; }
.lr-toggle__btn--sm .lr-toggle__knob { width: 16px; height: 16px; }

/* ON is a state, so the filled track is informational mint — the toggle's
   affordance is its shape, not its colour. See Toggle.prompt.md. */
.lr-toggle__btn[aria-checked="true"] { background: var(--colour-informational); border-color: var(--colour-informational); }
.lr-toggle__btn[aria-checked="true"]:hover:not([disabled]) { border-color: var(--colour-informational); }
.lr-toggle__btn--md[aria-checked="true"] .lr-toggle__knob { left: 20px; border-color: var(--surface-white); }
.lr-toggle__btn--sm[aria-checked="true"] .lr-toggle__knob { left: 16px; border-color: var(--surface-white); }

.lr-toggle__btn[disabled] { cursor: not-allowed; background: var(--surface-cell); border-color: var(--border-subtle); }
.lr-toggle__btn[disabled] .lr-toggle__knob { box-shadow: none; }
.lr-toggle__btn[data-busy="true"] { cursor: progress; opacity: 0.6; }

.lr-toggle__label { font-size: var(--font-small-size); line-height: var(--font-small-line); cursor: pointer; }
.lr-toggle__label--disabled { color: var(--text-disabled); cursor: not-allowed; }
.lr-toggle__state { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); min-width: 24px; }
.lr-toggle--reverse { flex-direction: row-reverse; }
.lr-toggle__sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* --- distribution-chart --- transplanted VERBATIM from components/data/DistributionChart.jsx
   (injectStyles('lr-distchart', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-chart { font-family: var(--font-family-base); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-1); padding: var(--space-6); }
.lr-chart__title { font-size: var(--font-body-size); line-height: var(--font-body-line); font-weight: var(--font-weight-bold); }
.lr-chart__caption { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); margin-top: var(--space-1); }
.lr-chart__plot { margin-top: var(--space-6); }

/* Vertical: seven buckets, ages 5–11 */
.lr-chart__cols { display: flex; align-items: stretch; gap: var(--space-3); }
.lr-chart__col { flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: flex-end; gap: var(--space-2); min-width: 0; }
.lr-chart__value { font-size: var(--font-small-size); line-height: var(--font-small-line);
  font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; }
.lr-chart__track { width: 100%; display: flex; align-items: flex-end; background: var(--surface-cell);
  border-radius: var(--radius-md); }
/* Data ink is informational mint: a bar is not clickable, and a bar in action
   blue reads as one. Flat fill, never a gradient. */
.lr-chart__bar { width: 100%; background: var(--colour-informational); border-radius: var(--radius-md);
  transition: background-color var(--motion-hover) var(--ease-out); }
.lr-chart__bar--quiet { background: var(--surface-tint-info); }
.lr-chart__tick { font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }
.lr-chart__baseline { height: 1px; background: var(--border-subtle); margin-top: var(--space-1); }

/* Horizontal */
.lr-chart__rows { display: flex; flex-direction: column; gap: var(--space-3); }
.lr-chart__row { display: grid; grid-template-columns: 96px 1fr 56px; align-items: center; gap: var(--space-3); }
.lr-chart__row .lr-chart__track { height: 20px; align-items: stretch; }
.lr-chart__row .lr-chart__bar { height: 100%; width: auto; }
.lr-chart__row .lr-chart__value { text-align: right; }

.lr-chart__state { padding: var(--space-8) 0; text-align: center;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }

/* --- drill-panel --- transplanted VERBATIM from components/data/DrillPanel.jsx
   (injectStyles('lr-drillpanel', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
@keyframes lr-drill-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes lr-drill-scrim-in { from { opacity: 0; } to { opacity: 1; } }

/* Lighter than the Dialog scrim (0.32): a drill is a lens on the page you are
   already reading, not a decision that blocks it. */
.lr-drill__scrim { position: fixed; inset: 0; z-index: 900; background: rgba(0,52,48,0.16); }
@media (prefers-reduced-motion: no-preference) { .lr-drill__scrim { animation: lr-drill-scrim-in var(--motion-overlay) var(--ease-out); } }

.lr-drill { position: fixed; top: 0; right: 0; bottom: 0; z-index: 901;
  display: flex; flex-direction: column; width: 720px; max-width: 92vw;
  background: var(--surface-white); box-shadow: var(--shadow-3);
  border-top-left-radius: var(--radius-md); border-bottom-left-radius: var(--radius-md);
  font-family: var(--font-family-base); color: var(--colour-text); }
/* Focused programmatically on open — the brand ring belongs on the controls
   inside, not on the container. */
.lr-drill:focus { outline: none; }
@media (prefers-reduced-motion: no-preference) { .lr-drill { animation: lr-drill-in var(--motion-overlay) var(--ease-out); } }

.lr-drill__head { display: flex; align-items: flex-start; gap: var(--space-4);
  padding: var(--space-6) var(--space-6) var(--space-4); border-bottom: 1px solid var(--border-subtle); }
.lr-drill__headmain { flex: 1; min-width: 0; }
.lr-drill__title { font-size: var(--font-h3-size); line-height: var(--font-h3-line); font-weight: var(--font-weight-bold); }
.lr-drill__desc { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); margin-top: var(--space-1); }

/* The number you came from travels with you, live, so the drawer reads as
   THIS number's contents rather than a new screen. */
.lr-drill__source { display: flex; align-items: baseline; gap: var(--space-3);
  margin-top: var(--space-3); padding: var(--space-3) var(--space-4);
  background: var(--surface-cell); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.lr-drill__source-value { font-size: var(--font-h3-size); line-height: var(--font-h3-line);
  font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; }
.lr-drill__source-label { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary); }
.lr-drill__source-note { margin-left: auto; font-size: var(--font-small-size);
  line-height: var(--font-small-line); color: var(--text-secondary); }

.lr-drill__body { flex: 1; overflow: auto; padding: var(--space-6); background: var(--surface-cell); }
.lr-drill__foot { padding: var(--space-4) var(--space-6); border-top: 1px solid var(--border-subtle);
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-3); background: var(--surface-white); }

@media (max-width: 720px) {
  .lr-drill { width: 100%; max-width: 100%; border-radius: 0; }
  .lr-drill__head, .lr-drill__body, .lr-drill__foot { padding-left: var(--space-4); padding-right: var(--space-4); }
}

/* --- date-range-picker --- transplanted VERBATIM from components/data/DateRangePicker.jsx
   (injectStyles('lr-daterange', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-daterange { position: relative; display: inline-block; font-family: var(--font-family-base); color: var(--colour-text); }

.lr-daterange__trigger { display: inline-flex; align-items: center; gap: var(--space-2);
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  padding: 9px var(--space-4); min-height: 40px; box-sizing: border-box; cursor: pointer;
  font-family: inherit; font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--colour-text);
  transition: border-color var(--motion-hover) var(--ease-out), color var(--motion-hover) var(--ease-out); }
.lr-daterange__trigger:hover { border-color: var(--colour-action); }
.lr-daterange__trigger:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-daterange__trigger[aria-expanded="true"] { border-color: var(--colour-action); }
.lr-daterange__triggerlabel { font-weight: var(--font-weight-bold); }
.lr-daterange__chev { color: var(--text-secondary); transition: transform var(--motion-hover) var(--ease-out); }
.lr-daterange__trigger[aria-expanded="true"] .lr-daterange__chev { transform: rotate(180deg); }

.lr-daterange__panel { position: absolute; z-index: 30; top: calc(100% + 6px); right: 0;
  display: flex; background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-2); overflow: hidden; }
.lr-daterange__panel--left { right: auto; left: 0; }

.lr-daterange__presets { display: flex; flex-direction: column; gap: 2px; padding: var(--space-2);
  background: var(--surface-cell); border-right: 1px solid var(--border-subtle); min-width: 168px; }
.lr-daterange__preset { text-align: left; background: transparent; border: 0; border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3); font-family: inherit;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--colour-text); cursor: pointer;
  transition: background-color var(--motion-hover) var(--ease-out); }
.lr-daterange__preset:hover { background: var(--surface-tint-info); }
.lr-daterange__preset:focus-visible { outline: none; box-shadow: var(--ring-focus); }
/* The active preset is "you are here" — status, so mint tint, never an action fill. */
.lr-daterange__preset[aria-pressed="true"] { background: var(--surface-tint-info); font-weight: var(--font-weight-bold); }

.lr-daterange__custom { padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); min-width: 264px; }

/* PHONE (⚖️ James, 17 Aug 2026 — ratified by appearance on Local, Claude Design being down):
   mobile-first — at the phone default the panel is one column sized to the viewport, so a
   right-anchored trigger never pushes the presets off the LEFT edge (measured 17 Aug at 375px:
   left −165px, the presets unreachable). ≥768px restores the two-column, right-anchored panel. */
.lr-daterange__panel { flex-direction: column; width: calc(100vw - 2 * var(--space-4)); }
.lr-daterange__presets { border-right: 0; border-bottom: 1px solid var(--border-subtle); min-width: 0; }
.lr-daterange__custom { min-width: 0; }
@media (min-width: 768px) {
  .lr-daterange__panel { flex-direction: row; width: auto; }
  .lr-daterange__presets { border-right: 1px solid var(--border-subtle); border-bottom: 0; min-width: 168px; }
  .lr-daterange__custom { min-width: 264px; }
}
.lr-daterange__legend { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary); }
.lr-daterange__fields { display: flex; align-items: center; gap: var(--space-2); }
.lr-daterange__dash { color: var(--text-secondary); }
.lr-daterange__input { flex: 1; min-width: 0; font-family: inherit;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  padding: 9px var(--space-3); min-height: 40px; box-sizing: border-box;
  transition: border-color var(--motion-hover) var(--ease-out), box-shadow var(--motion-hover) var(--ease-out); }
.lr-daterange__input:hover { border-color: var(--colour-action); }
.lr-daterange__input:focus-visible { outline: none; border-color: var(--colour-action); box-shadow: 0 0 0 3px rgba(100,120,165,0.18); }
.lr-daterange__error { font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--colour-error); }
.lr-daterange__actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-1); }

/* --- definition-popover --- transplanted VERBATIM from components/data/DefinitionPopover.jsx
   (injectStyles('lr-definition', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-def { position: relative; display: inline-block; font-family: var(--font-family-base); }

/* The trigger is a QUIET HELP ICON (⚖️ Hannah + James, 12 Aug 2026 — the dotted-underlined
   term read as condescending in use). It sits in disabled grey so it does not announce itself
   down a page of numbers that are fine, and turns action-blue on hover and while open so it is
   unmistakably operable once you are near it. Touch-visible: click/tap, never hover alone.
   The 16px glyph carries a 32px hit target (⚖️ James, 13 Aug 2026): padding grows the hit
   area, the matching negative margin gives it back, so no layout shifts. 32 clears WCAG's
   24px minimum without eating too far into the clickable tile the icon often sits inside. */
.lr-def__term { display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-disabled); background: none; border: 0; margin: calc(-1 * var(--space-2));
  padding: var(--space-2); cursor: help; border-radius: var(--radius-md);
  transition: color var(--motion-hover) var(--ease-out); }
.lr-def__term:hover, .lr-def__term[aria-expanded="true"] { color: var(--colour-action); }
.lr-def__term:focus-visible { outline: none; box-shadow: var(--ring-focus); }

.lr-def__pop { position: absolute; z-index: 40; top: calc(100% + 8px); left: 0; width: 300px; max-width: 78vw;
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  box-shadow: var(--shadow-2); padding: var(--space-4); text-align: left;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary);
  white-space: normal; font-weight: var(--font-weight-regular); }
.lr-def__pop--end { left: auto; right: 0; }
.lr-def__pop--up { top: auto; bottom: calc(100% + 8px); }
.lr-def__poptitle { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--colour-text); margin-bottom: var(--space-1); }
@media (prefers-reduced-motion: no-preference) {
  .lr-def__pop { animation: lr-def-in var(--motion-hover) var(--ease-out); }
}
@keyframes lr-def-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* --- file-upload --- transplanted VERBATIM from components/data/FileUpload.jsx
   (injectStyles('lr-fileupload', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-upload { font-family: var(--font-family-base); color: var(--colour-text); }
.lr-upload__label { display: block; font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); margin-bottom: var(--space-2); }
.lr-upload__hint { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); margin-top: var(--space-2); }

/* EMPTY — a drop target that is also a button. */
.lr-upload__drop { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2); width: 100%; box-sizing: border-box; min-height: 140px;
  padding: var(--space-6); text-align: center; cursor: pointer;
  background: var(--surface-cell); border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  font-family: inherit; font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary);
  transition: background-color var(--motion-hover) var(--ease-out), border-color var(--motion-hover) var(--ease-out); }
.lr-upload__drop:hover { border-color: var(--colour-action); background: var(--surface-white); }
.lr-upload__drop:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-upload__drop[data-dragging="true"] { border-color: var(--colour-action); background: var(--surface-tint-info); }
.lr-upload__drop[disabled] { cursor: not-allowed; color: var(--text-disabled); }
.lr-upload__dropicon { color: var(--colour-action); }
.lr-upload__dropstrong { color: var(--colour-text); font-weight: var(--font-weight-bold); }

/* SELECTED / UPLOADING / ERROR / REPLACING — one row, one shape. */
.lr-upload__file { display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  background: var(--surface-white); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); }
.lr-upload__file--error { border-color: var(--colour-error); }
.lr-upload__thumb { flex: none; width: 40px; height: 56px; border-radius: var(--radius-md);
  background: var(--surface-cell); border: 1px solid var(--border-subtle); object-fit: cover; }
.lr-upload__filemain { flex: 1; min-width: 0; }
.lr-upload__filename { font-size: var(--font-small-size); line-height: var(--font-small-line);
  font-weight: var(--font-weight-bold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lr-upload__filemeta { font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }
.lr-upload__filemeta--error { color: var(--colour-error); }
.lr-upload__actions { display: flex; align-items: center; gap: var(--space-1); flex: none; }

/* Progress: the fill is an action inside what you already have, so action blue. */
.lr-upload__bar { margin-top: var(--space-2); height: 6px; border-radius: var(--radius-pill);
  background: var(--surface-tint-info); overflow: hidden; }
.lr-upload__barfill { height: 100%; background: var(--colour-action); border-radius: var(--radius-pill);
  transition: width var(--motion-hover) var(--ease-out); }
.lr-upload__input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* --- trend-chart --- transplanted VERBATIM from components/data/TrendChart.jsx
   (injectStyles('lr-trendchart', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-trend { font-family: var(--font-family-base); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-1); padding: var(--space-6);
  box-sizing: border-box; min-width: 0; }
.lr-trend__title { font-size: var(--font-body-size); line-height: var(--font-body-line); font-weight: var(--font-weight-bold); }
.lr-trend__caption { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); margin-top: var(--space-1); }

/* The readout is a fixed row, never a floating tooltip: a tooltip needs a hover
   a phone cannot give, and a title attribute never renders on touch at all. */
.lr-trend__readout { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-4);
  margin-top: var(--space-4); padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
  font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); min-height: calc(var(--font-small-line) + var(--space-2)); }
.lr-trend__readout-day { font-weight: var(--font-weight-bold); color: var(--colour-text);
  font-variant-numeric: tabular-nums; }
.lr-trend__readout-item { display: inline-flex; align-items: baseline; gap: var(--space-2); }
.lr-trend__readout-value { font-weight: var(--font-weight-bold); color: var(--colour-text);
  font-variant-numeric: tabular-nums; }
.lr-trend__readout-absent { color: var(--text-disabled); }
.lr-trend__readout-hint { color: var(--text-disabled); }

/* One overlay covers every panel, so the cursor is shared: the whole point of
   stacking a lead over its lag is reading one day across both at once. */
.lr-trend__panels { position: relative; margin-top: var(--space-4); }
.lr-trend__surface { position: absolute; inset: 0; z-index: 2; border-radius: var(--radius-md);
  background: none; border: 0; padding: 0; margin: 0; width: 100%; cursor: crosshair; }
.lr-trend__surface:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.lr-trend__surface--flat { cursor: default; }

.lr-trend__panel + .lr-trend__panel { margin-top: var(--space-6); }
.lr-trend__panelhead { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4); margin-bottom: var(--space-2); }
.lr-trend__label { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary); }
.lr-trend__note { font-size: var(--font-label-size); line-height: var(--font-label-line);
  color: var(--text-disabled); text-align: right; font-variant-numeric: tabular-nums; }

.lr-trend__plot { position: relative; display: grid; grid-template-columns: auto 1fr;
  column-gap: var(--space-3); align-items: stretch; }
.lr-trend__scale { display: flex; flex-direction: column; justify-content: space-between;
  font-size: var(--font-label-size); line-height: var(--font-label-line);
  color: var(--text-secondary); font-variant-numeric: tabular-nums; text-align: right;
  padding-bottom: 0; }
.lr-trend__scale span:last-child { color: var(--text-disabled); }

/* Slots sit edge to edge so that a run of them reads as one band; the mark
   inside is narrower than its slot. */
.lr-trend__cols { display: grid; align-items: end; min-width: 0; }
.lr-trend__slot { position: relative; height: 100%; display: flex; align-items: flex-end;
  justify-content: center; border-bottom: 1px solid var(--border-subtle); min-width: 0; }

/* ABSENCE VOCABULARY — four readings, no fake zero anywhere.
   1. before the floor  → tinted band, and the baseline does not run
   2. missing snapshot  → baseline breaks, dotted rule stands in its place
   3. not complete yet  → the mark is hollow; dotted baseline where nothing is in yet
   4. zero              → an unbroken baseline and no mark: the honest picture of none */
.lr-trend__slot--prefloor { background: var(--surface-cell); border-bottom-color: transparent; }
.lr-trend__slot--gap { border-bottom-color: transparent; }
.lr-trend__gapline { position: absolute; left: 50%; top: 0; bottom: 0; width: 0;
  border-left: 1px dotted var(--text-disabled); }
.lr-trend__slot--pending { border-bottom-style: dotted; border-bottom-color: var(--text-disabled); }
.lr-trend__slot--edge { border-left: 1px dashed var(--border-subtle); }

/* Data ink is informational mint, flat — a bar in action blue reads as a control
   (DistributionChart, 12 Aug 2026). Hollow is never a second colour. */
.lr-trend__bar { width: 62%; max-width: 18px; min-width: 3px; background: var(--colour-informational);
  border-radius: var(--radius-md); }
.lr-trend__bar--pos { min-height: 2px; }
.lr-trend__bar--provisional { background: var(--surface-white); border: 1px solid var(--colour-informational); }

.lr-trend__step { position: absolute; left: 0; right: 0; border-top: 2px solid var(--colour-informational); }
.lr-trend__step--provisional { border-top-style: dashed; }
.lr-trend__riser { position: absolute; left: 0; width: 0; border-left: 2px solid var(--colour-informational); }
.lr-trend__riser--provisional { border-left-style: dashed; }
.lr-trend__dot { position: absolute; left: 50%; width: 5px; height: 5px; margin-left: -2.5px; margin-bottom: -2.5px;
  border-radius: var(--radius-pill); background: var(--colour-informational); }

/* The cursor is action blue: it is the affordance, not the data. */
.lr-trend__slot.is-cursor::after { content: ''; position: absolute; left: 50%; top: 0; bottom: -1px; width: 0;
  border-left: 1px solid var(--colour-action); }
.lr-trend__slot.is-cursor > * { position: relative; z-index: 1; }

.lr-trend__axis { display: grid; margin-top: var(--space-2); min-width: 0; }
.lr-trend__tick { font-size: var(--font-label-size); line-height: var(--font-label-line);
  color: var(--text-secondary); text-align: center; white-space: nowrap; }
.lr-trend__tick.is-cursor { color: var(--colour-action); font-weight: var(--font-weight-bold); }

.lr-trend__key { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-top: var(--space-4);
  font-size: var(--font-label-size); line-height: var(--font-label-line); color: var(--text-secondary); }
.lr-trend__keyitem { display: inline-flex; align-items: center; gap: var(--space-2); }
.lr-trend__sw { width: 12px; height: 12px; border-radius: var(--radius-md); flex: none; }
.lr-trend__sw--recorded { background: var(--colour-informational); }
.lr-trend__sw--provisional { background: var(--surface-white); border: 1px solid var(--colour-informational); }
.lr-trend__sw--gap { border-left: 1px dotted var(--text-disabled); width: 1px; height: 14px; border-radius: 0; }
.lr-trend__sw--prefloor { background: var(--surface-cell); border: 1px solid var(--border-subtle); }

.lr-trend__state { padding: var(--space-8) 0; text-align: center;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }

/* --- journey-funnel --- transplanted VERBATIM from components/data/JourneyFunnel.jsx
   (injectStyles('lr-journeyfunnel', ...)). Do not edit here: fix the ratified
   design and re-transplant. */
.lr-funnel { font-family: var(--font-family-base); color: var(--colour-text);
  background: var(--surface-white); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); box-shadow: var(--shadow-1); padding: var(--space-6);
  box-sizing: border-box; min-width: 0; }
.lr-funnel__title { font-size: var(--font-body-size); line-height: var(--font-body-line);
  font-weight: var(--font-weight-bold); }
.lr-funnel__caption { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); margin-top: var(--space-1); }
.lr-funnel__plot { margin-top: var(--space-6); }

/* THE SPINE — DistributionChart's horizontal row, unchanged in every measurable
   way (tick type, value type, 20px track, radius-md, printed value at the right)
   so the two charts read as one language on one page. */
.lr-funnel__spine { display: flex; flex-direction: column; gap: var(--space-3); }
.lr-funnel__row { display: grid; grid-template-columns: minmax(96px, 168px) 1fr 56px;
  align-items: center; gap: var(--space-3); }
.lr-funnel__label { font-size: var(--font-small-size); line-height: var(--font-small-line);
  color: var(--text-secondary); overflow-wrap: anywhere; }
.lr-funnel__value { font-size: var(--font-small-size); line-height: var(--font-small-line);
  font-weight: var(--font-weight-bold); font-variant-numeric: tabular-nums; text-align: right; }

.lr-funnel__track { height: 20px; background: var(--chart-track);
  border-radius: var(--radius-md); display: flex; align-items: stretch; min-width: 0; }
/* Data ink only. A bar is not clickable, and a bar in action blue reads as one
   (DistributionChart, 12 Aug 2026). Flat fill, never a gradient. */
.lr-funnel__bar { height: 100%; background: var(--chart-series-1);
  border-radius: var(--radius-md); }
.lr-funnel__bar--emphasis { background: var(--chart-series-1-emphasis); }
.lr-funnel__bar--branch { background: var(--chart-series-2); }
.lr-funnel__bar--branch.lr-funnel__bar--emphasis { background: var(--chart-series-2-emphasis); }

/* ABSENCE — not refreshed is NOT a row of zero bars. Zero keeps its filled
   track and prints 0; absence removes the track's fill, leaves a dashed
   hairline, and prints an em dash. (StatTile / TrendChart discipline.) */
.lr-funnel__track--absent { background: none; border: 1px dashed var(--border-subtle); }
.lr-funnel__value--absent { font-weight: var(--font-weight-regular); color: var(--text-disabled); }
.lr-funnel__absence { margin-top: var(--space-4); font-size: var(--font-small-size);
  line-height: var(--font-small-line); color: var(--text-secondary); }

/* THE FORK — one hairline under the last spine step, and a 1px drop into each
   branch column. No arrows, no boxes: the split is drawn, not diagrammed. */
.lr-funnel__fork { margin-top: var(--space-6); }
.lr-funnel__branches { display: grid; gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); padding-top: var(--space-2); }
.lr-funnel__branch { position: relative; min-width: 0; }
.lr-funnel__branchtitle { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary);
  margin-bottom: var(--space-3); overflow-wrap: anywhere; }
.lr-funnel__bsteps { display: flex; flex-direction: column; gap: var(--space-3); }
.lr-funnel__bstep { display: grid; grid-template-columns: 1fr 48px;
  column-gap: var(--space-3); row-gap: var(--space-1); align-items: center; }
.lr-funnel__bstep .lr-funnel__label { grid-column: 1 / -1; }

/* The scale sentence is part of the chart, not a caption: branch bars are read
   against the fork step, and a reader who does not know that misreads them. */
.lr-funnel__note { margin-top: var(--space-4); font-size: var(--font-label-size);
  line-height: var(--font-label-line); color: var(--text-secondary); }

.lr-funnel__state { padding: var(--space-8) 0; text-align: center;
  font-size: var(--font-small-size); line-height: var(--font-small-line); color: var(--text-secondary); }

/* --- signal-card --- transplanted from components/data/SignalCard.jsx
   (injectStyles('lr-signalcard', ...)). Do not edit here: fix the ratified design
   and re-transplant.

   ⚖️ ONE DELIBERATE DEVIATION from verbatim (coordinator ruling, 16 Aug 2026): the
   .jsx writes `var(--colour-series-2-emphasis, #A2618E)` on .lr-signal__m--result.
   The fallback is dead code here — the token IS vendored (ds-tokens.css, aliased to
   --chart-series-2-emphasis) and the design's own .prompt.md rules "the token, once
   written, is the single source" — and a raw hex fails the admin.css palette gate.
   The fallback is dropped; nothing else in this block is changed. */
.lr-signal { position: relative; display: flex; flex-direction: column; gap: var(--space-4);
  padding: var(--space-4); background: var(--surface-white);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  box-shadow: var(--shadow-1); font-family: var(--font-family-base); color: var(--colour-text);
  box-sizing: border-box; width: 100%; min-width: 0;
  transition: box-shadow var(--motion-hover) var(--ease-out),
              border-color var(--motion-hover) var(--ease-out),
              transform var(--motion-hover) var(--ease-out); }
.lr-signal:has(.lr-signal__link:hover) { box-shadow: var(--shadow-2); border-color: var(--colour-action); transform: translateY(-2px); }
.lr-signal:has(.lr-signal__link:active) { transform: translateY(0); }

.lr-signal__title { font-size: var(--font-body-size); line-height: var(--font-body-line);
  font-weight: var(--font-weight-bold); }

/* One measure = one label row, one number row, one spark. Nothing else. */
.lr-signal__m { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.lr-signal__m + .lr-signal__m { margin-top: var(--space-4); }
.lr-signal__m--result { margin-top: var(--space-4); padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle); }

.lr-signal__mhead { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); }
.lr-signal__label { font-size: var(--font-label-size); line-height: var(--font-label-line);
  font-weight: var(--font-weight-bold); color: var(--text-secondary); min-width: 0; }

/* A bet is a caveat, not a status — and the palette has no caveat colour, so it
   takes a SHAPE (hairline outline) and never a fill. Mint here would read as the
   as-of chip's family and claim the measure is confirmed status. */
.lr-signal__bet { display: inline-flex; align-items: center; margin-left: var(--space-2);
  padding: 0 6px; border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  font-size: var(--font-label-size); line-height: 18px; font-weight: var(--font-weight-bold);
  color: var(--text-secondary); white-space: nowrap; }

.lr-signal__num { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap;
  font-variant-numeric: tabular-nums; }
.lr-signal__value { font-size: 24px; line-height: 32px; font-weight: var(--font-weight-bold); }
.lr-signal__value--empty { color: var(--text-disabled); }
/* Never coloured by sentiment: the palette has no green-for-good, and half these
   measures are not better when they rise (StatTile, 12 Aug 2026). */
.lr-signal__delta { font-size: var(--font-small-size); line-height: var(--font-small-line);
  font-weight: var(--font-weight-bold); color: var(--colour-text); }
/* "0" beside a value of 0 reads as a second number. No change is not a number,
   so it is not printed as one — two quiet words, StatTile's wording. */
.lr-signal__delta--flat { font-weight: var(--font-weight-regular); color: var(--text-secondary); }

.lr-signal__plot { display: grid; align-items: end; min-width: 0; margin-top: var(--space-1); }
.lr-signal__slot { position: relative; height: 100%; display: flex; align-items: flex-end;
  justify-content: center; border-bottom: 1px solid var(--border-subtle); min-width: 0; }

/* ABSENCE VOCABULARY — identical to TrendChart, one size down. No fake zero.
   1. before the floor → tinted band, no baseline   2. missing snapshot → baseline breaks
   3. not complete yet → hollow mark                4. zero → unbroken baseline, no mark */
.lr-signal__slot--prefloor { background: var(--surface-cell); border-bottom-color: transparent; }
.lr-signal__slot--gap { border-bottom-color: transparent; }
.lr-signal__gapline { position: absolute; left: 50%; top: 0; bottom: 0; width: 0;
  border-left: 1px dotted var(--text-disabled); }
.lr-signal__slot--pending { border-bottom-style: dotted; border-bottom-color: var(--text-disabled); }

/* Data ink carries the ROLE of the measure: mint = the early sign, series 2 = the
   result. Two series, two panels, two scales — never one plot with two lines. */
.lr-signal__bar { width: 62%; max-width: 10px; min-width: 2px; border-radius: 2px;
  background: var(--lr-ink); }
.lr-signal__bar--pos { min-height: 2px; }
.lr-signal__bar--provisional { background: var(--surface-white); border: 1px solid var(--lr-ink); }
.lr-signal__step { position: absolute; left: 0; right: 0; border-top: 2px solid var(--lr-ink); }
.lr-signal__step--provisional { border-top-style: dashed; }
.lr-signal__riser { position: absolute; left: 0; width: 0; border-left: 2px solid var(--lr-ink); }
.lr-signal__riser--provisional { border-left-style: dashed; }

.lr-signal__m--lead   { --lr-ink: var(--colour-series-1, var(--colour-informational)); }
.lr-signal__m--result { --lr-ink: var(--colour-series-2-emphasis); }

/* Not refreshed is not a shape of the data — it is the absence of the answer, so
   the plot area holds its height and draws no axis at all. */
.lr-signal__flat { display: flex; align-items: center; justify-content: center;
  background: var(--surface-cell); border-radius: var(--radius-md); margin-top: var(--space-1);
  font-size: var(--font-label-size); line-height: var(--font-label-line); color: var(--text-secondary);
  text-align: center; padding: 0 var(--space-2); box-sizing: border-box; }

.lr-signal__foot { display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); margin-top: auto; padding-top: var(--space-1); }
.lr-signal__link { position: static; font-size: var(--font-small-size); line-height: var(--font-small-line);
  font-weight: var(--font-weight-bold); color: var(--colour-action); text-decoration: none;
  background: none; border: 0; padding: 0; font-family: inherit; cursor: pointer; border-radius: var(--radius-md); }
/* One link, one hit area: the footer link stretches over the whole card so the card
   is tappable without a second focus stop or a hover-only affordance. */
.lr-signal__link::after { content: ''; position: absolute; inset: 0; border-radius: var(--radius-md); }
.lr-signal__link:hover { color: var(--colour-action-hover); text-decoration: underline; }
.lr-signal__link:focus-visible { outline: none; }
.lr-signal__link:focus-visible::after { box-shadow: var(--ring-focus); }

.lr-signal__sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
/* --- admin pages: shared page chrome --- NOT a component transplant: page-level
   layout only. Tokens only; no colours beyond tokens; no component styling here. */
.lr-page { max-width: 1200px; margin: 0 auto; padding: var(--space-6) var(--space-4) var(--space-12);
  display: flex; flex-direction: column; gap: var(--space-6); }
.lr-filters { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap; }
.lr-filters .lr-search { flex: 1; min-width: 260px; }
.lr-coupon-create form { display: flex; gap: var(--space-3); align-items: flex-end; flex-wrap: wrap; }
.lr-coupon-create .lr-field { flex: 1; min-width: 220px; }
.lr-drill-link { color: var(--colour-action); font-weight: var(--font-weight-bold); text-decoration: none; }
.lr-drill-link:hover { text-decoration: underline; }
.lr-drill-link:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-md); }
.lr-detail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: var(--space-4); }
.lr-pulse { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-4); }
.lr-inline-form { display: inline-flex; vertical-align: middle; }
.lr-book-form { display: flex; flex-direction: column; gap: var(--space-4); max-width: 720px; }
/* Add/Edit book: the form and the reader preview side by side; the frame drops under the form
   when they cannot both fit. The phone frame keeps the walk device's CSS px (402×874) and is
   transform-scaled to the window; the touch layer stands in for the native gesture recognisers. */
.lr-book-layout { display: flex; gap: var(--space-8); align-items: flex-start; flex-wrap: wrap; }
.lr-book-layout .lr-book-form { flex: 1; min-width: 320px; }
.lr-reader-preview { flex: none; position: sticky; top: var(--space-4); }
.lr-reader-preview__title { font-size: var(--font-h3-size); line-height: var(--font-h3-line); font-weight: var(--font-weight-bold); }
.lr-reader-preview__note { color: var(--text-secondary); font-size: var(--font-small-size); line-height: var(--font-small-line); max-width: 402px; }
.lr-reader-preview__flag { color: var(--colour-error); font-weight: var(--font-weight-bold); font-size: var(--font-small-size); line-height: var(--font-small-line); max-width: 402px; }
.lr-reader-preview__phone { position: relative; overflow: hidden; border-radius: var(--radius-md); box-shadow: var(--shadow-2); }
.lr-reader-preview__frame { width: 402px; height: 874px; border: 0; transform-origin: 0 0; display: block; }
.lr-reader-preview__touch { position: absolute; inset: 0; touch-action: none; user-select: none; cursor: pointer; }
.lr-two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(520px, 1fr)); gap: var(--space-4); }
/* The book hero. The cover keeps its width; the facts beside it claim a floor, so on a phone
   (where the two cannot both fit) the facts drop under the cover instead of overflowing. */
.lr-hero { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.lr-hero > :first-child { flex: none; }
.lr-hero > :last-child { flex: 1; min-width: 260px; }
.lr-desc { margin: 0; padding: var(--space-3) 0; color: var(--text-secondary);
  font-size: var(--font-small-size); line-height: var(--font-small-line); overflow-wrap: anywhere; }
/* A Card is a surface, not a layout — it sets no gap between its children, so a header sitting
   straight on top of a table reads as one squashed block. Wrap the card's interior to give the
   pair the page's own rhythm. */
.lr-stack { display: flex; flex-direction: column; gap: var(--space-4); }
/* The sign-in card: one column, centred, capped so it never stretches on a desktop. */
.lr-login { display: flex; justify-content: center; padding: var(--space-12) var(--space-4); }
.lr-login .lr-card { width: 100%; max-width: 420px; }
.lr-login__logo { display: block; height: 40px; width: auto; margin: 0 auto; }
/* The user detail page opens a sent message in a dialog (?message=<id>); its body keeps the message's own line
   breaks there. Page-local — the ratified ListRow one-line rule is no longer overridden anywhere. */
.lr-message__body { white-space: pre-line; margin: 0; font-size: 14px; line-height: 20px; color: var(--colour-text); }
