/*
  Application shell styling. Everything here consumes semantic tokens from css/tokens.css — no raw
  colour or spacing values (ADR-0006, .claude/rules/blazor.md).
*/

html {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  background-color: var(--surface);
  color: var(--ink);

  /*
    WCAG 2.2 SC 1.4.10 (Reflow), and the one line that makes UI-003 hold for content this product does
    not author. A ticket's subject, a customer's message, a company name, a file name — any of them can
    contain a token with no break opportunity in it: a long URL, a 32-character reference, a German
    compound noun. One such token in an <h1> made the ticket page 152px wider than a 375px viewport and
    every element on it scroll sideways, which the browser suite caught at exactly that width.

    `break-word` only breaks a word that cannot fit on a line of its own, so ordinary prose is
    unaffected. Set here rather than per component because it inherits, and because the next screen to
    render somebody else's words should not have to remember.
  */
  overflow-wrap: break-word;
}

/*
  The same, for the two places `break-word` is not enough. It breaks a long token only once the line
  is laid out, but a label beside a checkbox and a code token in a list sit in rows sized to their
  min-content — which `break-word` leaves at the whole token's width, so a tag named with a reference
  or a custom field's key pushed the page sideways. `anywhere` also lowers the min-content. Not on
  `body`: every table would then squeeze into the screen by splitting words, where it should scroll.
*/
label,
code {
  overflow-wrap: anywhere;
}

h1,
h2,
h3 {
  line-height: 1.25;
  margin: 0 0 var(--space-3);
}

h1 {
  font-size: var(--text-xl);
}

a {
  color: var(--accent);
}

/* WCAG 2.2 AA: a visible focus indicator on everything reachable by keyboard. */
:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

/*
  SC 2.4.1 (Bypass Blocks). The first focusable thing on every page, off-screen until it has focus —
  so the twenty-odd shell links are one Tab away from being skipped rather than twenty.

  It is `position: fixed` rather than the usual negative-margin trick because the shell header is the
  first thing in the document: a statically-positioned skip link would push the header down by its own
  height the moment it appeared. `--focus-ring-offset` twice over keeps its own outline clear of the
  viewport edge.
*/
.rl-skip-link {
  position: fixed;
  /* Off-screen until focused. A plain negative length: calc() cannot multiply two lengths, and the
     first draft's `calc(var(--focus-ring-offset) * -10rem)` was invalid, so the declaration was
     dropped and the link sat visible over the header — which axe then reported as a contrast failure
     on the two buttons it was covering. An invalid calc() is silent in exactly the way an undefined
     custom property is. */
  inset-block-start: -10rem;
  inset-inline-start: var(--space-3);
  z-index: 1000;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background-color: var(--accent);
  color: var(--ink-on-accent);
  font-weight: 600;
  text-decoration: none;
}

.rl-skip-link:focus {
  inset-block-start: var(--space-3);
}

/*
  A wide table on a narrow screen. `overflow-x` alone would make a region a keyboard user cannot reach
  or scroll, which fails SC 2.1.1 — so it is a named, focusable region. The `tabindex` is what browsers
  need to let the arrow keys scroll it; the role and label are what stop that stop being an unlabelled
  one on the way through.

  Every table in the product sits in one, through `Components/Shared/ScrollRegion.razor`, because a
  table's width is set by what is in it and no test can bound that: measured at 320px, twenty-two
  screens scrolled sideways on tables alone.

  `position: relative` makes the box the containing block of what it holds. A visually hidden label is
  absolutely positioned, and an absolutely positioned element is clipped only by a scroll box that
  contains it in that sense — without this, the labels in an editable table escaped the box and widened
  the page by the table's own width.
*/
.rl-table-scroll {
  position: relative;
  overflow-x: auto;
  /* Without this the focus ring is clipped by the scroll container it is drawn on. */
  padding: var(--focus-ring-offset);
  margin-inline: calc(var(--focus-ring-offset) * -1);
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- Shared primitives ------------------------------------------------- */

.rl-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* SC 2.5.8, both dimensions — see --target-min. A pill button is already tall enough at the default
     font size; this is what keeps it so when a button's label is a single character. */
  min-block-size: var(--target-min);
  min-inline-size: var(--target-min);
  padding: var(--space-3) var(--space-5);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background-color: var(--accent);
  color: var(--ink-on-accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--transition-fast);
}

.rl-button:hover {
  background-color: var(--accent-strong);
}

.rl-button--quiet {
  background-color: transparent;
  border-color: var(--border-strong);
  color: var(--ink);
}

.rl-button--quiet:hover {
  background-color: var(--surface-sunken);
}

.rl-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);

  /*
    SC 1.4.10 (Reflow), and the half of it a ceiling on the control cannot reach.

    A field is very often a flex item -- `.rl-toolbar` holds several of these -- and a flex item's
    default `min-inline-size: auto` is its **min-content** width, which it will not go below. A field
    containing a `select` inherits that select's min-content, and a select's min-content is its widest
    *option*. So a picker filled with names somebody typed makes the field refuse to shrink, and
    `flex-wrap` does not help: wrapping gives the field its own line, not a narrower one.

    That is why the `max-inline-size: 100%` on the controls below is not enough on its own -- 100% of a
    parent that is itself sized to the same content is the same number. This is the rule that gives it
    a parent worth being 100% of.

    Measured on 2026-09-09 (roadmap row 32): `/tickets`' "Remove a saved view" field held at 429px in a
    320px viewport.
  */
  min-inline-size: 0;
}

.rl-field label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-muted);
}

.rl-field input {
  padding: var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
  color: var(--ink);
  font: inherit;
}

.rl-alert {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--danger);
  color: var(--danger);
  background-color: var(--surface-raised);
  margin-bottom: var(--space-4);
}

/* ---- Blazor's error overlay -------------------------------------------- */

#blazor-error-ui {
  display: none;
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 1000;
  padding: var(--space-4) var(--space-6);
  background-color: var(--surface-raised);
  color: var(--ink);
  border-top: 2px solid var(--danger);
  box-shadow: var(--shadow-raised);
}

#blazor-error-ui .dismiss {
  position: absolute;
  top: var(--space-2);
  right: var(--space-3);
  cursor: pointer;
}

/* Mobile-first: one column, because an 8rem label track and a value do not both fit on a phone.
   The two-column form is restored at --bp-md (40rem) in the breakpoint block at the end of this file. */
.rl-definitions {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2) var(--space-5);
  margin: 0 0 var(--space-5);
}

.rl-definitions dt {
  font-weight: 600;
  color: var(--ink-muted);
}

.rl-definitions dd {
  margin: 0;
}

.rl-note {
  color: var(--ink-muted);
  max-width: 60ch;
}

.rl-empty {
  color: var(--ink-muted);
  padding: var(--space-4);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  max-width: 60ch;
}

/* The character cast (design-language.md, "The cast"). Which drawing shows is the theme's token. */
.rl-cast {
  inline-size: var(--cast-size);
  max-inline-size: 100%;
  margin-block-end: var(--space-4);
}

.rl-cast__art {
  inline-size: 100%;
  block-size: auto;
}

.rl-cast__art--light {
  display: var(--cast-light-display);
}

.rl-cast__art--dark {
  display: var(--cast-dark-display);
}

.rl-key-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  padding: 0;
  margin: 0;
}

.rl-key-list li {
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  background-color: var(--accent-subtle);
  font-size: var(--text-sm);
}

.rl-alert--success {
  border-color: var(--success);
  color: var(--success);
}

/* ---- Two-step verification (SEC-001) ----------------------------------- */

.rl-field__hint {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.rl-steps {
  margin: 0 0 var(--space-5);
  padding-inline-start: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.rl-steps h2 {
  font-size: var(--text-base);
  margin: 0 0 var(--space-2);
}

/*
  A QR symbol has to stay dark-on-light in both themes — inverted codes defeat some scanners — so the
  panel keeps the fixed --qr-ground rather than following the surface token. The border and radius are
  still themed, so it reads as part of the page.
*/
.rl-qr {
  display: inline-flex;
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--qr-ground);
}

.rl-qr img {
  display: block;
  /* The generated SVG has no intrinsic size, so the width has to come from somewhere. */
  width: 13.75rem;
  max-width: 100%;
  height: auto;
}

.rl-code-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-2);
  list-style: none;
  padding: var(--space-4);
  margin: 0 0 var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--surface-sunken);
}

/* An embed snippet: one long line a person copies. It wraps rather than widening the page, and
   pre-wrap keeps every character, so what is copied is what is pasted. */
.rl-snippet {
  display: block;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.rl-code-list code {
  font-size: var(--text-lg);
  letter-spacing: 0.08em;
}

.rl-disclosure {
  margin: var(--space-5) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.rl-disclosure summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--accent-strong);
  margin-bottom: var(--space-3);
}

.rl-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: var(--space-5);
}

.rl-table th,
.rl-table td {
  text-align: start;
  padding: var(--density-row) var(--space-3);
  border-bottom: 1px solid var(--border);
}

.rl-table th {
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.rl-badge {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  border: 1px solid currentcolor;
}

.rl-badge--ok {
  color: var(--success);
}

.rl-badge--pending {
  color: var(--danger);
}

/* Available to a screen reader, absent from the visual layout (WCAG 2.2 AA). */
.rl-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- Admin area (slice 3) ------------------------------------------------
   Tokens only, no raw colour or spacing, and deliberately no media query: the
   navigation is built as a wrapping bar precisely so it needs no breakpoint
   ahead of the design-system pass (tech-debt #9, #22). */

.rl-admin-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-3) 0 var(--space-4);
  margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
}

.rl-admin-nav__group {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}

.rl-admin-nav__category {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.rl-admin-nav__home {
  font-weight: 700;
}

.rl-admin-nav__home,
.rl-admin-nav__link {
  color: var(--ink);
  text-decoration: none;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.rl-admin-nav__home:hover,
.rl-admin-nav__link:hover {
  background-color: var(--surface-sunken);
  text-decoration: underline;
}

/* The current page is marked twice over: colour for sighted readers, and
   aria-current="page" for everyone else. Neither alone is enough. */
.rl-admin-nav__link--current {
  background-color: var(--accent-subtle);
  color: var(--accent-strong);
  font-weight: 600;
}

.rl-admin-section {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.rl-admin-section:first-of-type {
  border-top: none;
}

.rl-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.rl-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0;
}

.rl-card {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
  box-shadow: var(--shadow-raised);
}

.rl-card__title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--ink);
}

.rl-card__summary {
  margin: var(--space-2) 0 0;
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/*
  SEA-006's card layout for the ticket list (roadmap row 28a) — the other answer to the problem
  `.rl-table-scroll` solves for the table, and the reason `--density-row` exists.

  Its own classes rather than `.rl-card` above: that one is the admin home's title-and-summary card,
  and coupling two unrelated screens through one selector is how a design system starts lying.

  `min()` inside the minmax minimum is not optional. A minmax minimum is a FLOOR, so a bare
  `minmax(22rem, 1fr)` does not shrink below 22rem on a narrower viewport — the grid pushes the page
  wider than the screen, which is SC 1.4.10 (Reflow) failing on the very technique used to avoid a
  breakpoint. `.claude/rules/blazor.md` records the two screens that shipped it.
*/
.rl-ticket-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  gap: var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-5);
}

.rl-ticket-card {
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
}

.rl-ticket-card__head {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.rl-ticket-card__title {
  margin: 0;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--ink);
}

/*
  Two columns, term then value, with the same vertical rhythm as a table row so the two shapes of the
  same list read alike. The term column is intrinsic rather than fixed: a long label wraps instead of
  forcing the card wider.
*/
.rl-ticket-card__facts {
  display: grid;
  grid-template-columns: minmax(min(7rem, 40%), auto) 1fr;
  gap: var(--density-row) var(--space-3);
  margin: var(--space-3) 0 0;
}

.rl-ticket-card__facts dt {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.rl-ticket-card__facts dd {
  margin: 0;
}

.rl-fieldset {
  margin: 0 0 var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);

  /* SC 1.4.10. A fieldset's default minimum is its min-content width, so one holding a picker of long
     names or a scrolling table refuses to be narrower than either, and the page goes sideways. */
  min-inline-size: 0;
}

.rl-fieldset legend {
  padding: 0 var(--space-2);
  font-weight: 600;
  color: var(--ink-muted);
  text-transform: capitalize;
}

.rl-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2) 0;
}

.rl-check label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.rl-check__description {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* Not a colour-only signal: the sentence itself says what the constraint is. */
.rl-check__warning {
  color: var(--danger);
  font-size: var(--text-sm);
}

/* --ink-on-accent, not --surface-raised: they happen to be near-identical in both themes today, so
   using the surface role here works by coincidence. The moment the design pass gives raised panels a
   tint, the three most destructive controls in the product would drop to low-contrast text on red. */
.rl-button--danger {
  background-color: var(--danger);
  border-color: var(--danger);
  color: var(--ink-on-accent);
}

/* A token, not `filter: brightness()`. A filter is a raw colour manipulation by another spelling, and
   it moved the wrong way in light theme — brightening #c62828 took white text from 5.6:1 to 4.8:1, so
   hovering a destructive button reduced its contrast. */
.rl-button--danger:hover {
  background-color: var(--danger-strong);
  border-color: var(--danger-strong);
}

/* .rl-alert is danger-coloured by default, which is right for a refusal and wrong
   for "nothing changed". This is the neutral variant, so an informational message
   does not read as an error. */
.rl-alert--neutral {
  border-color: var(--border);
  color: var(--ink-muted);
}

.rl-badge--off {
  color: var(--ink-muted);
}

/* Selects were not styled before this slice, because no screen had one. Same
   treatment as inputs so a form does not look assembled from two eras. */
.rl-field select,
.rl-field textarea {
  padding: var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
  color: var(--ink);
  font: inherit;

  /*
    SC 1.4.10 (Reflow), and the one control that cannot solve it for itself.

    A `select` sizes to its **widest option**, and several pickers in this product are filled with text
    somebody typed: a saved view's name, a canned form's, a rule's. A name with no spaces in it -- an
    order reference, a slug -- makes the control wider than a phone, and unlike a paragraph it cannot
    wrap: `body { overflow-wrap: break-word }` has nothing to break, because the text is inside a
    replaced widget the browser lays out itself.

    So the fix is a ceiling rather than a break. The option text is then clipped or ellipsised by the
    browser, which is the right trade: a name too long to read in a 320px picker is still selectable,
    and the alternative is a page that scrolls sideways.

    Measured on 2026-09-09 (roadmap row 32): `/tickets`' "Remove a saved view" picker reached 436px in
    a 320px viewport, carrying a view another journey had named with a 32-character token, and pushed
    the whole page out with it. It appears only in a *full* run, because the view has to exist -- and
    the first two attempts to fix it went to the wrong element, which is why
    `AssertNoSidewaysScrollAsync` now names what sticks out instead of only how far.
  */
  max-inline-size: 100%;
}

/* The ticket's own description. `pre-wrap` is the load-bearing part: a person types a description
   across several lines, and collapsing them would turn their reproduction steps into one paragraph. */
.rl-ticket-description {
  white-space: pre-wrap;
  max-width: 70ch;
  margin: 0 0 var(--space-5);
  padding: var(--space-4);
  background-color: var(--surface-sunken);
  border-radius: var(--radius-md);
}

/* The activity timeline (LC-014). A list of moments rather than a table: each entry is one sentence
   about one act, and what a reader scans down is the time column — so it gets its own column and
   tabular figures, which keep the sentences from shuffling left and right as the minutes change. */
.rl-timeline {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* A note's background would otherwise square off the rounded corners at either end of the list. */
  overflow: hidden;
}

/* One column on a phone; the 11rem "when/who" track returns at --bp-md. A fixed track is exactly the
   case intrinsic layout cannot solve, which is why this product's first breakpoints exist. */
.rl-timeline li {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-1) var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

.rl-timeline li:last-child {
  border-bottom: 0;
}

.rl-timeline__when {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

.rl-timeline__who {
  font-weight: 600;
}

/* Both sit under the actor rather than beside the time, so the sentence and its reason read as one
   block however long either becomes. */
.rl-timeline__what,
.rl-timeline__why {
  grid-column: 2;
}

.rl-timeline__why {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* An internal note on the timeline (COM-002, UX-007). Told apart from a system event by structure and
   text — the "Internal note" label and the body block — with the tint and the accent edge as
   reinforcement rather than the only signal (WCAG 1.4.1, both themes). */
.rl-timeline__note {
  background-color: var(--accent-subtle);
  border-left: var(--space-1) solid var(--accent);
}

/* An email on the timeline (EM-012, UX-007). A message like a note, so it is a block with its body
   rather than a bodyless event line — but a different edge, because what a customer said in public and
   what a colleague wrote privately must not read alike at a glance. Told apart by its "Email" label
   first; the edge is reinforcement (WCAG 1.4.1, both themes). */
.rl-timeline__message {
  background-color: var(--surface-raised);
  border-left: var(--space-1) solid var(--ink-muted);
}

.rl-timeline__kind {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background-color: var(--surface-raised);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* Under the author, like an event's sentence; pre-wrap for the same reason the description has it —
   the author's line breaks are their paragraphs. */
.rl-timeline__body {
  grid-column: 2;
  margin: 0;
  max-width: 70ch;
  white-space: pre-wrap;
}

/* ADM-002's priority colour, and the only place an administrator's own hex value
   reaches the page. It is set as --rl-swatch on the element itself, which is the
   one exception to "no raw colour in a component": this is data somebody typed,
   not a token a developer skipped.

   A dot beside the label, never a background behind it, so no colour anyone picks
   can put WCAG 2.2 AA text contrast at risk (plan T-D4). The border keeps it
   perceivable when the colour chosen is the surface colour itself, which satisfies
   1.4.11 for a non-text indicator without depending on the value at all. */
.rl-swatch {
  display: inline-block;
  width: var(--space-3);
  height: var(--space-3);
  margin-inline-end: var(--space-2);
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--rl-swatch, var(--surface-sunken));
  vertical-align: baseline;
}

/* §16's filter panel. A grid rather than a column, because a dozen narrow controls
   in one column push the table two screens down; and `auto-fill` rather than a
   fixed count, so it reflows to one column on a phone without a media query.

   The selects are styled beside .rl-field's inputs rather than inside it: a
   multi-select is a box with rows, and the shared rule sets a single-line height. */
.rl-filters__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0 var(--space-4);
  align-items: start;
}

.rl-field select {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
  color: var(--ink);
  font: inherit;

  /* The ceiling again: this rule comes later in the file and would otherwise reset nothing, but a
     reader arriving here should not have to know that the block above already set it. */
  max-inline-size: 100%;
}

/* COM-008 and EM-011, on the timeline entry rather than beside it: an agent decides what to do about a
   failure where the message is. Text first — "Sending", "Sent", "Not sent", "Bounced" — with the muted colour as
   reinforcement, never as the only signal (WCAG 1.4.1). It says "Sent" and never "Delivered", because a
   submission server accepting a message is not the same claim as somebody receiving it. */
.rl-timeline__status {
  grid-column: 2;
  color: var(--ink-muted);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* What the mail server said, kept verbatim so the agent can act on it (EM-011's "visible error"). Not a
   block alert: it belongs to one entry, and a page of alerts for a page of failures reads as noise. */
.rl-error-text {
  grid-column: 2;
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

/* COM-007's preview: a bounded region on the composer, distinct from the editor below it so nobody
   mistakes the read-only rendering for another set of fields. */
.rl-panel {
  margin-block: var(--space-5);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
}

/* The message as it will be sent, with the signature already on it. Pre-wrap for the reason the
   timeline body has it: the author's line breaks are their paragraphs. */
.rl-message-body {
  margin: 0;
  max-width: 70ch;
  white-space: pre-wrap;
  font-family: inherit;
  font-size: inherit;
}

/* ---------------------------------------------------------------- COM-005's rich text (ADR-0013) */

/* A rendered HTML body, wherever one is shown: the timeline, the composer's preview, a canned
   response. It is placed on top of whatever class the caller passed, so it inherits that block's grid
   placement and width and overrides only what HTML brings its own rules for. */
.rl-rich {
  /* The tags carry the structure now, so the pre-wrap every text body has would double every break. */
  white-space: normal;
}

.rl-rich > :first-child {
  margin-block-start: 0;
}

.rl-rich > :last-child {
  margin-block-end: 0;
}

.rl-rich p,
.rl-rich ul,
.rl-rich ol,
.rl-rich blockquote,
.rl-rich pre {
  margin-block: var(--space-2);
}

.rl-rich blockquote {
  margin-inline: 0;
  padding-inline-start: var(--space-3);
  border-inline-start: var(--space-1) solid var(--border);
  color: var(--ink-muted);
}

.rl-rich pre {
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background-color: var(--surface-sunken);
  overflow-x: auto;
}

.rl-rich code {
  font-family: ui-monospace, monospace;
  font-size: var(--text-sm);
}

.rl-rich img {
  max-width: 100%;
  height: auto;
}

/* CAN-004's video, in a body and while it is being written: a 16:9 player that never overflows its
   column, which is what a phone at 320px needs (UX-003). */
.rl-rich iframe,
.rl-editor__surface iframe {
  display: block;
  inline-size: 100%;
  max-inline-size: 40rem;
  aspect-ratio: 16 / 9;
  block-size: auto;
  margin-block: var(--space-2);
  border: 0;
  border-radius: var(--radius-md);
  background-color: var(--surface-sunken);
}

.rl-rich table {
  border-collapse: collapse;
}

.rl-rich th,
.rl-rich td {
  padding: var(--space-2);
  border: 1px solid var(--border);
  text-align: start;
}

/* The editing surface and its toolbar, both created by wwwroot/js/rich-text.js. Without the script
   none of these classes appears and the plain textarea is what the person types in. */
.rl-editor__toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-block-end: var(--space-2);
}

.rl-editor__tool {
  /* SC 2.5.8 is a bounding box, so both dimensions. This rule carried a min-width and no height floor
     at all, which is exactly the shape that passes a width check and fails the criterion. */
  min-inline-size: var(--target-min);
  min-block-size: var(--target-min);
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: var(--surface-raised);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.rl-editor__tool:hover {
  background-color: var(--surface-sunken);
}

/* Focus is visible on both, in both themes: a toolbar you cannot see yourself moving through is a
   toolbar a keyboard user cannot use (UI-010, WCAG 2.4.7). */
.rl-editor__tool:focus-visible,
.rl-editor__surface:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.rl-editor__surface {
  min-height: 12rem;
  max-width: 70ch;
  padding: var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background-color: var(--surface);
  color: var(--ink);
  overflow-y: auto;
}

/* Still in the form and still posted -- it is what carries the body. Hidden rather than removed,
   because removing it would take the field out of the form the enhancement is decorating. */
.rl-editor__source--enhanced {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* NOT-002's in-app notification centre: the shell link and its unread count.

   Every value is a semantic token — a raw hex or px here is review-blocking (.claude/rules/blazor.md)
   — so both themes work without a second rule, and the count keeps a real border rather than relying
   on colour alone, which WCAG 2.2 AA requires of anything carrying meaning. */
/* SC 2.5.8, both dimensions. The plan named this control and the first pass missed it; its own spec
   audit found it. A bell with a count beside it is narrow, which is the shape that fails on width. */
.rl-bell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-block-size: var(--target-min);
  min-inline-size: var(--target-min);
}

.rl-bell__count {
  display: inline-block;
  min-width: 1.5em;
  padding: 0 var(--space-2);
  border: 1px solid currentcolor;
  border-radius: var(--radius-pill);
  background: var(--accent-subtle);
  color: var(--accent-strong);
  font-size: var(--text-sm);
  text-align: center;
}

/* An unread notification in the centre. The left rule is the signal; the weight is the reinforcement,
   because colour alone is not a distinction a person with low vision can rely on. */
.rl-note--unread {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-3);
  font-weight: 600;
}

/* A body keeps its line breaks (roadmap row 58). The daily digest is one line per NOT-007 content, and
   HTML would otherwise run them together; every other body is a single line, which this leaves alone. */
.rl-note__body {
  white-space: pre-line;
}

/* ------------------------------------------------------------------ dashboards (roadmap row 20) */

/* The tile grid. Global rather than scoped to one component because five dashboards use it and none of
   them owns it — which is exactly what this sheet is for, while a tile's own appearance stays in
   DashboardTile.razor.css where only that component can reach it.

   auto-fit rather than a breakpoint: this product has no breakpoint scale and inventing one is roadmap
   row 28's design-system decision, so a dashboard collapses to fewer columns when fewer will fit and
   meets UI-003 with no media query — the same shape the chat console already uses (tech-debt #22). */
.rl-dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}

/* The wider panels — a trend, a breakdown table, a list of names — beside the tiles.

   `min(20rem, 100%)` rather than a bare `20rem`, and this is the idiom every `auto-fit` grid in this
   product needs: a minmax track's minimum is a *floor*, so on a viewport narrower than 20rem the track
   stays 20rem and the grid pushes the page wider than the screen. It made this screen 16px too wide at
   SC 1.4.10's 320px threshold, which the browser suite caught the moment roadmap row 28 measured
   reflow at the criterion's width rather than at a phone's. `min()` lets the track shrink to the
   container when there is no room for the ideal. */
.rl-dashboard-panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

/* CSAT-001's survey page (roadmap row 23), which is the second surface in this product a customer sees
   and the first one that is a whole page rather than a panel inside somebody else's site. One narrow
   column, because everything on it is one question and one answer, and nothing here is a raw value:
   the tokens are the same ones every internal screen uses, so ADR-0006's light and dark both hold on a
   device this product has never met. */
.rl-survey {
  max-width: 34rem;
  margin: 0 auto;
}

/* Ratings stack rather than sit in a row: five labels of unpredictable length, chosen by an Admin and
   eventually translated, do not fit across a phone, and a wrapped row of radio buttons is worse than a
   list. Each row is a large target, which UI-010's WCAG 2.2 AA pass will want anyway. */
.rl-survey__ratings {
  border: 0;
  margin: var(--space-4) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* --radius-2 does not exist and never did: this rule asked for it, the cascade dropped the whole
   declaration, and the rating rows have rendered square-cornered since CSAT shipped. No build error,
   no `dotnet format` complaint — an undefined custom property is legal CSS. Found by roadmap row 28's
   plan audit; --radius-md is what the neighbouring surfaces use. */
.rl-survey__rating {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background-color: var(--surface-raised);
}

/* A whole row is the target, so it is the row that has to meet SC 2.5.8 rather than the radio in it. */
.rl-survey__rating label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: var(--target-min);
  margin: 0;
  cursor: pointer;
}

/* CSAT-002's optional smiley. Sized off the reader's own font rather than in pixels, and `aria-hidden`
   in the markup, because the label beside it already says the same thing in words. */
.rl-survey__face {
  font-size: 1.4em;
  line-height: 1;
}

.rl-survey__question {
  font-weight: 600;
}

/* ---- UI-008's shortcut help (roadmap row 28) ------------------------------------------------- */

.rl-shortcuts {
  max-width: 28rem;
  padding: var(--space-5);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface-raised);
  color: var(--ink);
}

/* A <dialog>'s backdrop is the one surface with no token, because it is not a colour in the theme —
   it is a scrim over whatever is behind it, and it has to work over both. */
.rl-shortcuts::backdrop {
  background-color: rgb(0 0 0 / 45%);
}

.rl-shortcuts__list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0 0 var(--space-4);
}

.rl-shortcuts__list dd {
  margin: 0;
}

.rl-shortcuts kbd {
  display: inline-block;
  min-inline-size: 1.5rem;
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background-color: var(--surface-sunken);
  font-family: inherit;
  font-weight: 600;
  text-align: center;
}

/*
  ---- Breakpoints (roadmap row 28, tech-debt #22) --------------------------------------------------

  The product's first width-based media queries. Every rule above is the NARROW case; these two blocks
  widen it. Mobile-first is not a style preference here — it is what makes an unstyled-at-this-width
  screen degrade to one readable column rather than to a horizontally scrolling one.

  The literals are the scale declared in css/tokens.css, which explains at length why they cannot be
  custom properties: `@media (min-width: var(--bp-md))` is invalid CSS in every browser. The token name
  is in the comment on each block so a grep for --bp-md finds both the declaration and every use.

  Keep this block as the only place in the repository with a width query. A slice that needs a third
  breakpoint is making a design-system decision and belongs in a plan (.claude/rules/blazor.md).
*/

/* --bp-md */
@media (min-width: 40rem) {
  .rl-definitions {
    grid-template-columns: minmax(8rem, max-content) 1fr;
  }

  .rl-timeline li {
    grid-template-columns: minmax(11rem, max-content) 1fr;
  }
}

/*
  --bp-lg's rule is NOT here, and the reason is a trap worth stating where somebody adding the next
  breakpoint will read it.

  It was here, targeting `.rl-shell__main`, and it did nothing at all. That class is declared in
  MainLayout.razor.css, so Blazor's CSS isolation rewrites it to `.rl-shell__main[b-xxxxxxxxxx]` —
  specificity (0,2,0) against this file's (0,1,0) — and App.razor loads the scoped bundle *after*
  app.css. A global media query can never win against a scoped component's own declaration.

  So a width query for a component-scoped class belongs in that component's .razor.css, where scoped
  media queries work normally. This file's block is for global classes only. Found by roadmap row 28's
  own spec audit, in the row that introduced the scale: half of it was silently inert.
*/
