/*
 * app.css — the ONLY stylesheet. Hand-written, external, served from '/assets/'
 * on the app's own origin. Nothing here references an off-origin URL, imports a
 * font, or uses a data: URI. The asset inventory in 03-UI-SPEC.md is the
 * complete list; adding to it needs a CSP review (03-09 owns the policy).
 *
 * Values are TRANSCRIBED from the UI-SPEC (§Spacing Scale, §Typography, §Color,
 * §Components). Every colour is given there with its computed WCAG contrast —
 * they are not re-picked here.
 *
 * Two whole-file invariants, each a security or accessibility contract:
 *   - There is NO animation and NO transition anywhere in this file. Nothing to
 *     gate behind prefers-reduced-motion; it is the simplest way to satisfy
 *     WCAG 2.3.3. (The ONCyber brand's scroll-reveal and card-hover motion are
 *     deliberately NOT ported — this invariant outranks them.)
 *   - Palette is the ONCyber brand (oncyber-branding skill): the base is ink
 *     (#0a0b14) on paper/cream, and the ONLY chromatic accent is violet
 *     (#7c3aed), reserved for inline link text. The primary button is INK, not
 *     violet (brand rule: violet is never a button or a background). The focus
 *     ring, input borders, notices and headings carry no accent hue. The one
 *     gradient the brand calls for lives in the logo mark (brandmark.svg), never
 *     in CSS — a CSS gradient would need background-clip:text and lose contrast
 *     in forced-colors mode.
 */

:root {
  /* Type stacks — system only. A webfont under default-src 'self' would have to
     be self-hosted, and self-hosting a font to render four forms is unjustifiable
     weight on a 3.8 GB shared box. */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* Spacing scale — multiples of 4. */
  --space-xs: 4px;   /* notice left-border width */
  --space-sm: 8px;   /* label->input gap, helper gap */
  --space-md: 16px;  /* notice padding, input h-padding, page gutter */
  --space-lg: 24px;  /* field->field gap, notice bottom margin, card padding <=480 */
  --space-xl: 32px;  /* card padding, h1 bottom margin */
  --space-2xl: 48px; /* control height, page top padding */
  --space-3xl: 64px; /* page top padding >=480 */

  /* Radius is NOT spacing — separate tokens. Nested radius is deliberately
     smaller than its container. */
  --radius-sm: 6px;  /* input, button, notice */
  --radius-md: 8px;  /* card */

  /* Colour — ONCyber brand palette, mapped from the oncyber-branding skill's
     tokens with WCAG contrast recomputed against this app's surfaces (white card
     on a cream page). The base is ink + paper; violet is the sole accent hue. */
  --page: #f7f6f1;          /* ONCyber cream (paper-2) — canvas */
  --surface: #ffffff;       /* card */
  --text: #0a0b14;          /* ONCyber ink — 20.4:1 on white, ~19:1 on cream — AAA */
  --text-muted: #4a4c63;    /* ONCyber text-2 — 8.4:1 on white, 7.8:1 on cream — AAA */
  --border-input: #767f8a;  /* 4.1:1 on white — AA (>=3:1 boundary, WCAG 1.4.11) */
  --border-card: #e3e1d8;   /* ONCyber line — decorative only, carries no information */
  --accent: #0a0b14;        /* PRIMARY BUTTON background = ink (brand: violet is never a button) — 20.4:1 with #fff text */
  --accent-hover: #15172a;  /* ONCyber ink-2 — 17:1 with #fff */
  --accent-active: #1f2238; /* ONCyber ink-3 */
  --violet: #7c3aed;        /* ONCyber violet — inline LINK text ONLY. 5.7:1 on white, 5.3:1 on cream — AA */
  /* The focus ring is ink, ONE colour everywhere, at a 2px offset so a light
     card/page gap sits between ring and control: ring<->gap ~20:1 and gap<->button
     ~20:1, both clear. Deliberately not violet — a violet ring on an ink button is
     muddy, and a second ring colour for dark surfaces is a rule people forget. */
  --focus-ring: #0a0b14;
  --disabled-bg: #5c6470;   /* 6.0:1 with white text — AA */
  --notice-bg: #efeee7;     /* ONCyber paper-3 — reads as a panel against the cream page; the left bar carries the signal */
  --notice-bar: #4a4c63;
  --error-bg: #fdf0ef;
  --error-text: #8c1d18;    /* 8.2:1 on --error-bg — AAA */
  --error-bar: #b3261e;     /* 6.5:1 on white — AA */
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  background: var(--page);
  /* Top-aligned, NEVER vertically centred: vertical centring fights the mobile
     keyboard, which shoves a centred card off-screen on exactly the page where
     the user must type a code. 64px top >=480 (below). */
  padding: var(--space-2xl) var(--space-md);
}

/* --- Card ---------------------------------------------------------------- */

.card {
  max-width: 400px;
  margin: 0 auto;               /* centred horizontally, top-aligned */
  padding: var(--space-xl);
  background: var(--surface);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-md);
}

/* --- Typography ---------------------------------------------------------- */

/* The brand lockup: the ONCyber spiral mark + the wordmark, on one line. The
   mark is an <img> (same-origin SVG, no data: URI) so the CSP contract holds; the
   text wordmark stays live text (more accessible than an image of text, and the
   markup is asserted by the view tests). */
.brand {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin: 0 0 var(--space-lg);
}

.brand__mark {
  flex: none;
  display: block;
  width: 24px;
  height: 24px;
}

/* The wordmark uses the regular sans UI font, matching the rest of the UI. The
   ONCyber identity is carried by the spiral mark and the palette — not a mono
   eyebrow. */
.wordmark {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text-muted);
}

.wordmark__org {
  color: var(--text);
  font-weight: 600;
}

/* Headings carry the ONCyber typographic identity within the system stack:
   heavy weight + negative tracking. That tension is the whole point (SKILL §4);
   a 600-weight default-tracked heading would not read as ONCyber. */
h1 {
  margin: 0 0 var(--space-xl);
  font-size: 24px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.sub {
  margin: 0 0 var(--space-lg);
  color: var(--text-muted);
}

/* The signed-in placeholder paragraph renders the escaped email (<=254) and
   display name (<=60); anywhere-wrap keeps a long address from overflowing the
   card (§UI Considerations, long-text/static-content). */
.signed-in {
  margin: 0 0 var(--space-lg);
  overflow-wrap: anywhere;
}

/* --- Field --------------------------------------------------------------- */

.field {
  margin-bottom: var(--space-lg);
}

.field label {
  display: block;
  margin-bottom: var(--space-sm);
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
}

.field__help {
  display: block;
  margin-bottom: var(--space-sm);
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
}

/* Every input is >=16px: below 16px iOS Safari zooms the viewport on focus.
   No `placeholder` is styled or used on ANY field in this phase — a placeholder
   in the uppercase mono code field is indistinguishable from a typed code. */
.field input {
  width: 100%;
  height: var(--space-2xl);       /* 48px — clears WCAG 2.5.5 (44px) and 2.5.8 */
  padding: 0 var(--space-md);
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
}

/* The native file input renders its "Choose File" button top-aligned, and the
   button (::file-selector-button) is NOT a real flex item in Chrome, so
   `display:flex; align-items:center` does not centre it. The one method that
   reliably does: drop the fixed height and give the input EQUAL top/bottom
   padding — equal space above and below centres the button vertically whatever
   its intrinsic height. Horizontal padding stays --space-md, so the button
   remains LEFT-aligned. 12px block padding lands the box at ~48px to match the
   fixed-height text inputs beside it (button ~21px + 24px padding + 2px border);
   it is intentionally a raw value, not a --space token, because the target is a
   pixel height match, not a spacing-scale step. */
/* Match the 48px height of the text inputs beside it so the two toolbar boxes —
   and the buttons under them — line up. The native button is not a flex item, so
   height is set by box math: 11px top/bottom padding + the ~24px button (see its
   line-height below) + 2px border = 48px, exactly the text inputs' height. */
.field input[type="file"] {
  height: auto;
  padding-top: 11px;
  padding-bottom: 11px;
}

/* The native "Choose File" button renders in the BROWSER's default font — the one
   control on the page that looks foreign (the ::file-selector-button pseudo does
   not inherit the page font). Set the font explicitly and give it the app's
   secondary-control look (UI font, neutral outline, --radius-sm) so the picker
   reads as part of the UI. The value text ("No file chosen" → the chosen
   filename) already inherits the page font from `.field input` above.
   A single "Upload file"-only control is NOT possible here: choosing a file with
   no visible input needs JS to open the picker and echo the filename, and app.js
   is deliberately inert (no handlers, no `.value` reads — views.test.ts), and the
   form must work with JS disabled (IC-2). No accent hue and no motion are
   introduced — the whole-file invariants hold. */
.field input[type="file"]::file-selector-button {
  margin-right: var(--space-md);
  padding: var(--space-xs) var(--space-md);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;                    /* 14px text + 8px padding + 2px border = 24px tall */
  vertical-align: middle;
  color: var(--text);
  background: var(--page);
  border: 1px solid var(--border-input);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.field input[type="file"]::file-selector-button:hover {
  border-color: var(--text-muted);
}

.field input:hover {
  border-color: var(--text-muted);
}

/* Focus uses OUTLINE, never box-shadow: box-shadow is dropped in Windows High
   Contrast / forced-colors mode, and a focus ring that vanishes for the users
   most dependent on it is worse than none. Offset 2px, border unchanged. */
.field input:focus {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Invalid keeps the border at 1px — widening to 2px reflows the layout by a
   pixel on error. Colour is not the only invalid signal: the linked error text
   carries it via aria-describedby. */
.field input[aria-invalid="true"] {
  border-color: var(--error-bar);
}

/* The OTP field: monospace, 20px, 0.15em tracking, uppercased for display.
   Harmonizing this to the sans stack undoes the Crockford alphabet's purpose —
   the alphabet drops I/L/O/U precisely so a 0/O misread cannot cost a sign-in,
   and a proportional font re-tightens exactly those glyphs. Uppercase needs no
   JS: text-transform here, autocapitalize on the input, and the server
   uppercases before hashing — three independent layers. */
.field input.code {
  font-family: var(--font-mono);
  font-size: 20px;
  line-height: 1.2;
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

/* --- Button -------------------------------------------------------------- */
/* Primary only. There is no secondary button: the sole secondary action
   ("Start over") is a link. */

.btn {
  width: 100%;
  height: var(--space-2xl);       /* 48px */
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  color: #fff;
  background: var(--accent);      /* accent use #1 of 2 */
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.btn:hover {
  background: var(--accent-hover);
}

.btn:active {
  background: var(--accent-active);
}

/* outline-offset: 2px here is REQUIRED for WCAG 2.4.11, not decoration. The ring
   (--focus-ring, ink) and the button (--accent, ink) are the same colour, so a
   zero-offset ring would be invisible ON the button. The 2px offset places a
   light card/page gap between them: ring<->gap ~20:1 and gap<->button ~20:1,
   both clear. Reduce the offset to zero and the ring vanishes into the button and
   fails 2.4.11. Do not "tidy" this to zero. */
.btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* The disabled style is the ENTIRE loading affordance (set by app.js on submit):
   the button greys out and reads "received, working". White on #5c6470 stays
   readable at 6.0:1 — a disabled button the user cannot read looks broken. */
.btn:disabled {
  background: var(--disabled-bg);
  cursor: not-allowed;
}

/* --- Notice -------------------------------------------------------------- */
/* Text and a left border. No icons (a glyph is the fastest visual tell and
   would need an icon library or a CSP-blocked data: SVG anyway).
   THERE IS NO SUCCESS/GREEN NOTICE VARIANT, AND ONE MUST NOT BE ADDED (SR-7):
   this phase has no success state to render — both successes are redirects — so
   a green style would exist only to be misapplied to the generic notice, at
   which point green = authorized and the enumeration flow is over. */

.notice {
  margin-bottom: var(--space-lg);
  padding: var(--space-md);
  border-left: var(--space-xs) solid var(--notice-bar);
  border-radius: var(--radius-sm);
  font-size: 16px;
  font-weight: 400;
  color: var(--text);
  background: var(--notice-bg);
  /* The strings are constants of known length, but the echoed email (<=254
     chars) is not. */
  overflow-wrap: anywhere;
}

.notice--error {
  border-left-color: var(--error-bar);
  color: var(--error-text);
  background: var(--error-bg);
}

/* --- Link ---------------------------------------------------------------- */
/* Underlined ALWAYS, never underline-on-hover-only — a link distinguished by
   colour alone fails WCAG 1.4.1. */

.link {
  color: var(--violet);           /* the sole chromatic accent — link text only */
  text-decoration: underline;
}

.link:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* --- Responsive ---------------------------------------------------------- */
/* One breakpoint. >=480px: more generous card padding and page top padding.
   Nothing else changes. */

@media (min-width: 480px) {
  body {
    padding-top: var(--space-3xl);   /* 64px */
  }
}

/* Below 480px the base rules already apply the tighter values: card padding is
   32px (--space-xl) above and the design calls for 24px on the narrowest
   screens. */
@media (max-width: 479px) {
  .card {
    padding: var(--space-lg);        /* 24px */
  }
}

/* ========================================================================== */
/* Phase 4 — the workspace file-exchange surface.                             */
/*                                                                            */
/* ADDITIVE ONLY. Everything above is Phase 3 and is not touched. These rules */
/* extend the same design system: the 4-multiple spacing scale, the 4 type    */
/* sizes / 2 weights, the 60/30/10 colour split, and the two whole-file       */
/* invariants restated for this phase:                                        */
/*   - NO animation and NO transition (IC-4). Nothing below declares either.  */
/*   - NO success/green notice (IC-5) — success confirmations reuse the       */
/*     neutral `.notice`; the only new colour USE is the destructive button,  */
/*     which reuses the existing --error-bar / --error-text tokens (no new    */
/*     hue). The accent (#0b5cab) stays reserved for .btn and .link.          */
/* Every value is TRANSCRIBED from 04-UI-SPEC (§Layout, §Color, §Components).  */
/* ========================================================================== */

/* --- .app — the wide workspace container --------------------------------- */
/* Mirrors .card's panel rhythm at a wider max-width (04-UI-SPEC §Color maps
   #ffffff to the .app surface; "matching the card's responsive rhythm"). The
   page's top/side padding is on <body>; below 480px the panel padding tightens
   exactly as the card's does. */

.app {
  max-width: 880px;
  margin: 0 auto;
  padding: var(--space-xl);          /* 32px, as .card */
  background: var(--surface);
  border: 1px solid var(--border-card);
  border-radius: var(--radius-md);
}

/* --- .appbar — persistent workspace header ------------------------------- */
/* Flex row: wordmark left, identity + the inherited _logout form right. Wraps
   to two lines below 480px. The sign-out control is the inherited .btn inside
   the inherited form (one button per form, IC-10) — no new button style. */

/* Three-column grid so the nav link sits in the TRUE centre of the appbar: the
   brand and identity take equal 1fr columns, so the `auto` nav column between
   them is centred regardless of their differing widths. (space-between centred it
   only between the two, which drifts off-centre once the brand lockup is wider.)
   Below 480px it collapses to a single stacked column — see the media query. */
.appbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
  min-height: var(--space-2xl);      /* 48px floor */
  margin-bottom: var(--space-lg);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--border-card);
}

/* The brand lockup carries a 24px bottom margin in the card; inside the appbar
   that margin would offset it vertically, so it is reset here. Pinned to the
   start of its 1fr column. */
.appbar .brand {
  margin: 0;
  justify-self: start;
}

.appbar__identity {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-md);
  justify-self: end;                 /* right edge of its 1fr column */
}

/* Below 480px the three-column row cannot hold brand + nav + identity, so stack
   them left-aligned (replacing the flex-wrap the grid removed). */
@media (max-width: 479px) {
  .appbar {
    grid-template-columns: 1fr;
  }
  .appbar__nav,
  .appbar__identity {
    justify-self: start;
  }
}

.appbar__name {
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
  overflow-wrap: anywhere;           /* the display name is user-supplied */
}

/* --- .breadcrumb --------------------------------------------------------- */
/* 14/400 muted; a plain `/` text separator (never an icon); long names wrap.
   Segments are inherited .link (accent, underlined) except the current folder. */

.breadcrumb {
  margin-bottom: var(--space-lg);
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}

.breadcrumb__sep {
  padding: 0 var(--space-sm);
  color: var(--text-muted);
}

.breadcrumb__current {
  color: var(--text);
}

/* --- .toolbar — create-folder + upload ----------------------------------- */
/* Two inherited .field + .btn forms, side by side >=480px, stacked below (the
   flex-basis wraps them when the row cannot hold both). */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
  margin-bottom: var(--space-lg);
}

.toolbar__form {
  flex: 1 1 240px;
  min-width: 0;
}

/* --- h2 — section heading (new to the app in Phase 4) -------------------- */
/* Reuses the Body size at 600 weight — no new type token (§Typography). Heading
   order is h1 (folder) -> h2 (Folders, Files), never skipped. */

h2 {
  margin: 0 0 var(--space-sm);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: -0.01em;
}

/* --- .listing — the file/folder table ------------------------------------ */
/* A semantic full-width <table>, one section per type under an <h2>. Row
   padding md, 1px bottom border, NO zebra striping (a colour with no
   information). The name cell holds the attacker-controlled name → wraps. */

.listing__table {
  width: 100%;
  border-collapse: collapse;
}

.listing h2 {
  margin-top: var(--space-lg);       /* folders<->files section gap */
}

.listing__table th {
  padding: var(--space-sm) var(--space-md) var(--space-sm) 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
  text-align: left;
  border-bottom: 1px solid var(--border-card);
}

.listing__table td {
  padding: var(--space-md) var(--space-md) var(--space-md) 0;
  border-bottom: 1px solid var(--border-card);
  vertical-align: top;
}

/* The name cell is the attacker-controlled original_name / folder name: it MUST
   wrap so a crafted 255-char or markup-bearing name cannot break the layout
   (IC-3, T-04-07). Escaping is done in the template. */
.listing__name {
  overflow-wrap: anywhere;
}

.listing__meta {
  font-size: 14px;
  color: var(--text-muted);
  white-space: nowrap;
}

.listing__actions {
  white-space: nowrap;
}

/* --- .empty — empty-folder state ----------------------------------------- */
/* Centred text block; the toolbar above stays visible and the body points at
   it. No illustration (an image would need an off-origin / data: asset, IC-1). */

.empty {
  padding: var(--space-xl) var(--space-md);
  text-align: center;
}

.empty__title {
  margin: 0 0 var(--space-sm);
  font-size: 16px;
  font-weight: 600;
}

.empty__body {
  margin: 0;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
}

/* --- .btn--danger — the destructive button (new) ------------------------- */
/* Used ONLY on the delete-confirm page's `Delete file` button, applied as
   `class="btn btn--danger"` so it inherits every base .btn rule (48px height,
   --radius-sm, 16/600 font, #fff text, and the near-black focus ring at 2px
   offset that WCAG 2.4.11 depends on). It overrides ONLY the background: white
   on --error-bar #b3261e is 6.5:1 (AA); hover --error-text #8c1d18 is 8.2:1 with
   white (AA). This reuses the existing destructive tokens — no new hue. */

.btn--danger {
  background: var(--error-bar);      /* #b3261e — 6.5:1 with #fff */
}

.btn--danger:hover {
  background: var(--error-text);     /* #8c1d18 — 8.2:1 with #fff */
}

.btn--danger:active {
  background: var(--error-text);
}

/* --- Delete-confirm page copy -------------------------------------------- */
/* The disclosure paragraph carries the escaped filename; anywhere-wrap keeps a
   long name inside the narrow confirm card. Cancel is a .link (IC-10). */

.confirm-text {
  margin: 0 0 var(--space-lg);
  overflow-wrap: anywhere;
}

.confirm-cancel {
  display: inline-block;
  margin-top: var(--space-lg);
}

/* --- Responsive — the single 480px breakpoint --------------------------- */
/* Consistent with Phase 3's one breakpoint. Below 480px the .app panel padding
   tightens to 24px exactly as the card does. The appbar and toolbar already
   wrap via flex-wrap at any width, so no extra rules are needed for them. */

@media (max-width: 479px) {
  .app {
    padding: var(--space-lg);        /* 24px, as .card */
  }
}

/* ========================================================================== */
/* Phase 5 — the open audit-log surface.                                      */
/*                                                                            */
/* ADDITIVE ONLY. Everything above is Phases 3/4 and is not touched. The       */
/* audit log is another `list-collection`: it REUSES .app, .appbar, .listing   */
/* and .empty. The only new CSS is the three rules below plus one appbar-nav   */
/* gap rule to seat the Files ↔ Audit log context link. Every value is         */
/* TRANSCRIBED from 05-UI-SPEC (§Components, §Spacing, §Typography):            */
/*   - NO new hue: no rule below names a colour except the existing            */
/*     --text-muted token. The accent stays reserved for .btn and .link.       */
/*   - NO new spacing/type token — only --space-sm/-lg and weight 600 (Label). */
/*   - NO animation, NO transition (IC-4). Nothing below declares either.      */
/*   - The staff-row distinction (AUDIT-04) is TEXT + WEIGHT, colour-           */
/*     independent (WCAG 1.4.1): the `.wordmark__org` prefix is composed in the */
/*     template, and this rule adds only the 600 weight reinforcement.         */
/* ========================================================================== */

/* --- .listing__actor--staff — the staff-row weight cue (AUDIT-04) --------- */
/* Weight 600 is the existing Label weight — NOT a new type token. This is the */
/* secondary, colour-independent reinforcement of the composed `ONCyber · `    */
/* text prefix; it adds NO colour and NO accent (IC-6). */

.listing__actor--staff {
  font-weight: 600;
}

/* --- .appbar__nav — the Files ↔ Audit log context link slot --------------- */
/* One flex slot seating the single inherited .link between the wordmark and   */
/* the identity block. Adds only a gap using the existing token — no new hue,  */
/* no new component, no button (IC-10). */

.appbar__nav {
  display: flex;
  gap: var(--space-md);
  justify-self: center;              /* centred in the appbar's middle grid column */
}

/* --- .pager — keyset pagination control ---------------------------------- */
/* A flex row of inherited .links below the table (Older → / ← Newest). GET    */
/* navigations only (IC-2/IC-10) — no buttons, no JS. */

.pager {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
}

/* --- .log-filter — the folder-filter context line ------------------------ */
/* Shown only when a ?folder=<uuid> filter is active. Reuses the Meta type     */
/* role (14/400 --text-muted) — no new token. The folder name is attacker-     */
/* controlled → it wraps (IC-3). */

.log-filter {
  margin-bottom: var(--space-lg);
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
  overflow-wrap: anywhere;
}
