/* ==========================================================================
   idms — dashboard page set stylesheet
   Mirror of uiux/v1/design-system/idms-design-system.md §15 (tokens) plus the
   classes the files under uiux/v1/dashboard/ read. The token block is a copy,
   not a source: when the design system changes it changes there first, and
   this file follows in the same change (idms-dashboard.md §3.2).

   Palette: "Charcoal & Amber" — near-black charcoal canvas, one warm amber as
   the only saturated hue, warm stone structure. Dark is the shipped default.

   Responsive: mobile-first, min-width only, at --bp-md (768px) and
   --bp-lg (1024px). No max-width media query exists in this file.
   Every inset, margin, padding and border is a CSS logical property.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonts — self-hosted, same origin. No third-party CDN anywhere.
   Vazirmatn (SIL OFL 1.1) · JetBrains Mono (SIL OFL 1.1)
   The faces are vendored beside this stylesheet, so the `src` paths are
   relative to `assets/` and resolve identically for every route in the set,
   whatever its bucket depth. JetBrains Mono ships the Latin subset only,
   because the mono face is used for identifiers, paths and licence keys.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Vazirmatn";
  src: url("fonts/Vazirmatn-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Vazirmatn";
  src: url("fonts/Vazirmatn-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Vazirmatn";
  src: url("fonts/Vazirmatn-SemiBold.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Vazirmatn";
  src: url("fonts/Vazirmatn-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("fonts/JetBrainsMono-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* ==========================================================================
   Tokens — shared, theme-invariant (design-system §15.1)
   ========================================================================== */
:root {
  /* Type */
  --font-text: "Vazirmatn", "Segoe UI", Tahoma, system-ui, sans-serif;
  --font-display: "Vazirmatn", "Segoe UI", Tahoma, system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;

  --fs-display-lg: clamp(2.5rem, 4vw + 1rem, 4rem); --lh-display-lg: 1.15;
  --fs-display-md: 2rem;    --lh-display-md: 2.8125rem;
  --fs-heading-lg: 1.5rem;  --lh-heading-lg: 2.25rem;
  --fs-heading-md: 1.25rem; --lh-heading-md: 1.875rem;
  --fs-heading-sm: 1rem;    --lh-heading-sm: 1.5rem;
  --fs-body-lg: 1rem;       --lh-body-lg: 1.75rem;
  --fs-body-md: .875rem;    --lh-body-md: 1.5625rem;
  --fs-body-sm: .8125rem;   --lh-body-sm: 1.4375rem;
  --fs-label: .8125rem;     --lh-label: 1.25rem;
  --fs-caption: .75rem;     --lh-caption: 1.1875rem;
  --fs-code: .8125rem;      --lh-code: 1.3125rem;
  --fw-regular: 400; --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700;

  /* Space — 4pt grid */
  --space-0: 0; --space-1: .25rem; --space-2: .5rem; --space-3: .75rem;
  --space-4: 1rem; --space-5: 1.25rem; --space-6: 1.5rem; --space-7: 2rem;
  --space-8: 2.5rem; --space-9: 3rem; --space-10: 4rem; --space-11: 5rem; --space-12: 6rem;
  --stack-tight: var(--space-2); --stack-normal: var(--space-4); --stack-loose: var(--space-6);
  --rhythm-h-to-p: var(--space-2); --section-gap-y: var(--space-9);

  /* Radii */
  --radius-none: 0; --radius-sm: 6px; --radius-md: 10px;
  --radius-lg: 14px; --radius-xl: 20px; --radius-pill: 9999px;

  /* Borders + icons */
  --border-thin: 1px; --border-thick: 2px;
  --icon-sm: 16px; --icon-md: 20px; --icon-lg: 24px; --icon-xl: 32px;

  /* Containers */
  --page-max-marketing: 1280px; --page-max-app: 1680px;
  --content-max-dash: 1680px; --content-max-boxed: 1440px;
  --content-max-form: 480px; --content-max-prose: 68ch;
  --sidebar-w: 264px; --sidebar-w-collapsed: 64px;

  /* Motion */
  --dur-instant: 80ms; --dur-fast: 160ms; --dur-base: 240ms;
  --dur-slow: 400ms; --dur-drift: 24s;
  --ease-standard: cubic-bezier(.2, 0, 0, 1);
  --ease-emphasized: cubic-bezier(.3, 0, 0, 1);
  --ease-exit: cubic-bezier(.4, 0, 1, 1);

  /* Layers */
  --z-base: 0; --z-sticky: 100; --z-rail: 200; --z-dropdown: 300;
  --z-overlay: 400; --z-modal: 500; --z-toast: 600; --z-tooltip: 700;

  /* Brand constants */
  --gradient-brand-soft: linear-gradient(135deg, color-mix(in srgb, var(--brand-500) 16%, transparent), color-mix(in srgb, var(--brand-200) 10%, transparent));
  --glow-accent: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent),
                 0 8px 40px -8px color-mix(in srgb, var(--accent) 55%, transparent);
  --glow-focus: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
  --texture-grid: radial-gradient(circle at 1px 1px, color-mix(in srgb, var(--ink) 12%, transparent) 1px, transparent 0);
  --texture-grid-size: 24px 24px;

  /* Brand swatches (constant) */
  --brand-50: #FBF3E0; --brand-100: #F7E4B6; --brand-200: #F2D48C;
  --brand-300: #EDC368; --brand-400: #E8B24A; --brand-500: #E0A32E;
  --brand-600: #C2881C; --brand-700: #8A6210; --brand-800: #6B4B0C;
  --brand-900: #4A3408; --brand-950: #2A1D04;
  --neutral-0: #FFFFFF; --neutral-50: #F5F2EC; --neutral-100: #EDEAE4;
  --neutral-200: #D3CEC6; --neutral-300: #A9A29A; --neutral-400: #857E76;
  --neutral-500: #6E6862; --neutral-600: #4A4640; --neutral-700: #33302C;
  --neutral-800: #1C1C1C; --neutral-900: #121212; --neutral-950: #0C0C0C;

  /* App-shell layout (§17.1, §17.3) */
  --appbar-h: 64px;
  --shell-gap: var(--space-7);
  --gap-cards: var(--space-5);
  --card-pad: var(--space-5);
  --btn-min-w: 88px;
  --input-h: 40px;
  --focus-offset: 2px;

  /* The rail reserves no width below --bp-lg: the drawer replaces it there. */
  --rail-w: 0px;
}

/* ==========================================================================
   Dark theme — shipped default (design-system §15.2)
   ========================================================================== */
:root, [data-theme="dark"] {
  --bg: #121212;
  --surface: #1C1C1C;
  --surface-raised: #262626;
  --ink: #F5F2EC;
  --ink-muted: #A9A29A;
  --border: #2A2A2A;
  --border-strong: #6E6862;
  --glass: color-mix(in srgb, #121212 72%, transparent);

  --accent: #E0A32E;
  --accent-hover: #E8B24A;
  --accent-pressed: #C2881C;
  --on-accent: #1A1206;
  --focus-ring: #F5CE7A;
  --overlay: #000000B3;
  --gradient-brand: linear-gradient(135deg, #E0A32E 0%, #EDC368 45%, #F2D48C 100%);

  --success: #7FBF7F; --success-subtle: #17281A;
  --warning: #E8B457; --warning-subtle: #2E2410;
  --danger:  #E4736B; --danger-subtle:  #311615;
  --info:    #E0A32E; --info-subtle:    #2B2210;

  --data-1: #E0A32E; --data-2: #6FA8DC; --data-3: #C7B24A;
  --data-4: #7FBF7F; --data-5: #E4736B; --data-6: #9A938B;

  --elev-1: inset 0 0 0 1px var(--border);
  --elev-2: inset 0 0 0 1px var(--border-strong), 0 8px 32px -16px rgb(0 0 0 / .90);
  --elev-3: inset 0 0 0 1px var(--border-strong), 0 16px 48px -12px rgb(0 0 0 / .95);
  --elev-4: inset 0 0 0 1px var(--border-strong), 0 32px 80px -16px rgb(0 0 0 / 1);
}

/* ==========================================================================
   Light theme (design-system §15.3)
   ========================================================================== */
[data-theme="light"] {
  --bg: #F7F4EE;
  --surface: #FFFFFF;
  --surface-raised: #FFFFFF;
  --ink: #1A1712;
  --ink-muted: #4A4640;
  --border: #E1DCD2;
  --border-strong: #6E6862;
  --glass: color-mix(in srgb, #F7F4EE 78%, transparent);

  --accent: #8A6210;
  --accent-hover: #6B4B0C;
  --accent-pressed: #4A3408;
  --on-accent: #FFFFFF;
  --focus-ring: #8A6210;
  --overlay: #1212127A;
  --gradient-brand: linear-gradient(135deg, #8A6210 0%, #C2881C 45%, #E0A32E 100%);

  --success: #2F7A33; --success-subtle: #EDF6EC;
  --warning: #7A4E08; --warning-subtle: #FCF4E4;
  --danger:  #A32734; --danger-subtle:  #FAECEC;
  --info:    #8A6210; --info-subtle:    #FBF3E0;

  --data-1: #8A6210; --data-2: #2A6FA8; --data-3: #7A6A10;
  --data-4: #3F7A33; --data-5: #A33A2E; --data-6: #6E6862;

  --elev-1: 0 1px 2px rgb(26 23 18 / .06);
  --elev-2: 0 2px 8px rgb(26 23 18 / .10);
  --elev-3: 0 8px 24px rgb(26 23 18 / .14);
  --elev-4: 0 16px 48px rgb(26 23 18 / .20);
}

/* Mirror the OS preference only after «خودکار» has been chosen. */
@media (prefers-color-scheme: light) {
  [data-theme="auto"] {
    --bg: #F7F4EE; --surface: #FFFFFF; --surface-raised: #FFFFFF;
    --ink: #1A1712; --ink-muted: #4A4640;
    --border: #E1DCD2; --border-strong: #6E6862;
    --glass: color-mix(in srgb, #F7F4EE 78%, transparent);
    --accent: #8A6210; --accent-hover: #6B4B0C; --accent-pressed: #4A3408;
    --on-accent: #FFFFFF; --focus-ring: #8A6210; --overlay: #1212127A;
    --gradient-brand: linear-gradient(135deg, #8A6210 0%, #C2881C 45%, #E0A32E 100%);
    --success: #2F7A33; --success-subtle: #EDF6EC;
    --warning: #7A4E08; --warning-subtle: #FCF4E4;
    --danger: #A32734; --danger-subtle: #FAECEC;
    --info: #8A6210; --info-subtle: #FBF3E0;
    --data-1: #8A6210; --data-2: #2A6FA8; --data-3: #7A6A10;
    --data-4: #3F7A33; --data-5: #A33A2E; --data-6: #6E6862;
    --elev-1: 0 1px 2px rgb(26 23 18 / .06);
    --elev-2: 0 2px 8px rgb(26 23 18 / .10);
    --elev-3: 0 8px 24px rgb(26 23 18 / .14);
    --elev-4: 0 16px 48px rgb(26 23 18 / .20);
  }
}

/* ==========================================================================
   Brand palette — one seed per preset, everything else mixed from it.
   The shipped map above is the platform's own amber. The six alternates are
   one seed each; every value a theme map needs is mixed from that seed and the
   theme's own ink, so the same rule darkens the accent on the light plane and
   lightens it on the dark one, and one block serves all three theme contexts.
   A palette is not a second theme: it moves the accent and the tokens that hang
   off it — the accent's three states, the ink that sits on it, the focus ring,
   the brand gradients, the soft wash, the info pair and the first data series
   — and touches no surface, no ink, no border and no verdict colour. The
   selector carries both the element and the attribute test so it beats the
   bare `[data-theme]` maps and their `prefers-color-scheme` mirror whatever
   order the sheet declares them in.
   ========================================================================== */
:root {
  --palette-amber: #E0A32E; --palette-emerald: #2F9E77; --palette-blue: #3B7BE0;
  --palette-violet: #7A63D8; --palette-rose: #CE5480; --palette-orange: #DE7A28;
  --palette-slate: #6E7E90;
}
[data-palette="amber"]   { --brand-seed: var(--palette-amber); }
[data-palette="emerald"] { --brand-seed: var(--palette-emerald); }
[data-palette="blue"]    { --brand-seed: var(--palette-blue); }
[data-palette="violet"]  { --brand-seed: var(--palette-violet); }
[data-palette="rose"]    { --brand-seed: var(--palette-rose); }
[data-palette="orange"]  { --brand-seed: var(--palette-orange); }
[data-palette="slate"]   { --brand-seed: var(--palette-slate); }

html[data-palette]:not([data-palette="amber"]) {
  /* 66 / 34 is the measured floor: at 72 the lightest seed (orange) landed at
     4.33:1 as text on its own subtle plane, under AA's 4.5. Every palette is
     over 4.6 on both planes and over 4.5 for ink on a filled control at this
     split, in both themes. */
  --accent:         color-mix(in oklab, var(--brand-seed) 66%, var(--ink));
  --accent-hover:   color-mix(in oklab, var(--brand-seed) 54%, var(--ink));
  --accent-pressed: color-mix(in oklab, var(--brand-seed) 82%, var(--ink));
  --focus-ring:     color-mix(in oklab, var(--brand-seed) 45%, var(--ink));
  --on-accent:      color-mix(in oklab, var(--brand-seed) 8%, var(--bg));
  --gradient-brand: linear-gradient(135deg,
    color-mix(in oklab, var(--brand-seed) 82%, var(--ink)) 0%,
    color-mix(in oklab, var(--brand-seed) 66%, var(--ink)) 45%,
    color-mix(in oklab, var(--brand-seed) 54%, var(--ink)) 100%);
  --gradient-brand-soft: linear-gradient(135deg,
    color-mix(in srgb, var(--brand-seed) 16%, transparent),
    color-mix(in srgb, var(--brand-seed) 8%, transparent));
  --info: var(--accent);
  --info-subtle: color-mix(in srgb, var(--brand-seed) 16%, var(--surface));
  --data-1: var(--accent);
}

/* ==========================================================================
   Base
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: dark light; }

/* ==========================================================================
   Scrollbars (§17.3) — the shell's chrome is a silhouette, not a box
   ==========================================================================
   Three scroll containers exist in this set: the document, the side rail's
   destination column, and the drawer's body. Each one draws a native bar, and
   the two that matter here draw it inside the shell rather than beside it. The
   rail's bar is the worse of the two: in RTL it takes its 16px from the
   reading-start side of a 264px column, so every group head and every
   destination lost a sixth of its room to a grey track running down a surface
   that already has an edge rule — and the bar sits over the app bar's own
   corner besides, because the rail is flush to the viewport's reading-start
   edge and the document's bar is drawn at that same edge. The scroll is kept
   in every container: wheel, trackpad, touch and the keyboard all still move
   it, and a focused destination still scrolls itself into view. Only the drawn
   bar is gone, which is what the shell wants — the rail is a navigation
   silhouette, and a scrollbar on it reads as a second, competing edge.
   `scrollbar-width` is inherited, so the root declaration alone would cover
   every container; the per-container rules are written out anyway, because the
   `::-webkit-scrollbar` reset does not inherit and older engines need it on
   each scroller. */
html,
.rail,
.drawer__body,
.customize,
.filters__sheet {
  scrollbar-width: none;
}
/* Physical axes, not logical ones: the `::-webkit-scrollbar` pseudo-element
   takes only `width` and `height` — it has no inline axis to resolve against,
   so a logical declaration there is dropped and the bar is drawn anyway. */
html::-webkit-scrollbar,
.rail::-webkit-scrollbar,
.drawer__body::-webkit-scrollbar,
.customize::-webkit-scrollbar,
.filters__sheet::-webkit-scrollbar {
  width: 0;
  height: 0;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--fs-body-md);
  line-height: var(--lh-body-md);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

h1, h2, h3, h4 { margin: 0; line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
figure { margin: 0; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--accent-hover); }
img, svg { display: block; max-inline-size: 100%; }
bdi { unicode-bidi: isolate; }
code, kbd, samp { font-family: var(--font-mono); font-size: var(--fs-code); line-height: var(--lh-code); }

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

/* Visible only to assistive technology — never removed from the a11y tree. */
.sr-only {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}

/* Inline SVG sprite holder — vendored, same origin, no icon request leaves (§9.9). */
.sprite { display: none; }

/* Icon set — Lucide grid: 24×24, stroke 1.5, currentColor, round caps. */
.icon { inline-size: var(--icon-md); block-size: var(--icon-md); flex: none; fill: none;
        stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.icon--sm { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.icon--lg { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.icon--xl { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
/* Icons whose meaning is the reading direction, and the two ways a drawn glyph
   can be oriented against it. A trend line rises to the right and a leave arrow
   points at a door on its right: both are drawn for a left-to-right reading, so
   both mirror once the plane is right-to-left — which is the shipped direction,
   and why the rule reads as a mirror at rest. `icon--cue` is the other way
   round: the chevron is drawn pointing at the reading-end edge, left in this
   set, so it is right as drawn in RTL and flips for LTR. Clocks, checkmarks,
   calendar glyphs and the tenant mark never mirror at all (§9.9). */
.icon--directional,
.account-exit .icon { transform: scaleX(-1); }
html[dir="rtl"] .icon--directional,
html[dir="rtl"] .account-exit .icon { transform: scaleX(-1); }
html[dir="rtl"] .icon--cue,
html[dir="rtl"] .card__cue { transform: none; }

/* ==========================================================================
   Skip link — first focusable element, WCAG 2.4.1
   ========================================================================== */
.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  inset-block-start: var(--space-4);
  z-index: 1000;
  padding: var(--space-2) var(--space-3);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius-md);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform var(--dur-fast) var(--ease-standard);
}
.skip-link:focus { transform: translateY(0); color: var(--on-accent); }

/* ==========================================================================
   App bar (§9.7, §17.3) — 64px, sticky glass, elev-2 once the plane scrolls.
   The app bar and the rail never animate (§7.2).
   ========================================================================== */
.appbar {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-sticky);
  background: var(--glass);
  backdrop-filter: blur(18px) saturate(150%);
}
/* The bar closes on a hairline that leans on the accent, so the chrome reads as
   the same material as the surfaces under it rather than as a grey band laid
   over the plane. The hairline is drawn by the bar's own rule and not by its
   border, and it is held inside the bar's inline padding: a full-bleed rule
   under the chrome drew a box around the whole window, and the bar's ends and
   the plane's ends are not the same line. It spans the content column, so the
   rule under the bar and the card under it start and stop together. */
.appbar::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: var(--space-4);
  block-size: 1px;
  background: color-mix(in srgb, var(--accent) 22%, var(--border));
  pointer-events: none;
}
@supports not (backdrop-filter: blur(1px)) { .appbar { background: var(--bg); } }
.appbar.is-scrolled { box-shadow: var(--elev-2); }

/* Three zones — identity, the page's one field, the controls — with one gap
   value between and inside them. Before this the row was a single flex line
   whose children each carried their own spacing, so the field sat at a
   different offset from the brand than from the actions and the row read as a
   scatter rather than as three groups. */
.appbar__inner {
  min-block-size: var(--appbar-h);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-3);
}

.appbar__zone {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-inline-size: 0;
}
/* The controls zone never narrows: its entries are fixed-size buttons, and a
   zone allowed to shrink below its content overflows them out of the bar
   instead of giving the room back to the field beside it. */
.appbar__zone--trail {
  flex: none;
  min-inline-size: auto;
  /* The controls hold the row's reading-end edge. Without this the three zones
     set one after another at the reading-start edge and the leftover room fell
     between the controls and the field, which read as a gap inside the row. */
  margin-inline-start: auto;
}

/* The drawer trigger is the bar's one amber pane: it is the entry a member on a
   handset reaches for first, and the only control there that opens a surface
   rather than a menu. The rule names the bar around it rather than the button
   alone, because `.icon-btn`'s own background is declared later in the sheet
   and beats a bare class of equal specificity — the trigger was drawing the
   plain glass rest instead of the accent pair (design-system §17.3, the
   hidden-control rule's reason, applied to paint). */
.appbar__inner .appbar__burger {
  background: color-mix(in srgb, var(--accent) 16%, var(--surface-raised));
  border-color: color-mix(in srgb, var(--accent) 34%, var(--border));
  color: var(--accent);
}
.appbar__inner .appbar__burger:hover {
  background: color-mix(in srgb, var(--accent) 26%, var(--surface-raised));
  border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
  color: var(--accent);
}

/* The organization's lockup is the bar's identity: the product wordmark it
   used to sit beside is gone from the chrome, so this is what the bar opens
   on. Below the tablet tier it is not rendered: at a handset width the drawer
   trigger, the field and the four controls already take the row, and a lockup
   clipped to two letters is worse than none — the drawer head carries it
   there, one tap away.

   From the tablet tier up it is taken out of the row and pinned to the bar's
   reading-start corner, which is the column the side rail descends from, so
   the bar reads as the instance's own masthead over the rail it belongs to.
   Held in the flow instead, it sat at the start of the plane's column and
   pushed the field with it: the field then carried the rail's offset as its
   own edge and could neither hold a fixed measure nor keep the plane's line
   when the rail moved. Out of the flow, the field is the column's first
   entry and this is free to hold the corner. */
.appbar__org {
  display: none;
  position: absolute;
  inset-block: 0;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  text-decoration: none;
}
.appbar__org .placeholder { font-size: var(--fs-body-sm); }

/* The organization's mark. The platform's own mark is `TBD-BRAND-ASSETS`, but
   this tile is not that mark: each deployment serves one organization and each
   uploads its own logo (Logo upload › show it on the login page and main menu),
   so the tile is that upload's slot — held to the square the guard requires,
   on a plate that reads as unresolved until the file lands, and never an
   invented logo drawn by the system. */
.org-mark {
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: 32px;
  block-size: 32px;
  border: 1px dashed color-mix(in srgb, var(--accent) 45%, var(--border));
  border-radius: var(--radius-md);
  background: var(--info-subtle);
  color: var(--accent);
}

/* The organization's name is drawn on the unresolved-content chip. The chip is
   an inline-flex box, so its own text was free to wrap and the name broke onto
   two lines inside a one-line row; as a block that stops short of its box it
   clips to one line with an ellipsis instead, and the dashed outline — the mark
   that says the name is not resolved — survives the clipping. */
.appbar__org .placeholder,
.drawer__org .placeholder {
  display: block;
  inline-size: fit-content;
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The global search box. It is the row's own field, so it is built at control
   height on the raised plane with a control boundary rather than left as a
   hairline-outlined strip the eye skips; the query still answers to the
   section's own items and to the member's access ceiling (§Global search). */
.searchbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  /* The field is the row's one flexible entry below the tablet tier, so it takes
     exactly what the identity and the controls leave rather than bidding for
     room against them: grown from a zero basis it cannot out-weigh the wordmark
     beside it the way an auto basis let it. */
  flex: 1 1 0;
  min-inline-size: 2.5rem;
  min-block-size: var(--input-h);
  padding-inline: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
}
.searchbox:focus-within { border-color: var(--accent); box-shadow: var(--glow-focus); }
.searchbox__input {
  inline-size: 100%;
  min-inline-size: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-body-md);
}
/* What a member reads in the field is their own query, and it may be in either
   script while the plane around it is laid out in one of them: a Persian
   placeholder in a left-to-right field ran its neutral ellipsis to the wrong
   end. `plaintext` gives the text its own direction from its own first strong
   character, in both directions and both languages, without moving the field. */
.searchbox__input { unicode-bidi: plaintext; }
.searchbox__input:focus-visible { outline: none; }
.searchbox__input::placeholder { color: var(--ink-muted); }

/* The reference period the page reflects. It is a date range — the Jalali month
   every figure under it belongs to — not a plan, a licence or the
   organization's tier, so it is drawn as one dated chip with a calendar glyph
   and its label in words. A bare run of text in the chrome is what made a
   reader ask whether it was a licence name. */
.appbar__period {
  display: none;
  align-items: center;
  gap: var(--space-2);
  min-block-size: var(--input-h);
  padding-inline: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  white-space: nowrap;
}
.appbar__period .icon { color: var(--ink-muted); }
.appbar__period-label { font-size: var(--fs-caption); color: var(--ink-muted); }
.appbar__period time { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--ink); }

/* The controls sit on one even gap and at the row's own control height, so the
   bell, the two display controls and the account entry align on one line
   instead of stepping by each control's own padding. */
.appbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: auto;
}

/* The app-bar theme toggle is a shortcut for the panel's full three-state
   choice, so below the tablet tier the panel keeps it alone and the row keeps
   its room for the field. Scoped to the actions zone, because `.icon-btn`'s own
   display rule is declared later in the sheet and would beat a bare class of
   equal specificity (design-system §17.3, hidden-control rule). */
.appbar__actions .appbar__theme { display: none; }

/* A control is a fixed square and never a flex item that can be squeezed —
   the bar's rigid zones must not shrink their own buttons. Each one is a pane
   of glass on the bar rather than a glyph floating on it: a translucent
   surface with its own hairline, so the controls read as the same material as
   the bar they sit on and each one's target is legible before it is hovered. */
.icon-btn {
  position: relative;
  display: inline-grid; place-items: center;
  flex: none;
  inline-size: 40px; block-size: 40px;
  background: color-mix(in srgb, var(--surface-raised) 45%, transparent);
  color: var(--ink-muted);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  backdrop-filter: blur(8px);
  cursor: pointer;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.icon-btn:hover {
  background: color-mix(in srgb, var(--accent) 14%, var(--surface-raised));
  border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
  color: var(--ink);
}
/* A read/unread count is never carried by a dot alone — the label says it too. */
.icon-btn__dot {
  position: absolute;
  inset-block-start: 8px; inset-inline-end: 8px;
  inline-size: 8px; block-size: 8px;
  border-radius: var(--radius-pill);
  background: var(--accent);
}
/* The unread count reached zero, so the marker is taken out. `[hidden]` needs
   its own rule here: the global sheet sets no `display` for it, and the
   marker's own `position` would otherwise keep it drawn. */
.icon-btn__dot[hidden] { display: none; }

/* The account menu is a popover, not a browser-native dialog (§12). */
.usermenu { position: relative; }
.menu {
  position: absolute;
  inset-block-start: calc(100% + var(--space-2));
  inset-inline-end: 0;
  z-index: var(--z-dropdown);
  min-inline-size: 232px;
  padding: var(--space-2);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-3);
  display: grid;
  gap: var(--space-1);
}
.menu[hidden] { display: none; }
.menu__head { padding: var(--space-2) var(--space-3); font-size: var(--fs-caption); color: var(--ink-muted); }
.menu__item {
  display: block;
  inline-size: 100%;
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-body-sm);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.menu__item:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.menu__item--danger { color: var(--danger); }

/* --------------------------------------------------------------------------
   Display customization (§9.7, `Customization`)

   The member's own preferences, carried in the app bar's popover. Both groups
   are choices between named options, never free values, and both resolve to
   design-system tokens: the theme swaps the theme map, the density resizes the
   spacing tokens below. Neither can reach dashboard structure, a chart's data
   or the organization's brand — the catalogue's `Customization` row forbids
   exactly that, and it stores its settings at organization level.
   -------------------------------------------------------------------------- */
/* The panel opens from the control at the bar's reading-end edge and grows
   toward the start edge, so on a handset the room it has is the bar's own
   width past that control's row — the action row the bar spends before it.
   At `--sidebar-w` on a 320px viewport the panel's far edge landed 4px past
   the window. It takes the narrower of its own measure and that room. */
.customize {
  inline-size: min(var(--sidebar-w), calc(100vw - var(--space-10)));
  /* Seven choices do not fit a handset's height, and the popover opened past
     the bottom edge with its last two groups and its reset control out of
     reach and no way to scroll to them: measured at 320×660 the panel was
     795px tall against a 660px window. It is capped to the room under the app
     bar and scrolls inside itself. It draws no bar, like every other scroll
     container in the set. */
  max-block-size: calc(100vh - var(--appbar-h) - var(--space-4));
  overflow-y: auto;
}
/* The navigation axis is a choice the shell only has from `--bp-lg` up: below
   it there is no column to replace, the drawer carries the set either way, and
   the group is not rendered rather than offered and ignored. */
.customize__group--layout { display: none; }
.customize__group { display: grid; gap: var(--space-2); padding: var(--space-3) var(--space-3) 0; }
.customize__legend { font-size: var(--fs-caption); color: var(--ink-muted); }
/* The panel's closing row: an action that puts the member's stored choices
   back, divided from the choices above it rather than listed among them. It
   replaced the panel's link out to the organization's branding settings —
   which was the wrong section, at the wrong level: the organization's brand
   is not one of the member's display preferences, and the link sat where a
   member reads choices. */
.customize__foot {
  margin-block-start: var(--space-2);
  padding: var(--space-3) var(--space-3) var(--space-4);
  border-block-start: 1px solid var(--border);
}

/* The palette choice: the brand's own colours, one swatch each. A colour is
   the one thing here that cannot be named by a glyph, so each swatch carries
   its name in its accessible name and label, and the taken one is marked by a
   ring and a check as well as by its colour — a state is never carried by hue
   alone (§11). The dot shows the seed itself, so the row reads the same in
   either theme. */
.palette { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.palette__btn {
  display: inline-grid;
  place-items: center;
  /* Seven swatches hold one row inside the panel's own measure. */
  inline-size: 28px;
  block-size: 28px;
  padding: 0;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.palette__btn:hover { border-color: var(--border-strong); }
.palette__btn[aria-pressed="true"] { border-color: var(--accent); }
.palette__dot,
.palette__check { grid-area: 1 / 1; }
.palette__dot {
  inline-size: 20px;
  block-size: 20px;
  border-radius: var(--radius-pill);
  background: var(--swatch);
}
/* The check sits on the dot and takes the plane's own colour, so it holds on
   every seed in either theme. */
.palette__check { display: none; inline-size: var(--icon-sm); block-size: var(--icon-sm); color: var(--bg); }
.palette__btn[aria-pressed="true"] .palette__check { display: block; }
[data-palette-set="amber"]   { --swatch: var(--palette-amber); }
[data-palette-set="emerald"] { --swatch: var(--palette-emerald); }
[data-palette-set="blue"]    { --swatch: var(--palette-blue); }
[data-palette-set="violet"]  { --swatch: var(--palette-violet); }
[data-palette-set="rose"]    { --swatch: var(--palette-rose); }
[data-palette-set="orange"]  { --swatch: var(--palette-orange); }
[data-palette-set="slate"]   { --swatch: var(--palette-slate); }

/* A segmented choice: two or three short options, one of them taken. The taken
   option is marked by the accent surface and by `aria-pressed`, never by hue
   alone (§11). */
.seg {
  display: flex;
  /* Options that do not fit one line take two rather than running past the
     panel: an option's own label is never the thing that gives way (§10). */
  flex-wrap: wrap;
  gap: var(--space-1);
  padding: var(--space-1);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.seg__btn {
  flex: 1 1 0;
  /* Never narrower than its own label: an option that cannot hold its text in
     one row takes a row of the row it needs. The English reading spells its
     options out — «Comfortable» beside «راحت» — and at `flex: 1 1 0` with the
     default `min-inline-size: auto` the three of them stood 291px wide inside
     a 222px group: measured, the whole row ran 69px past the panel's edge with
     its scrollbar drawn nowhere. The row wraps instead, and the label that
     needs two lines takes two. */
  min-inline-size: max-content;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-block-size: var(--space-7);
  /* A label that does not fit its third of the row wraps onto a second line
     instead of being clipped: an English option is spelled out — «Comfortable»
     beside «راحت» — and a control that cannot hold its label grows or wraps
     before it truncates (§10). */
  white-space: normal;
  text-align: center;
  padding-inline: var(--space-2);
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  cursor: pointer;
}
/* Each option carries its own glyph, so the choice is legible before the label
   is read and the three named options line up on one baseline. */
.seg__btn .icon { flex: none; }
.seg__btn:hover { color: var(--ink); background: color-mix(in srgb, var(--ink) 6%, transparent); }
.seg__btn[aria-pressed="true"] { background: var(--info-subtle); color: var(--accent); }

/* Density. Three steps, each one token value per rhythm token, so a step is the
   same design at tighter or roomier spacing — never a smaller type size and
   never a hidden row (`Customization`'s Don't: not the structure, not the
   data). Comfortable is the shipped default and owns no class: the bare
   `:root` is already that step, so a member who has stored nothing gets it
   without a rule to add. The three steps are one step apart on the spacing
   scale in both directions, so no step is a one-off:
     compact     card 16 · gap 16 · section 32
     comfortable card 20 · gap 20 · section 48   (--space-5 / --space-5 / --space-9)
     spacious    card 24 · gap 24 · section 64
   The rhythm tokens themselves are also what the panel's own panels read —
   the briefing card's padding and the section head's gap are composed from
   `--card-pad` — so a step moves the whole plane and not only its cards.
   Before this the step reached card padding and the section gap alone, and a
   page 2771px tall moved 76px between comfortable and compact: the briefing
   card, the section heads and every panel that had hard-coded its own
   padding stayed put. */
html.density-compact {
  --card-pad: var(--space-4);
  --gap-cards: var(--space-4);
  --section-gap-y: var(--space-7);
}
html.density-spacious {
  --card-pad: var(--space-6);
  --gap-cards: var(--space-6);
  --section-gap-y: var(--space-10);
}

/* Reading width — the plane's other display preference. The fluid plane is the
   shipped step and owns no class, so the bare `:root` is already it. Boxed caps
   the plane at `--content-max-boxed` and gives the room it frees to the
   reading-end side, so the plane stays against the rail instead of floating
   between two gutters. The cap is the design system's own reading measure and
   not `--content-max-dash`, which is the same number the plane already reaches
   on a wide window: capped at the maximum, the choice moved nothing until the
   rail was collapsed and 1680px of plane was freed — measured at 1912px with
   the rail open, the plane is 1648px and the step did nothing at all. At its
   own measure the step moves the plane wherever there is room to move it. This
   is a member's choice and not the cap that was removed from the plane: that
   one applied at every width and turned the 200px a collapsed rail frees into a
   dead band, and it is exactly why the fluid step is the one a member gets
   without asking. */
html.container-boxed .page {
  max-inline-size: var(--content-max-boxed);
  margin-inline-end: auto;
}

/* ==========================================================================
   Side rail (§9.7, §17.1) — inline-start edge, 264px, 64px collapsed.
   Below --bp-lg the drawer carries the same destinations.
   ========================================================================== */
.rail { display: none; }

.rail__list { display: grid; gap: var(--space-1); }
/* One group of destinations, foldable. The head is the disclosure's own
   `summary`, so the fold works from the keyboard and with JavaScript off, and
   it is still the list's accessible name. The cue sits at the row's far end
   and points down while the group is open, back toward the reading-start edge
   while it is closed. */
.rail__group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-block-start: var(--space-2);
  padding: var(--space-2);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
  cursor: pointer;
  list-style: none;
}
.rail__group-head::-webkit-details-marker { display: none; }
.rail__group-head:hover {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  border-color: var(--border);
}
/* A fold cue turns toward the edge the closed group's own content comes from:
   to the left in RTL and to the right in LTR, which is the same mirrored
   convention a tree uses in either direction. */
.rail__group:not([open]) .rail__group-cue { transform: rotate(90deg); }
.rail__group-cue { color: currentColor; }
.rail__link {
  display: flex; align-items: center; gap: var(--space-3);
  min-block-size: 40px;
  /* The selected destination's edge rule is a border and not an inset shadow:
     a shadow's offset is physical, so `inset 2px` drew the rule on the left edge
     — the inline-end in this RTL set — against the design system's rule that it
     sits at the inline-start edge. Every row carries the border, transparent
     until it is the current one, so no row shifts by 2px when it is selected. */
  border-inline-start: 2px solid transparent;
  padding-inline: calc(var(--space-3) - 2px) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
  font-size: var(--fs-body-md);
}
.rail__link:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink); }
/* The selected destination is marked by an edge rule and weight, not by hue alone. */
.rail__link[aria-current="page"] {
  background: var(--info-subtle);
  border-inline-start-color: var(--accent);
  color: var(--accent);
  font-weight: var(--fw-medium);
}
.rail__icon { color: currentColor; }
/* The rail's head holds the collapse control at the top edge. It is sticky, so
   a destination set long enough to scroll does not carry the control away with
   it. Its own top padding cancels the rail's, so the sticky box starts at the
   rail's true top edge and nothing scrolls through the gap above it. */
/* The member, on the rail's bottom edge and sticky there. It takes the room
   the destinations leave, so it holds that edge however long the set is, and
   it is sticky besides, because once the set is taller than the rail there is
   no free room left for the auto margin to absorb and the block would fall
   below the fold. Bled to the rail's edges by cancelling the rail's own inline
   padding, so its plane and its closing rule run the rail's full width. */
.rail__foot {
  position: sticky;
  inset-block-end: calc(-1 * var(--space-4));
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-start: auto;
  margin-inline: calc(-1 * var(--space-3));
  padding: var(--space-4) var(--space-3);
  background: var(--surface);
  border-block-start: 1px solid var(--border);
}
/* The member's own row, one component used by both containers that hold it —
   the rail's bottom edge on a desktop and the drawer's on a handset and a
   tablet — so the member's block is the same block wherever the member is.
   The account link is one surface, not a second menu: it opens the profile and
   nothing else. The logout action sits beside it as its own control, so the
   two never have to be told apart inside one link. */
.account {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 auto;
  min-inline-size: 0;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
}
.account:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); }
.account__text { display: grid; min-inline-size: 0; }
.account__name { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--ink); }
.account__role { font-size: var(--fs-caption); color: var(--ink-muted); }

/* The avatar carries a glyph rather than a drawn mark, and the accent pair —
   never hue alone. */
.avatar {
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: 32px;
  block-size: 32px;
  border-radius: var(--radius-pill);
  background: var(--info-subtle);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
}

/* Leave, drawn as its own control beside the account link. It takes the danger
   pair on hover only: at rest it is a quiet control, not a warning. */
.account-exit {
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: 32px;
  block-size: 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
  text-decoration: none;
}
.account-exit:hover {
  color: var(--danger);
  background: var(--danger-subtle);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
}

/* The rail's collapse handle. It hangs on the rail's inner edge, placed from
   `--rail-w` so it tracks that edge as the rail opens and closes, and it is
   fixed to the viewport so the destination set scrolling inside the rail
   cannot carry it away or clip it. Half of it is over the rail and half over
   the plane, so it reads as the rail's own edge rather than as another
   destination in the column. It replaced the rail's sticky head, which crossed
   the top of the column with an opaque band and its own rule for the sake of
   one control. Declared here and shown from `--bp-lg`, where the rail exists. */
.rail__toggle { display: none; }

/* Drawer + scrim — below --bp-lg only. */
.scrim {
  position: fixed; inset: 0; z-index: var(--z-overlay);
  background: var(--overlay);
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-standard), visibility var(--dur-base);
}
.scrim.is-open { opacity: 1; visibility: visible; }

/* Host clips the closed drawer so its off-screen box never widens the page. */
.drawer-host { position: fixed; inset: 0; z-index: var(--z-modal); overflow: hidden; pointer-events: none; }
.drawer {
  position: absolute; z-index: var(--z-modal);
  inset-block: 0;
  inset-inline-start: 0;
  pointer-events: auto;
  inline-size: min(320px, 86vw);
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  box-shadow: var(--elev-4);
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* The one column is capped at the drawer's own width. As an implicit `auto`
     track it was sized by its widest item's max-content, so a head whose
     organization chip is held on one line sized the track 44px wider than the
     drawer: the footer's logout button and its profile row then hung past the
     drawer's reading-end edge — 364px of content in a 320px panel — and, with
     the drawer parked off-screen, that overhang was exactly the width that
     stayed inside the viewport. Every row is as wide as the drawer now, and an
     entry too long for it clips or ellipsises instead of widening the panel. */
  grid-template-columns: minmax(0, 1fr);
  transform: translateX(-105%);
  transition: transform var(--dur-slow) var(--ease-emphasized);
}
[dir="rtl"] .drawer { transform: translateX(105%); }
.drawer.is-open { transform: translateX(0); }
.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-3);
  border-block-end: 1px solid var(--border);
}
/* Below --bp-md the app bar has no room for the organization's lockup, so the
   drawer head carries it rather than dropping it (§3.4) — the same mark and
   the same name, at the drawer's own scale, so the identity a member meets on
   opening the menu is the identity the bar holds on a desktop. */
.drawer__brand {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-inline-size: 0;
  color: var(--ink);
  text-decoration: none;
}
.drawer__org { min-inline-size: 0; max-inline-size: 100%; font-size: var(--fs-caption); }
.drawer__body { overflow-y: auto; padding: var(--space-3) var(--space-4) var(--space-5); }
/* The drawer's groups fold exactly as the rail's do — same `details`, same
   cue, same side — so the two destination sets behave alike and a member who
   learned one has learned the other. */
.drawer__group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-block-start: var(--space-4);
  margin-block-end: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
  cursor: pointer;
  list-style: none;
}
.drawer__group-head::-webkit-details-marker { display: none; }
.drawer__group-head:hover {
  color: var(--ink);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  border-color: var(--border);
}
.drawer__group:not([open]) .drawer__group-cue { transform: rotate(90deg); }
.drawer__list { display: grid; gap: var(--space-1); }
.drawer__list a {
  display: flex; align-items: center;
  min-block-size: 44px;
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
  font-size: var(--fs-body-md);
}
.drawer__list a:hover { background: color-mix(in srgb, var(--ink) 6%, transparent); }
.drawer__list a[aria-current="page"] { background: var(--info-subtle); color: var(--accent); font-weight: var(--fw-medium); }
/* The member's row on the drawer's bottom edge — one gap, the account link
   taking the row and leave beside it, exactly the rail's arrangement. */
.drawer__foot {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-4);
  border-block-start: 1px solid var(--border);
}
.drawer__foot .account-exit { inline-size: 40px; block-size: 40px; }

/* ==========================================================================
   Page plane (§17.1) — the dashboard canvas, wide and free-form inside.
   ========================================================================== */
/* The plane takes the room it is given. It was capped at `--content-max-dash`
   and centred with `margin-inline: auto`; with the rail open the cap never bit,
   but collapsing the rail freed 200px and the cap turned it into a dead band
   down the plane's reading-end side instead of letting the blocks beside the
   rail grow into it. */
.page {
  padding: var(--space-6) var(--space-4) var(--space-12);
}

/* The plane's briefing card — one washed surface holding what the page is, the
   two ways in, and the state of the data under it. `--gradient-brand-soft` is
   the one decorative gradient the dashboard set carries (§3.2); it is painted
   over `--surface`, never over a plot, a table or a form. */
.page-head {
  position: relative;
  display: grid;
  gap: var(--space-6);
  /* The briefing card's own padding is the card padding plus one step, so the
     card that opens the plane follows the member's density choice with every
     other panel instead of holding a fixed 32px inside a page that has
     tightened around it. Comfortable resolves to the shipped 32px / 24px. */
  padding: calc(var(--card-pad) + var(--space-3)) calc(var(--card-pad) + var(--space-1));
  background-color: var(--surface);
  background-image: var(--gradient-brand-soft);
  border: 1px solid var(--border);
  /* `--radius-lg` and `--elev-1`: the resting card's own pair. The accent edge
     beside the title is what separates this panel from the cards under it, not
     a wider radius or a heavier elevation — both are closed sets (§17.6,
     §17.17), and `--radius-xl` belongs to the hero card the app plane may not
     carry. */
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
/* The briefing card opens on the accent edge. It is the page's heading surface,
   not one card among the three blocks under it, and the edge is what says so
   without a second colour, a rule across the top or a moving plane. The edge is
   the flat `--accent`, not `--gradient-brand`: the brand gradient is the landing
   and sign-in hero's material and is barred from the app's data plane. */
.page-head::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 3px;
  background: var(--accent);
}
.page-head__text { display: grid; gap: var(--rhythm-h-to-p); max-inline-size: var(--content-max-prose); }
/* A kicker, not the landing set's eyebrow pill — §17.14 keeps that pill on the
   public set, so the app plane leads with a caption line over a short accent
   rule, which is the editorial mark and not the marketing control. */
.page-kicker {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
}
.page-kicker::before {
  content: "";
  inline-size: 1.75rem;
  block-size: 2px;
  border-radius: var(--radius-pill);
  background: var(--accent);
}
.page-title { font-family: var(--font-display); font-size: var(--fs-display-md); line-height: var(--lh-display-md); font-weight: var(--fw-bold); color: var(--ink); }
.page-lead { color: var(--ink-muted); font-size: var(--fs-body-md); }
.page-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
/* A head with nothing to brief: the settings routes open on the same surface,
   the same accent edge and the same resting elevation as the plane's briefing
   card, and drop only the one decorative gradient the set carries — that wash is
   spent once, on the home page (§3.2), and a second surface wearing it would
   stop being the page's own mark. The head carries no action row, so its text
   is the whole column. */
.page-head--plain { background-image: none; }

/* Live stamp — a timestamp plus the word «کهنه» when the plane is stale (§9.11). */
.live {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  margin-block-end: var(--space-7);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
/* Inside the briefing card the stamp closes the panel instead of standing on
   its own line under it. */
.page-head .live {
  grid-column: 1 / -1;
  margin-block-end: 0;
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
}

/* ==========================================================================
   Sections (app pages divide by spacing, not by a plane change — §17.14)
   ========================================================================== */
.section { margin-block-start: var(--section-gap-y); }
/* The head carries its own title and its one action on a single line. It draws
   no rule: an app plane divides by spacing, and the ruled heading is the landing
   set's device (design-system §17.14). Four ruled heads down one page read as
   four repeated strips rather than as four different blocks, and the rule sat
   closest to the block the reader had just finished reading rather than the one
   they were entering. */
.section__head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  /* One card-width of air under the head, on the density step rather than a
     fixed 20px, so a section's head and the cards it opens tighten together. */
  margin-block-end: var(--card-pad);
}
.section__title {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold);
}
/* Each block opens on a short accent bar at the reading-start edge. It is a mark
   on the title, not a rule under the head: the app plane still divides by
   spacing, and the bar sits inside the title's own line box. */
.section__title::before,
.chart-frame__title::before {
  content: "";
  flex: none;
  inline-size: 3px;
  block-size: 1.125rem;
  border-radius: var(--radius-pill);
  background: var(--accent);
}
.chart-frame__title { display: flex; align-items: center; gap: var(--space-2); }
/* How many items the block holds — a count of what is rendered, never a claim. */
.section__count {
  padding: 2px var(--space-2);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
}
/* Each section head closes on the one action that leaves it, carried by the
   design system's ghost button in the inline-end slot: a bare link in that
   position read as part of the title rather than as the way out of the block. */
.section__head > .btn { flex: none; }

/* ==========================================================================
   Grids (§17.2) — 1 column mobile; wider tiers add columns.
   ========================================================================== */
.grid { display: grid; gap: var(--gap-cards); grid-template-columns: 1fr; }

/* ==========================================================================
   Buttons (§9.1, §17.8)
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-block-size: 40px; min-inline-size: var(--btn-min-w);
  padding-inline: var(--space-4);
  font-family: inherit; font-size: var(--fs-label); font-weight: var(--fw-medium);
  border: 1px solid transparent; border-radius: var(--radius-md);
  background: transparent; color: var(--ink);
  text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard),
              transform var(--dur-instant) var(--ease-standard);
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent); color: var(--on-accent); box-shadow: var(--glow-accent); }
.btn--primary:hover { background: var(--accent-hover); color: var(--on-accent); }
.btn--secondary { background: var(--surface-raised); color: var(--ink); border-color: var(--border-strong); }
.btn--secondary:hover { background: color-mix(in srgb, var(--accent) 10%, var(--surface-raised)); color: var(--ink); }
.btn--ghost { color: var(--ink); }
.btn--ghost:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink); }
/* The label takes the plane colour: white on the dark theme's red measured 3.02:1, under AA. */
.btn--danger { background: var(--danger); color: var(--bg); }
.btn--sm { min-block-size: 32px; min-inline-size: 0; padding-inline: var(--space-3); }
.btn--lg { min-block-size: 48px; padding-inline: var(--space-5); font-size: var(--fs-body-lg); }
.btn--block { inline-size: 100%; }
[disabled] { opacity: .5; cursor: not-allowed; pointer-events: none; }

/* ==========================================================================
   Cards (§9.6, §17.6) — one bounded surface for one idea.
   ========================================================================== */
.card {
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.card--interactive {
  display: grid;
  padding: 0;
  overflow: hidden;
  color: var(--ink);
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard);
}
.card--interactive:hover,
.card--interactive:focus-visible {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
  box-shadow: var(--elev-2);
  color: var(--ink);
}

/* A saved layout preview — a picture, never a figure (§4.1). Each thumb paints
   the shape of the layout its author saved: a plot, a ring, a matrix or a pair
   of running lines, in the dashboard's own palette on the app's own dark plane.
   The art carries no axis, no label and no value, so a card can never be read as
   a measurement; it is drawn at 16:9 and cropped to fill, so every card in a row
   shares one frame whatever the column count. Each picture is a file under
   `assets/img/`, reached from the stylesheet rather than from a `style`
   attribute, which also makes the path independent of the page's bucket depth. */
.card__thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  background-color: var(--surface-raised);
  background-position: 50% 50%;
  background-repeat: no-repeat;
  background-size: cover;
  border-block-end: 1px solid var(--border);
}
.card__thumb--bed-flow { background-image: url("img/preview-bed-flow.svg"); }
.card__thumb--er-queue { background-image: url("img/preview-er-queue.svg"); }
.card__thumb--discharge { background-image: url("img/preview-discharge.svg"); }
.card__thumb--revenue { background-image: url("img/preview-revenue.svg"); }
.card__thumb--attendance { background-image: url("img/preview-attendance.svg"); }
.card__thumb--safety { background-image: url("img/preview-safety.svg"); }
.card__thumb--tpl-bed-flow { background-image: url("img/preview-tpl-bed-flow.svg"); }
.card__thumb--tpl-er-queue { background-image: url("img/preview-tpl-er-queue.svg"); }
.card__thumb--tpl-attendance { background-image: url("img/preview-tpl-attendance.svg"); }

/* A card carries its saved layout over its text, then a footer that names its
   category and the way in (§17.6). */
.dash-card, .tpl-card { grid-template-rows: auto 1fr auto; }

.card__body { display: grid; gap: var(--space-1); align-content: start; padding: var(--space-4) var(--card-pad) var(--space-3); }
.card__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); color: var(--ink); }
.card__desc { font-size: var(--fs-caption); color: var(--ink-muted); }
.card__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--card-pad);
  border-block-start: 1px solid var(--border);
}
/* The cue is the way in; it takes the accent only on hover or focus, so it
   never reads as a state (§2.3). */
.card__cue { color: var(--ink-muted); transition: color var(--dur-fast) var(--ease-standard); }
.card--interactive:hover .card__cue,
.card--interactive:focus-visible .card__cue { color: var(--accent); }

/* ==========================================================================
   KPI strip (§9.8) — the headline figures as one bordered plane divided by
   hairlines, so they read as one reading rather than four unrelated cards.
   The 1px gap over a border-coloured plane is the divider: it needs no
   tier-specific rule, and it survives the column changes intact.
   ========================================================================== */
.kpi-strip {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  padding: 0;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
.kpi-strip > .metric { padding: var(--card-pad); background: var(--surface); }

/* Metric tile (§9.8) — one number, its unit, its period, its target and its
   state. The owner and refresh line is the tile's footer, pinned to the bottom
   of the tile and bled to its edges, so the four footers form one band across
   the strip whatever each tile's body measures. */
.metric { display: grid; grid-template-rows: repeat(4, auto) 1fr; gap: var(--space-1); align-content: start; }
.metric__head { display: flex; align-items: center; gap: var(--space-2); color: var(--ink-muted); }
/* The unit's icon sits in its own small plane, so a column of tiles has a
   repeatable anchor at the reading-start edge (§9.8). */
.metric__head .icon {
  inline-size: var(--icon-xl); block-size: var(--icon-xl);
  padding: var(--space-1);
  color: var(--accent);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
  border-radius: var(--radius-md);
}
/* On the one tile past its target the unit plane takes the warning pair with the
   rest of the tile, so the tinted plane is not crossed by an amber anchor. */
.metric--threshold .metric__head .icon {
  color: var(--warning);
  background: var(--warning-subtle);
  border-color: color-mix(in srgb, var(--warning) 32%, var(--border));
}
.metric__label { font-size: var(--fs-label); color: var(--ink-muted); }
/* The number, then what it did. The movement comes second — immediately under
   the figure it qualifies — and the unit line follows it, so a tile reads as a
   value, its movement, its unit, its target and its owner rather than as five
   unrelated lines. The chip keeps its own line at every tier rather than
   sitting beside the number when it happens to fit: beside it on one tile and
   under it on the next put the four numbers at four different heights down one
   strip. */
.metric__figure { display: grid; gap: var(--space-1); justify-items: start; }
.metric__value { font-size: var(--fs-display-md); line-height: var(--lh-display-md); font-weight: var(--fw-bold); }
.metric__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
/* A direction is not a state: the delta states the move in words and stays in
   the muted ink, so hue never has to carry the meaning (§11). It keeps its own
   line under the figure it qualifies, so a column of tiles reads at one offset,
   but it draws no chip of its own: a bordered box around a sentence is a control
   to the eye, and a tile carrying one boxed movement line, one unit line and one
   target line read as a stack of three small controls under the number rather
   than as one reading. The glyph and the words carry it. */
.metric__delta {
  display: flex; align-items: flex-start; gap: var(--space-2);
  inline-size: fit-content;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--ink-muted);
}
.metric__delta .icon { margin-block-start: 1px; }
/* The KPI's own target, stated in words under the value (§9.8). */
.metric__target { font-size: var(--fs-caption); color: var(--ink-muted); }
/* The one tile that has passed its target: its plane is tinted, it carries a
   rule at the reading-start edge, and it names the breached threshold in words,
   so the state never rides on hue alone (§11). */
.metric--threshold,
.kpi-strip > .metric--threshold {
  background: var(--warning-subtle);
  border-inline-start: 2px solid var(--warning);
}
/* The breached threshold takes the same shape as the delta, in the warning
   tokens and at medium weight, so the plane stays the carrier of the state and
   the line only names it. */
.metric__state {
  display: flex; align-items: flex-start; gap: var(--space-2);
  inline-size: fit-content;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
  color: var(--warning);
}
.metric__state .icon { margin-block-start: 1px; }
.metric__owner {
  /* The footer is bled to the tile's edges by cancelling the tile's own
     padding, so the hairline and the recessed plane run the full width of the
     tile and the four footers read as one band across the strip. */
  align-self: end;
  margin-inline: calc(-1 * var(--card-pad));
  margin-block-end: calc(-1 * var(--card-pad));
  padding: var(--space-3) var(--card-pad);
  border-block-start: 1px solid var(--border);
  background: var(--bg);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
/* On the one tinted tile the band is tinted with it, so the warning plane is
   not crossed by a grey footer. */
.metric--threshold .metric__owner { background: color-mix(in srgb, var(--warning) 8%, transparent); }

/* ==========================================================================
   Chart frame (§9.8) — figure + figcaption, plot area never rounded.
   ========================================================================== */
.chart-frame { display: grid; gap: var(--space-4); }
.chart-frame__head {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
  gap: var(--space-3);
}
.chart-frame__titles { display: grid; gap: var(--space-1); }
.chart-frame__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.chart-frame__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.chart-frame__sub { font-size: var(--fs-caption); color: var(--ink-muted); }
.chart-frame__foot {
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--border);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}

/* The plot is a two-column grid: the value axis at the reading-start edge, and
   beside it the plot box with the month ticks under it. The plot box is its own
   plane so the reading guides, the bars and the value labels riding above them
   are measured against one height — a guide that does not line up with the bar
   it measures is worse than no guide (§9.8). */
.chart {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: 1fr auto;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  /* Headroom for the value label that rides above the tallest bar. */
  padding-block-start: var(--space-5);
  block-size: 210px;
}
.chart__scale {
  grid-column: 1; grid-row: 1;
  display: flex; flex-direction: column; justify-content: space-between;
  align-items: flex-end;
  font-size: var(--fs-caption);
  line-height: 1;
  color: var(--ink-muted);
}
.chart__plot {
  grid-column: 2; grid-row: 1;
  position: relative;
  block-size: 100%;
  /* The baseline only. The box's closing rule at the top of the plot was drawn
     at the value the axis stops at rather than at a value any bar reaches, so it
     read as a frame around the plot instead of as the ground the bars stand on;
     the axis scale above already names that step. */
  border-block-end: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  /* Reading guides, not decoration: a hairline every quarter, mixed from --ink
     so the same rule stays legible in both themes. */
  background-image: repeating-linear-gradient(to top,
    color-mix(in srgb, var(--ink) 9%, transparent) 0 1px, transparent 1px 25%);
}
.chart__cols { display: flex; align-items: flex-end; gap: var(--space-1); block-size: 100%; }
/* The KPI's own target as a reference line. Dashed, so it is never read as a
   series, and labelled in words at the reading-start edge, where the short bars
   leave the room for it (§9.8). */
.chart__target {
  position: absolute;
  inset-block-end: 85%;
  inset-inline: 0;
  border-block-start: var(--border-thin) dashed var(--border-strong);
  pointer-events: none;
}
.chart__target-label {
  position: absolute;
  inset-block-end: var(--space-1);
  inset-inline-start: 0;
  padding: 2px var(--space-2);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-caption);
  line-height: 1.2;
  color: var(--ink-muted);
  white-space: nowrap;
}
.chart__col { position: relative; flex: 1 1 0; block-size: 100%; }
.chart__bar {
  position: absolute;
  inset-block-end: 0;
  /* A bar keeps air around it: the plot reads as six columns with a gutter,
     not as a wall of fill. */
  inset-inline: 26%;
  min-block-size: 6px;
  /* A rounded bar tip is a chart-bar radius, not a frame radius (§5.1). */
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  /* The months behind the reference period are a second series, not a fading
     copy of the first: they take `--data-6`, the stone of the closed six-token
     set, and the current period alone takes `--data-1`. Two ambers at 58 % and
     100 % were told apart by lightness alone and blended with the charcoal into
     one mud colour, so the accent read as a dimmer copy of the bars instead of
     as the one period the page is about. The legend names both, and the current
     period's tick and value label carry the ink weight besides (§9.8, §11).
     The fill is stepped between 88 % and 58 % of that series token: a wider
     range faded the top of a bar into the plane behind it and left the bar's own
     height unreadable, in both themes. */
  background: linear-gradient(to top,
    color-mix(in srgb, var(--data-6) 88%, transparent),
    color-mix(in srgb, var(--data-6) 58%, transparent));
}
.chart__col:last-child .chart__bar { background: var(--data-1); }
.chart__value {
  position: absolute;
  inset-inline: 0;
  font-size: var(--fs-caption);
  line-height: 1.2;
  color: var(--ink-muted);
  text-align: center;
}
.chart__col:last-child .chart__value { color: var(--ink); font-weight: var(--fw-medium); }
/* Bar heights and their labels are geometry, so they live here rather than in
   a style attribute. */
.chart__col:nth-child(1) .chart__bar { block-size: 58%; }
.chart__col:nth-child(2) .chart__bar { block-size: 61%; }
.chart__col:nth-child(3) .chart__bar { block-size: 64%; }
.chart__col:nth-child(4) .chart__bar { block-size: 69%; }
.chart__col:nth-child(5) .chart__bar { block-size: 74%; }
.chart__col:nth-child(6) .chart__bar { block-size: 86%; }
.chart__col:nth-child(1) .chart__value { inset-block-end: calc(58% + var(--space-1)); }
.chart__col:nth-child(2) .chart__value { inset-block-end: calc(61% + var(--space-1)); }
.chart__col:nth-child(3) .chart__value { inset-block-end: calc(64% + var(--space-1)); }
.chart__col:nth-child(4) .chart__value { inset-block-end: calc(69% + var(--space-1)); }
.chart__col:nth-child(5) .chart__value { inset-block-end: calc(74% + var(--space-1)); }
.chart__col:nth-child(6) .chart__value { inset-block-end: calc(86% + var(--space-1)); }
/* The month ticks take the plot's own gutter, so each label sits under the
   column it names at every tier. */
.chart__ticks { grid-column: 2; grid-row: 2; display: flex; gap: var(--space-1); }
.chart__tick {
  flex: 1 1 0;
  font-size: var(--fs-caption); line-height: 1.25;
  color: var(--ink-muted); text-align: center;
}
.chart__tick:last-child { color: var(--ink); font-weight: var(--fw-medium); }

/* The legend. Max six entries, and every entry names its series in words,
   because a hue never carries a series on its own (§9.8, §11). */
.chart__legend {
  display: flex; flex-wrap: wrap; gap: var(--space-4);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
.chart__legend-item { display: inline-flex; align-items: center; gap: var(--space-2); }
.chart__swatch {
  inline-size: var(--space-4); block-size: var(--space-2);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--data-6) 68%, transparent);
}
.chart__swatch--current { background: var(--data-1); }
.chart__swatch--target {
  block-size: var(--space-0);
  border-block-start: var(--border-thin) dashed var(--border-strong);
  border-radius: var(--radius-none);
  background: none;
}

/* The tabular view is the plot's text alternative, reachable by keyboard. It
   is anchored to the reading-start edge rather than drifting under the plot. */
.table-view > summary {
  display: block;
  list-style: none;
  cursor: pointer;
  inline-size: fit-content;
  margin-inline-end: auto;
}
.table-view > summary::-webkit-details-marker { display: none; }
.table-view[open] > summary { margin-block-end: var(--space-3); }

/* ==========================================================================
   Data table (§9.8) — real table markup, sticky header, numeric cells at the
   inline-end. At 200 % zoom the plane reflows rather than scrolling sideways.
   ========================================================================== */
.ds { inline-size: 100%; border-collapse: collapse; }
.ds caption { text-align: start; padding-block-end: var(--space-2); font-size: var(--fs-caption); color: var(--ink-muted); }
.ds th, .ds td { text-align: start; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); }
.ds thead th { background: var(--surface-raised); font-size: var(--fs-label); font-weight: var(--fw-medium); color: var(--ink-muted); }
.ds tbody tr:hover { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.ds .num { text-align: end; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Navigation atoms (§9.3, §9.5)
   ========================================================================== */
.pill {
  display: inline-flex; align-items: center; gap: var(--space-1);
  min-block-size: 32px;
  padding-inline: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--ink);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  text-decoration: none;
}
.pill:hover { background: color-mix(in srgb, var(--accent) 12%, var(--surface)); border-color: var(--accent); color: var(--ink); }
/* The one pill whose view is on screen — the ticket list's state filter. Marked
   by the accent pair and by the attribute, never by hue alone (§11). */
.pill[aria-current="page"] { background: var(--info-subtle); border-color: var(--accent); color: var(--accent); }

.catnav { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-end: var(--card-pad); }

/* ==========================================================================
   Content directory + guides (content/index.html, content/guides.html)
   ========================================================================== */

/* The directory's own search: the field and its submit on one row, the row's
   label above it and its scope sentence below — the design system's field
   anatomy (§9.2) rather than a bare input dropped on the plane. The field
   gives way first when the row runs short, so the button keeps its label. */
.find { display: grid; gap: var(--space-3); max-inline-size: var(--content-max-form); }
.findbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.findbar .input { flex: 1 1 14rem; inline-size: auto; }

/* The category tree, two levels deep. The pills inside a card are navigation,
   not filters: the filters that bind to a query live on the dashboard viewer,
   where the row that asks for them is in scope. */
.grid--cat { grid-template-columns: 1fr; }
.cat { display: grid; gap: var(--space-3); align-content: start; }
.cat__list { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* A listing row's lifecycle state, under the title it qualifies. The words
   carry the state and the badge only reinforces them (§11, §9.3). */
.card__state { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }

/* A guide topic: the steps that answer it, then the one line that closes it. */
.guide { display: grid; gap: var(--space-5); }
/* Steps are tiles that fill the card's width, so a wide plane is not left
   half empty beside one narrow prose column. */
.guide__steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: var(--space-4); }
/* The step number is decoration on an ordered list, so it is drawn by the
   markup and hidden from assistive technology: the `ol` already carries the
   order. It is set in Persian digits in the page, never generated by a counter
   whose numeral system the stylesheet would have to guess. */
.guide__steps > li {
  position: relative;
  display: grid;
  gap: var(--space-3);
  align-content: start;
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}
.guide__num {
  display: grid;
  place-items: center;
  inline-size: 24px;
  block-size: 24px;
  border-radius: var(--radius-pill);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  color: var(--accent);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
}
.guide__note {
  max-inline-size: var(--content-max-prose);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}

/* A closing line under a block — a deliberate omission stated in words, or the
   way to the surface that holds what the block leaves out. */
.section__note {
  margin-block-start: var(--card-pad);
  max-inline-size: var(--content-max-prose);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}

/* ==========================================================================
   Badges, chips, alerts (§9.3, §9.4) — held for the sibling pages of the set.
   ========================================================================== */
.badge, .chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 2px var(--space-2);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
}
.badge--neutral { background: var(--neutral-700); color: var(--neutral-200); }
[data-theme="light"] .badge--neutral { background: var(--neutral-100); color: var(--ink-muted); }
.badge--accent { background: var(--info-subtle); color: var(--accent); }
.badge--success { background: var(--success-subtle); color: var(--success); }
.badge--warning { background: var(--warning-subtle); color: var(--warning); }
.badge--danger { background: var(--danger-subtle); color: var(--danger); }

.chip {
  border-radius: var(--radius-pill);
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--ink);
  /* A chip is also the version badge that opens a record's history, so it is
     reached as a link as well as used as a filter. It never wears an
     underline: inside a pill the rule crosses the label's own reading. */
  text-decoration: none;
}
.chip button {
  font: inherit; font-size: var(--fs-caption); line-height: 1;
  border: 0; background: transparent; color: var(--ink-muted);
  min-inline-size: 24px; min-block-size: 24px;
  border-radius: var(--radius-pill); cursor: pointer;
}
.chip button:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }

.alert {
  display: flex; gap: var(--space-3);
  /* The standing notice a page opens a section with — the activity history's
     read-only rule — sits between the head and the block it qualifies, so it
     carries that block's own gap. */
  margin-block-end: var(--card-pad);
  padding: var(--space-3) var(--space-4);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--info) 40%, transparent);
  border-radius: var(--radius-md);
}
.alert--success { background: var(--success-subtle); border-color: color-mix(in srgb, var(--success) 40%, transparent); }
.alert--warning { background: var(--warning-subtle); border-color: color-mix(in srgb, var(--warning) 40%, transparent); }
.alert--danger  { background: var(--danger-subtle);  border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.alert__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.alert__body { font-size: var(--fs-body-sm); color: var(--ink-muted); }

/* ==========================================================================
   Forms (§9.2, §17.9) — held for the sibling pages of the set.
   ========================================================================== */
.field { display: grid; gap: var(--space-2); margin-block-end: var(--space-4); }
.field__label { font-size: var(--fs-label); font-weight: var(--fw-medium); }
.field__req { color: var(--danger); margin-inline-start: 2px; }
.field__hint { font-size: var(--fs-caption); color: var(--ink-muted); }
.field__error { font-size: var(--fs-caption); color: var(--danger); }
.input, .select, .textarea {
  inline-size: 100%;
  min-block-size: var(--input-h);
  padding-inline: var(--space-3);
  background: var(--surface-raised);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font: inherit;
}
.textarea { block-size: auto; padding: var(--space-3); resize: vertical; }
.input:focus-visible, .select:focus-visible, .textarea:focus-visible {
  border-color: var(--accent);
  outline: none;
  box-shadow: var(--glow-focus);
}
.input[aria-invalid="true"] { border-color: var(--danger); }

/* ==========================================================================
   Auth bucket (§3.10) — the sign-in and recovery set under `auth/`.

   These four files carry no app chrome: no bar, no rail, no drawer. Their own
   chrome is one top row — the organization's lockup and the theme toggle — over
   a two-part plane: the sign-in hero on the reading-start side and the form
   panel beside it. The hero is the design system's `hero` card (§9.6):
   `--radius-xl` and `--gradient-brand`, which §2.3 allows on the landing and
   sign-in sets only — and which is why every string standing on it is
   `--on-accent` at `heading-md` or larger. The page canvas stays the flat
   `--bg` the whole dashboard set sits on (§3.2): the gradient is the panel's
   material, never the page's.
   ========================================================================== */

/* The page is two rows — the bucket's chrome, then the plane — and the plane
   takes the height the chrome leaves, so the panel sits at the optical middle
   of a tall window and starts at the top of a short one. */
.auth-page { display: grid; grid-template-rows: auto 1fr; min-block-size: 100vh; }

.auth-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  inline-size: 100%;
  max-inline-size: var(--page-max-marketing); /* §17.1: the landing and sign-in set */
  margin-inline: auto;
  padding: var(--space-4);
}
.auth-top__org { display: flex; align-items: center; gap: var(--space-3); min-inline-size: 0; }
/* The name is held on one line and clipped, exactly as the app bar holds it:
   an unresolved organization name is never allowed to wrap the row. */
.auth-top__org .placeholder {
  display: block;
  font-family: var(--font-text);
  inline-size: fit-content;
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.auth-main { display: grid; align-items: center; padding: var(--space-4) var(--space-4) var(--space-10); }
.auth {
  inline-size: 100%;
  max-inline-size: 36rem;
  margin-inline: auto;
  display: grid;
  gap: var(--space-6);
}

/* The form panel: the sign-in card's own radius (§5.2) and the single-column
   form's own measure (§17.1). */
.auth__panel {
  padding: var(--space-6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--elev-1);
}
.auth__head { display: grid; gap: var(--space-1); margin-block-end: var(--space-5); }
.auth__panel-title { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
.auth__lead { font-size: var(--fs-body-sm); color: var(--ink-muted); }

/* Where a code was sent, and the way back to the number. The number sits on the
   unresolved-content chip: this set is a static mockup with no session to carry
   it, and the chip is the same "not resolved here" mark the shell uses. */
.auth__target { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-block-end: var(--space-4); font-size: var(--fs-body-sm); color: var(--ink-muted); }

/* The resend control and the cooldown that refuses it. The countdown is
   `aria-hidden` — a live region updating every second would announce every
   second — and the second, `sr-only` region speaks once, when the control
   frees (§9.4, §9.10). */
.auth__resend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-block-start: var(--space-4);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
}
.auth__cooldown { font-size: var(--fs-caption); color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* The panel's closing row: the one navigation out of it — a password reset, a
   way back to sign-in — divided from the form rather than listed among its
   fields. */
.auth__alt {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-block-start: var(--space-4);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border);
  font-size: var(--fs-body-sm);
}

/* A rule the surface has to state — the reset
   ceiling, the code's window — in the muted ink, never as a claim the surface
   cannot make. */
.auth__note { display: flex; align-items: flex-start; gap: var(--space-2); margin-block-start: var(--space-4); font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--ink-muted); }
.auth__note .icon { margin-block-start: 1px; }

/* Two writes on one panel — the primary signs in, the secondary carries the
   number to the code step — so the pair sits on one gap at the panel's width.
   Exactly one of them is primary (§9.1). */
.auth__actions { display: grid; gap: var(--space-2); }

/* The one-time code field: a code is read back digit by digit, so it is centred
   and set a step above a text field. No letter-spacing is applied — the digits
   may be typed in either numeral family, and a tracked Arabic-script run is a
   defect (§10). */
.input--code { min-block-size: 48px; text-align: center; font-size: var(--fs-heading-md); font-variant-numeric: tabular-nums; }

/* The recovery flow: one step at a time, each addressed by its own fragment
   (`#verify`, `#reset`), so every step is linkable and a reader without
   JavaScript still walks the flow. `:has()` releases the default step the
   moment another one is targeted, and the track's accent follows it. */
.auth__step { display: none; }
.auth__step:first-of-type { display: grid; }
.auth__step-panels:has(:target) .auth__step:first-of-type { display: none; }
.auth__step:target { display: grid; }

/* ==========================================================================
   The member's own settings routes (§9.2, §9.5, §9.8)

   Three pages under one sub-navigation — profile, sessions, password — reached
   from the account menu and the account block rather than from the rail, which
   carries the organization's own destinations. The family is one surface: the
   same head, the same tab strip, the same single-column form measure. Every
   action that would need a server is rendered disabled with its reason stated,
   the treatment the home page gives its deployment actions (§3.7).
   ========================================================================== */
/* The form measure. A settings form is one column of labelled fields, so it
   holds the design system's own reading measure for a form and never stretches
   across a wide plane (§4.4). */
.form { display: grid; max-inline-size: var(--content-max-form); }

/* The tab strip opens the family under the head, at the head's own gap rather
   than at a section's. */
.page-head + .tabs { margin-block-start: var(--space-6); }

/* The member's own surface. The rail and the drawer mark the current
   destination by a plane and the accent weight (§9.7), and on these three
   routes the destination the member is on is the account block itself — no rail
   entry is current, because the rail carries the organization's destinations. */
.account[aria-current="page"] { background: var(--info-subtle); }
.account[aria-current="page"] .account__name { color: var(--accent); }
/* A block's action row — the form's closing row, and the one closing action a
   list carries under itself. It sits at the block's own edge rather than inside
   the field column, so the page's one primary action stands under the whole
   form. */
.actions { display: flex; flex-wrap: wrap; gap: var(--space-3); padding-block-start: var(--space-2); }

/* The member themselves, at the top of their own profile: the avatar the shell
   already carries, one step larger, beside the name and role the account block
   shows. Both are sample content in this mockup, like the figures on
   the page. */
.identity { display: flex; align-items: center; gap: var(--space-4); margin-block-end: var(--card-pad); }
.identity__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.identity__name { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); color: var(--ink); }
.identity__meta { font-size: var(--fs-body-sm); color: var(--ink-muted); }
/* `--space-10` is the avatar's size, not a new value: the shell draws it at
   32px, and the profile draws the same component at the next step of the
   spacing scale rather than at a radius or an icon size. */
.avatar--lg { inline-size: var(--space-10); block-size: var(--space-10); }
.avatar--lg .icon { inline-size: var(--icon-lg); block-size: var(--icon-lg); }

/* The file field (§9.2). Its anatomy is a control the member presses plus the
   value it carries, and a native `<input type="file">` cannot be dressed as
   one — the browser draws its own button and its own two strings in the
   browser's language, which on a Farsi page is a control the page cannot read
   or translate. The input is therefore kept, keyboard-reachable and
   `aria-describedby`-wired, and moved out of sight; the button the member sees
   is its label, and the name of the chosen file is written beside it. */
.filefield { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.filefield__input {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}
/* The hidden input keeps the focus, so the ring is drawn on the label that
   stands for it — the control the member sees is the control that reports
   focus (§11). */
.filefield__input:focus-visible + .btn {
  outline: 2px solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}
/* A file name is an identifier: mono, Latin digits, and isolated so it cannot
   reorder against the Persian label beside it (§4.1.5, §10). */
.filefield__name {
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
  unicode-bidi: isolate;
}

/* A named choice between two or three short options, carried by the design
   system's segmented control. It is the same `.seg` the display panel uses —
   the control is a control — with its own legend because the panel's legend
   class belongs to the popover. */
.choice { margin-block-end: var(--card-pad); }
.choice__legend { font-size: var(--fs-caption); color: var(--ink-muted); }
/* An alert stands between the choice and the form it qualifies, so it carries
   the card's own rhythm under it rather than sitting on the first field. */
.card > .alert { margin-block-end: var(--card-pad); }
/* The alert's own text column: the atom pairs an icon with its content, and the
   title and the body stack inside that column. */
.alert__text { display: grid; gap: var(--space-1); }
/* One of the choice's panels. Both are rendered, because the markup carries no
   script of its own and a member without JavaScript must still reach both
   forms; `pages.js` hides the one that is not taken. */
.pane { display: grid; gap: var(--space-2); }
.pane[hidden] { display: none; }
/* With the script the family's choice leaves one pane standing and this rule is
   never drawn. Without it both forms show, and a hairline is what tells the
   member where one ends and the next begins. */
.pane + .pane { margin-block-start: var(--card-pad); padding-block-start: var(--card-pad); border-block-start: 1px solid var(--border); }

/* A block-level statement — what an action does, or what this mockup renders in
   place of it. Kept at body-sm in the muted ink with its own row, so it reads
   as the caption to the block rather than as content of its own. */
.note {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
}

/* The active sessions, as one bordered plane divided by hairlines — the KPI
   strip's own idiom, so a list of the member's sessions reads as one set rather
   than as N separate cards (§9.8). */
.sessions {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
.session {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
}
/* The device's glyph sits in its own plane, the same accent plate the KPI
   tiles' unit glyphs take, so a column of sessions has a repeatable anchor at
   the reading-start edge (§9.8). */
.session__icon {
  padding: var(--space-1);
  color: var(--accent);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
  border-radius: var(--radius-md);
}
.session__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.session__device { font-size: var(--fs-body-md); font-weight: var(--fw-medium); color: var(--ink); }
/* The device and the two times are the row's own reading; every date on it is
   Jalali with its `<time>` value in ISO-8601 (§3.5). */
.session__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
/* Below the tablet tier the action takes its own row under the session it acts
   on, so a long device name is never squeezed beside a control. */
.session__action { grid-column: 1 / -1; display: flex; flex-wrap: wrap; justify-content: flex-end; }

/* ==========================================================================
   Tabs, breadcrumb, pagination (§9.5)
   ========================================================================== */
.tabs { display: flex; flex-wrap: wrap; gap: var(--space-4); margin-block-end: var(--card-pad); border-block-end: 1px solid var(--border); }
/* The block rhythm applies between sections; the first one inside a panel
   already stands under the tab row's own rule. */
[role="tabpanel"] > .section:first-child { margin-block-start: 0; }
[role="tabpanel"]:focus-visible { outline-offset: var(--space-2); }
.tabs [role="tab"] {
  font: inherit; font-size: var(--fs-label); font-weight: var(--fw-medium);
  background: transparent; border: 0; color: var(--ink-muted);
  padding: var(--space-2) 2px;
  border-block-end: var(--border-thick) solid transparent;
  cursor: pointer;
}
.tabs [role="tab"][aria-selected="true"] { color: var(--accent); border-block-end-color: var(--accent); font-weight: var(--fw-semibold); }
/* The tab strip whose tabs are the page's own siblings — the settings routes,
   each of which is its own address, so a member can bookmark and share one.
   WAI-ARIA's own guidance separates the two cases: a tablist is for panels
   inside one document, a row of links is navigation, and the selected entry is
   marked by `aria-current="page"` rather than by `aria-selected`. The
   treatment is the underline tab's, and the target is the 40px the design
   system sets for a tab, so the two shapes read alike without pretending to be
   the same control. */
.tabs a {
  display: inline-flex;
  align-items: center;
  min-block-size: 40px;
  font-size: var(--fs-label); font-weight: var(--fw-medium);
  color: var(--ink-muted);
  padding: var(--space-2) 2px;
  border-block-end: var(--border-thick) solid transparent;
  text-decoration: none;
}
.tabs a:hover { color: var(--ink); }
.tabs a[aria-current="page"] {
  color: var(--accent);
  border-block-end-color: var(--accent);
  font-weight: var(--fw-semibold);
}

/* The trail stands above the page head it belongs to. It is the `ol` of a
   `nav`, so the list keeps its own semantics while the row lays out flat. */
.crumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
.crumbs a:hover, .crumbs a:focus-visible { color: var(--accent); text-decoration: underline; }
.crumbs [aria-current="page"] { color: var(--ink); font-weight: var(--fw-semibold); }

.pager { display: flex; gap: var(--space-1); }
.pager a, .pager span {
  min-inline-size: 32px; min-block-size: 32px;
  display: inline-grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--ink); text-decoration: none; font-size: var(--fs-body-sm);
}
.pager [aria-current="page"] { background: var(--accent); color: var(--on-accent); border-color: transparent; }

/* ==========================================================================
   Empty and loading states (§9.10) — held for the sibling pages.
   ========================================================================== */
.empty { display: grid; gap: var(--space-2); justify-items: center; text-align: center; padding: var(--space-10) var(--space-4); }
.empty__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.empty__body { font-size: var(--fs-body-sm); color: var(--ink-muted); }

.skeleton { position: relative; overflow: hidden; background: var(--neutral-700); border-radius: var(--radius-sm); }
[data-theme="light"] .skeleton { background: var(--neutral-100); }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--ink) 8%, transparent), transparent);
  transform: translateX(-100%);
  animation: shimmer var(--dur-slow) linear infinite;
}
@keyframes shimmer { to { transform: translateX(100%); } }
[dir="rtl"] .skeleton::after { animation-name: shimmer-rtl; }
@keyframes shimmer-rtl { from { transform: translateX(100%); } to { transform: translateX(-100%); } }

/* ==========================================================================
   Support tickets — `tickets/index.html`, `tickets/detail.html`,
   `tickets/new.html`
   The member's own tickets, the page of one ticket, and the form that opens
   one. The list is a run of links, one per ticket: the row carries the topic,
   the state badge and the date, and nothing else — the description, the
   attachments and the conversation with the support desk are read on the
   ticket's own page. The list keeps one column at every tier; the detail page
   and the form gain a side card from the laptop tier, and that is the only
   tier change the block makes.
   ========================================================================== */
.tickets { display: grid; gap: var(--space-3); }

.ticket {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.ticket[hidden] { display: none; }
.ticket:hover { border-color: var(--accent); background: color-mix(in srgb, var(--ink) 4%, var(--surface)); }
.ticket:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
/* The chevron points the way a link goes: it sits at the reading-end edge. */
.ticket__cue { color: var(--ink-muted); }
.ticket__title {
  flex: 1 1 14rem;
  min-inline-size: 0;
  font-size: var(--fs-heading-sm);
  line-height: var(--lh-heading-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink);
}
.ticket__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--fs-caption); color: var(--ink-muted); }
.ticket__text { margin: 0; font-size: var(--fs-body-md); color: var(--ink); }
.ticket__files { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.ticket__file {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-2);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
.ticket__file .icon { color: var(--accent); }
/* A file's name is a Latin identifier inside Persian copy, so it isolates. */
.ticket__file bdi { color: var(--ink); }

/* Search by title: one field and its submit on one row, in the field anatomy
   the shell's own search uses. The field gives way first when the row runs
   short, so the button keeps its label. */
.ticket-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-inline-size: var(--content-max-form);
  margin-block-end: var(--space-3);
  padding-block: var(--space-1);
  padding-inline: var(--space-3) var(--space-1);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
}
.ticket-search:focus-within { border-color: var(--accent); box-shadow: var(--glow-focus); }
.ticket-search__input {
  flex: 1 1 0;
  min-inline-size: 0;
  min-block-size: calc(var(--input-h) - var(--space-3));
  background: transparent;
  border: 0;
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-body-sm);
}
.ticket-search__input:focus-visible { outline: none; }

/* One ticket's page. The conversation reads as one column of messages, newest
   last; from the laptop tier the ticket's facts stand in a card beside it. */
.ticket-detail { display: grid; gap: var(--space-5); align-items: start; }
.ticket-detail[hidden] { display: none; }
.ticket-facts { display: grid; gap: var(--space-4); order: -1; }
/* The facts come first below the laptop tier, so the state and the responder
   are read before the conversation; beside it from the laptop tier. */
.ticket-topbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); margin-block-end: var(--space-6); }
.ticket-topbar .crumbs { margin-block-end: 0; }
.ticket-facts__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); color: var(--ink); }
.ticket-facts__list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-4); margin: 0; }
.ticket-facts__row { display: grid; gap: var(--space-1); }
.ticket-facts__row dt { font-size: var(--fs-caption); color: var(--ink-muted); }
.ticket-facts__row dd { margin: 0; font-size: var(--fs-body-md); color: var(--ink); }

.thread { display: grid; gap: var(--space-4); margin: 0; padding: 0; list-style: none; }
.msg {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
}
.msg__avatar { inline-size: 40px; block-size: 40px; font-size: var(--fs-label); font-weight: var(--fw-semibold); }
.msg__card {
  display: grid;
  gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
/* The support desk's message takes the info pair, so who spoke is read from the
   name and the role word as well as from the ground (§11). */
.msg--desk .msg__card { background: var(--info-subtle); border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }
.msg__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.msg__name { font-weight: var(--fw-semibold); color: var(--ink); }
.msg__time { margin-inline-start: auto; font-size: var(--fs-caption); color: var(--ink-muted); }
.msg__text { margin: 0; font-size: var(--fs-body-md); color: var(--ink); }
/* A state change is a line between messages, not a message. */
.thread__event {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-caption); color: var(--ink-muted);
}
.thread__event::before, .thread__event::after { content: ""; flex: 1 1 0; block-size: 1px; background: var(--border); }
.thread__wait { margin: 0; font-size: var(--fs-body-sm); color: var(--ink-muted); text-align: center; }

/* The new-ticket form: two cards. The fields and the actions are one card; what
   happens after submitting is a second card beside it from the laptop tier, so
   neither card leaves a void beside it, and below the tier the two stack in the
   order they are read. The fields are stacked on one gap value rather than on
   each field's own bottom margin (§17.9). */
.ticket-form { display: grid; gap: var(--space-5); align-items: start; }
.ticket-form .field { margin-block-end: 0; }
.ticket-form__main { display: grid; gap: var(--space-5); }
.ticket-form__aside { display: grid; gap: var(--space-4); align-content: start; }
.ticket-form__aside-title {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); color: var(--ink);
}
.ticket-form__aside-title .icon { color: var(--accent); }
.ticket-form__actions {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  padding-block-start: var(--space-5);
  border-block-start: 1px solid var(--border);
}

/* The file field is the native control, so the platform's own picker opens and
   the field works with JavaScript off; the button inside it takes the token set
   through `::file-selector-button`, the one part of it a stylesheet may
   reach. */
.file {
  inline-size: 100%;
  min-block-size: var(--input-h);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-raised);
  color: var(--ink-muted);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--fs-body-sm);
}
.file::file-selector-button {
  margin-inline-end: var(--space-3);
  padding: var(--space-1) var(--space-3);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  cursor: pointer;
}
.ticket-form .file {
  padding: var(--space-4);
  border-style: dashed;
  background: color-mix(in srgb, var(--accent) 4%, var(--surface-raised));
}
.file:hover::file-selector-button {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border-color: var(--accent);
}
.file:focus-visible { border-color: var(--accent); outline: none; box-shadow: var(--glow-focus); }
/* ==========================================================================
   System pages — the 404 and 500 pair (§9.10)
   Every area carries its own pair under its own `system/` bucket, because a
   member who lands on one got there from inside that area and has to be handed
   back into it: the pair wears the set's own shell — app bar, rail, drawer —
   and only the plane changes. The panel is one bounded surface in the field
   the plane gives it, holding the code, what failed, and the way out; it
   carries no destination list of its own, because the rail and the drawer are
   already holding the set and a second menu inside the pane would be the same
   menu drawn twice.
   ========================================================================== */
.error {
  display: grid;
  justify-items: center;
  padding-block: var(--space-9) var(--space-12);
}
.error__panel {
  display: grid;
  gap: var(--rhythm-h-to-p);
  inline-size: 100%;
  max-inline-size: var(--content-max-prose);
  padding: calc(var(--card-pad) + var(--space-4)) var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
/* The mark says which kind of failure it is, and it is never the only thing
   that says so: the code above the title and the title itself both carry the
   meaning in words, so nothing here rests on a colour (§11). A missing address
   is a neutral condition and takes the raised plane with the muted ink; a
   failure the system records is a system state and takes the danger pair
   (§9.10). Neither takes `--accent` — the accent is interaction, and an error
   page has no action it is marking. */
.error__mark {
  display: inline-grid;
  place-items: center;
  inline-size: 56px;
  block-size: 56px;
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  color: var(--ink-muted);
}
.error__mark--danger { background: var(--danger-subtle); color: var(--danger); }
/* The code is a machine value, so it is set in the monospace face with Latin
   digits and marked `dir="ltr"` in the markup: a numeral read inside a
   right-to-left line must not be reordered against the text around it (§10). */
.error__code {
  font-family: var(--font-mono);
  font-size: var(--fs-heading-lg);
  line-height: var(--lh-heading-lg);
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
}
.error__title {
  font-family: var(--font-display);
  font-size: var(--fs-heading-lg);
  line-height: var(--lh-heading-lg);
  font-weight: var(--fw-semibold);
  color: var(--ink);
}
.error__lead { color: var(--ink-muted); font-size: var(--fs-body-md); }
/* The error's own reference line. The identifier arrives from the server when
   the event is recorded (`Event details` keeps received errors traceable), so
   the page carries the unresolved-content plate rather than a made-up value. */
.error__ref {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
.error__ref .placeholder { max-inline-size: var(--content-max-prose); }
/* Below the tablet tier the two ways out stack full width, so neither one is a
   narrow target beside the other on a handset (§11). */
.error__actions { display: grid; gap: var(--space-3); margin-block-start: var(--space-3); }
.error__actions .btn { inline-size: 100%; }
/* The mark and the code are one side group and the words are one body, so the
   tablet and desktop tiers can set them beside each other. Below the tablet
   tier the two stack in the same order as before. */
.error__side { display: grid; gap: var(--rhythm-h-to-p); justify-items: start; }
.error__body { display: grid; gap: var(--rhythm-h-to-p); align-content: start; }

/* ==========================================================================
   Unresolved content — an explicit placeholder, never an invented value (§9.11)
   ========================================================================== */
.placeholder {
  display: inline-flex; align-items: center;
  padding: 2px var(--space-2);
  border: 1px dashed color-mix(in srgb, var(--accent) 38%, var(--border-strong));
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  font-family: var(--font-mono);
  font-size: var(--fs-caption);
  color: var(--neutral-200);
}
[data-theme="light"] .placeholder { color: var(--ink-muted); }

/* ==========================================================================
   Activity history — the record list and its filter bar
   (`User activity history section`, `Event details`, §9.8)

   The section shows every event the organization records — sign-in, password
   change, a dashboard, report or form view, and a received error — with the
   user, the time and the related content each one carries. It is read-only and
   it is filtered by those same three axes.

   The filter bar is a plain form, so it works with JavaScript off. Below the
   tablet tier it collapses behind one «فیلترها» control that opens the same
   controls as a sheet — the collapse the design system's breakpoint contract
   asks for (§4.3) and the `sheet` variant of its filter bar (§9.8). The sheet
   is the bar itself, not a second set of controls: one form, two seats.
   ========================================================================== */
.filters { margin-block-end: var(--card-pad); }
/* The control that opens the sheet exists only below the tablet tier. */
.filters__toggle { justify-self: start; }
.filters__sheet {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-modal);
  display: grid;
  grid-template-rows: auto 1fr auto;
  /* A handset's worth of controls still fits well inside this, and the sheet
     scrolls inside itself rather than running off the top of the window. */
  max-block-size: 80vh;
  overflow-y: auto;
  background: var(--surface);
  border-block-start: 1px solid var(--border);
  border-start-start-radius: var(--radius-lg);
  border-start-end-radius: var(--radius-lg);
  box-shadow: var(--elev-4);
  transform: translateY(105%);
  transition: transform var(--dur-slow) var(--ease-emphasized);
}
.filters__sheet.is-open { transform: none; }
.filters__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  border-block-end: 1px solid var(--border);
}
.filters__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.filters__bar { display: grid; gap: var(--space-4); padding: var(--space-4); }
/* The field's own bottom margin would double the bar's gap between controls. */
.filters__field { margin-block-end: 0; }
.filters__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.filters__actions .btn { flex: 1 1 auto; }

/* The record table (`User activity history section`). Below the tablet tier
   each row is one record card, and every value carries its column's name beside
   it; from the tablet tier it is a real table again. The header row stays in
   the accessibility tree at both tiers — clipped rather than hidden — so every
   cell keeps its column's name, and the name a narrow tier shows in place of
   the header is the same string, read from the cell's own `data-label`. */
.ds--records,
.ds--records > caption,
.ds--records tbody,
.ds--records tr,
.ds--records td { display: block; }
.ds--records thead {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
}
.ds--records tbody { display: grid; gap: var(--space-3); }
.ds--records tr {
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.ds--records td { padding: var(--space-2) 0; border: 0; }
.ds--records td::before {
  content: attr(data-label);
  display: block;
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}

/* ==========================================================================
   Settings surface — the organization's own configuration and identity pages
   (`/admin/settings`, `/admin/branding`).

   Both pages are one column of panels: a titled group of fields inside the
   set's own `.card`, divided by that card's edges rather than by a rule. A
   panel never introduces a value — every size, radius, colour and gap below
   resolves to a documented token (§3.2).
   ========================================================================== */
.panel { display: grid; gap: var(--space-5); }
/* A single-column form is held to the design system's own form measure, so a
   name field on a 1680px plane is not a screen-wide box (§17.9). */
.panel--narrow { max-inline-size: var(--content-max-form); }
.panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.panel__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-heading-sm);
  font-weight: var(--fw-semibold);
}
.panel__note {
  max-inline-size: var(--content-max-prose);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
/* The panel owns the rhythm between its fields, so a field inside one carries
   no trailing margin of its own — the page's two-column pair and the single
   column alike close on the panel's own gap. */
.panel .field { margin-block-end: 0; }
/* A badge dropped into a field is a label, not a row: `.field` is a grid, so a
   badge left to the default stretch fills the whole column and reads as a band
   rather than as the standing it names. */
.field > .badge { justify-self: start; }

/* The page's closing action row. The controls stand together and the note
   takes the row's remaining room, so the reader meets the action first and the
   reason it is inert second. */
.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.form-actions__note {
  flex: 1 1 22rem;
  min-inline-size: 0;
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}

/* The organization's brand colour, chosen from the platform's presets. It is
   the same swatch language the member's own panel uses (`palette`), driven by
   a native radio rather than a scripted button, so the choice is a real form
   control and the taken one is marked by a ring and a check — never by hue
   alone (§11). */
.palette__opt {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: 28px;
  block-size: 28px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.palette__opt:hover { border-color: var(--border-strong); }
.palette__opt input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.palette__opt:has(input:checked) { border-color: var(--accent); }
.palette__opt:has(input:checked) .palette__check { display: block; }
.palette__opt:has(input:focus-visible) {
  outline: 2px solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

/* A list of granted capabilities. The check carries the meaning and the accent
   only anchors it — the sentence says what is granted (§11). */
.check-list { display: grid; gap: var(--space-2); }
.check-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--fs-body-sm);
}
.check-list .icon { margin-block-start: 1px; color: var(--accent); }

/* The organization's tables lay their rows out as blocks below the tablet
   tier, where four columns cannot hold their own content, and each cell names
   its own column through `data-label`. */
.ds--stack,
.ds--stack tbody,
.ds--stack tr,
.ds--stack th,
.ds--stack td { display: block; }
/* A caption left as a `table-caption` inside a block table shrinks to its
   longest word; as a block it holds the column like any other row. */
.ds--stack caption { display: block; }
.ds--stack tr { padding-block: var(--space-3); border-block-end: 1px solid var(--border); }
.ds--stack th,
.ds--stack td { padding-block: var(--space-1); padding-inline: 0; border-block-end: 0; }
/* A stacked cell reads as its column's name over its value. The name is a real
   node the language pass can translate, not a value read back through
   `content: attr()` — a generated string is invisible to the reading pass and
   would leave a Persian label on an English plane. The selector carries the
   table's own class as well as the modifier's, so this layer's reading of the
   stack holds whatever else in the sheet describes the same table. */
.ds.ds--stack td {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-1) 0;
  border: 0;
}
.ds.ds--stack td::before { content: none; }
/* A row's own name leads its block, in the interface's own voice rather than
   the grid's. */
.ds tbody th { font-weight: var(--fw-medium); }

/* A file field is a control like any other, so it takes the control box and
   gives its own button the set's secondary treatment. */
.input[type="file"] { padding-block: var(--space-2); }
.input[type="file"]::file-selector-button {
  margin-inline-end: var(--space-3);
  padding: var(--space-1) var(--space-3);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  cursor: pointer;
}
.input[type="file"]::file-selector-button:hover {
  background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}
.ds--records td + td { border-block-start: 1px solid var(--border); }
/* The first cell carries the event's status and the link into its own record. */
.ds--records td .badge { margin-inline-end: var(--space-2); }

/* A record row ends on one button that opens the full report. */
.ds--records td.rec__go::before { display: none; }
.ds--records td.rec__go .btn { inline-size: 100%; justify-content: center; }

/* The record list's own footer: how many rows this page holds, and the pager. */
.ds-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: var(--space-3);
  margin-block-start: var(--space-4);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}

/* ==========================================================================
   Notification centre (`In-app notification center`) — the member's own
   notifications, each naming the event that produced it and carrying its own
   read state. The state is never carried by colour alone: every row states it
   in words beside an accent marker, and the unread row repeats it as a rule at
   the inline-start edge (§11). The list is one column at every tier — a
   notification is read, not compared across a grid.
   ========================================================================== */
.notify-list { display: grid; gap: var(--space-3); }

.notify {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  /* The unread rule is a border and not a shadow: a shadow's offset is
     physical, and every row carries the border transparent so no row shifts
     by 2px when it becomes read. */
  border-inline-start: 2px solid transparent;
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.notify--unread { border-inline-start-color: var(--accent); }

/* The scan marker: accent while unread, the control boundary once read. It is
   `aria-hidden` — the word beside it carries the state for a screen reader. */
.notify__dot {
  inline-size: var(--space-2);
  block-size: var(--space-2);
  margin-block-start: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--border-strong);
}
.notify--unread .notify__dot { background: var(--accent); }

.notify__main { display: grid; gap: var(--space-1); min-inline-size: 0; }
.notify__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.notify__time { font-size: var(--fs-caption); color: var(--ink-muted); }
.notify__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.notify__body { font-size: var(--fs-body-sm); color: var(--ink-muted); max-inline-size: var(--content-max-prose); }
/* The row's closing line: the way into what the notification is about, the
   read state in words, and the one action the row offers. */
.notify__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3);
  margin-block-start: var(--space-2);
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--border);
}
.notify__link {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium);
}
/* The state and the action that changes it are one cluster at the row's
   reading-end, and the way into the content holds the reading-start. The auto
   margin is on the state rather than on the action, so a read row — which has
   no action — still closes at the same end as an unread one instead of jumping
   back beside the link. */
.notify__state { margin-inline-start: auto; font-size: var(--fs-caption); color: var(--ink-muted); }
.notify--unread .notify__state { color: var(--accent); font-weight: var(--fw-medium); }
/* The read action is an icon-only control carrying a visible tooltip and an
   accessible name: its label — «علامتگذاری بهعنوان خواندهشده» — does not fit
   a row action, and a control that cannot hold its label drops to an icon
   rather than truncating (§10). */
.notify__mark {
  display: inline-grid; place-items: center;
  inline-size: 32px; block-size: 32px;
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}
.notify__mark:hover {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border-strong));
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-raised));
}

/* ==========================================================================
   Responsive — min-width only (§3.6, §4.3)
   ========================================================================== */

/* Tablet — 768px */
@media (min-width: 768px) {
  .page { padding-inline: var(--space-6); }
  .page-head { grid-template-columns: minmax(0, 1fr) auto; align-items: start; padding: var(--space-7); }
  .page-actions { justify-content: end; padding-block-start: var(--space-1); }
  /* A head with no action row keeps one column at every tier, so its title and
     lead hold the full measure instead of half of it beside an empty track. */
  .page-head--plain { grid-template-columns: minmax(0, 1fr); }

  /* A session's action rejoins the row once there is room for it beside the
     device, and the 40px control no longer sits on a line of its own. */
  .session { grid-template-columns: auto minmax(0, 1fr) auto; }
  .session__action { grid-column: auto; }
  /* The two ways out of an error page stand side by side from the tablet tier
     up, where there is room for them on one line. */
  .error__actions { display: flex; flex-wrap: wrap; }
  .error__actions .btn { inline-size: auto; }

  .appbar__inner { padding-inline: var(--space-6); gap: var(--space-3); }
  .appbar::after { inset-inline: var(--space-6); }
  /* From the tablet tier up the row has room to give the search box its own
     measure, so it stops being a strip stretched across whatever the identity
     and the controls leave. It holds that measure at every wider tier instead
     of growing into the slack, so the room a wider window or a collapsed rail
     frees shows as the field standing further from the bar's reading-end edge
     — the field keeps the plane's line and travels with it. */
  .searchbox {
    flex: 0 1 18rem;
    min-inline-size: 12rem;
  }
  /* On the tablet tier the lockup holds the row's own start edge, in the flow:
     there is no rail here and so no column for it to stand over, and out of
     the flow it landed across the drawer trigger and the field. It leaves the
     flow only from --bp-lg, where the rail's column is there to hold it. */
  .appbar__org { display: flex; position: static; }
  .appbar__actions .appbar__theme { display: inline-grid; }

  .grid--kpi { grid-template-columns: repeat(2, 1fr); }
  .grid--dash { grid-template-columns: repeat(2, 1fr); }
  .grid--tpl { grid-template-columns: repeat(2, 1fr); }
  .grid--cat { grid-template-columns: repeat(2, 1fr); }

  /* The settings surface: two fields hold one row, the logo is read against
     both planes at once, and a table goes back to being a table — the row
     blocks and their in-cell labels are the handset's reading, not this one. */
  /* The caption goes back above its table: as a block inside a `display: table`
     it is laid out after the rows, which put the table's own title under its
     header. */
  .ds--stack caption { display: table-caption; }
  .ds--stack tr { padding-block: 0; }
  .ds--stack th,
  .ds--stack td {
    display: table-cell;
    padding: var(--space-3) var(--space-4);
    border-block-end: 1px solid var(--border);
  }
  /* The header cells are named here as well as above, because a later block in
     this sheet restores `thead` and the body cells but leaves `th` as a block:
     a block inside a `table-header-group` is wrapped in its own anonymous row,
     so the column names stack down the header instead of running across it.
     The extra class in the selector is what keeps the restore whole. */
  .ds--stack thead th { display: table-cell; }
  /* The stacked cell's own grid is released with the table, at the same
     weight the base rule declares it at. */
  .ds.ds--stack td {
    display: table-cell;
    padding: var(--space-3) var(--space-4);
    border-block-end: 1px solid var(--border);
  }
  /* Two columns with an odd count leave a hole, so the last card takes the row
     and turns on its side: the motif keeps its own column instead of stretching
     into a billboard. The same card at one column or three needs no rule. */
  .grid--dash > :last-child:nth-child(odd),
  .grid--tpl > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    grid-template-columns: minmax(0, 16rem) minmax(0, 1fr);
    grid-template-rows: 1fr auto;
  }
  .grid--dash > :last-child:nth-child(odd) .card__thumb,
  .grid--tpl > :last-child:nth-child(odd) .card__thumb {
    grid-row: 1 / -1;
    aspect-ratio: auto;
    block-size: 100%;
    border-block-end: 0;
    border-inline-end: 1px solid var(--border);
  }
  .grid--dash > :last-child:nth-child(odd) .card__body,
  .grid--tpl > :last-child:nth-child(odd) .card__body { grid-column: 2; align-content: center; }
  .grid--dash > :last-child:nth-child(odd) .card__foot,
  .grid--tpl > :last-child:nth-child(odd) .card__foot { grid-column: 2; }

  /* Wider tiers lengthen the plot and widen the bar gutter, so the six month
     labels are never truncated and the axis scale stays readable. */
  .chart { block-size: 260px; }
  .chart__cols, .chart__ticks { gap: var(--space-2); }

  /* The auth set takes the tier's own padding and step, so the sign-in plane
     sits in the same rhythm as every other surface at 768px. */
  .auth-top { padding: var(--space-6); }
  .auth-main { padding: var(--space-8) var(--space-6) var(--space-11); }
  .auth { gap: var(--space-8); }
  .auth__panel { padding: var(--space-7); }
  /* From the tablet tier there is room for the filter controls themselves, so
     the bar is the bar and the sheet is not a sheet: the same form, seated
     inline, with the control that opened it gone. */
  .filters__toggle { display: none; }
  .filters__head { display: none; }
  .filters__sheet {
    position: static;
    z-index: auto;
    max-block-size: none;
    overflow: visible;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    transform: none;
  }
  .filters__bar { display: flex; flex-wrap: wrap; align-items: end; gap: var(--space-3); padding: 0; }
  .filters__field { flex: 1 1 13rem; min-inline-size: 0; }
  .filters__actions { flex: none; }
  .filters__actions .btn { flex: none; }

  /* The record table is a table again, and every cell drops the name it was
     carrying in place of the header row. */
  .ds--records { display: table; }
  .ds--records > caption { display: table-caption; }
  .ds--records thead {
    position: static;
    display: table-header-group;
    inline-size: auto; block-size: auto;
    margin: 0; padding: 0; overflow: visible;
    clip-path: none;
  }
  .ds--records tbody { display: table-row-group; gap: 0; }
  .ds--records tr {
    display: table-row;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .ds--records td {
    display: table-cell;
    padding: var(--space-3) var(--space-4);
    border-block-end: 1px solid var(--border);
  }
  .ds--records td::before { content: none; }
  .ds--records td + td { border-block-start: 0; }
  .ds--records td { vertical-align: middle; }
  .ds--records td[data-label="زمان"], .ds--records td[data-label="کاربر"] { white-space: nowrap; }
  .ds--records td.rec__go, .ds--records th:last-child { white-space: nowrap; text-align: center; }
  .ds--records td .badge { white-space: nowrap; }
  .ds--records td.rec__go .btn { inline-size: auto; }
}

/* Laptop — 1024px. The rail appears here; the drawer and its trigger retire. */
@media (min-width: 1024px) {
  :root { --rail-w: var(--sidebar-w); }
  html.rail-collapsed { --rail-w: var(--sidebar-w-collapsed); }

  .appbar__burger { display: none; }
  .appbar__period { display: flex; }
  /* From here the bar's row is the plane's column, not the window's width: it
     starts where the plane starts, past the rail, and ends at the plane's
     reading-end padding. The field then holds the plane's own edge, and the
     rule under the bar spans the same column as the card beneath it. It is
     also what makes the field follow the rail: collapse the rail and the
     column's start edge moves 200px toward the window's start edge, and the
     field — no longer bidding for slack against the identity, which is out of
     the flow — travels with it by the same amount. */
  .appbar__inner {
    padding-inline-start: calc(var(--rail-w) + var(--space-7));
    padding-inline-end: var(--space-7);
  }
  .appbar::after {
    inset-inline-start: calc(var(--rail-w) + var(--space-7));
    inset-inline-end: var(--space-7);
  }
  /* The lockup holds the rail's own inline edge here, so the mark stands over
     the rail's destination column instead of floating at the bar's arbitrary
     padding. Collapsed, the rail is a 64px strip of icons and the lockup's own
     name would sit in the room the plane has just taken back, so the name is
     kept in the accessible name and taken out of the row — the same treatment
     the rail gives its own labels (design-system §9.7). */
  .appbar__org { position: absolute; inset-inline-start: var(--space-3); }
  html.rail-collapsed .appbar__org .placeholder {
    position: absolute;
    inline-size: 1px; block-size: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; white-space: nowrap;
    clip-path: inset(50%);
  }
  /* The controls stand off the period chip on one gap, with no rule between
     them: the bar closes on its own hairline alone, and a vertical rule inside
     it read as a line down the bar's edge rather than as a divider. */
  .appbar__actions { gap: var(--space-3); }

  /* The rail is a flex column: the head holds the top edge, the footer — the
     signed-in member — takes the remaining space above it and so sits on the
     bottom edge, whatever number of destinations the role can reach. */
  .rail {
    display: flex;
    flex-direction: column;
    position: fixed;
    inset-block-start: var(--appbar-h);
    inset-block-end: 0;
    inset-inline-start: 0;
    inline-size: var(--rail-w);
    z-index: var(--z-rail);
    overflow-y: auto;
    /* Extra room at the top for the collapse handle, which hangs on the rail's
       edge above the first destination rather than over it. */
    padding: var(--space-8) var(--space-3) var(--space-4);
    background: var(--surface);
    border-inline-end: 1px solid var(--border);
  }
  /* A collapsed rail keeps every label in the accessible name and shows it
     again on hover or focus (§9.7). */
  html.rail-collapsed .rail__label {
    position: absolute;
    inline-size: 1px; block-size: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; white-space: nowrap;
    clip-path: inset(50%);
  }
  html.rail-collapsed .rail__link { justify-content: center; padding-inline: 0; }
  /* Collapsed, a group head cannot be read — at 64px its two or three words
     wrapped onto their own lines and a column of icons was cut by broken
     sentences. It is clipped like a collapsed destination's label rather than
     dropped, because the list beside it is labelled by it. What it used to do
     visually — close one group and open the next — is carried by a hairline on
     the list that follows it. */
  html.rail-collapsed .rail__group-head {
    position: absolute;
    inline-size: 1px; block-size: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; white-space: nowrap;
    clip-path: inset(50%);
  }
  /* With the heads clipped, the hairline that closed one group and opened the
     next moves onto the groups themselves. */
  html.rail-collapsed .rail__group {
    margin-block-start: var(--space-4);
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--border);
  }
  html.rail-collapsed .rail__link:hover .rail__label,
  html.rail-collapsed .rail__link:focus-visible .rail__label {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: calc(100% + var(--space-2));
    z-index: var(--z-tooltip);
    inline-size: auto; block-size: auto;
    margin: 0; padding: var(--space-1) var(--space-2);
    transform: translateY(-50%);
    clip-path: none;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--elev-3);
    font-size: var(--fs-caption);
    white-space: nowrap;
  }
  /* The handle hangs on the rail's inner edge and moves with it: placed from
     the width token rather than from a fixed offset, it needs no rule of its
     own for the collapsed step. Its arrow is mirrored for the open rail, where
     the edge it moves is the one at the reading-start side, and left alone once
     collapsed, where the edge returns. */
  .rail__toggle {
    position: fixed;
    inset-block-start: calc(var(--appbar-h) + var(--space-2));
    inset-inline-start: calc(var(--rail-w) - var(--space-4));
    z-index: var(--z-rail);
    display: grid;
    place-items: center;
    inline-size: 32px;
    block-size: 32px;
    padding: 0;
    background: var(--surface-raised);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    box-shadow: var(--elev-2);
    color: var(--ink-muted);
    cursor: pointer;
  }
  .rail__toggle:hover {
    color: var(--ink);
    border-color: color-mix(in srgb, var(--accent) 45%, var(--border-strong));
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-raised));
  }
  /* The arrow points to the edge the rail's width moves to, and which edge
     that is depends on the side the rail sits on — the one place in this set
     where a physical transform is the right tool, like the drawer's own slide.
     RTL: the rail is at the right, so opening retreats to the right (mirrored)
     and collapsing returns to the left (plain). LTR: it is at the left, so the
     two are the other way round — as the plain arrow, the open rail's control
     pointed away from the edge it was going to move. */
  html[dir="rtl"] .rail__toggle .icon { transform: scaleX(-1); }
  html[dir="rtl"].rail-collapsed .rail__toggle .icon { transform: none; }
  /* Collapsed, the block stacks: a 64px column holds one control per line, so
     the avatar sits over the logout action and the name and role stay in the
     link's accessible name. */
  html.rail-collapsed .rail__foot { flex-direction: column; gap: var(--space-1); }
  html.rail-collapsed .account { flex: none; justify-content: center; padding-inline: 0; }
  html.rail-collapsed .account__text {
    position: absolute;
    inline-size: 1px; block-size: 1px;
    margin: -1px; padding: 0; border: 0;
    overflow: hidden; white-space: nowrap;
    clip-path: inset(50%);
  }

  .page { margin-inline-start: var(--rail-w); padding-inline: var(--space-7); }

  .grid--kpi { grid-template-columns: repeat(4, 1fr); }
  .grid--dash { grid-template-columns: repeat(3, 1fr); }
  .grid--tpl { grid-template-columns: repeat(3, 1fr); }
  .grid--cat { grid-template-columns: repeat(4, 1fr); }
  /* Three columns can absorb the odd card, so the filler rule is released —
     and released whole. Releasing only `grid-column` left the card carrying the
     horizontal template it was given at --bp-md, so its motif stayed in a fixed
     16rem column and squeezed its text into the remainder; every property the
     tablet rule set is put back here. */
  .grid--dash > :last-child:nth-child(odd),
  .grid--tpl > :last-child:nth-child(odd) {
    grid-column: auto;
    grid-template-columns: none;
    grid-template-rows: auto 1fr auto;
  }
  .grid--dash > :last-child:nth-child(odd) .card__thumb,
  .grid--tpl > :last-child:nth-child(odd) .card__thumb {
    grid-row: auto;
    border-inline-end: 0;
    border-block-end: 1px solid var(--border);
  }
  .grid--dash > :last-child:nth-child(odd) .card__thumb,
  .grid--tpl > :last-child:nth-child(odd) .card__thumb { aspect-ratio: 16 / 9; block-size: auto; }
  .grid--dash > :last-child:nth-child(odd) .card__body,
  .grid--tpl > :last-child:nth-child(odd) .card__body { grid-column: auto; align-content: start; }
  .grid--dash > :last-child:nth-child(odd) .card__foot,
  .grid--tpl > :last-child:nth-child(odd) .card__foot { grid-column: auto; }

  /* The ticket form and the ticket page: the main card takes the room and the
     side card a fixed measure at the reading-end edge. The first track is the
     inline-start one, so this holds in either direction. */
  .ticket-form, .ticket-detail {
    grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem);
    gap: var(--space-6);
  }
  .ticket-facts { order: 0; }
  .ticket-facts__list { grid-template-columns: minmax(0, 1fr); }

  .chart { block-size: 300px; }

  .auth-top { padding-inline: var(--space-7); }
  .auth-main { padding: var(--space-9) var(--space-7) var(--space-12); }
}

/* ==========================================================================
   Top navigation — the shell's second shape (§3.4).
   The same destination set, folded the same way, laid along one bar under the
   app bar instead of down a column. The groups stay `details`, so a group is
   still opened from the keyboard and with JavaScript off; the only difference
   is that an open list is a popover under the head that owns it rather than a
   run of rows under it. Below --bp-lg the shape is inert — there is no rail to
   replace there, and the drawer already carries the set.
   ========================================================================== */
@media (min-width: 1024px) {
  /* The bar takes the rail's place in the flow, so the plane below it starts at
     the window's own edge. It is sticky under the app bar, and it never clips:
     the rail's own scroll container would cut every popover away. */
  html.layout-topnav .rail {
    position: sticky;
    inset-block-start: var(--appbar-h);
    inset-block-end: auto;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--space-1);
    inline-size: 100%;
    overflow: visible;
    padding: var(--space-1) var(--space-7);
    border-inline-end: 0;
    border-block-end: 1px solid var(--border);
  }
  /* Only the entry row lies along the bar; a group's own list is the popover. */
  html.layout-topnav .rail > .rail__list {
    display: flex;
    align-items: center;
    gap: var(--space-1);
  }
  html.layout-topnav .rail__link,
  html.layout-topnav .rail__group-head {
    inline-size: auto;
    min-block-size: 36px;
    padding-inline: var(--space-3);
    border-inline-start: 0;
    white-space: nowrap;
  }
  html.layout-topnav .rail__group {
    position: relative;
    margin: 0;
    padding: 0;
    border-block-start: 0;
  }
  html.layout-topnav .rail__group-head { margin: 0; }
  html.layout-topnav .rail__group > .rail__list {
    position: absolute;
    inset-block-start: calc(100% + var(--space-1));
    inset-inline-start: 0;
    z-index: var(--z-dropdown);
    min-inline-size: 14rem;
    padding: var(--space-2);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--elev-3);
  }
  html.layout-topnav .rail__group:not([open]) > .rail__list { display: none; }
  /* The account block belongs to the column. With no column, the app bar's own
     account menu is the only entry — exactly as it is below --bp-lg. */
  html.layout-topnav .rail__foot,
  html.layout-topnav .rail__toggle { display: none; }
  /* A collapsed rail is not a state this shape has, so its clipping is undone
     rather than left to hide every label along the bar. */
  html.layout-topnav.rail-collapsed .rail__label,
  html.layout-topnav.rail-collapsed .rail__group-head {
    position: static;
    inline-size: auto; block-size: auto;
    margin: 0; padding: var(--space-2) var(--space-3); border: 0;
    overflow: visible; white-space: nowrap;
    clip-path: none;
  }
  /* The plane takes the window's own width, and the field moves to the
     reading-end edge beside the controls: with no rail there is no column for
     it to hold, and the lockup keeps the bar's own corner. */
  html.layout-topnav .page { margin-inline-start: 0; }
  html.layout-topnav .appbar__inner { padding-inline-start: var(--space-7); }
  html.layout-topnav .appbar::after { inset-inline-start: var(--space-7); }
  html.layout-topnav .searchbox { margin-inline-start: auto; }
  /* One auto margin in the row, not two: with both the field and the controls
     claiming the slack, the free room is split between them and the field
     parks in the middle of the bar instead of standing beside the controls. */
  html.layout-topnav .appbar__zone--trail { margin-inline-start: 0; }
  /* The bar stands over the nav row, popovers and all. The app bar is a
     stacking context of its own, so the customization popover it owns is
     painted inside it: below the nav row's own level, that panel opened
     behind the bar and its first group was covered. One step above the rail's
     level, and the popover inside it sorts with it. */
  html.layout-topnav .appbar { z-index: calc(var(--z-rail) + 1); }
  .customize__group--layout { display: grid; }
}

/* Wide desktop — 1440px. The plane stops growing; the cards do not. */
@media (min-width: 1440px) {
  .page { padding-inline: var(--space-8); }
  .auth-top { padding-inline: var(--space-8); }
  .auth-main { padding-inline: var(--space-8); }
  .appbar__inner {
    padding-inline-start: calc(var(--rail-w) + var(--space-8));
    padding-inline-end: var(--space-8);
  }
  .appbar::after {
    inset-inline-start: calc(var(--rail-w) + var(--space-8));
    inset-inline-end: var(--space-8);
  }
}

/* ==========================================================================
   Reduced motion, contrast and transparency (§7.3, §11)
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: var(--dur-fast) !important;
  }
  .skeleton::after { display: none; }
  .card--interactive:hover { transform: none; }
}
@media (prefers-contrast: more) {
  :root, [data-theme="dark"] { --border: var(--border-strong); }
}
@media (prefers-reduced-transparency: reduce) {
  .appbar { background: var(--bg); backdrop-filter: none; }
}

/* ==========================================================================
   Administration surfaces — the `/admin` bucket (§3.2)

   The organization's own administration section. It wears the same shell and
   the same tokens as the rest of the set and adds only the shapes its
   surfaces need: a panel with its own head and foot, a toolbar over a table,
   the list variant a table reflows to below the tablet tier, a permission
   matrix, the form and choice shapes, and a switch. Every rule composes
   documented tokens; no value is invented here.

   Two rules bind everything below. Access level is never encoded in colour
   (`Brand colour` › «Encode access level in colour» is a Don't), so a tier, a
   confidentiality layer and a matrix cell all carry their meaning in words and
   in the shape of their glyph, and take the ink tokens. A state — expired,
   revoked, breached — may take a feedback token, because a state is what the
   feedback tokens are for.
   ========================================================================== */

/* A metric tile with three content rows instead of the home strip's four:
   head, figure and one meta line, with the tile's footer band landing in the
   strip's own slack row and pinned to the bottom edge by `align-self: end`. */
.admin-metric { grid-template-rows: repeat(3, auto) 1fr; }

/* Entry cards — 1 / 2 / 3 columns, and no odd-count filler rule: these cards
   carry no preview plate, so the tablet tier's horizontal treatment has
   nothing to lay out and the grid simply fills. */
.grid--admin { grid-template-columns: 1fr; }
.admin-card { grid-template-rows: auto 1fr auto; }
.admin-card .card__body { gap: var(--space-2); }
.admin-card__icon {
  display: inline-grid;
  place-items: center;
  inline-size: var(--space-8);
  block-size: var(--space-8);
  margin-block-end: var(--space-1);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border));
  border-radius: var(--radius-md);
  color: var(--accent);
}

/* A section's lead line. It takes the head's second row, so the title and the
   block's one action keep the first line to themselves. */
.section__lead {
  flex: 1 1 100%;
  /* The prose cap is padding, not max-inline-size: a capped basis would let the
     line fit beside the title instead of taking its own row. */
  box-sizing: border-box;
  padding-inline-end: max(0px, calc(100% - var(--content-max-prose)));
  margin-block-start: calc(-1 * var(--space-1));
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}

/* ==========================================================================
   Section card — one bounded region with its own head, body and foot. It is
   the card's own surface and its own closed set; what it adds is the head and
   the foot a table or a form needs to state what it holds and what closes it.

   It is not named `panel`, because that word already belongs to the settings
   surface above — a *transparent grouping* of fields inside a `.card`. This is
   a *surface*: one bounded plane with its own edge. Two different things, two
   names.
   ========================================================================== */
.section-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
  overflow: hidden;
}
.section-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--card-pad);
  border-block-end: 1px solid var(--border);
}
.section-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-heading-sm);
  font-weight: var(--fw-semibold);
}
.section-card__title::before {
  content: "";
  flex: none;
  inline-size: 3px;
  block-size: 1.125rem;
  border-radius: var(--radius-pill);
  background: var(--accent);
}
.section-card__body { padding: var(--card-pad); }
.section-card__body--flush { padding: 0; }
.section-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--card-pad);
  border-block-start: 1px solid var(--border);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
/* A region whose whole content is a decision that ends access. It takes the
   danger boundary and nothing else: the action inside it carries the verb. */
.section-card--danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }

/* ==========================================================================
   Toolbar and filter chips — the row above a table, scoped to the section's
   own items (`Global search` › «Scope each search to the section's own items»).
   ========================================================================== */
/* The field keeps its own measure inside a toolbar rather than taking the
   whole row, so the filters beside it stay on the same line at the wider
   tiers. Declared here, before the tier rules, because this selector is more
   specific than `.searchbox` and would otherwise lose to the tablet rule. */
.toolbar .searchbox { flex: 1 1 16rem; min-inline-size: 12rem; }
/* A select inside a toolbar takes its own measure rather than the full row a
   form field normally claims, so it stands beside the field and not under it. */
.toolbar .select { flex: 0 1 auto; inline-size: auto; min-inline-size: 11rem; }
.toolbar__spacer { margin-inline-start: auto; }
/* A toolbar standing inside a section head is the head's own control, not a
   row above a table, so it takes no block margin of its own. */
.section__head .toolbar { margin-block-end: 0; }
.toolbar__note { font-size: var(--fs-caption); color: var(--ink-muted); }
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-block-end: var(--space-4); }

/* ==========================================================================
   Tables (§9.8, §17.10) — the stack contract itself is declared with the
   settings surface above; what follows is the piece the wider tables add, and
   one repair to that contract's header row.
   ========================================================================== */
/* A sortable column header is a real button carrying `aria-sort` on its `th`,
   so the state is announced and the control is reachable from the keyboard. */
.ds__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.ds__sort:hover { color: var(--ink); }
.ds th[aria-sort] .ds__sort { color: var(--ink); font-weight: var(--fw-semibold); }
.ds th[aria-sort] .ds__sort .icon { color: var(--accent); }

/* ==========================================================================
   Permission matrix (§9.8) — rows are a scope, columns an access layer.
   A cell states its own grant in words and in the shape of its glyph; no cell
   is read by hue (§11, `Brand colour` › «Encode access level in colour»).
   ========================================================================== */
.matrix { inline-size: 100%; border-collapse: collapse; }
.matrix th, .matrix td {
  padding: var(--space-3);
  border-block-end: 1px solid var(--border);
  border-inline-start: 1px solid var(--border);
  font-size: var(--fs-body-sm);
  text-align: center;
}
.matrix tr > :first-child { text-align: start; border-inline-start: 0; }
.matrix thead th {
  background: var(--surface-raised);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
}
.matrix__cell { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-caption); }
.matrix__cell--granted { color: var(--ink); font-weight: var(--fw-medium); }
.matrix__cell--none { color: var(--ink-muted); }
.matrix__cell--inherited { color: var(--ink); }

/* A legend for any glyph-bearing surface: every entry names its mark in words. */
.legend { display: flex; flex-wrap: wrap; gap: var(--space-4); font-size: var(--fs-caption); color: var(--ink-muted); }
.legend__item { display: inline-flex; align-items: center; gap: var(--space-2); }

/* ==========================================================================
   Panels of facts — a key over its value, hairlines between rows. Used for
   the instance's own facts, an account's own record and a setting's key-value
   pair, so a column of facts reads the same wherever it appears.
   ========================================================================== */
.kv__row { border-block-end: 1px solid var(--border); }
.kv__row:last-child { border-block-end: 0; padding-block-end: 0; }
/* A list whose key is a badge rather than a label takes one column: the badge
   leads its own sentence instead of standing in a key column wide enough to
   hold a phrase, which left most of the row empty and pushed the reading away
   from the mark it belongs to. Carried over the tablet rule by weight, so the
   two shapes can sit in one stylesheet. */
.kv--inline .kv__row { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
.kv__val .badge, .kv__val .placeholder { vertical-align: middle; }

/* ==========================================================================
   Forms (§9.2, §17.9)
   ========================================================================== */
.form-grid { display: grid; gap: 0 var(--space-4); }
.form-grid > .field { min-inline-size: 0; }
.form-grid > .field--full { grid-column: 1 / -1; }
/* The action row and its note are the settings surface's own `.form-actions`
   and `.form-actions__note`, declared above; a two-column form only has to
   put the row's controls on the grid's full width. */
.form-grid > .form-actions { padding-block-start: var(--space-2); }

/* A group of choices, each a real control with its own title and hint. The
   taken one is marked by the accent boundary and by the control's own state,
   never by hue alone (§11). */
.choices { display: grid; gap: var(--space-2); margin: 0; padding: 0; border: 0; }
.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
}
.choice:has(input:checked) { border-color: var(--accent); background: var(--info-subtle); }
.choice:has(input:disabled) { opacity: .5; cursor: not-allowed; }
.choice input { flex: none; inline-size: 18px; block-size: 18px; margin: 3px 0 0; accent-color: var(--accent); }
.choice__text { display: grid; gap: 2px; min-inline-size: 0; }
.choice__title { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.choice__hint { font-size: var(--fs-caption); color: var(--ink-muted); }

/* A switch: the control is a real checkbox, taken out of the paint and kept in
   the accessibility tree, and the track is its visible state. */
.switch { display: inline-flex; align-items: center; gap: var(--space-3); min-block-size: 32px; cursor: pointer; }
.switch input { position: absolute; inline-size: 1px; block-size: 1px; clip-path: inset(50%); }
.switch__track {
  position: relative;
  flex: none;
  inline-size: 40px;
  block-size: 22px;
  background: var(--neutral-700);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
}
[data-theme="light"] .switch__track { background: var(--neutral-100); }
.switch__track::after {
  content: "";
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: 2px;
  inline-size: 16px;
  block-size: 16px;
  background: var(--ink-muted);
  border-radius: var(--radius-pill);
  transition: inset-inline-start var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.switch input:checked + .switch__track { background: var(--info-subtle); border-color: var(--accent); }
.switch input:checked + .switch__track::after {
  inset-inline-start: calc(100% - 18px);
  background: var(--accent);
}
.switch input:focus-visible + .switch__track { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.switch__text { font-size: var(--fs-body-md); }

/* ==========================================================================
   Administration layout — the section's own column pair and the desk layout
   the design system's shell templates name as `content + panel` (§17.1).
   ========================================================================== */
.split { display: grid; gap: var(--gap-cards); }
.grid--panel { grid-template-columns: minmax(0, 1fr); }
/* A section that opens a grid already stands under the grid's own edge: the
   block rhythm applies between sections, not above the first one in a column. */
.grid > .section:first-child { margin-block-start: 0; }

/* Tablet — 768px */
@media (min-width: 768px) {
  .grid--admin { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two columns with an odd count leave a hole, so the last card takes the
     whole row; the span is released at the laptop tier, where three columns
     absorb the odd card again. Same rule the dashboard and template grids
     carry, on the entry cards' own class. */
  .grid--admin > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The list variant is released here, whole: every declaration the base rule
     set is put back, because a table that kept one of them — a block row, a
     grid cell — renders as neither a table nor a list. */
  .ds--stack { display: table; }
  .ds--stack tbody { display: table-row-group; }
  .ds--stack tr { display: table-row; padding: 0; border-block-end: 0; }
  .ds--stack td { display: table-cell; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--border); }
  .ds--stack td::before { content: none; }
  .ds--stack .num { text-align: end; }
}

/* Laptop — 1024px */
@media (min-width: 1024px) {
  .grid--admin { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid--admin > :last-child:nth-child(odd) { grid-column: auto; }
  .split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Content + panel, held to one reading measure: the design system's own
     shell template for a detail surface beside its controls (§17.1). */
  .grid--panel { grid-template-columns: minmax(0, 1fr) minmax(0, 480px); align-items: start; }
}

/* ==========================================================================
   Print (§13) — chrome is dropped, a printed chart keeps its data table.
   ========================================================================== */
@media print {
  .appbar, .rail, .drawer, .scrim, .drawer-host, .skip-link, .page-actions, .catnav { display: none !important; }
  /* The auth set prints its form alone: the top row is chrome and the hero is
     the gradient, and §13 drops both from a printed sheet. */
  .auth-top { display: none !important; }
  .auth-page { display: block; min-block-size: 0; }
  .auth__panel { background: #fff; border-color: #000; box-shadow: none; }
  .appbar, .rail, .drawer, .scrim, .drawer-host, .skip-link, .page-actions, .catnav,
  .session__action, .actions { display: none !important; }
  .filters, .ds-foot { display: none !important; }
  .notify__mark, .notify__dot { display: none !important; }
  body { background: #fff; color: #000; }
  /* The plane's ink and its surfaces are forced where they are declared, not
     class by class: every panel reads the theme tokens, and the shipped dark
     map's near-white ink on a forced-white page printed as white on white.
     Design-system §13 asks for black-on-white ink, and this is where the set
     delivers it — including the accent, which on paper is ink and not a hue. */
  :root, [data-theme="dark"], [data-theme="light"], [data-theme="auto"] {
    --bg: #fff; --surface: #fff; --surface-raised: #fff;
    --ink: #000; --ink-muted: #333;
    --border: #000; --border-strong: #000;
    --accent: #000; --accent-hover: #000; --on-accent: #fff;
    --info: #000; --info-subtle: #fff;
    --success: #000; --success-subtle: #fff;
    --warning: #000; --warning-subtle: #fff;
    --danger: #000; --danger-subtle: #fff;
  }
  /* A badge printed as a filled box disappears where the printer drops
     backgrounds, so every badge and placeholder takes a boundary and the ink. */
  .badge, .placeholder { background: #fff; color: #000; border-color: #000; }
  .card, .metric, .chart-frame, .kpi-strip, .kpi-strip > .metric, .metric--threshold,
  .page-head, .metric__delta, .metric__state, .metric__owner, .chart__target-label,
  .section-card, .section-card__head, .section-card__foot, .matrix thead th, .choice, .switch__track {
    background: #fff; border-color: #000; box-shadow: none;
  }
  .sessions, .session { background: #fff; border-color: #000; box-shadow: none; }
  .ticket, .ticket__file, .msg__card, .alert, .file {
    background: #fff; border-color: #000; box-shadow: none;
  }
  /* A link cue describes a control, not the record: on paper the row stands. */
  .ticket__cue { display: none; }
  .notify {
    background: #fff; border-color: #000; box-shadow: none;
  }
  /* No decorative wash on paper. */
  .page-head { background-image: none; }
  .notify__foot { border-color: #000; }
  .error__panel, .error__mark {
    background: #fff; border-color: #000; box-shadow: none;
  }
  /* An error page printed keeps its mark's ink: a plate forced to the page is
     a hole where the icon was. */
  .error__mark { color: #000; }
  /* The plot keeps its guides on paper; a printed chart reads against them. */
  .chart__plot { border-color: #000; }
  .chart__target { border-block-start-color: #000; }
  /* The saved-layout plate is a picture on the app's dark plane, so it prints
     as the plate does on screen; its frame is ruled in ink like the rest. */
  .card__thumb { background-color: #fff; border-color: #000; }
  .placeholder { color: #000; border-color: #000; background: none; }
  .page { margin-inline-start: 0; }
  /* A chart printed without its figures is a defect: force the table open.
     The table sits inside the section card the administration block gives it,
     so the selector reaches it as a descendant rather than as a direct child. */
  .table-view table { display: table; }
  /* The record list prints as a table whatever the window measures, so the
     sheet it collapses into on a narrow window never reaches paper. */
  .ds--records, .ds--records > caption, .ds--records tbody,
  .ds--records thead, .ds--records tr, .ds--records td { display: revert; }
  .ds--records thead {
    position: static;
    inline-size: auto; block-size: auto;
    margin: 0; padding: 0; overflow: visible;
    clip-path: none;
  }
  .ds--records tr { box-shadow: none; }
  .ds--records td::before { content: none; }
}

/* ==========================================================================
   Admin surfaces — resources, ingestion, monitoring, notifications
   (idms-dashboard.md §4.2 to §4.5)
   ==========================================================================
   Four administrative routes share one layer: a crumb row, a control row
   above a table, a strip of counters, a flat panel with its own head, key and
   value rows for one object's detail, a feed for event-shaped records, and a
   toggle list for per-item switches. Every class below is a composition of
   documented tokens and declares layout; it introduces no colour, radius,
   space, type size or elevation of its own (§3.2). */

/* Breadcrumb — these routes sit two levels into the set, so each carries its
   own trail. The container is a flex row and the list is the one item in it;
   without the list rules the two crumbs ran together with no separator. */
.crumbs__list { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.crumbs__list li + li::before { content: "/"; margin-inline-end: var(--space-2); color: var(--border-strong); }
.crumbs a { color: var(--ink-muted); text-decoration: none; }
.crumbs a:hover { color: var(--accent); text-decoration: underline; }
.crumbs { margin-block-end: var(--space-6); }

/* The control row above a table: the page's own field, the applied filters,
   and the actions that act on them. It wraps rather than scrolls — a control
   that leaves the row is an action a reader cannot reach. */
.toolbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3); margin-block-end: var(--card-pad); }
.toolbar__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-inline-start: auto; }

/* The page's own search field. `.field` is the form's stacked single-column
   field; on the toolbar it stands beside the filters, so it keeps its label
   and loses the form's bottom margin and its full measure. */
.field--inline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-block-end: 0; flex: 1 1 18rem; min-inline-size: 0; }
.field--inline .input { flex: 1 1 12rem; inline-size: auto; }

/* The filter bar (§9.8). An applied filter is a chip carrying its own label,
   its value and its remove control; «افزودن پالایه» and «بازنشانی» stand
   beside them rather than inside them. */
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); flex: 1 1 auto; }

/* A dense table on a handset. The design system's §9.8 asks a table to reflow
   to its `list` variant rather than scroll sideways; these admin tables carry
   five or six columns, and a per-row card stack would repeat every column's
   name above every cell. The scroller is the same answer the landing set
   already ships for its comparison table, and it keeps the parallel columns
   comparable at every tier — recorded in §3.6. */
.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); box-shadow: var(--elev-1); }
.table-wrap .ds { min-inline-size: 40rem; }

/* Counters, as one bordered plane divided by hairlines — the home page's KPI
   strip construction, for figures that carry no target and no owner: a
   connector count, a run's success rate, an unread total. */
.stat-grid { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--elev-1); overflow: hidden; }
.stat { display: grid; gap: var(--space-1); align-content: start; padding: var(--card-pad); background: var(--surface); }
.stat__label { font-size: var(--fs-caption); color: var(--ink-muted); }
.stat__value { font-size: var(--fs-heading-lg); line-height: var(--lh-heading-lg); font-weight: var(--fw-bold); }
.stat__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
/* A counter that reports a condition rather than a total. The plane carries
   the state and the label under the figure names it in words, so the hue is
   never the only cue (§11). */
.stat--warning { background: var(--warning-subtle); }
.stat--warning .stat__value { color: var(--warning); }

/* A panel's own head: the title on one side, the state or the count on the
   other. `.section__head` is the page's head and carries the section rhythm;
   a card's head is quieter and draws no accent bar of its own. */
.panel-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); margin-block-end: var(--space-4); }
.panel-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.panel-title .icon { color: var(--accent); }
.panel-note { font-size: var(--fs-caption); color: var(--ink-muted); }
.panel-foot { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-4); padding-block-start: var(--space-4); border-block-start: 1px solid var(--border); }

/* One object's detail, read as key and value rows: the key column narrow and
   muted, the value column carrying the reading. */
.kv { display: grid; }
.kv__row { display: grid; grid-template-columns: minmax(0, 7rem) minmax(0, 1fr); gap: var(--space-3); padding-block: var(--space-3); border-block-start: 1px solid var(--border); align-items: start; }
.kv__row:first-child { border-block-start: 0; padding-block-start: 0; }
.kv__key { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.kv__val { font-size: var(--fs-body-sm); color: var(--ink); }

/* A line of explanation under a panel or a table: what the rows above mean,
   or the rule they are read against. Never a rule of its own. */
.note { margin-block-start: var(--space-3); max-inline-size: var(--content-max-prose); font-size: var(--fs-caption); color: var(--ink-muted); }

/* The stages of one run, in order. Each stage names itself, says what it does
   and carries its own outcome, so a stage that failed is found where it
   failed rather than by inference from the run's own state. */
.steps { display: grid; }
.steps__item { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--space-3); align-items: start; padding-block: var(--space-3); border-block-start: 1px solid var(--border); }
.steps__item:first-child { border-block-start: 0; padding-block-start: 0; }
.steps__mark { display: grid; place-items: center; inline-size: 32px; block-size: 32px; border-radius: var(--radius-md); background: var(--info-subtle); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border)); }
.steps__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.steps__title { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.steps__body { font-size: var(--fs-body-sm); color: var(--ink-muted); }

/* A feed of event-shaped records — an incoming event, a notification. A list
   rather than a table: each entry carries a sentence and its own metadata,
   and its state stands beside it in words (§11). On a handset the state and
   the entry's own action take a second row under the text. */
.feed { display: grid; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); box-shadow: var(--elev-1); overflow: hidden; }
.feed__item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3); padding: var(--card-pad); border-block-start: 1px solid var(--border); align-items: start; }
.feed__item:first-child { border-block-start: 0; }
.feed__icon { display: grid; place-items: center; inline-size: 32px; block-size: 32px; border-radius: var(--radius-md); background: var(--info-subtle); color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 26%, var(--border)); }
.feed__body { display: grid; gap: var(--space-1); min-inline-size: 0; }
.feed__title { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.feed__text { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.feed__meta { display: flex; flex-wrap: wrap; gap: var(--space-1); font-size: var(--fs-caption); color: var(--ink-muted); }
.feed__side { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); grid-column: 1 / -1; }
/* An unread entry is marked by its surface and its weight as well as by its
   own state badge, so the reading never rests on the tint alone (§11). */
.feed__item--unread { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.feed__item--unread .feed__title { font-weight: var(--fw-semibold); }

/* A per-item switch and its state. The control is a real checkbox — it keeps
   its own keyboard behaviour and its own visible check — and its state stands
   beside it in words. */
.toggle-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-3); align-items: center; padding-block: var(--space-3); border-block-start: 1px solid var(--border); }
.toggle-row:first-child { border-block-start: 0; padding-block-start: 0; }
.toggle-row__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.toggle-row__label { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.toggle-row__hint { font-size: var(--fs-caption); color: var(--ink-muted); }
.toggle-row__control { display: flex; align-items: center; gap: var(--space-2); flex: none; }
.switch { inline-size: 20px; block-size: 20px; accent-color: var(--accent); }

/* Two panels side by side once the tier can hold them. */
.grid--panels { grid-template-columns: 1fr; }

@media (min-width: 768px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .kv__row { grid-template-columns: minmax(0, 10rem) minmax(0, 1fr); }
  /* From the tablet tier the entry's state and its action hold the row's far
     end, where they line up down the feed. */
  .feed__item { grid-template-columns: auto minmax(0, 1fr) auto; }
  .feed__side { grid-column: auto; align-self: stretch; flex-direction: column; align-items: flex-end; justify-content: space-between; }
  .grid--panels { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 1024px) {
  .stat-grid { grid-template-columns: repeat(4, 1fr); }
}

/* Print — the admin layer's controls and its dense frames (§13). */
@media print {
  .crumbs, .toolbar, .filters, .feed__icon, .steps__mark { display: none !important; }
  .card, .stat, .stat-grid, .stat--warning, .feed, .table-wrap,
  .steps__mark, .feed__icon { background: #fff; border-color: #000; box-shadow: none; }
  .table-wrap { overflow: visible; }
  .table-wrap .ds { min-inline-size: 0; }
}

/* ==========================================================================
   Administration surfaces — the /admin bucket's management components
   ==========================================================================
   The home plane carries one chart, one strip and two card grids; the
   organization's own administration section carries tables, filters, states
   and forms instead. Every class below is a composition of the design
   system's own tokens — the same discipline §3.2 states for the rest of the
   layer — and each one answers a component §9.8 already names: the filter
   bar, the table and its list variant, the lifecycle stepper, the metric
   tile's definition surface, and the permission matrix's own plane.
   ========================================================================== */

/* The section lead's own rule and the toolbar's own rule live in the block
   above this one, where the settings surface declares them; a lead written
   under a section head and a toolbar written over a table take both
   unchanged. What this block adds is the two pieces those two do not carry:
   the label a filter row states its filters under, and the scope note the
   design system's §9.8 requires when a default is in force — each on its own
   line, because both describe the whole row rather than one chip in it. */
.chips > .toolbar__note:first-child { margin-inline-end: var(--space-1); }
.chips__note {
  flex: 1 1 100%;
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
/* A chip in a filter row is a control, so it is built at the target size the
   rest of the set's chips take; the base rule is a badge-sized box. */
.chips .chip { min-block-size: 32px; }

/* --------------------------------------------------------------------------
   Table, stacked below the tablet tier (§9.8). The stack contract itself —
   the block rows, the cell that reads its column's name over its value, and
   the release to a real table from the tablet tier up — is declared in the
   block above this one, where the settings surface writes it for the whole
   bucket. What this block adds is the one rule that contract still needs
   from this side: the cell's column name, painted from the cell's own
   `data-label`.
   -------------------------------------------------------------------------- */
/* The selector carries the attribute test and the `ds` class as well, and both
   are load-bearing. The settings surface's own block above writes
   `.ds.ds--stack td::before { content: none }` — two classes and an element,
   the same weight as a bare `.ds--stack td` — so a rule of the plainer shape
   loses to it and no label is ever painted; the attribute test is what makes
   the label appear only on a cell that actually has one. Verified in a
   browser: with the plainer selector every stacked table in this bucket —
   the settings surface's own included — rendered its labels blank. */
.ds.ds--stack td[data-label]::before {
  content: attr(data-label);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
.ds--stack .num,
.ds--stack td.num { text-align: start; }
/* A stacked cell whose whole value is a state pill — a lifecycle state, an
   account tier, a publish level — would otherwise stretch the pill across the
   cell's value column and turn a badge into a full-width band of colour:
   measured on the indicator list at 500px, the inactive row's pill ran the
   whole row. The pill keeps its own measure and sits at the cell's start
   edge, which is where the value column already reads. */
.ds--stack td > .badge,
.ds--stack td > .chip,
.ds--stack td > .placeholder { justify-self: start; }

@media (min-width: 768px) {
  /* From the tablet tier up the cell is a real table cell with its column
     named by the header row above it, so the painted name goes. Kept here
     rather than folded into the contract above, because the contract block
     is shared with the settings surface and this pair is what this side
     needs from it. */
  .ds--stack td::before { display: none; }
  .ds--stack .num,
  .ds--stack td.num { text-align: end; }
}

/* --------------------------------------------------------------------------
   Lifecycle stepper (§9.8) — the states a dashboard moves through, the step
   it stands on, the owner of the move, and the one action that makes it. The
   step track is a list, not a plot: it holds on a handset exactly as it holds
   on a laptop, and the current step is marked by the accent plane **and** by
   the word beside it (§11).
   -------------------------------------------------------------------------- */
.stepper { display: grid; gap: var(--space-5); }
.stepper__list { display: grid; gap: var(--space-2); }
.stepper__step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.stepper__dot {
  display: grid;
  place-items: center;
  inline-size: var(--icon-lg);
  block-size: var(--icon-lg);
  margin-block-start: 1px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--ink-muted);
}
.stepper__step--done .stepper__dot {
  background: var(--success-subtle);
  border-color: color-mix(in srgb, var(--success) 45%, transparent);
  color: var(--success);
}
.stepper__step--current {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  background: var(--info-subtle);
}
.stepper__step--current .stepper__dot {
  background: var(--surface);
  border-color: var(--accent);
  color: var(--accent);
}
.stepper__label {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-body-md);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
.stepper__meta,
.stepper__owner {
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
.stepper__owner { margin-block-start: var(--space-1); }
.stepper__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.stepper__foot .btn { flex: none; }

/* A record's own readings — the lifecycle's owner and version, a definition's
   unit and source, a share's audience and level — take the key-over-value
   rule the block above declares for the whole bucket, with `.kv__val` for the
   value column. Nothing is added here: one shape, one definition, read the
   same on every administration surface in the set. */

/* --------------------------------------------------------------------------
   Form grid — a definition's fields as one column on a handset and two from
   the tablet tier, with the field that needs the row carrying `--full` and
   the closing action row taking `.form-actions`. Both are declared in the
   block above; a definition form below takes them unchanged.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Form grid — a definition's fields as one column on a handset and two from
   the tablet tier, so a long form does not become a narrow strip with the
   save control a screen below its first field. A field that needs the row
   (a formula, a description) carries the wide modifier.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Chart — the usage cut. The administration section reads the same six-column
   frame the home plane reads; only the bar heights and their labels differ, so
   the plot box, the reading guides, the legend and the tabular view are all
   the ones §9.8 already defines and the home page already carries. The target
   line is omitted, because a usage figure has no threshold the product sets.
   -------------------------------------------------------------------------- */
.chart--usage .chart__col:nth-child(1) .chart__bar { block-size: 52%; }
.chart--usage .chart__col:nth-child(2) .chart__bar { block-size: 58%; }
.chart--usage .chart__col:nth-child(3) .chart__bar { block-size: 61%; }
.chart--usage .chart__col:nth-child(4) .chart__bar { block-size: 66%; }
.chart--usage .chart__col:nth-child(5) .chart__bar { block-size: 72%; }
.chart--usage .chart__col:nth-child(6) .chart__bar { block-size: 84%; }
.chart--usage .chart__col:nth-child(1) .chart__value { inset-block-end: calc(52% + var(--space-1)); }
.chart--usage .chart__col:nth-child(2) .chart__value { inset-block-end: calc(58% + var(--space-1)); }
.chart--usage .chart__col:nth-child(3) .chart__value { inset-block-end: calc(61% + var(--space-1)); }
.chart--usage .chart__col:nth-child(4) .chart__value { inset-block-end: calc(66% + var(--space-1)); }
.chart--usage .chart__col:nth-child(5) .chart__value { inset-block-end: calc(72% + var(--space-1)); }
.chart--usage .chart__col:nth-child(6) .chart__value { inset-block-end: calc(84% + var(--space-1)); }

/* --------------------------------------------------------------------------
   Administration blocks on paper. The filter and search controls are
   chrome — they do not describe the record — so they leave the sheet; the
   states, the definitions and every figure stay.
   -------------------------------------------------------------------------- */
@media print {
  .toolbar, .chips { display: none !important; }
  .stepper__step { border-color: #000; background: #fff; }
  .kv__row { border-color: #000; }
}

/* ==========================================================================
   Repair — the stacked table's header row

   The stack contract takes the header row out of the layout below the tablet
   tier with `display: none`, and that removes the column names from the
   accessibility tree as well as from the paint: a cell whose column is named
   only by generated content is read as a bare value. The row is taken out of
   the paint instead — clipped, not removed — so the names stay in the reading
   order and nothing about the layout moves (§11, "Labels and names present").
   Written as one pair and declared last, so it holds over the two contract
   blocks above it; folded into them, the next edit to either would drop it.
   ========================================================================== */
.ds--stack thead {
  position: absolute;
  inline-size: 1px; block-size: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; white-space: nowrap;
  clip-path: inset(50%);
  display: block;
}
@media (min-width: 768px) {
  .ds--stack thead {
    display: table-header-group;
    position: static;
    inline-size: auto; block-size: auto;
    margin: 0; overflow: visible; white-space: normal;
    clip-path: none;
  }
}

/* ==========================================================================
   Home surface (`.page--home`) — finish for an enterprise reading plane.
   One idea carries the page: each KPI tile shows where its figure sits
   against its target, as a hairline gauge with a tick for the target. The
   figure and the target are already stated in words on the tile, so the gauge
   is a second, faster reading of the same numbers and never the only one
   (it is `aria-hidden`). Everything else stays quiet: no entrance animation,
   no decorative dot, only tokens, and the only motion answers a pointer.
   ========================================================================== */
.page--home .page-title { letter-spacing: -0.01em; }
.page--home .page-lead { line-height: 1.9; }
@media (min-width: 1024px) {
  /* Laptop and up: the briefing reads as a row, the text at the reading-start
     edge and the two ways in at the other, so the card is no taller than its
     copy needs. */
  .page--home .page-head {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
  }
  .page--home .page-head .page-actions { align-self: end; justify-content: flex-end; }
}

.page--home .metric { grid-template-rows: repeat(5, auto) 1fr; }
.page--home .metric__value { font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

/* Gauge — a hairline track, the figure as a fill from the reading-start edge,
   and a tick at the target. Positions are fractions of each unit's own scale
   (0-100 %, 0-8 days, 0-60 people, 0-10 %), set per tile below. */
.gauge {
  position: relative;
  display: block;
  inline-size: 100%;
  block-size: 4px;
  margin-block: var(--space-2) var(--space-1);
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  border-radius: var(--radius-pill);
}
.gauge__fill {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: calc(var(--v) * 1%);
  background: var(--accent);
  border-radius: inherit;
}
.gauge__tick {
  position: absolute;
  inset-block: -4px;
  inset-inline-start: calc(var(--t) * 1%);
  inline-size: 2px;
  margin-inline-start: -1px;
  background: var(--ink);
  border-radius: 1px;
}
.gauge--occupancy { --v: 86.4; --t: 85; }
.gauge--stay { --v: 63.75; --t: 60; }
.gauge--queue { --v: 70; --t: 66.7; }
.gauge--cancel { --v: 38; --t: 50; }
/* The tile past its threshold keeps the warning pair, as the rest of it does. */
.metric--threshold .gauge__fill { background: var(--warning); }

/* The tile answers the pointer with a line across its top edge. */
.page--home .kpi-strip > .metric:not(.metric--threshold)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  block-size: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: inline-start;
}
.page--home .kpi-strip > .metric { position: relative; }
.page--home .kpi-strip > .metric:not(.metric--threshold):hover::after { transform: scaleX(1); }

/* The chart frame separates its head from the plot with a hairline. */
.page--home .chart-frame__head {
  padding-block-end: var(--space-4);
  border-block-end: 1px solid var(--border);
}

/* Saved-layout cards: the title takes the accent on hover or focus, and the
   picture eases in slightly. */
.page--home .card__thumb { overflow: hidden; }
.page--home .card--interactive:hover .card__title,
.page--home .card--interactive:focus-visible .card__title { color: var(--accent); }

@media (prefers-reduced-motion: no-preference) {
  .page--home .kpi-strip > .metric:not(.metric--threshold)::after {
    transition: transform var(--dur-base) var(--ease-standard);
  }
  .page--home .card__thumb {
    background-size: 100%;
    transition: background-size var(--dur-slow) var(--ease-standard);
  }
  .page--home .card--interactive:hover .card__thumb { background-size: 105%; }
}

   Dashboard viewer, template gallery and licence surface
   (`/dashboards/view`, `/dashboards/templates`, `/admin/license` — idms-dashboard.md §4.36 to §4.38)
   ==========================================================================
   Appended block. Nothing above it is altered. Every rule composes documented
   tokens and declares layout or a token binding; none introduces a colour,
   radius, space, type size or elevation of its own. Mobile-first, min-width
   only, at --bp-md (768px) and --bp-lg (1024px). Logical properties throughout.
   ========================================================================== */

/* --- The head: the dashboard's state, version chip and its history ------- */
.version { margin-block-start: var(--space-2); }
.version > summary { display: inline-flex; list-style: none; cursor: pointer; }
.version > summary::-webkit-details-marker { display: none; }
.version__panel {
  display: grid;
  margin-block-start: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.version__row { display: grid; gap: var(--space-1); padding: var(--space-3) var(--space-4); border-block-start: 1px solid var(--border); }
.version__row:first-child { border-block-start: 0; }
.version__row--current { background: var(--info-subtle); }
.version__tag { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-weight: var(--fw-medium); }
.version__what { font-size: var(--fs-body-sm); color: var(--ink); }
.version__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
.version__act { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.version__foot { padding: var(--space-3) var(--space-4); border-block-start: 1px solid var(--border); font-size: var(--fs-caption); color: var(--ink-muted); }

/* Live indicator: a word and a dot together; a stale reading takes the warning dot and says «کهنه». */
.live__state { display: inline-flex; align-items: center; gap: var(--space-2); font-weight: var(--fw-medium); color: var(--ink); }
.live__dot { inline-size: var(--space-2); block-size: var(--space-2); border-radius: var(--radius-pill); background: var(--success); }
.live__dot--stale { background: var(--warning); }

/* --- Filter bar ----------------------------------------------------------- */
.viewfilter { display: grid; gap: var(--space-5); }
.viewfilter__legend { padding: 0; font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--ink); }
.viewfilter__block { display: grid; gap: var(--space-4); min-inline-size: 0; margin: 0; padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); }
.viewfilter__block .field { margin-block-end: 0; }
.viewfilter__title { padding-inline: var(--space-2); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--ink); }
.viewfilter__row { display: grid; gap: var(--space-3); grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
.viewfilter__row:not(.viewfilter__row--pair) > .field:first-child, .viewfilter__row:not(.viewfilter__row--pair) > .field:last-child { grid-column: 1 / -1; }
.viewfilter__row--pair { grid-template-columns: minmax(0, 1fr); }
.viewfilter__row .seg { inline-size: 100%; }
.viewfilter__row .seg__btn { flex: 1 1 0; justify-content: center; }
.viewfilter__ready { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); inline-size: 100%; }
.viewfilter__ready .seg__btn { justify-content: center; white-space: normal; text-align: center; }
.viewfilter__ready .seg__btn:first-child { grid-column: 1 / -1; }
.viewfilter__tips { display: grid; gap: var(--space-1); margin: 0; padding: 0; list-style: none; font-size: var(--fs-caption); color: var(--ink-muted); }
.viewfilter__default { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.viewfilter__foot { display: grid; gap: var(--space-3); padding-block-start: var(--space-4); border-block-start: 1px solid var(--border); }
.viewfilter__actions { display: grid; gap: var(--space-2); grid-template-columns: minmax(0, 1fr); }
.viewfilter__actions .btn { min-block-size: 44px; inline-size: 100%; justify-content: center; }
.viewfilter__note { font-size: var(--fs-caption); color: var(--ink-muted); }

/* --- Drill path: the context bar over the canvas -------------------------- */
.drill {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin-block-end: var(--gap-cards);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.drill .crumbs, .drill .chips { margin-block-end: 0; }
.drill__up { margin-inline-start: auto; }

/* --- Canvas --------------------------------------------------------------- */
.canvas { display: grid; gap: var(--gap-cards); grid-template-columns: minmax(0, 1fr); }
.canvas > .card { min-inline-size: 0; align-content: start; }
.chart-frame__how { font-size: var(--fs-caption); color: var(--ink-muted); }
.chart-frame__how-label { font-weight: var(--fw-semibold); color: var(--ink); }
.chart__swatch--s2 { background: var(--data-2); }
.chart__swatch--s3 { background: var(--data-3); }
.chart__swatch--s4 { background: var(--data-4); }
.chart__swatch--s5 { background: var(--data-5); }
.chart__swatch--s6 { background: var(--data-6); }
.chart__swatch--dot { inline-size: var(--space-3); block-size: var(--space-3); border-radius: var(--radius-pill); }
.chart__swatch--square { inline-size: var(--space-3); block-size: var(--space-3); border-radius: var(--radius-none); }
.chart__swatch--hollow { inline-size: var(--space-3); block-size: var(--space-4); background: var(--surface); border: var(--border-thick) solid var(--data-2); border-radius: var(--radius-sm); }
.chart__swatch--solid-d2 { inline-size: var(--space-3); block-size: var(--space-4); background: var(--data-2); border-radius: var(--radius-sm); }

/* Inline SVG plots. Colour comes from the closed data-series tokens; geometry
   is the viewBox's. The value axis sits at the reading-start edge, and the
   plot is mirrored with the document direction by the one canvas transform
   below — a chart canvas is the only surface where a physical transform is
   permitted (design-system §10). */
.vz { display: block; inline-size: 100%; max-inline-size: 24rem; block-size: auto; margin-inline: auto; overflow: visible; }
.vz text { font-family: var(--font-text); font-size: var(--fs-caption); fill: var(--ink-muted); font-variant-numeric: tabular-nums; }
.vz .vz__val { fill: var(--ink); }
.vz .vz__big { font-size: var(--fs-heading-lg); font-weight: var(--fw-bold); fill: var(--ink); }
.vz__grid { stroke: color-mix(in srgb, var(--ink) 9%, transparent); stroke-width: var(--border-thin); }
.vz__base { stroke: color-mix(in srgb, var(--ink) 22%, transparent); stroke-width: var(--border-thin); }
.vz__s1 { fill: var(--data-1); }
.vz__s2 { fill: var(--data-2); }
.vz__s3 { fill: var(--data-3); }
.vz__s4 { fill: var(--data-4); }
.vz__s5 { fill: var(--data-5); }
.vz__s6 { fill: var(--data-6); }
.vz__line { fill: none; stroke-width: var(--border-thick); stroke-linecap: round; stroke-linejoin: round; }
.vz__l1 { stroke: var(--data-1); }
.vz__l6 { stroke: var(--data-6); }
.vz__line--dash { stroke-dasharray: var(--space-2) var(--space-1); }
.vz__area { fill: color-mix(in srgb, var(--data-1) 16%, transparent); }
.vz__wick { stroke: var(--data-2); stroke-width: var(--border-thick); }
.vz__candle { fill: var(--surface); stroke: var(--data-2); stroke-width: var(--border-thick); }
.vz__candle--up { fill: var(--data-2); }
.vz__ring { fill: none; stroke-width: var(--space-5); }
.vz__d1 { stroke: var(--data-1); }
.vz__d2 { stroke: var(--data-2); }
.vz__d3 { stroke: var(--data-3); }
.vz__d6 { stroke: var(--data-6); }
.vz__t1 { fill: var(--data-1); }
.vz__t2 { fill: var(--data-2); }
.vz__t3 { fill: var(--data-3); }
.vz__t4 { fill: var(--data-4); }
.vz__t6 { fill: var(--data-6); }
.vz .vz__tilelabel { fill: var(--bg); font-weight: var(--fw-semibold); }
.vz .vz__tilevalue { fill: var(--bg); }
.vz__h1 { fill: color-mix(in srgb, var(--data-1) 10%, var(--surface-raised)); }
.vz__h2 { fill: color-mix(in srgb, var(--data-1) 28%, var(--surface-raised)); }
.vz__h3 { fill: color-mix(in srgb, var(--data-1) 50%, var(--surface-raised)); }
.vz__h4 { fill: color-mix(in srgb, var(--data-1) 75%, var(--surface-raised)); }
.vz__h5 { fill: var(--data-1); }
.vz .vz__heatval { fill: var(--ink); }
.vz .vz__heatval--hi { fill: var(--bg); font-weight: var(--fw-semibold); }

/* The province cartogram is a direction-agnostic picture: map orientation
   never mirrors (design-system §9.9). It holds a measure the tile labels can
   be read at, and on a handset it scrolls on its own axis rather than shrink
   a province name below the caption size. */
.vz-scroll { overflow-x: auto; scrollbar-width: none; }
.vz-scroll::-webkit-scrollbar { width: 0; height: 0; }
.vz--fixed { min-inline-size: 26rem; max-inline-size: 34rem; }
.vz__c1 { fill: var(--brand-100); }
.vz__c2 { fill: var(--brand-300); }
.vz__c3 { fill: var(--brand-500); }
.vz__c4 { fill: var(--brand-700); }
.vz__c5 { fill: var(--brand-900); }
.vz__nodata { fill: var(--surface-raised); stroke: var(--border-strong); stroke-width: var(--border-thin); stroke-dasharray: var(--space-1) var(--space-1); }
.vz .vz__maplabel { fill: var(--brand-950); }
.vz .vz__maplabel--hi { fill: var(--neutral-0); }
.legend__swatch { inline-size: var(--space-4); block-size: var(--space-4); border-radius: var(--radius-sm); border: 1px solid var(--border); }
.legend__swatch--c1 { background: var(--brand-100); }
.legend__swatch--c2 { background: var(--brand-300); }
.legend__swatch--c3 { background: var(--brand-500); }
.legend__swatch--c4 { background: var(--brand-700); }
.legend__swatch--c5 { background: var(--brand-900); }
.legend__swatch--none { background: var(--surface-raised); border-style: dashed; border-color: var(--border-strong); }

/* The hosted-report frame: the portal's own placeholder where a hosted report
   renders. No external embed is drawn. */
.hosted {
  display: grid; gap: var(--space-2); place-items: center; align-content: center; text-align: center;
  min-block-size: 12rem; padding: var(--space-6) var(--space-4);
  background: var(--surface-raised);
  border: 1px dashed color-mix(in srgb, var(--accent) 38%, var(--border-strong));
  border-radius: var(--radius-md);
  color: var(--ink-muted);
}
.hosted .icon { color: var(--accent); }

/* An annotation sits next to the data it explains and is its owner's text. */
.annotation {
  display: grid; gap: var(--space-1);
  max-inline-size: var(--content-max-prose);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius-md);
}
.annotation__text { font-size: var(--fs-body-sm); color: var(--ink); }
.annotation__meta { font-size: var(--fs-caption); color: var(--ink-muted); }

/* --- Template gallery ----------------------------------------------------- */
.tpl-card--gallery { display: grid; padding: 0; overflow: hidden; }
.tpl-card--gallery .card__foot { flex-wrap: wrap; }
.tpl-card--gallery .card__tags { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.card__thumb--tpl-discharge { background-image: url("img/preview-tpl-discharge.svg"); }
.card__thumb--tpl-revenue { background-image: url("img/preview-tpl-revenue.svg"); }
.card__thumb--tpl-safety { background-image: url("img/preview-tpl-safety.svg"); }
.tplfilter { align-items: flex-end; }
.tplfilter__group { display: grid; gap: var(--space-1); min-inline-size: 0; }
.tplfilter__label { font-size: var(--fs-caption); color: var(--ink-muted); }
.tplfilter .select { min-inline-size: 14rem; }
[data-tpl-card][hidden], [data-tpl-empty][hidden] { display: none; }

/* --- Licence surface ------------------------------------------------------ */
.license__tier { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.license__tier-name { font-size: var(--fs-heading-lg); line-height: var(--lh-heading-lg); font-weight: var(--fw-bold); }

@media (min-width: 768px) {
  .viewfilter__row--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .viewfilter__ready { display: flex; inline-size: max-content; max-inline-size: 100%; }
  .viewfilter__ready .seg__btn:first-child { grid-column: auto; }
  .viewfilter__foot { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: var(--space-5); }
  .viewfilter__actions { grid-template-columns: auto auto; }
  .viewfilter__actions .btn { inline-size: auto; }
  .version__row { grid-template-columns: minmax(0, 9rem) minmax(0, 1fr) auto; align-items: center; gap: var(--space-4); }
  .canvas { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .canvas > .canvas__wide { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
  .viewfilter__row { grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr)) minmax(0, 1.2fr); }
  .viewfilter__row:not(.viewfilter__row--pair) > .field:first-child, .viewfilter__row:not(.viewfilter__row--pair) > .field:last-child { grid-column: auto; }
  .viewfilter__row--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Print: the tokens are forced to black on white by the print block above, so
   these rules read the tokens and name no colour of their own. */
@media print {
  .version__panel, .annotation, .hosted, .drill, .version__row--current { background: var(--surface); border-color: var(--border); box-shadow: none; }
  .viewfilter, .drill__up { display: none !important; }
  .vz text { fill: var(--ink); }
  .vz__grid, .vz__base { stroke: var(--ink); }
}

.vz g:has(.vz__nodata) .vz__maplabel { fill: var(--ink-muted); }

/* A fieldset used as a field group (the requested-features list) carries no browser frame. */
fieldset.field { min-inline-size: 0; margin-inline: 0; padding: 0; border: 0; }

/* A counter strip of three: the fourth track of the shared strip would stand empty. */
@media (min-width: 768px) {
  .stat-grid--3 > :last-child { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
  .stat-grid--3 { grid-template-columns: repeat(3, 1fr); }
  .stat-grid--3 > :last-child { grid-column: auto; }
}

.vz .vz__tick, .vz .vz__cat { fill: var(--ink-muted); }
.vz__tile, .vz__heat { stroke: none; }

/* ==========================================================================
   App bar data-range picker (`Native Jalali calendar`, `Jalali + Gregorian
   calendar`, `Single-slider calendar switch`, `Ready-made date filters`,
   `Period filters`).

   The chip is a button now and opens a popover built by `initPeriodPicker`. It
   is the account menu's popover — `.menu` supplies the surface, the elevation
   and the anchoring — sized for a month grid. Every value is an existing token.
   ========================================================================== */
.appbar__periodwrap { position: relative; display: none; }
.appbar__period { font: inherit; color: inherit; cursor: pointer; }
.appbar__period:hover { border-color: var(--border-strong); background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.appbar__period[aria-expanded="true"] { border-color: var(--accent); }

/* Capped to the room under the app bar and scrolling inside itself, drawing no
   bar, like the display panel. Past the viewport's own edge it flips to the
   other edge of the chip (a class the script sets after measuring). */
.periodpop {
  inline-size: min(22rem, calc(100vw - var(--space-6)));
  max-block-size: calc(100vh - var(--appbar-h) - var(--space-4));
  overflow-y: auto;
  scrollbar-width: none;
}
.periodpop::-webkit-scrollbar { width: 0; height: 0; }
.periodpop--flip { inset-inline-end: auto; inset-inline-start: 0; }
.periodpop__nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.periodpop__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); color: var(--ink); }
.periodpop__readout, .periodpop__hint { font-size: var(--fs-caption); color: var(--ink-muted); }
.periodpop__foot { display: grid; gap: var(--space-3); }
.periodpop__actions { display: flex; gap: var(--space-2); }
.periodpop__hint { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); }

/* The month grid. A day inside the range is told by a tinted ground and a rule
   under it, the two ends by a filled ground and weight, today by a ring — none
   of them by hue alone (§11). */
.cal { inline-size: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0 var(--space-1); }
.cal th { padding-block: var(--space-1); font-size: var(--fs-caption); font-weight: var(--fw-medium); color: var(--ink-muted); text-align: center; }
.cal td { padding: 0; text-align: center; }
.cal__btn {
  inline-size: 100%;
  block-size: var(--space-7);
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.cal__btn:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.cal__day.is-today .cal__btn { box-shadow: inset 0 0 0 1px var(--ink-muted); }
.cal__day.is-range .cal__btn {
  background: var(--info-subtle);
  border-block-end: 2px solid var(--accent);
  border-radius: 0;
}
.cal__day.is-start .cal__btn,
.cal__day.is-end .cal__btn {
  background: var(--accent);
  color: var(--on-accent);
  font-weight: var(--fw-semibold);
  border-block-end: 2px solid var(--accent);
}
.cal__day.is-start .cal__btn { border-start-end-radius: 0; border-end-end-radius: 0; }
.cal__day.is-end .cal__btn { border-start-start-radius: 0; border-end-start-radius: 0; }
.cal__day.is-start.is-end .cal__btn { border-radius: var(--radius-sm); }

@media (min-width: 1024px) {
  .appbar__periodwrap { display: block; }
}
@media print {
  .periodpop { display: none; }
}

/* ==========================================================================
   The member's own pages — activity history and event details, support tickets,
   profile, sessions and password (idms-dashboard.md §3.11)

   One delimited block, member-prefixed, added after the administration layer:
   the review-state strip every page of the family ends on, the ticket
   timeline, the filter summary, the loading rows, the field error, the chosen
   file list, the next-step list and the finished-state panel. Every value is an
   existing token; nothing here draws a colour, a radius or a size of its own.
   ========================================================================== */
[data-mview-only][hidden],
[data-member-hide][hidden],
[data-mview-not][hidden] { display: none !important; }

/* The review strip. It lists the states a page can be in so a reviewer can open
   each one; a signed-in member's real page never needs it, so it is folded and
   labelled «برای بازبینی». */
.member-views {
  margin-block-start: var(--space-8);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
.member-views > summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  min-block-size: 32px;
  cursor: pointer;
  list-style: none;
}
.member-views > summary::-webkit-details-marker { display: none; }
.member-views > summary:hover { color: var(--ink); }
.member-views > summary:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); border-radius: var(--radius-sm); }
.member-views__list { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-3); }
.member-views__note { margin-block-start: var(--space-3); font-size: var(--fs-caption); }

/* What the filter has done to the list, in words, with the one way back. */
.member-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  margin-block-end: var(--space-4);
  padding: var(--space-2) var(--space-3);
  background: var(--info-subtle);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-md);
  font-size: var(--fs-body-sm);
}
.member-summary[hidden] { display: none; }
.member-summary__text { flex: 1 1 14rem; min-inline-size: 0; }

/* Loading rows — the shape of the list that is about to arrive. */
.member-skel { display: grid; gap: var(--space-3); padding: var(--card-pad); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.member-skel__line { block-size: var(--space-4); }
.member-skel__line--tall { block-size: var(--space-6); }
.member-skel__line--mid { inline-size: 60%; }
.member-skel__line--short { inline-size: 35%; }

/* A field that was refused: the rule, in words, under the field it concerns. */
.member-form [aria-invalid="true"] { border-color: var(--danger); }
.member-form [aria-invalid="true"]:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 35%, transparent); }
.member-form .filefield__input[aria-invalid="true"] + .btn { border-color: var(--danger); color: var(--danger); }
.member-error { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-caption); color: var(--danger); }
.member-error .icon { flex: none; inline-size: var(--icon-sm); block-size: var(--icon-sm); margin-block-start: 1px; }
.member-error[hidden] { display: none; }

/* The files the member has chosen, with their sizes, before anything is sent. */
.member-files { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.member-files:empty { display: none; }

/* What happens after the member presses the primary action. */
.member-next { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.member-next__item { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3); align-items: start; font-size: var(--fs-body-sm); color: var(--ink-muted); }
.member-next__num {
  display: grid; place-items: center;
  inline-size: var(--icon-lg); block-size: var(--icon-lg);
  background: var(--info-subtle); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-pill);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
}
.member-next__title { display: block; font-size: var(--fs-body-md); font-weight: var(--fw-medium); color: var(--ink); }

/* The finished state of a form: what happened, what it did to the rest of the
   account, and where to go next. */
.member-done { display: grid; gap: var(--space-4); justify-items: start; }
.member-done__icon {
  display: grid; place-items: center;
  inline-size: var(--icon-xl); block-size: var(--icon-xl);
  padding: var(--space-1);
  background: var(--success-subtle); color: var(--success);
  border: 1px solid color-mix(in srgb, var(--success) 45%, transparent);
  border-radius: var(--radius-pill);
}
.member-done__title { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
.member-done__body { max-inline-size: var(--content-max-prose); font-size: var(--fs-body-md); color: var(--ink-muted); }
.member-done__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* The consequence stated beside the act it follows from. */
.member-consequence { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.member-consequence .icon { flex: none; color: var(--accent); margin-block-start: 2px; }

/* A session's row once it has been ended. */

@media (prefers-reduced-motion: no-preference) {
  .member-done { animation: toast-in var(--dur-base) var(--ease-standard); }
}
@media (prefers-reduced-motion: reduce) {
  .member-skel .skeleton::after { animation: none; }
}

/* ==========================================================================
   Shell contract additions — role, feedback, triage (idms-dashboard.md §3.11)

   One delimited block, appended: role-following navigation, the toast, the
   confirm dialog, the no-access state, the home page's attention panel and the
   notification centre's tools. Every rule composes documented tokens and
   declares layout; none introduces a colour, a size or a font of its own.
   ========================================================================== */

/* Role. An entry tagged with the lowest role that may see it is not rendered
   below that role — it is removed, not dimmed, so an operator's menu holds no
   admin-only destination (`Operator role separation`). The server enforces the
   same boundary; this mirrors it. */
html[data-role="member"] [data-min-role="operator"],
html[data-role="member"] [data-min-role="admin"],
html[data-role="operator"] [data-min-role="admin"] { display: none !important; }

/* The no-access state replaces the page body and names neither the page nor
   what is on it (§9.10). */
main.is-no-access > :not([data-no-access]) { display: none !important; }
.empty--noaccess { min-block-size: 50vh; align-content: center; gap: var(--space-3); }
.empty--noaccess .empty__body { max-inline-size: var(--content-max-prose); }
.empty--noaccess .empty__title:focus-visible { outline: none; }
.empty[hidden] { display: none; }
.empty .icon--xl { color: var(--ink-muted); }

/* The review harness for the account's role: a labelled, sample-view-only
   control in the account menu. A deployment reads the role from the account and
   draws nothing here. */
.menu__section { display: grid; gap: var(--space-2); padding: var(--space-2) var(--space-3) var(--space-1); border-block-start: 1px solid var(--border); margin-block-start: var(--space-1); }
.menu__section-label { font-size: var(--fs-caption); color: var(--ink-muted); }
.menu__section .seg__btn { min-inline-size: 0; }

/* Toast — success feedback after an action. Polite live region; never the only
   record of the action (the page's own state changes too). */
.toast-host {
  position: fixed;
  inset-inline: var(--space-4);
  inset-block-end: var(--space-5);
  z-index: var(--z-toast);
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  max-inline-size: min(32rem, 100%);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  color: var(--ink);
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius-md);
  box-shadow: var(--elev-3);
  font-size: var(--fs-body-sm);
}
.toast--success { border-inline-start-color: var(--success); }
.toast--danger { border-inline-start-color: var(--danger); }
@media (prefers-reduced-motion: no-preference) {
  .toast { animation: toast-in var(--dur-base, 200ms) var(--ease-standard, ease-out); }
  .toast.is-leaving { opacity: 0; transition: opacity 200ms linear; }
}
@keyframes toast-in { from { opacity: 0; transform: translateY(var(--space-2)); } to { opacity: 1; transform: none; } }

/* Confirm dialog — the consequence is stated before the act, the safe choice
   holds focus, and the page behind is inert. Not a browser-native dialog. */
.confirm-scrim {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: var(--space-4);
  background: var(--overlay);
}
.confirm {
  inline-size: min(28rem, 100%);
  display: grid;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-4);
}
.confirm__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.confirm__body { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.confirm__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-2); }
@media (prefers-reduced-motion: no-preference) {
  .confirm { animation: toast-in 200ms var(--ease-standard, ease-out); }
}

/* Page hint — one line under a page head saying where the member is and what
   comes next. */
.page-hint {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-block: var(--space-3) 0;
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
.page-hint a { color: var(--accent); }

/* Triage — the home page's «what needs me / what changed» panel: two columns
   of rows, each row one sentence and one way in. */
.triage { display: grid; gap: var(--gap-cards); }
@media (min-width: 1024px) { .triage { grid-template-columns: 3fr 2fr; } }
.triage__col { display: grid; align-content: start; gap: var(--space-3); }
.triage__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.triage__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.triage__list { display: grid; gap: 0; }
.triage__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-3);
  border-block-start: 1px solid var(--border);
}
.triage__item:first-child { border-block-start: 0; padding-block-start: 0; }
.triage__icon {
  display: grid;
  place-items: center;
  inline-size: var(--space-8);
  block-size: var(--space-8);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--ink-muted);
}
.triage__item--warning .triage__icon { background: var(--warning-subtle); color: var(--warning); }
.triage__item--accent .triage__icon { background: var(--info-subtle); color: var(--accent); }
.triage__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.triage__what { font-weight: var(--fw-semibold); font-size: var(--fs-body-md); }
.triage__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
.triage__cta { grid-column: 2; justify-self: start; }
@media (min-width: 768px) {
  .triage__item { grid-template-columns: auto minmax(0, 1fr) auto; }
  .triage__cta { grid-column: auto; justify-self: end; }
}
.changes { display: grid; gap: 0; }
.changes__item { display: grid; gap: var(--space-1); padding-block: var(--space-3); border-block-start: 1px solid var(--border); }
.changes__item:first-child { border-block-start: 0; padding-block-start: 0; }
.changes__item time { font-size: var(--fs-caption); color: var(--ink-muted); }
.changes__item a { color: var(--ink); text-decoration-color: var(--border-strong); text-underline-offset: 3px; }
.changes__item a:hover { color: var(--accent); }

/* Notification tools — the filter beside the count. */
.notify-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); margin-block-end: var(--space-3); }
.notify-tools .seg { inline-size: max-content; max-inline-size: 100%; }
.notify[hidden] { display: none; }

/* System pages — the line that says what to do next, between the lead and the
   two ways out. */
.error__next { max-inline-size: var(--content-max-prose); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.error__next a { color: var(--accent); }

/* ==========================================================================
   Content bucket review pass (`/dashboards`, `/dashboards/view`,
   `/dashboards/templates`, `/dashboards/guides` — idms-dashboard.md §4.4,
   §4.5, §4.36, §4.37)
   ==========================================================================
   Appended block. Every class is new and carries the `content-` prefix; no rule
   above is renamed, reordered or altered. Every rule composes documented tokens
   and declares layout; none introduces a colour, radius, space, type size or
   elevation of its own. Mobile-first, min-width only, logical properties only.
   ========================================================================== */

/* --- Sample-state strip: the review harness for the states a page can be in.
   Labelled and sample-only, on the rule the role switch already follows. ---- */
.content-statebar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  margin-block-start: var(--section-gap-y);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  font-size: var(--fs-caption);
  color: var(--ink-muted);
}
.content-statebar__label { font-weight: var(--fw-semibold); color: var(--ink); }
.content-statebar .pill { min-block-size: 32px; }

/* --- State panels (loading, error, no access, session expired) ------------- */
.content-state { display: grid; gap: var(--space-4); }
.content-state[hidden] { display: none; }
.content-state__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: center; }
.content-state__actions--start { justify-content: flex-start; }
.content-state .alert { margin-block-end: 0; }
.content-loading { display: grid; gap: var(--space-4); }
.content-loading__bar { block-size: var(--space-4); max-inline-size: 40ch; }
.content-loading__block { block-size: 9rem; border-radius: var(--radius-lg); }
.content-loading__grid { display: grid; gap: var(--gap-cards); grid-template-columns: minmax(0, 1fr); }
.content-loading__note { font-size: var(--fs-body-sm); color: var(--ink-muted); }
/* While content is being read, the regions that wait are replaced by the page's
   own skeleton; after a failed read the last good reading stays and is named
   stale in words. */
main[data-state="loading"] > [data-ready],
main[data-state="noaccess"] > [data-ready],
main[data-state="noaccess"] > [data-identity],
main[data-state="expired"] > [data-ready],
main[data-state="expired"] > [data-identity],
main[data-state="empty"] > [data-ready-full] { display: none !important; }
main[data-state="nodata"] [data-ready-data] { display: none !important; }
.content-stale-note { color: var(--warning); font-weight: var(--fw-medium); }
[data-live-stale] { display: none; }
main[data-state="error"] [data-live-on] { display: none; }
main[data-state="error"] [data-live-stale] { display: inline; }
main[data-state="error"] .live__dot { background: var(--warning); }

/* --- Directory: the results bar, the lifecycle filter, the state legend ---- */
.content-dirbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin-block-start: var(--space-4);
  padding-block-start: var(--space-3);
  border-block-start: 1px solid var(--border);
  font-size: var(--fs-body-sm);
  color: var(--ink-muted);
}
.content-dirbar__result { font-weight: var(--fw-semibold); color: var(--ink); }
.content-dirbar .chips { margin-block-end: 0; }
.content-filterrow { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-2); margin-block-start: var(--space-6); }
.page-hint > span { flex: 1 1 16rem; min-inline-size: 0; }
.content-subhead { margin-block-start: var(--section-gap-y); }
.content-dirbar [hidden] { display: none !important; }
.content-filterrow__label { font-size: var(--fs-caption); color: var(--ink-muted); }
.content-filterrow .seg { inline-size: fit-content; max-inline-size: 100%; flex-wrap: wrap; }
.content-lifecycle { margin-block-start: var(--space-4); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.content-lifecycle > summary { cursor: pointer; display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 32px; color: var(--ink); font-weight: var(--fw-medium); }
.content-lifecycle > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); border-radius: var(--radius-sm); }
.content-lifecycle__list { display: grid; gap: var(--space-2); margin-block-start: var(--space-3); max-inline-size: var(--content-max-prose); }
.content-lifecycle__list > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2); }
.content-cat-link { color: inherit; text-decoration: none; }
.content-cat-link:hover, .content-cat-link:focus-visible { color: var(--accent); }
.pill[aria-current="true"] { background: var(--info-subtle); color: var(--accent); border-color: var(--accent); font-weight: var(--fw-semibold); }
.content-dircard[hidden] { display: none; }

/* --- Viewer: section nav, filter status, applied bar, draft banner --------- */
.drill__up[hidden] { display: none; }
.content-sectionnav { margin-block-start: var(--space-4); }
.content-vfstatus { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.content-vfstatus--dirty { color: var(--warning); }
.content-vfstatus--clean { color: var(--ink-muted); }
.content-appliedbar { display: grid; gap: var(--space-2); }
.content-appliedbar__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); }
.content-appliedbar .chips { margin-block-end: 0; }
.content-drill-note { font-size: var(--fs-caption); color: var(--ink-muted); margin-block-end: var(--gap-cards); }
.content-edit-banner[hidden], .content-draft-banner[hidden] { display: none; }
.content-edit-banner, .content-draft-banner { margin-block-end: var(--gap-cards); align-items: center; flex-wrap: wrap; }
.content-edit-banner > div, .content-draft-banner > div { flex: 1 1 18rem; }
.content-nodata { margin-block-start: var(--section-gap-y); }
.content-comment--new { border-inline-start: var(--border-thick) solid var(--accent); }
.content-rollback { display: inline-flex; align-items: center; gap: var(--space-1); }
.content-rollback-note { display: none; padding: var(--space-3) var(--space-4); border-block-start: 1px solid var(--border); font-size: var(--fs-caption); color: var(--ink-muted); }
html[data-role="member"] .content-rollback-note { display: block; }
.content-guidelink { display: inline-flex; align-items: center; gap: var(--space-1); font-size: var(--fs-caption); color: var(--accent); }
.live__dot { transition: background-color var(--dur-fast) var(--ease-standard); }
@media (prefers-reduced-motion: no-preference) {
  .live__dot:not(.live__dot--stale) { animation: content-pulse 2.4s var(--ease-standard) infinite; }
  main[data-state="error"] .live__dot { animation: none; }
}
@keyframes content-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 45%, transparent); }
  50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--success) 0%, transparent); }
}

/* --- Templates: the three-step track, the preview, the success panel ------ */
.content-steps { margin-block-end: var(--card-pad); }
.content-steps__list { display: grid; gap: var(--space-2); grid-template-columns: minmax(0, 1fr); }
.content-preview {
  display: grid; gap: var(--space-5);
  margin-block-end: var(--card-pad);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.content-preview[hidden] { display: none; }
.content-preview__thumb { inline-size: 100%; min-inline-size: 0; align-self: start; border: 1px solid var(--border); border-radius: var(--radius-md); }
.content-preview__body { display: grid; gap: var(--space-3); align-content: start; }
.content-preview__title { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
.content-preview__title:focus-visible { outline: none; }
.content-preview__facts { display: grid; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.content-preview__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.content-created { margin-block-end: var(--card-pad); }
.content-created[hidden] { display: none; }
.content-membernote { display: none; margin-block-end: var(--card-pad); }
html[data-role="member"] .content-membernote { display: flex; }
.content-tpl-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* --- Anchors sit clear of the sticky app bar ------------------------------- */
.section[id] { scroll-margin-block-start: calc(var(--appbar-h) + var(--space-4)); }

@media (min-width: 768px) {
  .content-loading__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .content-preview { grid-template-columns: minmax(0, 16rem) minmax(0, 1fr); }
  .content-steps__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .content-loading__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media print {
  .content-statebar, .content-edit-banner { display: none; }
}
/* ==========================================================================
   Auth bucket, review pass (§3.10) — the whole flow, its states and its cues.

   Every class is `auth__`-prefixed and composes documented tokens only. The
   flow track replaces the short step lists the first pass drew; the state
   alerts, the method choice, the expiry meter and the sample-state strip are
   new. A panel's `data-state` is the one switch the states hang from
   (`error`, `invalid`, `expired`, `locked`, `loading`, `success`);
   `assets/pages.js` sets it, and `?state=` sets it for review.
   ========================================================================== */

/* --- The flow track: where the member is in the whole sign-in --------------- */
.auth__flow {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-1);
  margin: 0 0 var(--space-6);
  padding: 0;
  list-style: none;
}
.auth__flow-item {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: start;
  gap: var(--space-1);
  text-align: center;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--ink-muted);
}
/* The connector joins one dot to the next; it runs between the two dots'
   centres, so it follows the reading direction without a physical offset. */
.auth__flow-item + .auth__flow-item::before {
  content: "";
  position: absolute;
  inset-block-start: 13px;
  inset-inline-start: calc(-50% + 20px);
  inset-inline-end: calc(50% + 20px);
  block-size: 2px;
  background: var(--border-strong);
  border-radius: var(--radius-pill);
}
.auth__flow-item--done + .auth__flow-item::before { background: var(--accent); }
.auth__flow-dot {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 28px;
  block-size: 28px;
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
}
.auth__flow-item--done .auth__flow-dot { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.auth__flow-item--current { color: var(--ink); font-weight: var(--fw-semibold); }
.auth__flow-item--current .auth__flow-dot { border-color: var(--accent); color: var(--accent); box-shadow: var(--glow-focus); }
.auth__flow-item--done.auth__flow-item--current .auth__flow-dot { color: var(--on-accent); }
.auth__flow-label { }

/* --- Headings, cues and links ------------------------------------------------- */
.auth__panel-title { text-wrap: balance; }
.auth__lead { max-inline-size: 42ch; }
.auth__next {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-block-start: var(--space-3);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--ink-muted);
}
.auth__next .icon { flex: none; margin-block-start: 1px; }
.auth__alt a, .auth__target a { display: inline-flex; align-items: center; gap: var(--space-1); min-block-size: 32px; }
.auth__alt a:focus-visible, .auth__target a:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); border-radius: var(--radius-sm); }
.auth__alt { justify-content: space-between; }
.auth__target .placeholder { font-family: var(--font-text); }
.auth__form .field__error { display: flex; align-items: center; gap: var(--space-1); }
.auth__form .field__error[hidden] { display: none; }
.auth__form > .auth__note { margin-block-end: var(--space-4); }

/* --- State alerts: one voice per outcome, the words carry it ------------------ */
.auth__alert {
  display: none;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
  margin-block-end: var(--space-5);
  padding: var(--space-3) var(--space-4);
  border: 1px solid transparent;
  border-inline-start-width: 3px;
  border-radius: var(--radius-md);
  color: var(--ink);
}
.auth__alert .icon { margin-block-start: 2px; }
.auth__alert-title { font-size: var(--fs-body-md); font-weight: var(--fw-semibold); }
.auth__alert-body { font-size: var(--fs-body-sm); color: var(--ink); }
.auth__alert--danger  { background: var(--danger-subtle);  border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.auth__alert--danger .icon  { color: var(--danger); }
.auth__alert--warning { background: var(--warning-subtle); border-color: color-mix(in srgb, var(--warning) 45%, transparent); }
.auth__alert--warning .icon { color: var(--warning); }
.auth__alert--success { background: var(--success-subtle); border-color: color-mix(in srgb, var(--success) 45%, transparent); }
.auth__alert--success .icon { color: var(--success); }
.auth__alert--info    { background: var(--info-subtle);    border-color: color-mix(in srgb, var(--info) 40%, transparent); }
.auth__alert--info .icon { color: var(--accent); }
.auth__alert-body a { font-weight: var(--fw-semibold); }
.auth__timer { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }

.auth__panel[data-state="error"] .auth__alert[data-auth-when~="error"],
.auth__panel[data-state="expired"] .auth__alert[data-auth-when~="expired"],
.auth__panel[data-state="locked"] .auth__alert[data-auth-when~="locked"],
.auth__panel[data-state="success"] .auth__alert[data-auth-when~="success"] { display: grid; }

/* A state that refuses the form says so by greying it, not by hiding it. */
.auth__panel[data-state="locked"] .auth__form,
.auth__panel[data-state="success"] .auth__form,
.auth__panel[data-state="expired"] .auth__form--code { opacity: .5; }

/* --- The method choice: one primary at a time ---------------------------------- */
.auth__methods { margin: 0 0 var(--space-4); padding: 0; border: 0; min-inline-size: 0; }
.auth__methods legend { margin-block-end: var(--space-2); padding: 0; font-size: var(--fs-label); font-weight: var(--fw-medium); }
.auth__methods-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.auth__method-opt {
  position: relative;
  display: grid;
  gap: 2px;
  align-content: center;
  min-block-size: 56px;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard), background-color var(--dur-fast) var(--ease-standard);
}
.auth__method-opt:hover { border-color: var(--accent); }
.auth__method-opt input { position: absolute; inset-block-start: 0; inset-inline-start: 0; inline-size: 1px; block-size: 1px; opacity: 0; }
.auth__method-opt:has(input:checked) { border-color: var(--accent); background: var(--info-subtle); box-shadow: inset 0 0 0 1px var(--accent); }
.auth__method-opt:has(input:focus-visible) { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
.auth__method-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.auth__method-title .icon { color: var(--accent); opacity: 0; }
.auth__method-opt:has(input:checked) .auth__method-title .icon { opacity: 1; }
.auth__method-hint { font-size: var(--fs-caption); color: var(--ink-muted); }
.auth__method--pass { display: none; }
.auth__form:has(#login-m-pass:checked) .auth__method--pass { display: block; }
.auth__form:has(#login-m-pass:checked) .auth__method--otp { display: none; }

/* --- Busy: the label changes and a ring turns; the words carry the state ------ */
.auth__spin { display: none; inline-size: 16px; block-size: 16px; border: 2px solid currentColor; border-inline-end-color: transparent; border-radius: var(--radius-pill); }
.btn[aria-busy="true"] { pointer-events: none; }
.btn[aria-busy="true"] .auth__spin { display: inline-block; }
@media (prefers-reduced-motion: no-preference) {
  .btn[aria-busy="true"] .auth__spin { animation: auth-spin 800ms linear infinite; }
  .auth__alert { animation: auth-rise var(--dur-base) var(--ease-standard); }
  .auth__step:target { animation: auth-rise var(--dur-base) var(--ease-standard); }
}
@keyframes auth-spin { to { transform: rotate(360deg); } }
@keyframes auth-rise { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
.btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }

/* --- The code window: a meter and the words beside it -------------------------- */
.auth__expiry { display: grid; gap: var(--space-2); margin-block-end: var(--space-4); }
.auth__expiry-text { display: flex; justify-content: space-between; gap: var(--space-2); font-size: var(--fs-caption); color: var(--ink-muted); }
.auth__expiry-meter { appearance: none; inline-size: 100%; block-size: 4px; border: 0; border-radius: var(--radius-pill); background: var(--border); overflow: hidden; }
.auth__expiry-meter::-webkit-progress-bar { background: var(--border); }
.auth__expiry-meter::-webkit-progress-value { background: var(--accent); }
.auth__expiry-meter::-moz-progress-bar { background: var(--accent); }
/* An expired code leaves one thing to do, so the resend takes the primary look
   and the dead form is greyed; a lockout leaves nothing to count down. */
.auth__panel[data-state="expired"] [data-auth-resend]:not(:disabled) { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.auth__panel[data-state="locked"] .auth__expiry { display: none; }
.auth__resend-note { flex-basis: 100%; font-size: var(--fs-caption); color: var(--ink-muted); }
.auth__resend-ok { display: none; flex-basis: 100%; align-items: center; gap: var(--space-2); font-size: var(--fs-caption); color: var(--success); }
.auth__resend-ok.is-on { display: flex; }

/* --- Done: the recovery's last step ---------------------------------------------- */
.auth__done { display: grid; justify-items: center; gap: var(--space-3); text-align: center; margin-block-end: var(--space-5); }
.auth__done-mark { display: grid; place-items: center; inline-size: 56px; block-size: 56px; border-radius: var(--radius-pill); background: var(--success-subtle); color: var(--success); border: 1px solid color-mix(in srgb, var(--success) 45%, transparent); }



@media (min-width: 768px) {
  .auth__flow-item { font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); }
}
@media print {
  .auth__alert, .auth__flow { display: none !important; }
}

/* ==========================================================================
   Operations layer — `/admin/notifications`, `/admin/analytics`,
   `/admin/monitoring`, `/admin/security`. Every class here is `ops-` prefixed
   and composes documented tokens only. The state harness (`data-ops-view`)
   mirrors the role harness: a reviewer's switch for seeing a surface's loading,
   empty and failed states, which the mockup has no server to produce.
   ========================================================================== */

/* The review harness — one row inside the page head's closing stamp. */
.ops-harness { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-inline-start: auto; }
.ops-harness__label { font-size: var(--fs-caption); color: var(--ink-muted); }
.ops-harness .seg { inline-size: max-content; max-inline-size: 100%; }

/* A block draws only in the view it names; without script the markup's own
   `ready` view holds. */
[data-ops-view="ready"]   [data-ops-when]:not([data-ops-when~="ready"]),
[data-ops-view="loading"] [data-ops-when]:not([data-ops-when~="loading"]),
[data-ops-view="empty"]   [data-ops-when]:not([data-ops-when~="empty"]),
[data-ops-view="error"]   [data-ops-when]:not([data-ops-when~="error"]) { display: none; }

/* Where am I, what next — the page's own three-step path. */
.ops-flow { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-4) 0 0; padding: 0; list-style: none; counter-reset: ops-step; }
.ops-flow__step { counter-increment: ops-step; }
.ops-flow__step a {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-block-size: 36px; padding-inline: var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--ink); font-size: var(--fs-body-sm); text-decoration: none;
}
.ops-flow__step a::before {
  content: counter(ops-step, persian);
  display: grid; place-items: center; inline-size: 22px; block-size: 22px;
  border-radius: var(--radius-pill); background: var(--info-subtle); color: var(--accent);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
}
.ops-flow__step a:hover { border-color: var(--accent); }
.ops-flow__step a:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* State panels — empty, failed and no-result, each saying what, why, and the one way on. */
.ops-state {
  display: grid; justify-items: center; gap: var(--space-2); text-align: center;
  padding: var(--space-8) var(--space-4);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-lg); background: var(--surface);
}
.ops-state .icon { color: var(--ink-muted); }
.ops-state__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.ops-state__body { max-inline-size: var(--content-max-prose); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.ops-state--error { border-style: solid; border-color: color-mix(in srgb, var(--danger) 40%, transparent); background: var(--danger-subtle); }
.ops-state--error .icon { color: var(--danger); }
.ops-skel { display: grid; gap: var(--space-2); padding: var(--card-pad); border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }
.ops-skel .skeleton { block-size: var(--space-6); }
.ops-skel .skeleton:nth-child(odd) { inline-size: 100%; }
.ops-skel .skeleton:nth-child(even) { inline-size: 72%; }

/* The alert builder, read as one sentence. */
.ops-sentence { display: grid; gap: var(--space-4); }
.ops-sentence__flow { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.ops-sentence__part { display: grid; gap: var(--space-1); min-inline-size: 0; }
.ops-sentence__word { font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--accent); }
.ops-sentence__part { align-content: start; }
.ops-sentence__part .field { display: grid; gap: var(--space-1); align-content: start; margin-block-end: 0; }
.ops-sentence__preview {
  padding: var(--space-3) var(--space-4); border-inline-start: 3px solid var(--accent);
  border-radius: var(--radius-md); background: var(--info-subtle); font-size: var(--fs-body-md);
}
.ops-sentence__preview[data-state="error"] { border-inline-start-color: var(--danger); background: var(--danger-subtle); }
.ops-sentence__errors { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--space-1); color: var(--danger); font-size: var(--fs-body-sm); }
.ops-sentence__errors:empty { display: none; }
.ops-sentence__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
@media (min-width: 768px) { .ops-sentence__flow { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .ops-sentence__flow { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* The per-item switch: a real checkbox, its track, and its state in words. */
.ops-switch { display: inline-flex; align-items: center; gap: var(--space-2); min-block-size: 36px; cursor: pointer; }
.ops-switch__input { position: absolute; inline-size: 1px; block-size: 1px; clip-path: inset(50%); }
.ops-switch__track {
  position: relative; flex: none; inline-size: 40px; block-size: 22px;
  background: var(--neutral-700); border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
}
[data-theme="light"] .ops-switch__track { background: var(--neutral-100); }
.ops-switch__track::after {
  content: ""; position: absolute; inset-block-start: 2px; inset-inline-start: 2px;
  inline-size: 16px; block-size: 16px; background: var(--ink-muted); border-radius: var(--radius-pill);
}
.ops-switch__input:checked + .ops-switch__track { background: var(--info-subtle); border-color: var(--accent); }
.ops-switch__input:checked + .ops-switch__track::after { inset-inline-start: calc(100% - 18px); background: var(--accent); }
.ops-switch__input:focus-visible + .ops-switch__track { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
@media (prefers-reduced-motion: no-preference) {
  .ops-switch__track::after { transition: inset-inline-start var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard); }
}
.ops-item { padding: var(--space-3) var(--card-pad); }
.ops-item:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }
.ops-item[data-on="false"] .toggle-row__label { color: var(--ink-muted); }

/* Registers: a row that opens something shows it, and the open one stays marked. */
.ops-table tbody tr:hover { background: color-mix(in srgb, var(--ink) 3%, transparent); }
.ops-table tbody tr[aria-selected="true"] { background: color-mix(in srgb, var(--accent) 8%, transparent); border-inline-start: 3px solid var(--accent); }
.ops-table a { text-underline-offset: 3px; }

/* The live indicator: a dot that pulses only when motion is welcome. */
.ops-live { display: inline-flex; align-items: center; gap: var(--space-2); }
.ops-live__dot { inline-size: 8px; block-size: 8px; border-radius: var(--radius-pill); background: var(--success); }
.ops-live[data-paused="true"] .ops-live__dot { background: var(--ink-muted); }
@media (prefers-reduced-motion: no-preference) {
  .ops-live[data-paused="false"] .ops-live__dot { animation: ops-pulse 2s ease-in-out infinite; }
  @keyframes ops-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
}
.ops-newbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-3); margin-block-end: var(--space-3); padding: var(--space-2) var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); font-size: var(--fs-body-sm); }

/* The pipeline that explains a server-side filter: request, filter, reply. */
.ops-pipe { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; counter-reset: ops-pipe; }
.ops-pipe__step { counter-increment: ops-pipe; display: grid; gap: var(--space-1); padding: var(--space-3) var(--space-4); border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--surface); }
.ops-pipe__step::before { content: counter(ops-pipe, persian); font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--accent); }
.ops-pipe__title { font-weight: var(--fw-semibold); font-size: var(--fs-body-md); }
.ops-pipe__body { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.ops-pipe__step--server { border-color: var(--accent); }
@media (min-width: 768px) { .ops-pipe { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* An always-on state: a lock and a word, never a switch. */
.ops-always { display: inline-flex; align-items: center; gap: var(--space-1); }
.ops-always .icon { inline-size: 14px; block-size: 14px; }

/* A block hidden by script stays hidden even where its own display is set. */
.ops-table [hidden], [data-ops-feed] [hidden], [data-ops-feed][hidden], .ops-table[hidden], .table-wrap[hidden], .ops-state[hidden], .ops-newbar[hidden], .alert[hidden] { display: none; }

/* A standing statement — a rule or a boundary — on the neutral plane: the amber
   tint of the default alert would read it as a warning, and it is not one. */
.alert.ops-note { background: var(--surface); border-color: var(--border); }
.alert.ops-note > .icon { color: var(--accent); }
.grid + .alert, .section-card + .alert, .stat-grid + .alert, .section-card + .section-card, .alert + .section-card, .alert + .alert { margin-block-start: var(--space-4); }
.ops-pipe { margin-block-end: var(--space-4); }

/* ==========================================================================
   Data pipeline — the shared layer of the five data-chain admin routes
   (`admin/data-sources`, `ingestion`, `semantic`, `kpis`, `dashboards`;
   idms-dashboard.md §3.12)

   One delimited block, appended. Every class is `data-` prefixed and composes
   documented tokens only. It adds the pipeline strip each of the five pages
   opens with, the «next step» cue each closes with, the review harness that
   shows a section's loading / empty / error reading, the consequence panel
   drawn before a destructive or bulk act, and the masked-credential field.
   ========================================================================== */

/* Pipeline strip — source → mapping → schedule → run → cleaning → semantic
   layer → KPI → dashboard, as one list every page of the chain carries. The
   step the page owns is marked by the accent plane, by `aria-current="step"`
   **and** by the words «شما اینجا» (§11): the accent is also the interaction
   colour, so hue never carries the position alone. */
.data-flow {
  margin-block-start: var(--space-5);
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.data-flow__head {
  display: grid; gap: var(--space-1);
  margin-block-end: var(--space-4);
}
.data-flow__title { font-size: var(--fs-label); font-weight: var(--fw-semibold); color: var(--ink); }
.data-flow__lead { font-size: var(--fs-caption); color: var(--ink-muted); }
.data-flow__list { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
.data-flow__step { min-inline-size: 0; }
.data-flow__link {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
  min-block-size: 44px;
  block-size: 100%;
  padding: var(--space-2) var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
}
.data-flow__link:hover { border-color: var(--border-strong); background: color-mix(in srgb, var(--accent) 8%, var(--surface-raised)); color: var(--ink); }
.data-flow__n {
  display: grid; place-items: center;
  inline-size: var(--icon-lg); block-size: var(--icon-lg);
  border: 1px solid var(--border-strong); border-radius: var(--radius-pill);
  font-size: var(--fs-caption); font-weight: var(--fw-medium); color: var(--ink-muted);
}
.data-flow__text { display: grid; gap: 2px; min-inline-size: 0; justify-items: start; }
.data-flow__name { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.data-flow__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
.data-flow__here {
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--accent); color: var(--on-accent);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold);
}
.data-flow__step[data-state="warn"] .data-flow__meta { color: var(--warning); }
.data-flow__step[data-state="warn"] .data-flow__n { border-color: var(--warning); color: var(--warning); }
.data-flow__link[aria-current="step"] {
  border-color: var(--accent);
  background: var(--info-subtle);
}
.data-flow__link[aria-current="step"] .data-flow__n { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
@media (min-width: 768px) {
  .data-flow__list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (min-width: 1280px) {
  .data-flow__list { grid-template-columns: repeat(8, minmax(0, 1fr)); }
  .data-flow__link { grid-template-columns: minmax(0, 1fr); align-content: start; justify-items: start; gap: var(--space-2); }
}
@media (prefers-reduced-motion: no-preference) {
  .data-flow__link { transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard); }
}

/* Review harness — a sample-view-only control that shows a section's other
   readings (loading, empty, failed) the way the account menu's role switch
   shows another role's shell. A deployment draws none of it. */
.data-states {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  margin-block-end: var(--space-3);
  font-size: var(--fs-caption); color: var(--ink-muted);
}
.data-states .seg { inline-size: max-content; max-inline-size: 100%; }
.data-state-panel[hidden] { display: none; }
@media (prefers-reduced-motion: no-preference) {
  .data-state-panel { animation: data-fade var(--dur-base) var(--ease-standard); }
}
@keyframes data-fade { from { opacity: 0; transform: translateY(var(--space-1)); } to { opacity: 1; transform: none; } }
.data-loading { display: grid; gap: var(--space-3); padding: var(--card-pad); }
.data-loading__row { block-size: var(--space-6); }
.data-loading__row:nth-child(2) { inline-size: 82%; }
.data-loading__row:nth-child(3) { inline-size: 64%; }
.data-loading__note { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.data-state-card {
  display: grid; gap: var(--space-2); justify-items: start;
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.data-state-card--danger { background: var(--danger-subtle); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.data-state-card__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.data-state-card__body { font-size: var(--fs-body-sm); color: var(--ink-muted); max-inline-size: var(--content-max-prose); }

/* Consequence panel — what an act changes, stated before it is confirmed. */
.data-impact {
  display: grid; gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.data-impact__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.data-impact__list { display: grid; max-inline-size: 56rem; }
.data-impact__row {
  display: grid; gap: var(--space-1);
  padding-block: var(--space-3);
  border-block-start: 1px solid var(--border);
}
.data-impact__row:first-child { border-block-start: 0; }
.data-impact__key { font-size: var(--fs-caption); color: var(--ink-muted); }
.data-impact__val { font-size: var(--fs-body-sm); }
.data-impact__val s { color: var(--ink-muted); }
.data-impact__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
@media (min-width: 768px) {
  .data-impact__row { grid-template-columns: 9rem minmax(0, 1fr); align-items: baseline; gap: var(--space-4); }
}

/* Masked credential — never the value itself: a fixed run of dots, the name of
   the vault that holds it and the date it was last replaced. */
.data-secret { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.data-secret__dots { font-family: var(--font-mono); letter-spacing: .15em; color: var(--ink-muted); }
.data-secret__note { font-size: var(--fs-caption); color: var(--ink-muted); }

/* A form-level summary and a saved confirmation, hidden until the act. */
.data-form-msg[hidden] { display: none; }
.data-inline-note { font-size: var(--fs-caption); color: var(--ink-muted); }

/* A revealed result starts hidden: the alert sets its own display, so the attribute needs a rule. */
.alert[hidden] { display: none; }

/* A counter strip of two: the empty third and fourth tracks would show the strip ground. */
@media (min-width: 768px) {
  .kpi-strip.data-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ==========================================================================
   Access surfaces — the people-and-access pages of the /admin bucket
   (`users`, `user-detail`, `roles`, `access`, `sessions`).

   Every class below is prefixed `access-` so it cannot collide with a block
   another page of the set owns. Nothing here invents a value: sizes, radii,
   colours and motion are the tokens above. The two rules the bucket already
   states hold here too — an access level is never carried by hue (a tier, a
   layer, a publish level takes the ink tokens and its own word), and a state
   (expired, revoked, suspended) may take a feedback token because a state is
   what those tokens are for.
   ========================================================================== */

/* Where-am-I strip — the four destinations of the people-and-access group as
   one numbered path. The page the reader is on is named by `aria-current`, the
   accent underline and the weight; the numbers are written by the markup. */
.access-journey { margin-block-end: var(--space-5); }
.access-journey__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.access-journey__link {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: 44px;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--ink-muted);
  font-size: var(--fs-body-sm);
  text-decoration: none;
  transition: border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.access-journey__link:hover { color: var(--ink); border-color: var(--border-strong); background: var(--surface-raised); }
.access-journey__link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
.access-journey__num {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 24px;
  block-size: 24px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  font-size: var(--fs-caption);
}
.access-journey__text { display: grid; min-inline-size: 0; }
.access-journey__name { font-weight: var(--fw-medium); color: var(--ink); }
.access-journey__what { font-size: var(--fs-caption); color: var(--ink-muted); }
.access-journey__link[aria-current="page"] {
  border-color: var(--accent);
  background: var(--info-subtle);
  box-shadow: inset 0 -2px 0 var(--accent);
}
.access-journey__link[aria-current="page"] .access-journey__num { background: var(--accent); border-color: transparent; color: var(--on-accent); }

/* A row of small figures — what the surface holds, said before the table. */
.access-stats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin-block-end: var(--space-5);
}
.access-stat {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.access-stat__fig { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
.access-stat__lab { font-size: var(--fs-caption); color: var(--ink-muted); }
.access-stat--warn { border-color: color-mix(in srgb, var(--warning) 45%, var(--border)); }
.access-stat--warn .access-stat__lab { color: var(--warning); }

/* "Who can see what" — one plane per account tier, naming what the tier opens
   and what it never shows. No tier is coloured. */
.access-tiers { display: grid; gap: var(--space-3); margin-block-end: var(--space-5); }
.access-tier {
  display: grid;
  gap: var(--space-2);
  align-content: start;
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.access-tier__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.access-tier__name { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.access-tier__count { font-size: var(--fs-caption); color: var(--ink-muted); }
.access-tier__sees,
.access-tier__never { margin: 0; font-size: var(--fs-body-sm); color: var(--ink); }
.access-tier__never { color: var(--ink-muted); }
.access-tier__label { display: block; font-size: var(--fs-caption); color: var(--ink-muted); }

/* A menu entry an account does or does not have, in words and in the shape of
   a glyph — a tick or a dash — never by hue. */
.access-menu { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.access-menu__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-caption);
  color: var(--ink);
}
.access-menu__item--no { color: var(--ink-muted); border-style: dashed; }

/* A set of layer checkboxes that reads as one row of chips. */
.access-layerpick { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; border: 0; }
.access-layerpick__opt {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-block-size: 40px;
  padding: var(--space-1) var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.access-layerpick__opt:hover { border-color: var(--ink-muted); }
.access-layerpick__opt:has(input:checked) { border-color: var(--accent); background: var(--info-subtle); }
.access-layerpick__opt:has(input:focus-visible) { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
.access-layerpick__opt input { inline-size: 16px; block-size: 16px; margin: 0; accent-color: var(--accent); }

/* A segmented choice that sits inside a form (add / remove). */

/* The record a change wrote, read from both ends. Both readings are the same
   record: the panel says so in its own head and its two columns repeat it. */
.access-result {
  display: grid;
  gap: var(--space-3);
  margin-block-start: var(--space-4);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
}
.access-result[hidden] { display: none; }
.access-result__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }
.access-result__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.access-result__pair { display: grid; gap: var(--space-3); }
.access-result__side { display: grid; gap: var(--space-1); padding: var(--space-3); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.access-result__from { font-size: var(--fs-caption); color: var(--ink-muted); }
.access-result__line { font-size: var(--fs-body-md); }
.access-result__note { margin: 0; font-size: var(--fs-caption); color: var(--ink-muted); }

/* A line that states the consequence of the act under it, before it is made. */
.access-consequence {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  padding: var(--space-3);
  background: var(--surface-raised);
  border-inline-start: 3px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-body-sm);
  color: var(--ink);
}
.access-consequence .icon { flex: none; margin-block-start: 2px; color: var(--ink-muted); }
.access-consequence--warn { border-inline-start-color: var(--warning); }
.access-consequence--danger { border-inline-start-color: var(--danger); }

/* A receipt: what an act did, shown where the act was made, with the way to
   its record. It stays until the next act replaces it. */
.access-receipt { margin-block-end: var(--space-4); }
.access-receipt[hidden] { display: none; }
.access-receipt .alert__body { color: var(--ink); }
.access-receipt__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-block-start: var(--space-2); }

/* The state preview a reviewer uses to see a surface's other states. It is
   named as a sample control and sits in a section card's head. */
.access-states { display: inline-flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.access-states__label { font-size: var(--fs-caption); color: var(--ink-muted); }
[data-view][hidden] { display: none; }
.access-skel { display: grid; gap: var(--space-3); padding: var(--card-pad); }
.access-skel__row { block-size: 20px; }
.access-skel__row:nth-child(3n+1) { inline-size: 92%; }
.access-skel__row:nth-child(3n+2) { inline-size: 76%; }
.access-skel__row:nth-child(3n) { inline-size: 84%; }
.access-error { display: grid; gap: var(--space-2); justify-items: center; padding: var(--space-8) var(--space-4); text-align: center; }
.access-error .icon { color: var(--danger); }

/* Row states. A revoked, ended or expired row stays in the list — it is a
   record — and reads quieter than a live one; the state word carries it. */
.ds tr[data-state="revoked"] > *,
.ds tr[data-state="ended"] > *,
.ds tr[data-state="expired"] > * { color: var(--ink-muted); }
.ds tr[data-state="revoked"] a,
.ds tr[data-state="ended"] a { color: var(--ink-muted); }
.ds tr[hidden] { display: none; }
.access-select { inline-size: 1%; white-space: nowrap; }
.access-select input { inline-size: 18px; block-size: 18px; margin: 0; accent-color: var(--accent); vertical-align: middle; }
.access-select input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
.access-self { font-size: var(--fs-caption); color: var(--ink-muted); }
@media (prefers-reduced-motion: no-preference) {
  @keyframes access-flash { from { background: var(--info-subtle); } to { background: transparent; } }
  .access-flash > * { animation: access-flash var(--dur-slow) var(--ease-standard) 1; }
}

/* A record of what changed on this account, one sentence per event. */
.access-log { display: grid; margin: 0; padding: 0; list-style: none; }
.access-log__item { display: grid; gap: var(--space-1); padding-block: var(--space-3); border-block-end: 1px solid var(--border); }
.access-log__item:last-child { border-block-end: 0; padding-block-end: 0; }
.access-log__what { font-size: var(--fs-body-sm); }
.access-log__when { font-size: var(--fs-caption); color: var(--ink-muted); }

/* The plain-language summary the inspection opens with. */
.access-summary { display: grid; gap: var(--space-2); margin: 0 0 var(--space-4); padding: 0; list-style: none; }
.access-summary__item {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  font-size: var(--fs-body-md);
}
.access-summary__item .icon { flex: none; margin-block-start: 3px; color: var(--ink-muted); }

/* Printed inspection head — present on paper only. */
.access-printhead { display: none; }

@media (min-width: 768px) {
  .access-journey__list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .access-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .access-tiers { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .access-result__pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media print {
  .access-printhead { display: block; margin-block-end: var(--space-4); }
  .access-states, .access-journey { display: none; }
}
@media (forced-colors: active) {
  .access-journey__link[aria-current="page"] { outline: 2px solid Highlight; }
  .access-layerpick__opt:has(input:checked) { outline: 2px solid Highlight; }
}

/* Small pieces the access pages share. */
.select[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
.access-layerpick__opt:has(input[aria-invalid="true"]) { border-color: var(--danger); }
.field .toolbar { margin-block-end: var(--space-2); }
.field .toolbar .input { flex: 0 1 14rem; inline-size: auto; min-inline-size: 10rem; }
.matrix__cell--expired { color: var(--warning); font-weight: var(--fw-medium); }
/* A checkbox that leads a row's first cell, beside the name it selects. */
.access-pickrow { display: inline-flex; align-items: center; gap: var(--space-3); }
.access-pickrow input { flex: none; inline-size: 18px; block-size: 18px; margin: 0; accent-color: var(--accent); }
.access-pickrow input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
th > .access-self { display: block; margin-block-start: var(--space-1); font-weight: var(--fw-regular); }
.access-layerpick__opt .access-self { white-space: nowrap; }
.access-states .seg { inline-size: max-content; max-inline-size: 100%; }
.access-result:focus-visible, .access-receipt:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: var(--focus-offset); }
.access-summary__item { line-height: var(--lh-body-md); }
.page-head .page-actions { row-gap: var(--space-2); }
.field > .switch { justify-self: start; inline-size: max-content; max-inline-size: 100%; }
.matrix th .card__desc { display: block; }

/* ==========================================================================
   Organization administration pass — /admin, /admin/branding, /admin/settings
   and /admin/license (idms-dashboard.md §4.22 to §4.24 and §4.38).

   One delimited block, appended. Every class is `org-` prefixed and composes
   documented tokens only; none introduces a colour, a size, a radius or a font
   of its own. Motion is confined to `prefers-reduced-motion: no-preference`.
   ========================================================================== */

/* Where-am-I cue: the page's own sections as jump links under the head. */
.org-toc { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin-block-start: var(--space-5); }
.org-toc__label { font-size: var(--fs-caption); color: var(--ink-muted); }
.org-toc__list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.org-toc__link {
  display: inline-flex;
  align-items: center;
  min-block-size: 32px;
  padding-inline: var(--space-3);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  font-size: var(--fs-body-sm);
  text-decoration: none;
}
.org-toc__link:hover { background: color-mix(in srgb, var(--accent) 10%, var(--surface)); border-color: var(--accent); }
.org-toc__link:active { transform: translateY(1px); }

/* A numbered section title: the number says the order a first-time reader works
   in, and replaces the title's accent bar rather than standing beside it. */
.org-num {
  display: inline-grid;
  place-items: center;
  flex: none;
  inline-size: var(--icon-lg);
  block-size: var(--icon-lg);
  background: var(--info-subtle);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
  border-radius: var(--radius-pill);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  line-height: 1;
}
.section__title:has(> .org-num)::before { display: none; }

/* A section's one-line purpose, and the tag it carries while it holds an
   unsaved change. */
.org-section-lead { max-inline-size: var(--content-max-prose); margin-block-end: var(--space-4); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.org-dirty-tag[hidden], .org-pending[hidden], .alert[hidden], .filefield .btn[hidden], .org-impact__now[hidden] { display: none; }

/* Two columns at the desktop tier: the edit sections, and the live preview that
   stays in view beside them. Below it the preview follows the sections. */
.org-split { display: grid; gap: 0; grid-template-columns: minmax(0, 1fr); }
.org-split__aside { margin-block-start: var(--section-gap-y); }
@media (min-width: 1440px) {
  .org-split { grid-template-columns: minmax(0, 1fr) minmax(22rem, 27rem); column-gap: var(--space-7); align-items: start; }
  .org-split__aside {
    position: sticky;
    inset-block-start: calc(var(--appbar-h) + var(--space-4));
    max-block-size: calc(100vh - var(--appbar-h) - var(--space-6));
    overflow: auto;
    scrollbar-width: none;
  }
  .org-split__aside::-webkit-scrollbar { width: 0; height: 0; }
}

/* ---- The save bar: the page's one primary action, always in reach ---------- */
.org-savebar { position: sticky; inset-block-end: var(--space-4); z-index: var(--z-sticky); margin-block-start: var(--section-gap-y); }
.org-savebar__inner {
  display: grid;
  gap: var(--space-2) var(--space-4);
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border-strong);
  border-inline-start-width: 3px;
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-3);
}
.org-savebar[data-state="dirty"] .org-savebar__inner { border-inline-start-color: var(--accent); }
.org-savebar[data-state="saved"] .org-savebar__inner { border-inline-start-color: var(--success); }
.org-savebar[data-state="invalid"] .org-savebar__inner { border-inline-start-color: var(--danger); }
.org-savebar__status { display: flex; align-items: center; gap: var(--space-2); min-inline-size: 0; font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.org-savebar__status .icon { flex: none; color: var(--ink-muted); }
.org-savebar[data-state="dirty"] .org-savebar__status .icon { color: var(--accent); }
.org-savebar[data-state="saved"] .org-savebar__status .icon { color: var(--success); }
.org-savebar[data-state="invalid"] .org-savebar__status .icon { color: var(--danger); }
.org-savebar__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.org-savebar__note { font-size: var(--fs-caption); line-height: var(--lh-caption); color: var(--ink-muted); }
.org-savebar__errors { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); font-size: var(--fs-body-sm); }
.org-savebar__errors[hidden] { display: none; }
.org-savebar__errors a { color: var(--danger); }
@media (min-width: 768px) {
  .org-savebar__inner { grid-template-columns: minmax(0, 1fr) auto; }
  .org-savebar__errors, .org-savebar__note { grid-column: 1 / -1; }
}
/* The toast rises above the bar instead of covering the status it repeats. */
body:has(.org-savebar) .toast-host { inset-block-end: calc(var(--space-12) + var(--space-6)); }

/* ---- Key-value configuration ---------------------------------------------- */
.org-key { display: grid; gap: var(--space-1); }
.org-key code { font-family: var(--font-mono); font-size: var(--fs-code); line-height: var(--lh-code); }
.org-key__desc { font-size: var(--fs-caption); line-height: var(--lh-caption); font-weight: var(--fw-regular); color: var(--ink-muted); }
.org-valuecell { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.org-kv .select { max-inline-size: 100%; }
@media (min-width: 768px) { .org-kv .select { min-inline-size: 13rem; } }
.org-row.is-dirty { background: color-mix(in srgb, var(--accent) 7%, transparent); }

/* ---- The impact of the global default date filter -------------------------- */
.org-impact { display: grid; gap: var(--space-4); }
.org-impact__now { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); font-size: var(--fs-body-sm); }
.org-impact__now .icon--cue { color: var(--ink-muted); }
.org-impact__cols { display: grid; grid-template-columns: minmax(0, 1fr); }
.org-impact__col { display: grid; align-content: start; gap: var(--space-2); padding-block: var(--space-4); }
.org-impact__col + .org-impact__col { border-block-start: 1px solid var(--border); }
.org-impact__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.org-impact__head .icon { flex: none; }
.org-impact__col--yes .org-impact__head .icon { color: var(--success); }
.org-impact__col--no .org-impact__head .icon { color: var(--ink-muted); }
.org-impact__list { display: grid; gap: var(--space-2); }
.org-impact__item { display: grid; gap: var(--space-1); font-size: var(--fs-body-sm); }
.org-impact__item span { font-size: var(--fs-caption); color: var(--ink-muted); }
@media (min-width: 768px) {
  .org-impact__cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .org-impact__col + .org-impact__col { border-block-start: 0; border-inline-start: 1px solid var(--border); padding-inline-start: var(--space-5); }
  .org-impact__col:first-child { padding-inline-end: var(--space-5); }
}

/* ---- Recent changes (every change is recorded) ----------------------------- */
.org-log { display: grid; }
.org-log__item { display: grid; gap: var(--space-1); padding-block: var(--space-3); border-block-start: 1px solid var(--border); }
.org-log__item:first-child { border-block-start: 0; padding-block-start: 0; }
.org-log__what { font-size: var(--fs-body-sm); }
.org-log__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); font-size: var(--fs-caption); color: var(--ink-muted); }

/* ---- Lists of what a choice does and does not move -------------------------- */
.check-list.org-list--no .icon { color: var(--ink-muted); }
.org-scope { display: grid; gap: var(--space-4); }
.org-scope__head { margin-block-end: var(--space-2); font-size: var(--fs-label); font-weight: var(--fw-semibold); }
@media (min-width: 768px) { .org-scope { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--space-6); } }

/* ---- Per-upload checks -------------------------------------------------------- */
.org-checks { display: grid; gap: var(--space-2); }
.org-checks li { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--ink-muted); }
.org-checks .icon { flex: none; margin-block-start: 1px; }
.org-checks [data-state="pass"] { color: var(--ink); }
.org-checks [data-state="pass"] .icon { color: var(--success); }
.org-checks [data-state="fail"] { color: var(--ink); }
.org-checks [data-state="fail"] .icon { color: var(--danger); }
.org-checks [data-state="warn"] { color: var(--ink); }
.org-checks [data-state="warn"] .icon { color: var(--warning); }
.org-file-facts { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); align-items: center; font-size: var(--fs-caption); color: var(--ink-muted); }
.org-file-facts[hidden] { display: none; }
.org-file-facts bdi { font-family: var(--font-mono); }
.org-field--error .input, .org-field--error .btn--secondary { border-color: var(--danger); }
.org-field--error .field__hint { color: var(--ink-muted); }

/* ---- Contrast of the brand colour, measured on the live preview ------------- */
.org-contrast { inline-size: 100%; border-collapse: collapse; }
.org-contrast th, .org-contrast td { padding: var(--space-2) var(--space-3); text-align: start; border-block-end: 1px solid var(--border); font-size: var(--fs-body-sm); vertical-align: middle; }
.org-contrast thead th { background: var(--surface-raised); font-size: var(--fs-label); font-weight: var(--fw-medium); color: var(--ink-muted); }
.org-contrast tbody th { font-weight: var(--fw-medium); }
.org-contrast caption { text-align: start; padding-block-end: var(--space-2); font-size: var(--fs-caption); color: var(--ink-muted); }
.org-ratio { display: inline-flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.org-ratio strong { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.org-contrast__floor { display: block; font-size: var(--fs-caption); font-weight: var(--fw-regular); color: var(--ink-muted); }

/* ---- The live preview: app bar and sign-in page, light and dark ------------- */
.org-pv { display: grid; gap: var(--space-4); }
.org-pv__lead { font-size: var(--fs-body-sm); color: var(--ink-muted); }
.org-frame {
  display: grid;
  grid-template-rows: auto auto auto;
  background: var(--bg);
  color: var(--ink);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--elev-1);
  font-family: var(--font-text);
}
/* A frame carries its own palette the way the page carries the member's: the
   same one-seed mix, scoped to the frame so the page's own accent never moves. */
.org-frame[data-palette]:not([data-palette="amber"]) {
  --accent:         color-mix(in oklab, var(--brand-seed) 66%, var(--ink));
  --accent-hover:   color-mix(in oklab, var(--brand-seed) 54%, var(--ink));
  --accent-pressed: color-mix(in oklab, var(--brand-seed) 82%, var(--ink));
  --focus-ring:     color-mix(in oklab, var(--brand-seed) 45%, var(--ink));
  --on-accent:      color-mix(in oklab, var(--brand-seed) 8%, var(--bg));
  --gradient-brand: linear-gradient(135deg,
    color-mix(in oklab, var(--brand-seed) 82%, var(--ink)) 0%,
    color-mix(in oklab, var(--brand-seed) 66%, var(--ink)) 45%,
    color-mix(in oklab, var(--brand-seed) 54%, var(--ink)) 100%);
  --info: var(--accent);
  --info-subtle: color-mix(in srgb, var(--brand-seed) 16%, var(--surface));
}
.org-frame__cap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--surface-raised);
  border-block-end: 1px solid var(--border);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--ink-muted);
}
.org-frame__zone { padding: var(--space-2) var(--space-4) 0; font-size: var(--fs-caption); color: var(--ink-muted); }
.org-frame__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin: var(--space-2) var(--space-4) 0;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.org-frame__org { display: flex; align-items: center; gap: var(--space-2); min-inline-size: 0; }
.org-frame__name { display: block; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.org-frame__name .placeholder { display: block; inline-size: fit-content; max-inline-size: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.org-frame__tools { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.org-frame__link { font-size: var(--fs-caption); color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.org-frame__login { display: grid; gap: var(--space-2); margin: var(--space-2) var(--space-4) var(--space-4); }
.org-frame__top { display: flex; align-items: center; gap: var(--space-2); min-inline-size: 0; }
.org-frame__hero {
  position: relative;
  display: grid;
  gap: var(--space-1);
  align-content: end;
  min-block-size: 7.5rem;
  padding: var(--space-4);
  background: var(--gradient-brand);
  color: var(--on-accent);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.org-frame__hero > * { position: relative; z-index: 1; }
.org-frame__title { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-bold); }
.org-frame__welcome { font-size: var(--fs-heading-md); line-height: var(--lh-heading-md); font-weight: var(--fw-semibold); }
.org-frame__hero .placeholder { color: inherit; border-color: currentColor; background: transparent; }
.org-frame__form { display: grid; gap: var(--space-2); padding: var(--space-3); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.org-frame__field { padding: var(--space-2) var(--space-3); background: var(--surface); color: var(--ink-muted); border: 1px solid var(--border-strong); border-radius: var(--radius-md); font-size: var(--fs-caption); }
.org-frame__btn { pointer-events: none; }
/* The saved name takes the place of the unresolved-content chip in the shell's
   lockup, on the same one-line clip. */
.org-name-set { display: block; min-inline-size: 0; max-inline-size: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.org-mark--set { padding: 3px; background: transparent; border: 1px solid var(--border); }
.org-mark__img { display: block; inline-size: 100%; block-size: 100%; object-fit: contain; }
.org-probe { position: absolute; inline-size: 0; block-size: 0; overflow: hidden; visibility: hidden; }
.org-probe--accent { color: var(--accent); }
.org-probe--on-accent { color: var(--on-accent); }
.org-probe--surface { color: var(--surface); }
.org-probe--raised { color: var(--surface-raised); }
.org-probe--bg { color: var(--bg); }
.org-probe--ring { color: var(--focus-ring); }

/* ---- Licence requests as status trackers ------------------------------------ */
.org-req-list { display: grid; gap: var(--gap-cards); }
.org-req { display: grid; gap: var(--space-4); padding: var(--card-pad); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.org-req__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-3); }
.org-req__title { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.org-req__meta { font-size: var(--fs-caption); color: var(--ink-muted); }
.org-req__feats { font-size: var(--fs-body-sm); }
.org-req__foot { padding-block-start: var(--space-3); border-block-start: 1px solid var(--border); font-size: var(--fs-caption); color: var(--ink-muted); }
.org-req.is-new { border-color: var(--accent); }
.org-track { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.org-track__step { position: relative; display: grid; justify-items: center; align-content: start; gap: var(--space-1); text-align: center; }
.org-track__step + .org-track__step::before {
  content: "";
  position: absolute;
  inset-block-start: calc(var(--icon-xl) / 2 - 1px);
  inset-inline-end: 50%;
  inline-size: 100%;
  block-size: 2px;
  background: var(--border-strong);
}
.org-track__step--done + .org-track__step::before { background: var(--accent); }
.org-track__dot {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  inline-size: var(--icon-xl);
  block-size: var(--icon-xl);
  background: var(--surface);
  color: var(--ink-muted);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-pill);
}
.org-track__step--done .org-track__dot { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.org-track__step--current .org-track__dot { background: var(--info-subtle); color: var(--accent); border-color: var(--accent); }
.org-track__label { font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.org-track__step:not(.org-track__step--done):not(.org-track__step--current) .org-track__label { color: var(--ink-muted); font-weight: var(--fw-regular); }
.org-track__when { font-size: var(--fs-caption); color: var(--ink-muted); }

/* ---- The request form's own summary ------------------------------------------ */
.org-summary { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-3) var(--space-4); background: var(--info-subtle); border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); border-radius: var(--radius-md); font-size: var(--fs-body-sm); }
.org-summary .icon { flex: none; margin-block-start: 1px; color: var(--accent); }

/* ---- /admin: the directory of the section's areas ---------------------------- */
.org-dir { display: grid; gap: var(--gap-cards); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px) { .org-dir { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.org-dir__card { display: grid; align-content: start; gap: var(--space-3); padding: var(--card-pad); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--elev-1); }
.org-dir__head { display: grid; gap: var(--space-1); }
.org-dir__title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); }
.org-dir__title .icon { color: var(--accent); }
.org-dir__lead { font-size: var(--fs-caption); color: var(--ink-muted); }
.org-dir__list { display: grid; }
.org-dir__list > li + li { border-block-start: 1px solid var(--border); }
.org-dir__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  padding: var(--space-3) var(--space-2);
  margin-inline: calc(-1 * var(--space-2));
  border-radius: var(--radius-md);
  color: var(--ink);
  text-decoration: none;
}
.org-dir__link:hover { background: color-mix(in srgb, var(--ink) 5%, transparent); }
.org-dir__link:hover .org-dir__name { color: var(--accent); }
.org-dir__name { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.org-dir__what { grid-column: 1; font-size: var(--fs-caption); color: var(--ink-muted); }
.org-dir__link .icon--cue { grid-column: 2; grid-row: 1 / span 2; color: var(--ink-muted); }
.org-for-operator { display: none; }
html[data-role="operator"] .org-for-operator { display: inline-flex; }

/* ---- Motion: calm, small, and off under reduced motion ------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .org-toc__link, .org-dir__link, .org-row, .org-savebar__inner { transition: background-color var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard); }
  .org-log__item.is-new, .org-req.is-new { animation: org-in var(--dur-base) var(--ease-emphasized); }
  .org-track__step + .org-track__step::before { transition: background-color var(--dur-base) var(--ease-standard); }
}
@keyframes org-in { from { opacity: 0; transform: translateY(calc(-1 * var(--space-2))); } to { opacity: 1; transform: none; } }
@media print { .org-savebar, .org-toc { display: none !important; } }

/* System pages at the tablet and desktop tiers: the panel stops being a narrow
   column in an empty field. The mark and the large code stand on one side, a
   hairline apart from the title, the lead and the two ways out, and the pane
   is centred in the height the plane gives it. */
@media (min-width: 768px) {
  .error { align-content: center; min-block-size: calc(100dvh - 14rem); padding-block: var(--space-8); }
  .error__panel {
    grid-template-columns: minmax(9rem, 12rem) minmax(0, 1fr);
    gap: var(--space-8);
    max-inline-size: 52rem;
    padding: var(--space-8);
    align-items: start;
  }
  .error__side { align-content: start; padding-inline-end: var(--space-8); border-inline-end: 1px solid var(--border); min-block-size: 100%; }
  .error__mark { inline-size: 72px; block-size: 72px; }
  .error__mark .icon { inline-size: 32px; block-size: 32px; }
  .error__code { font-size: var(--fs-display-lg); line-height: var(--lh-display-lg); }
  .error__title { font-size: var(--fs-display-md); line-height: var(--lh-display-md); }
  .error__lead { font-size: var(--fs-body-lg); line-height: var(--lh-body-lg); }
}
@media (min-width: 1024px) {
  .error__panel { max-inline-size: 64rem; padding: var(--space-9) var(--space-10); gap: var(--space-10); grid-template-columns: 14rem minmax(0, 1fr); }
  .error__side { padding-inline-end: var(--space-10); }
}

/* A password field with its show/hide control. The button sits at the inline
   end of the field and the text keeps clear of it; the password stays LTR. */
.input-wrap { position: relative; display: grid; }
.input-wrap .input { padding-inline-end: 48px; }
.input-wrap__btn {
  position: absolute; inset-block: 0; inset-inline-end: 0;
  display: inline-grid; place-items: center;
  inline-size: 44px; padding: 0; border: 0; background: transparent;
  color: var(--ink-muted); border-radius: var(--radius-md); cursor: pointer;
}
.input-wrap__btn:hover { color: var(--ink); }
.input-wrap__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }


/* A card that pairs a summary with the form beside it: from --bp-lg the two
   sit side by side, the summary as a tinted panel and the form's two fields on
   one row, so neither leaves the rest of a wide card empty. */
/* The identity panel is a tinted tile at every tier: the avatar beside the name
   and role on a phone, stacked and centred beside the form from the laptop
   tier. */
.card--split > .identity {
  margin-block-end: var(--space-5); padding: var(--space-4);
  background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.filefield--zone {
  padding: var(--space-4);
  background: color-mix(in srgb, var(--accent) 4%, var(--surface-raised));
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
}
/* The form takes the card's whole width at every tier — capped to the form
   measure it left an empty block beside it on a tablet — and its two fields
   stand on one row from the tablet tier. */
.card--split .form { max-inline-size: none; gap: var(--space-5); }
.card--split .form .field { margin-block-end: 0; align-content: start; }
.card--split .actions { padding-block-start: var(--space-5); border-block-start: 1px solid var(--border); }
@media (min-width: 768px) {
  .card--split .form { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5) var(--space-6); align-content: start; }
  .card--split .form > .actions, .card--split .form > input { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
  .card--split { display: grid; grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr); align-items: stretch; column-gap: var(--space-6); }
  .card--split > .identity {
    flex-direction: column; justify-content: center; text-align: center;
    margin-block-end: 0; padding: var(--card-pad);
    background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg);
  }
  .card--split > .identity .identity__text { justify-items: center; }
}

/* The sign-in identity: the number the account is built on, with the rule that
   it is not changed from the profile beside it from the laptop tier. */
.signin { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3) var(--space-4); align-items: center; }
.signin__icon {
  display: grid; place-items: center;
  inline-size: var(--space-10); block-size: var(--space-10);
  background: var(--info-subtle); color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-md);
}
.signin__text { display: grid; gap: var(--space-1); }
.signin__hint { grid-column: 1 / -1; margin: 0; padding-block-start: var(--space-3); border-block-start: 1px solid var(--border); font-size: var(--fs-body-sm); color: var(--ink-muted); }
@media (min-width: 1024px) {
  .signin { grid-template-columns: auto minmax(12rem, 18rem) minmax(0, 1fr); column-gap: var(--space-5); }
  .signin__hint { grid-column: auto; padding: 0; padding-inline-start: var(--space-5); border-block-start: 0; border-inline-start: 1px solid var(--border); }
}

/* The sessions block's one danger control: the consequence beside the button,
   in a card of its own with the danger edge, so the press is never read as one
   more row of the list above it. */
.danger-zone {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-3) var(--space-4); align-items: center;
  margin-block-start: var(--space-4);
  border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
  background: color-mix(in srgb, var(--danger) 6%, var(--surface));
}
.danger-zone__icon { display: grid; place-items: center; inline-size: var(--space-10); block-size: var(--space-10); background: var(--danger-subtle); color: var(--danger); border-radius: var(--radius-md); }
/* On a phone the icon and the title share a row and the sentence and the button
   take the card's full width under them. */
.danger-zone__text { display: contents; }
.danger-zone__icon { inline-size: var(--space-8); block-size: var(--space-8); }
.danger-zone__title { align-self: center; }
.danger-zone__body { grid-column: 1 / -1; }
.danger-zone__title { font-weight: var(--fw-semibold); color: var(--ink); }
.danger-zone__body { margin: 0; font-size: var(--fs-body-sm); color: var(--ink-muted); max-inline-size: var(--content-max-prose); }
.danger-zone > .btn { grid-column: 1 / -1; justify-self: start; }
@media (min-width: 768px) {
  .danger-zone { grid-template-columns: auto minmax(0, 1fr) auto; }
  .danger-zone__text { display: grid; gap: var(--space-1); }
  .danger-zone__icon { inline-size: var(--space-10); block-size: var(--space-10); }
  .danger-zone__body { grid-column: auto; }
  .danger-zone > .btn { grid-column: auto; justify-self: end; }
}

/* The history button closes a settings page's section head on the same row as
   the title, whatever the width. */
.section__head--tools { flex-wrap: nowrap; }
.section__head--tools .section__title { flex-wrap: nowrap; min-inline-size: 0; white-space: nowrap; gap: var(--space-2); }
.section__head--tools > .btn { white-space: nowrap; }

/* The password card: the forms in one column that takes the room, and what the
   change does to the rest of the account in a column beside them from the
   laptop tier; below it the notice stands first, then the forms. */
.pw-card { display: grid; gap: var(--space-5); }
.pw-card__main { display: grid; gap: var(--card-pad); align-content: start; min-inline-size: 0; }
.pw-card__main > .choice { margin-block-end: 0; }
.pw-card__aside { display: grid; gap: var(--space-4); align-content: start; order: -1; }
.pw-card__aside > .alert { margin-block-end: 0; }
.pw-card .field { margin-block-end: var(--space-2); }
@media (min-width: 1024px) {
  .pw-card { grid-template-columns: minmax(0, 1fr) minmax(16rem, 22rem); gap: var(--space-8); align-items: start; }
  .pw-card__aside { order: 0; }
}

/* The page head is a plain heading row — the title and its actions — not a
   briefing card: no panel, accent edge or wash behind it. */
.page-head { padding: 0; background: none; border: 0; border-radius: 0; box-shadow: none; overflow: visible; gap: var(--space-4); }
.page-head::before { content: none; }
.page-head .live { padding-block-start: 0; border-block-start: 0; }
.page--home .page-head { padding: 0; }


/* ==========================================================================
   The password card, glazed. The card, its notice and its fields share one
   finish: a faint accent wash over the surface, a glass edge, and an accent
   ring where the member is typing. The method choice is two large options
   rather than a boxed control — `.choice` here is the segmented group's own
   wrapper, not the radio card of the same name.
   ========================================================================== */
.pw-card {
  padding: var(--card-pad);
  background:
    radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 60%),
    linear-gradient(160deg, color-mix(in srgb, var(--accent) 2%, var(--surface)), var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border));
  box-shadow: var(--elev-2), 0 0 36px -22px color-mix(in srgb, var(--accent) 22%, transparent);
}
.pw-card .choice { display: block; padding: 0; background: none; border: 0; border-radius: 0; cursor: auto; }
.pw-card .choice__legend { margin-block-end: var(--space-2); font-weight: var(--fw-medium); }
.pw-card .seg {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-1);
  padding: var(--space-1);
  background: color-mix(in srgb, var(--ink) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, var(--border));
  border-radius: var(--radius-lg);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.pw-card .seg__btn { min-inline-size: 0; min-block-size: 48px; border-radius: var(--radius-md); font-weight: var(--fw-medium); }
.pw-card .seg__btn[aria-pressed="true"] {
  background: linear-gradient(160deg, color-mix(in srgb, var(--accent) 14%, var(--surface)), color-mix(in srgb, var(--accent) 7%, var(--surface)));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent);
}
.pw-card .input {
  min-block-size: 52px;
  background: color-mix(in srgb, var(--ink) 4%, var(--surface-raised));
  border-color: color-mix(in srgb, var(--accent) 10%, var(--border-strong));
  border-radius: var(--radius-lg);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.pw-card .input:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border-strong)); }
.pw-card .input:focus-visible { border-color: var(--accent); box-shadow: var(--glow-focus); }
.pw-card .field__label { font-weight: var(--fw-semibold); }
.pw-card .pane { gap: var(--space-5); }
.pw-card .pane .field { margin-block-end: 0; }
.pw-card .pane[hidden] + .pane { margin: 0; padding: 0; border: 0; }
.pw-card .member-consequence { padding: var(--space-3); background: color-mix(in srgb, var(--accent) 4%, transparent); border-radius: var(--radius-md); }
.pw-card .actions { padding-block-start: 0; }
.pw-card .actions .btn--primary { min-block-size: 48px; padding-inline: var(--space-6); }
.pw-card__aside > .alert {
  padding: var(--space-4);
  background: linear-gradient(160deg, color-mix(in srgb, var(--accent) 8%, var(--surface)), color-mix(in srgb, var(--accent) 3%, var(--surface)));
  border-color: color-mix(in srgb, var(--accent) 28%, var(--border));
  border-radius: var(--radius-lg);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
@media (max-width: 767px) {
  .pw-card { padding: var(--space-4); }
  .pw-card .actions .btn--primary { inline-size: 100%; justify-content: center; }
}
@media (min-width: 1024px) {
  .pw-card { padding: var(--space-8); }
  .pw-card__aside { position: sticky; inset-block-start: calc(var(--space-8) + var(--space-8)); }
}


/* ==========================================================================
   Profile and sessions — one quiet finish shared with the password card: round
   corners, a hairline accent edge, and no wash behind the content.
   ========================================================================== */
.card--split, .signin {
  border-radius: var(--radius-lg);
  border-color: color-mix(in srgb, var(--accent) 14%, var(--border));
}
.card--split > .identity { background: color-mix(in srgb, var(--ink) 3%, var(--surface-raised)); }
.card--split > .identity .avatar--lg { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 10%, transparent); }
.card--split .input {
  min-block-size: 48px;
  border-radius: var(--radius-lg);
  transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
.card--split .input:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border-strong)); }
.card--split .actions .btn--primary { min-block-size: 44px; padding-inline: var(--space-6); }
@media (max-width: 767px) {
  .card--split .actions .btn--primary { flex: 1 1 auto; justify-content: center; }
}

/* The sessions are separate cards rather than one divided plane: each device is
   its own thing the member can act on, and the one being read from carries the
   accent edge as well as its badge. */
.sessions { gap: var(--space-3); background: none; border: 0; border-radius: 0; box-shadow: none; overflow: visible; }
.session {
  border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--elev-1);
  transition: border-color var(--dur-fast) var(--ease-standard);
}
.session:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.session:has(.badge--accent) { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: color-mix(in srgb, var(--accent) 3%, var(--surface)); }
.session__icon { inline-size: 44px; block-size: 44px; padding: var(--space-3); border-radius: var(--radius-md); }
.session__device { font-weight: var(--fw-semibold); }
.session__action { padding-block-start: var(--space-3); border-block-start: 1px solid var(--border); }
@media (min-width: 768px) {
  .session { grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--space-4); }
  .session__action { grid-column: auto; padding: 0; border: 0; }
}
.danger-zone { margin-block-start: var(--space-5); background: color-mix(in srgb, var(--danger) 4%, var(--surface)); border-color: color-mix(in srgb, var(--danger) 35%, var(--border)); }

/* ==========================================================================
   Pass: guide contents, notification rows, organization settings layout
   ========================================================================== */

/* Guide contents — six topics fall in two even rows on the tablet tier. */
@media (min-width: 768px) {
  .catnav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .catnav .pill { justify-content: center; }
}
@media (min-width: 1024px) {
  .catnav { display: flex; }
  .catnav .pill { justify-content: flex-start; }
}

/* Notifications — a long body is clamped; an arrow in the foot opens it. */
.notify__body {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.notify--open .notify__body { display: block; overflow: visible; }
.notify__more {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 0; background: none; border: 0;
  font: inherit; font-size: var(--fs-body-sm); font-weight: var(--fw-medium);
  color: var(--ink-muted); cursor: pointer;
}
.notify__more[hidden] { display: none; }
.notify__more:hover { color: var(--accent); }
.notify__more .icon { transform: rotate(-90deg); transition: transform var(--dur-fast) var(--ease-standard); }
.notify--open .notify__more .icon { transform: rotate(90deg); }
.notify-tools { padding: var(--space-2); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.notify-tools .btn { margin-inline-start: auto; }

/* Organization key-value table — saving state never moves the layout. */
.org-kv .org-pending[hidden] { display: inline-flex; visibility: hidden; }
.org-kv .org-pending { white-space: nowrap; }
@media (min-width: 768px) {
  .org-kv { table-layout: fixed; }
  .org-kv thead th:nth-child(1) { inline-size: 44%; }
  .org-kv thead th:nth-child(2) { inline-size: 36%; }
  .org-valuecell { flex-wrap: nowrap; }
  .org-kv .select { min-inline-size: 0; flex: 1 1 auto; }
}

/* Default-period impact — two tiles, each its own surface. */
.org-impact__now { padding: var(--space-3) var(--space-4); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.org-impact__now .btn { margin-inline-start: auto; }
.org-impact__cols { gap: var(--space-4); }
.org-impact__col { padding: var(--space-4); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.org-impact__col--yes { border-color: color-mix(in srgb, var(--success) 40%, var(--border)); }
.org-impact__col + .org-impact__col { border-block-start: 1px solid var(--border); }
.org-impact__item { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-1) var(--space-3); padding-block-start: var(--space-2); border-block-start: 1px solid var(--border); }
.org-impact__item:first-child { border-block-start: 0; padding-block-start: 0; }
@media (min-width: 768px) {
  .org-impact__col + .org-impact__col { border-block-start: 1px solid var(--border); border-inline-start: 1px solid var(--border); padding-inline-start: var(--space-4); }
  .org-impact__col:first-child { padding-inline-end: var(--space-4); }
  .org-impact__col--yes { border-color: color-mix(in srgb, var(--success) 40%, var(--border)); }
}

/* Save bar on a phone — status above, two equal buttons below. */
.org-savebar { inset-block-end: var(--space-2); }
.org-savebar__inner { padding: var(--space-3); }
.org-savebar__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.org-savebar__actions .btn { inline-size: 100%; justify-content: center; padding-inline: var(--space-2); }
.org-savebar__note { display: none; }
@media (min-width: 768px) {
  .org-savebar { inset-block-end: var(--space-4); }
  .org-savebar__inner { padding: var(--space-3) var(--space-4); }
  .org-savebar__actions { display: flex; }
  .org-savebar__actions .btn { inline-size: auto; padding-inline: revert; }
  .org-savebar__note { display: block; }
}

/* Guide: the way into the related surface closes its card; contents pills read
   as quiet navigation, distinct from the buttons around them. */
.guide__more { margin-block-start: var(--space-4); }
.guide__more .btn { inline-size: 100%; justify-content: center; }
@media (min-width: 768px) { .guide__more .btn { inline-size: auto; } }
.catnav .pill { background: var(--surface-raised); border-color: transparent; font-weight: var(--fw-regular); color: var(--ink-muted); }
.catnav .pill .icon { color: var(--accent); }
.catnav .pill:hover { color: var(--ink); }

/* Notifications on a phone: the filter fills its row, the bulk action sits
   below it, and «جزئیات» sits right under the body it opens. */
.notify-tools { display: grid; gap: var(--space-2); }
.notify-tools .seg { inline-size: 100%; }
.notify-tools .seg__btn { flex: 1 1 0; justify-content: center; }
.notify-tools .btn { margin-inline-start: 0; inline-size: 100%; justify-content: center; }
.notify__more { justify-self: start; padding-block: var(--space-1); }
@media (min-width: 768px) {
  .notify-tools { display: flex; }
  .notify-tools .seg { inline-size: max-content; }
  .notify-tools .seg__btn { flex: none; }
  .notify-tools .btn { margin-inline-start: auto; inline-size: auto; }
}

/* Guide: the related-surface button stays beside the section title, also on a phone. */
.guide__more { display: none; }
#g-start .section__head, #g-read .section__head, #g-filter .section__head,
#g-kpi .section__head, #g-access .section__head, #g-versions .section__head { flex-wrap: nowrap; align-items: center; }
#g-start .section__title, #g-read .section__title, #g-filter .section__title,
#g-kpi .section__title, #g-access .section__title, #g-versions .section__title { flex: 1 1 auto; min-inline-size: 0; }
#g-start .section__head .btn, #g-read .section__head .btn, #g-filter .section__head .btn,
#g-kpi .section__head .btn, #g-access .section__head .btn, #g-versions .section__head .btn { flex: none; white-space: nowrap; }

/* Notifications — compact card: the detail arrow sits in the head row. */
.notify { gap: var(--space-2); padding: var(--space-3) var(--space-4); }
.notify__dot { margin-block-start: var(--space-1); }
.notify__main { gap: var(--space-1); }
.notify__title { font-size: var(--fs-body); }
.notify__body { line-height: var(--lh-body-sm, 1.6); }
.notify__head .notify__more {
  margin-inline-start: auto;
  inline-size: 28px; block-size: 28px; padding: 0;
  display: inline-grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-md);
  background: var(--surface-raised);
}
.notify__head .notify__more[hidden] { display: none; }
.notify__foot { flex-wrap: nowrap; gap: var(--space-2); margin-block-start: var(--space-1); padding-block-start: var(--space-2); }
.notify__link { flex: 1 1 auto; min-inline-size: 0; }
.notify__state { flex: none; white-space: nowrap; }
.notify__mark { flex: none; inline-size: 28px; block-size: 28px; }

/* Notifications — phone: both controls span the bar's full width. */
.notify-tools { grid-template-columns: minmax(0, 1fr); }
.notify-tools .seg, .notify-tools .btn { inline-size: 100%; max-inline-size: none; }
@media (min-width: 768px) {
  .notify-tools { grid-template-columns: none; }
  .notify-tools .seg { inline-size: max-content; }
  .notify-tools .btn { inline-size: auto; }
}

/* Notifications — desktop: state and the read action join the head row, the
   title and body run full width, and the link closes the card. */
@media (min-width: 1024px) {
  .notify__main { grid-template-columns: minmax(0, 1fr) auto auto; column-gap: var(--space-3); align-items: center; }
  .notify__main > * { grid-column: 1 / -1; }
  .notify__main > .notify__head { grid-column: 1; grid-row: 1; }
  .notify__main > .notify__foot { display: contents; }
  .notify__foot .notify__state { grid-column: 2; grid-row: 1; margin: 0; }
  .notify__foot .notify__mark { grid-column: 3; grid-row: 1; }
  .notify__foot .notify__link { grid-column: 1 / -1; grid-row: 4; justify-self: start; margin-block-start: var(--space-1); }
  .notify__body { max-inline-size: none; }
}
@media (min-width: 1024px) { .notify__body { -webkit-line-clamp: 1; } }

/* Notifications — the text of a clamped body ends with its own «جزئیات بیشتر» on the desktop plane;
   the head arrow serves the narrower tiers. */
.notify__more--inline { display: none; }
@media (min-width: 1024px) {
  .notify__text { display: flex; align-items: flex-start; gap: var(--space-3); }
  .notify__text .notify__body { flex: 1 1 auto; min-inline-size: 0; }
  .notify__text .notify__more--inline { display: inline-flex; flex: none; color: var(--accent); white-space: nowrap; }
  .notify__text .notify__more--inline[hidden] { display: none; }
  .notify__head .notify__more { display: none; }
}


/* ==========================================================================
   Dashboard builder (`content/builder.html`, §4.39) — every class `build`-
   prefixed, composing documented tokens only. Mobile-first: the base is the
   handset; `min-width` blocks add the tablet and desktop readings.
   ========================================================================== */
.build { display: grid; gap: var(--gap-cards); margin-block-end: var(--card-pad); }
.build__card .section-card__body { display: grid; gap: var(--card-pad); }
.build__typegrid { display: grid; gap: var(--space-2); grid-template-columns: minmax(0, 1fr); }
.build__typegrid .choice { margin-block-end: 0; }
.build__types { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
.build__panelform { align-items: start; }
.build__panelform [hidden] { display: none; }
.build__bind { margin: 0; }
.build__subtitle { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); margin: 0; }
.build__empty[hidden], .build__slots[hidden] { display: none; }
.build__slots { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; counter-reset: slot; }
.build__slot {
  display: grid;
  gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.build__slot-main { display: grid; gap: var(--space-2); min-inline-size: 0; }
.build__slot-title { font-size: var(--fs-body-md); font-weight: var(--fw-semibold); margin: 0; }
.build__slot-tags { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.build__slot-meta, .build__slot-how { font-size: var(--fs-body-sm); color: var(--ink-muted); margin: 0; }
.build__slot-acts { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.build__slot-acts .btn { min-block-size: 44px; }
.build__savebar {
  display: grid;
  gap: var(--space-3);
  padding: var(--card-pad);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--elev-1);
}
.build__status { margin: 0; font-weight: var(--fw-semibold); }
.build__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.build__actions .btn { min-block-size: 44px; }
.build .btn { min-block-size: 44px; }

@media (min-width: 768px) {
  .build__typegrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .build__slot { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
}
@media (min-width: 1024px) {
  .build__typegrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media print {
  .build__savebar, .build__slot-acts { display: none; }
}


/* ==========================================================================
   Content categories (`admin/publishing/categories.html`, §4.40) — every class
   `cat`-prefixed, composing documented tokens only. Mobile-first.
   ========================================================================== */
.cat-tree, .cat-tree__sub { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--space-3); }
.cat-tree__sub { margin-block-start: var(--space-3); padding-inline-start: var(--space-5); border-inline-start: 2px solid var(--border); }
.cat-tree__row {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.cat-tree__main { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); min-inline-size: 0; }
.cat-tree__name { font-weight: var(--fw-semibold); }
.cat-tree__why { font-size: var(--fs-caption); color: var(--ink-muted); }
.cat-tree__acts { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.cat-tree__acts .btn { min-block-size: 44px; }
.cat-tree__edit { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.cat-tree__edit .input { flex: 1 1 12rem; min-inline-size: 0; }
.cat-tree__item--new > .cat-tree__row { border-color: var(--accent); }

@media (min-width: 768px) {
  .cat-tree__row { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
}
@media print {
  .cat-tree__acts { display: none; }
}
.build__catask { display: none; }
html[data-role="operator"] .build__catask { display: block; }

/* ==========================================================================
   Content bucket, mobile pass — one-row page actions, the status-meaning
   disclosure read as a button, the data-type explainer and the template filter
   as a tidy card on the handset. Mobile-first; `min-width` blocks restore the
   wider readings.
   ========================================================================== */
.page-actions--row { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: var(--space-2); inline-size: 100%; }
.page-actions--row .btn { min-inline-size: 0; min-block-size: 44px; padding-inline: var(--space-2); justify-content: center; text-align: center; white-space: normal; line-height: 1.4; font-size: var(--fs-body-sm); }

.content-lifecycle > summary {
  display: flex; justify-content: space-between; inline-size: 100%;
  min-block-size: 44px; padding-inline: var(--space-4);
  background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: var(--radius-md);
  list-style: none;
}
.content-lifecycle > summary::-webkit-details-marker { display: none; }
.content-lifecycle > summary:hover { border-color: var(--accent); }
.content-lifecycle > summary .icon { color: var(--accent); }
.content-lifecycle[open] > summary .icon { transform: rotate(180deg); }
.content-lifecycle__list { max-inline-size: none; grid-template-columns: minmax(0, 1fr); }
.content-lifecycle__list > li {
  display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: var(--space-3); align-items: start;
  padding: var(--space-3); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md);
}
.content-lifecycle__list > li > .badge { justify-self: start; }

.content-datatypes { display: grid; gap: var(--space-3); margin-block-end: var(--card-pad); padding: var(--card-pad); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.content-datatypes__title { font-size: var(--fs-heading-sm); font-weight: var(--fw-semibold); margin: 0; }
.content-datatypes__list { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; grid-template-columns: minmax(0, 1fr); }
.content-datatypes__item { display: grid; gap: var(--space-2); justify-items: start; }
.content-datatypes__item p { margin: 0; font-size: var(--fs-body-sm); color: var(--ink-muted); }

.tplfilter { display: grid; gap: var(--space-4); padding: var(--card-pad); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.tplfilter .seg { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.tplfilter .select { inline-size: 100%; min-inline-size: 0; }
.tplfilter .toolbar__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.tplfilter .toolbar__actions .btn { min-block-size: 44px; }

@media (min-width: 768px) {
  .page-actions--row { display: flex; flex-wrap: wrap; inline-size: auto; }
  .page-actions--row .btn { min-inline-size: 0; padding-inline: var(--space-4); font-size: inherit; white-space: nowrap; }
  .content-lifecycle > summary { display: inline-flex; inline-size: auto; gap: var(--space-3); }
  .content-lifecycle__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .content-datatypes__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .tplfilter { display: flex; flex-wrap: wrap; align-items: flex-end; padding: 0; background: none; border: 0; border-radius: 0; }
  .tplfilter .seg { display: inline-flex; }
  .tplfilter .select { inline-size: auto; min-inline-size: 14rem; }
  .tplfilter .toolbar__actions { display: flex; }
}
@media (min-width: 1024px) {
  .content-lifecycle__list { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ==========================================================================
   Builder tidy pass and the status-disclosure cue. The fold cue follows the
   rail's rule: turned toward the content edge while closed, pointing down once
   open. The builder's fields share one row from tablet up, the unit and source
   sit side by side, and the save row reads as text on one side and actions on
   the other.
   ========================================================================== */
.content-lifecycle[open] > summary .icon { transform: none; }
.content-lifecycle:not([open]) > summary .icon { transform: rotate(90deg); }

.build .form-grid { align-items: start; }
.build__bind { display: grid; gap: var(--space-2); grid-template-columns: minmax(0, 1fr); padding: var(--space-3); background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-md); }
.build__bind .kv__row { padding-block: 0; border: 0; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-3); }
.build__panelform .seg { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.build__savebar { gap: var(--space-4); }
.build__savetext { display: grid; gap: var(--space-1); min-inline-size: 0; }
.build__savetext .form-actions__note { margin: 0; }
.build__actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.build__actions .btn--primary { grid-column: 1 / -1; }

@media (min-width: 768px) {
  .build .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 var(--space-5); }
  .build__bind { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .build__panelform .seg { display: inline-flex; }
  .build__savebar { grid-template-columns: minmax(0, 1fr) auto; align-items: center; }
  .build__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; }
  .build__actions .btn--primary { order: 3; }
}

/* ==========================================================================
   Viewer head and filter pass — the version history spans the head, the head's
   actions sit as equal buttons, and the filter card reads as two named blocks.
   ========================================================================== */
.page-head > .version { grid-column: 1 / -1; margin-block-start: 0; }
.page-head > .version > summary { min-block-size: 40px; }
.page-head > .version[open] > summary { color: var(--accent); }
.page-actions--grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-2); inline-size: 100%; }
.page-actions--grid .btn { min-block-size: 44px; justify-content: center; text-align: center; white-space: normal; line-height: 1.4; }
@media (min-width: 768px) {
  .page-actions--grid { grid-template-columns: minmax(12rem, 1fr); inline-size: auto; min-inline-size: 13rem; }
  .page-head > .version .version__row { grid-template-columns: minmax(0, 9rem) minmax(0, 1fr) auto; }
}

/* ==========================================================================
   Users page (admin/people/users.html) — tier lines, the account list on
   small screens and the creation form. Mobile first; wider tiers add columns.
   ========================================================================== */

/* "Who sees what": each tier states what it opens and what it never shows as
   two plainly labelled lines. The two are told apart by the tick or cross, the
   word, and a solid or dashed edge — never by hue (the tiers stay uncoloured). */
.access-tier--lines { gap: var(--space-3); }
.tier-line {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface-raised);
}
.tier-line--no { border-style: dashed; background: transparent; }
.tier-line__label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink);
}
.tier-line__label .icon { flex: none; color: var(--accent); }
.tier-line--no .tier-line__label .icon { color: var(--ink-muted); }
.tier-line__text { margin: 0; font-size: var(--fs-body-sm); line-height: 1.8; color: var(--ink); }
.tier-line--no .tier-line__text { color: var(--ink-muted); }

/* The directory's filters: search on its own row, the two selects side by side,
   the count under them. From the tablet tier it is the usual wrapping toolbar. */
.toolbar--filters { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); align-items: stretch; }
.toolbar--filters .searchbox { grid-column: 1 / -1; min-inline-size: 0; }
.toolbar--filters .select { inline-size: 100%; min-inline-size: 0; }
.toolbar--filters .toolbar__spacer { display: none; }
.toolbar--filters .toolbar__note { grid-column: 1 / -1; }

/* Below the tablet tier each account is its own card: who it is and the
   manage button on the first row, then the tier, the state and the expiry as
   small name-over-value pairs, the roles across the full width. */
.ds--accounts tr[data-row] {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
  margin-block-end: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.ds--accounts tr[data-row][hidden] { display: none; }
.ds--accounts tr[data-row] > th { grid-column: 1 / -1; display: grid; gap: var(--space-1); padding: 0 0 var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); }
.ds.ds--accounts td { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); align-content: start; padding: 0; }
.ds.ds--accounts td[data-label="سطح حساب"] { order: 1; }
.ds.ds--accounts td[data-label="وضعیت"] { order: 2; }
.ds.ds--accounts td[data-label="نقش‌ها"] { order: 3; }
.ds.ds--accounts td[data-label="انقضای دسترسی"] { order: 4; }
.ds.ds--accounts td[data-label="عملیات"] { order: 5; grid-column: 1 / -1; }
.ds.ds--accounts td[data-label="عملیات"]::before { display: none; }
.ds.ds--accounts td[data-label="عملیات"] .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
@media (min-width: 768px) {
  .toolbar--filters { display: flex; align-items: flex-end; }
  .toolbar--filters .searchbox { flex: 1 1 16rem; min-inline-size: 12rem; }
  .toolbar--filters .select { inline-size: auto; min-inline-size: 11rem; }
  .toolbar--filters .toolbar__spacer { display: block; }
  .toolbar--filters .toolbar__note { grid-column: auto; }
  .ds--accounts tr[data-row] { display: table-row; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; }
  .ds--accounts tr[data-row] > th { display: table-cell; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--border); }
  .ds.ds--accounts td { display: table-cell; padding: var(--space-3) var(--space-4); }
  .ds.ds--accounts td[data-label="عملیات"] .btn { inline-size: auto; min-block-size: 0; border: 0; }
}

/* The creation form: three titled blocks, each its own plane, then the
   actions. The blocks hold the fields in one column on mobile and two from
   the tablet tier. */
.form-block {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-4);
  min-inline-size: 0;
  margin: 0 0 var(--space-4);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.form-block__title { padding-inline: var(--space-2); font-size: var(--fs-body-md); font-weight: var(--fw-semibold); }
.form-block__grid { display: grid; gap: var(--space-4); align-items: start; }
.form-block__grid > .field, .form-block > .field { min-inline-size: 0; margin: 0; }

/* Expiry: the date, then four equal preset buttons under it. */
.expiry { display: grid; gap: var(--space-3); }
.expiry__input { inline-size: 100%; }
.expiry__presets { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.expiry__presets .btn { min-block-size: 44px; justify-content: center; }

/* A switch that fills its row: the control at the start, a line saying what it
   does and a second line saying what happens when it is off. */
.switch.switch--row {
  display: flex;
  inline-size: 100%;
  block-size: auto;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
}
.switch--row .switch__track { margin-block-start: 2px; }
.switch--row .switch__text { display: grid; gap: var(--space-1); min-inline-size: 0; }
.switch__title { font-size: var(--fs-body-md); font-weight: var(--fw-medium); }
.switch__desc { font-size: var(--fs-caption); color: var(--ink-muted); line-height: 1.7; }
.switch--row:has(input:checked) { border-color: var(--accent); }

/* Create / reset: two equal buttons side by side on mobile, natural width from
   the tablet tier, the primary one first. */
.form-actions--split { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.form-actions--split .btn { min-block-size: 44px; justify-content: center; }
@media (min-width: 768px) {
  .form-block { padding: var(--space-5); }
  .form-block__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); }
  .expiry { grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr); align-items: center; }
  .expiry__presets { grid-template-columns: repeat(4, auto); justify-content: start; }
  .form-actions--split { display: flex; justify-content: flex-start; }
  .form-actions--split .btn { min-inline-size: 9rem; }
}

/* A list card's closing row: the note and the pager each take their own line,
   centred, below the desktop tier; from there they sit at the row's two ends. */
.section-card__foot--pager { flex-direction: column; justify-content: center; text-align: center; }
.section-card__foot--pager .pager { justify-content: center; }
@media (min-width: 1024px) {
  .section-card__foot--pager { flex-direction: row; justify-content: space-between; text-align: start; }
  .section-card__foot--pager .pager { justify-content: flex-start; }
}

/* The summary figures follow the page head with the section rhythm between
   them; the step strip that used to sit there carried that space. */
.page-head + .access-stats { margin-block-start: var(--space-6); }

/* ==========================================================================
   Account record (admin/people/user-detail.html) — head actions, the tidy
   two-column form, the read-only field, the sees-what menu grid.
   ========================================================================== */

/* The head's three actions: the first two equal, side by side; «بازگشت به
   فهرست» on its own full row below, bordered so it reads as a button. From the
   tablet tier all three sit in one row at their natural width. */
.page-actions--trio { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.page-actions--trio .btn { min-block-size: 44px; justify-content: center; text-align: center; }
.page-actions--trio .btn--ghost { grid-column: 1 / -1; border: 1px solid var(--border-strong); }
@media (min-width: 768px) {
  .page-actions--trio { display: flex; flex-wrap: wrap; }
  .page-actions--trio .btn { min-block-size: 0; }
  .page-actions--trio .btn--ghost { border-color: transparent; }
}

/* A form whose fields keep their own height: nothing stretches to match the
   field beside it, and every field carries a hint so the rows line up. */
.form-grid--tidy { align-items: start; row-gap: var(--space-5); }
.form-grid--tidy > .field { display: grid; gap: var(--space-2); align-content: start; margin: 0; }
.form-grid--tidy .field__hint { margin: 0; }
.form-grid--tidy > .field--full { grid-column: 1 / -1; }
.form-grid--tidy > .field--full > .field__label { margin-block-start: var(--space-2); }
.form-grid--tidy > .field--full > .expiry { margin-block-end: var(--space-1); }

/* A value that is shown and not edited: the same box as an input, without the
   input's border, so it cannot be mistaken for a field to fill in. */
.field__static {
  display: flex;
  align-items: center;
  min-block-size: 44px;
  margin: 0;
  padding: 0 var(--space-3);
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  font-size: var(--fs-body-md);
}

/* The closing save button: full width on mobile, at the end edge from tablet. */
.form-actions--end { grid-column: 1 / -1; display: grid; }
.form-actions--end .btn { min-block-size: 44px; justify-content: center; }
@media (min-width: 768px) {
  .form-actions--end { display: flex; justify-content: flex-end; }
  .form-actions--end .btn { min-inline-size: 11rem; }
}

/* What the account sees: a short lead, then the menu as a grid of equal tiles
   (two across on mobile, three from tablet), then the facts under their own
   title. A tile the tier lacks is dashed and muted, as before. */
.sees-lead { margin: 0 0 var(--space-4); font-size: var(--fs-body-sm); color: var(--ink-muted); line-height: 1.8; }
.sees-title { margin: 0 0 var(--space-2); font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); color: var(--ink); }
.access-menu--grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); margin-block-end: var(--space-5); }
.access-menu--grid .access-menu__item { min-block-size: 40px; padding: var(--space-2) var(--space-3); font-size: var(--fs-body-sm); }
.access-menu--grid .access-menu__item .icon { flex: none; color: var(--accent); }
.access-menu--grid .access-menu__item--no .icon { color: var(--ink-muted); }
@media (min-width: 768px) {
  .access-menu--grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .access-menu--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Three presets share one row on every tier. */
.expiry__presets--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* On the record page the expiry sits in a column that can be narrow: the date
   always takes its own row and the presets share the next, never wrapping. */
.form-grid--tidy .expiry { grid-template-columns: minmax(0, 1fr); }
.expiry__presets--three .btn { white-space: nowrap; }

/* Roles page: three figures in one row, the count and the way out of the list
   on one line under its title, one-column filters on mobile, role cards below
   the tablet tier. */
.access-stats--three { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); }
.access-stats--three .access-stat { justify-items: center; padding: var(--space-3) var(--space-2); text-align: center; }
@media (min-width: 768px) {
  .access-stats--three { gap: var(--space-3); }
  .access-stats--three .access-stat { justify-items: start; padding: var(--space-3) var(--space-4); text-align: start; }
}

.section__head--count .section__title { flex: 1 1 100%; }
.section__head--count .section__count { margin-inline-end: auto; }
@media (min-width: 768px) {
  .section__head--count .section__title { flex: 0 1 auto; }
}

.toolbar--single .select { grid-column: 1 / -1; }

.ds--roles tr[data-row] {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
  margin-block-end: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.ds--roles tr[data-row][hidden] { display: none; }
.ds--roles tr[data-row] > th { grid-column: 1 / -1; padding: 0 0 var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-caption); color: var(--ink-muted); }
.ds.ds--roles td { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); align-content: start; padding: 0; }
.ds.ds--roles td[data-label="نقش"],
.ds.ds--roles td[data-label="لایه‌های مجوز"],
.ds.ds--roles td[data-label="عملیات"] { grid-column: 1 / -1; }
.ds.ds--roles td[data-label="نقش"] { font-weight: var(--fw-semibold); font-size: var(--fs-body-md); }
.ds.ds--roles td[data-label="نقش"]::before,
.ds.ds--roles td[data-label="عملیات"]::before { display: none; }
.ds.ds--roles td[data-label="لایه‌های مجوز"] { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.ds.ds--roles td[data-label="لایه‌های مجوز"]::before { flex: 1 0 100%; }
.ds.ds--roles td[data-label="عملیات"] .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
@media (min-width: 768px) {
  .ds--roles tr[data-row] { display: table-row; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; }
  .ds--roles tr[data-row] > th { display: table-cell; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--border); font-size: inherit; color: inherit; }
  .ds.ds--roles td { display: table-cell; padding: var(--space-3) var(--space-4); }
  .ds.ds--roles td[data-label="نقش"] { font-size: inherit; }
  .ds.ds--roles td[data-label="نقش"] .badge { display: inline-block; margin-inline-start: var(--space-2); white-space: nowrap; }
  .ds.ds--roles td[data-label="عملیات"] .btn { inline-size: auto; min-block-size: 0; border: 0; }
}
@media (min-width: 768px) {
  .ds.ds--roles td time { white-space: nowrap; }
  .ds.ds--roles td[data-label="لایه‌های مجوز"] { display: table-cell; }
}

/* Sessions page: the head's lead button takes the full row with the two links
   side by side under it; the four figures are compact boxes; each session or
   grant is a card below the tablet tier; on the tablet tier the table keeps
   times and addresses on one line. */
.page-actions--lead { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.page-actions--lead .btn { min-block-size: 44px; justify-content: center; text-align: center; }
.page-actions--lead .btn--primary { grid-column: 1 / -1; }
@media (min-width: 768px) {
  .page-actions--lead { display: flex; flex-wrap: wrap; }
  .page-actions--lead .btn { min-block-size: 0; }
}

.access-stats--tidy { gap: var(--space-2); }
.access-stats--tidy .access-stat { gap: 0; padding: var(--space-3); }
.access-stats--tidy .access-stat__fig { font-size: var(--fs-heading-sm, var(--fs-heading-md)); }
.access-stats--tidy .access-stat__lab { line-height: 1.6; }
@media (min-width: 768px) {
  .access-stats--tidy { gap: var(--space-3); }
  .access-stats--tidy .access-stat { gap: var(--space-1); padding: var(--space-3) var(--space-4); }
}

.toolbar--filters .access-layerpick__opt { justify-content: center; min-block-size: 44px; }
@media (min-width: 768px) {
  .toolbar--filters .access-layerpick__opt { min-block-size: 0; }
}

.ds--cards tr[data-row] {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2) var(--space-4);
  margin-block-end: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.ds--cards tr[data-row][hidden] { display: none; }
.ds--cards tr[data-row] > th { grid-column: 1 / -1; padding: 0 0 var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); }
.ds--cards tr[data-row] > th .badge { margin-inline-start: var(--space-2); }
.ds.ds--cards td { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); align-content: start; padding: 0; }
.ds.ds--cards td[data-label="دستگاه"],
.ds.ds--cards td[data-label="پرونده"] { grid-column: 1 / -1; }
.ds.ds--cards td[data-label="پرونده"]::before { display: none; }
.ds.ds--cards td[data-label="پرونده"] .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
@media (min-width: 768px) {
  .ds--cards tr[data-row] { display: table-row; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; }
  .ds--cards tr[data-row] > th { display: table-cell; padding: var(--space-3) var(--space-2); border-block-end: 1px solid var(--border); font-size: inherit; }
  .ds.ds--cards td { display: table-cell; padding: var(--space-3) var(--space-2); }
  .ds.ds--cards td[data-label="پرونده"] .btn { inline-size: auto; min-block-size: 0; border: 0; }
  .ds.ds--cards td time,
  .ds.ds--cards td[data-when],
  .ds.ds--cards td code,
  .ds.ds--cards td .badge { white-space: nowrap; }
  .ds--cards tr[data-row] > th { min-inline-size: 9rem; }
}
@media (min-width: 1024px) {
  .ds--cards tr[data-row] > th,
  .ds.ds--cards td { padding-inline: var(--space-4); }
}
@media (min-width: 768px) {
  .access-stats--tidy { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Access page: below the tablet tier each matrix row is a card — the content's
   name on top, the four layers as labelled pairs — so no column is cut off;
   the bulk form stacks its target and its layers, and the second step is set
   apart from the first. */
@media (max-width: 767.98px) {
  .matrix, .matrix tbody { display: block; }
  .matrix caption { display: block; text-align: start; }
  .matrix thead { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
  .matrix tbody tr {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2) var(--space-4);
    margin-block-end: var(--space-3);
    padding: var(--space-4);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
  }
  .matrix tbody th, .matrix tbody td { padding: 0; border: 0; text-align: start; }
  .matrix tbody th { grid-column: 1 / -1; padding-block-end: var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); }
  .matrix tbody td { display: grid; gap: var(--space-1); align-content: start; justify-items: start; }
  .matrix tbody td::before { font-size: var(--fs-caption); color: var(--ink-muted); }
  .matrix tbody td:nth-of-type(1)::before { content: "مشاهده"; }
  .matrix tbody td:nth-of-type(2)::before { content: "ویرایش"; }
  .matrix tbody td:nth-of-type(3)::before { content: "انتشار"; }
  .matrix tbody td:nth-of-type(4)::before { content: "لغو"; }
}

.form-grid--bulk { grid-template-columns: minmax(0, 1fr); }
.form-grid--bulk .access-layerpick { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2); }
.form-grid--bulk .access-layerpick__opt { justify-content: center; min-block-size: 44px; }
.form-grid--bulk + .section-card__title { margin-block: var(--space-5) var(--space-3); padding-block-start: var(--space-5); border-block-start: 1px solid var(--border); }
@media (min-width: 768px) {
  .form-grid--bulk .access-layerpick { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* The anonymous-link notice sits clear of the two assignment cards above it and
   of the section that follows. */
.access-note { margin-block: var(--space-6) var(--space-8); }

/* Groups page: members as small badges on one wrapping line; below the tablet
   tier each group is a card — the name on top, its members, the edit button. */
.group-members { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.group-picker { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.group-picker .access-layerpick__opt { min-block-size: 44px; }
.ds--groups tr[data-row] {
  display: grid;
  gap: var(--space-2);
  margin-block-end: var(--space-3);
  padding: var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.ds--groups tr[data-row] > th { padding: 0 0 var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); }
.ds--groups tr[data-row] > th .card__desc { display: block; }
.ds.ds--groups td { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); padding: 0; }
.form-actions > [hidden] { display: none; }
.ds.ds--groups td[data-label="عملیات"]::before { display: none; }
.ds.ds--groups td[data-label="عملیات"] .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
@media (min-width: 768px) {
  .group-picker { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ds--groups tr[data-row] { display: table-row; margin: 0; padding: 0; background: none; border: 0; border-radius: 0; }
  .ds--groups tr[data-row] > th { display: table-cell; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--border); font-size: inherit; }
  .ds.ds--groups td { display: table-cell; padding: var(--space-3) var(--space-4); }
  .ds.ds--groups td[data-label="عملیات"] .btn { inline-size: auto; min-block-size: 0; border: 0; }
  .ds.ds--groups td time { white-space: nowrap; }
}

/* Access page, simplified: the two assignment cards sit side by side from the
   laptop tier and start on one line; the result is a title and one sentence;
   the bulk table is a checkbox, the content and what changes. */
@media (min-width: 1024px) {
  .split--assign { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
}
.split--assign > .section-card { display: flex; flex-direction: column; margin-block: 0; }
.split--assign > .section-card > .section-card__body { flex: 1; }
.access-result__note { margin: 0; }
.ds--pick .access-select { inline-size: 44px; }
.ds--pick td[data-prop] { color: var(--ink-muted); }
@media (max-width: 767.98px) {
  .ds--pick tr { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-1) var(--space-3); align-items: center; }
  .ds--pick thead { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
  .ds--pick td[data-prop] { grid-column: 2; padding-block-start: 0; }
  .ds--pick td[data-prop]::before { display: none; }
  .ds--pick .access-select { inline-size: auto; grid-row: 1 / span 2; }
}

/* Four layers: the badge is a fixed column and the explanation sits beside it,
   so the four rows line up. */
.kv--layers .kv__val { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: var(--space-3); align-items: start; }
.kv--layers .badge { justify-self: stretch; justify-content: center; text-align: center; }

/* Branding, name and welcome text: the two fields sit side by side from the
   tablet tier so the card has no empty half. */
.org-fields { display: grid; gap: var(--space-5); }
@media (min-width: 768px) {
  .org-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}

/* License request form: one padded card, the tier on its own row, the
   requestable features first in two columns and the ones already held below
   them, quiet. The actions stand as two equal buttons on a handset. */
.org-lic-form { padding: var(--card-pad); }
.org-lic-form .form-grid { gap: var(--space-5) var(--space-4); }
.org-lic-form .form-grid > .field { margin: 0; }
.org-lic-form .select { max-inline-size: 28rem; }
.org-lic-form .choices { gap: var(--space-2); }
.org-lic-form .choice { margin: 0; padding: var(--space-3) var(--space-4); }
.org-lic-form .choice:has(input:disabled) { order: 2; opacity: 1; background: transparent; border-color: var(--border); }
.org-lic-form .choice:has(input:disabled) .choice__title { color: var(--ink-muted); font-weight: var(--fw-regular); }
.org-lic-form__lead { margin-block: 0 var(--space-3); }
.org-lic-form .form-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
.org-lic-form .form-actions .btn { justify-content: center; min-block-size: 44px; }
.org-lic-form .form-actions__note { grid-column: 1 / -1; margin: 0; }
@media (min-width: 768px) {
  .org-lic-form .choices { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .org-lic-form .form-actions { grid-template-columns: max-content max-content 1fr; align-items: center; }
  .org-lic-form .form-actions__note { grid-column: auto; }
}

.chips[hidden] { display: none; }

/* ==========================================================================
   Monitoring, analytics and notification settings on a handset
   ========================================================================== */
/* On a handset a stacked table is a list of cards, one per record: its own
   name on top, then the cells as name | value lines (the name at the start
   edge, the value at the end edge). The block that held the table drops its
   own frame so the cards are not boxed twice. */
@media (max-width: 767px) {
  .table-wrap:has(.ds--cards),
  .section-card:has(.ds--cards) { background: none; border: 0; box-shadow: none; }
  .table-wrap:has(.ds--cards) { overflow: visible; }
  .section-card:has(.ds--cards) > .section-card__body { padding: 0; }
  .table-wrap .ds--cards { min-inline-size: 0; }
  .ds--cards tbody tr { margin-block-end: var(--space-3); padding: var(--space-4); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
  .ds--cards tbody tr:last-child { margin-block-end: 0; border-block-end: 1px solid var(--border); }
  .ds--cards tbody th { display: block; margin-block-end: var(--space-2); padding: 0 0 var(--space-3); border-block-end: 1px solid var(--border); font-weight: var(--fw-semibold); }
  .ds.ds--cards td { grid-template-columns: auto minmax(0, 1fr); justify-items: end; gap: var(--space-3); align-items: center; padding: var(--space-2) 0; font-weight: var(--fw-medium); text-align: end; }
  .ds.ds--cards td[data-label]::before { justify-self: start; text-align: start; font-weight: var(--fw-regular); }
  .ds--cards td .btn { justify-self: end; }
  .ds--cards td:last-child { padding-block-end: 0; }

  /* An event card is time and state on one line, then what happened, then who. */
  .ds.ds--events tbody tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-2) var(--space-3); align-items: center; }
  .ds.ds--events tbody th { grid-column: 1; margin: 0; padding: 0; border: 0; font-weight: var(--fw-regular); color: var(--ink-muted); font-size: var(--fs-caption); }
  .ds.ds--events td { display: block; padding: 0; text-align: start; font-weight: var(--fw-regular); }
  .ds.ds--events td[data-label]::before { content: none; }
  .ds.ds--events td[data-label="وضعیت"] { grid-column: 2; grid-row: 1; }
  .ds.ds--events td[data-label="رویداد"] { grid-column: 1 / -1; font-weight: var(--fw-semibold); font-size: var(--fs-body-md); }
  .ds.ds--events td[data-label="کاربر و واحد"] { grid-column: 1 / -1; color: var(--ink-muted); font-size: var(--fs-caption); }
}

/* A filter that is a row of choices scrolls sideways on a handset instead of
   wrapping into a ragged block; «show all» only appears once something is
   filtered out. */
@media (max-width: 767px) {
  .toolbar > .seg { max-inline-size: 100%; overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; }
  .toolbar > .seg .seg__btn { flex: none; white-space: nowrap; }
}
.toolbar:has([data-ops-kind="all"][aria-pressed="true"]) [data-ops-kind-reset] { display: none; }

/* Period filter: the name over the field, the action beside it. */
.toolbar--period { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--space-3); align-items: end; }
.toolbar--period .field { display: grid; gap: var(--space-1); }
.toolbar--period .select { inline-size: 100%; }
.toolbar--period .btn { min-block-size: 44px; }
@media (min-width: 768px) {
  .toolbar--period { display: flex; flex-wrap: wrap; }
  .toolbar--period .select { inline-size: auto; min-inline-size: 12rem; }
}

/* A long feed line is cut to two lines; the button opens the rest. */
.feed__text--clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.feed__more { justify-self: start; padding: 0; background: none; border: 0; color: var(--accent); font: inherit; font-size: var(--fs-caption); cursor: pointer; text-decoration: underline; text-underline-offset: 3px; min-block-size: 32px; }

/* The two channel cards: the name over its value so a long value has the whole
   width of the card, and a cell of the delivery log keeps its time and channel
   on one line with the result's badge and retry side by side. */
.grid--panels .kv__row { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
.grid--panels .kv__key { font-size: var(--fs-caption); }
@media (min-width: 768px) {
  .ops-table th[scope="row"], .ops-table td[data-label="کانال"] { white-space: nowrap; }
  .ops-table td[data-label="نتیجه"] .btn { display: flex; margin-block-start: var(--space-2); }
}


/* On a tablet the connector table is narrow: the credential cell keeps its
   dots and drops the note (the line under the table says the same), and a
   record's name gets room before it wraps. */
@media (min-width: 768px) and (max-width: 1023px) {
  .ds--cards .data-secret__note { display: none; }
  .ds--cards tbody th[scope="row"] { min-inline-size: 8rem; }
}

/* A search field with its filter buttons: the field's label sits above the
   input and the buttons are as tall as the input, so all three share one
   baseline. On a handset the field takes its own row and the buttons share the
   next one. */
.toolbar--search { align-items: flex-end; }
.toolbar--search .field--inline { flex-direction: column; align-items: stretch; gap: var(--space-2); flex: 1 1 18rem; }
.toolbar--search .field--inline .input { flex: 0 0 auto; inline-size: 100%; }
.toolbar--search .filters { flex: 0 0 auto; margin: 0; gap: var(--space-3); }
.toolbar--search .field--inline .input,
.toolbar--search .filters .btn { min-block-size: 44px; }
@media (max-width: 767px) {
  .toolbar--search .field--inline { flex-basis: 100%; }
  .toolbar--search .filters { flex: 1 1 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto; }
  .ds--cards .data-secret__note { display: none; }
  .ds.ds--cards td[data-label="عملیات"] { grid-template-columns: minmax(0, 1fr); padding-block-start: var(--space-3); }
  .ds.ds--cards td[data-label="عملیات"]::before { display: none; }
  .ds.ds--cards td[data-label="عملیات"] .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
}

/* The API consumers and the rules that bind them are two parts of one section:
   a gap and a title for the second part so it does not read as a stray card. */
.api-rules { margin-block-start: var(--space-4); }

/* A card that follows another block directly keeps the section's rhythm. */
.table-wrap + .card,
.card--follow { margin-block-start: var(--space-4); }
.data-state-panel > .table-wrap + .card { margin-block-start: var(--space-4); }

/* A rule's long wording gets the whole card width: the label above, the text
   below in a regular weight, instead of a bold block beside a narrow label. */
@media (max-width: 767px) {
  .ds.ds--cards td[data-label="دامنه"],
  .ds.ds--cards td[data-label="کاری که انجام می‌شود"] { grid-template-columns: minmax(0, 1fr); justify-items: start; text-align: start; font-weight: var(--fw-regular); }
}

/* The formula sits under a KPI's name in the list: small, left-to-right, and
   allowed to wrap so a long one never widens the table. */
.kpi-formula { display: block; margin-block-start: var(--space-1); font-size: var(--fs-caption); font-weight: var(--fw-regular); color: var(--ink-muted); overflow-wrap: break-word; }
.card-follow { margin-block-start: var(--gap-cards); }

/* A long form split into short, titled groups: each group is a few related
   fields under one plain question, so the form reads as steps and not one
   wall of inputs. */
.form-group { margin-block-end: var(--space-6); }
.form-group__title { margin: 0 0 var(--space-4); padding-block-end: var(--space-2); border-block-end: 1px solid var(--border); font-size: var(--fs-body-md); font-weight: var(--fw-semibold); }
.form-grid--tidy > .field--full { grid-column: 1 / -1; }
.choices--grid { grid-template-columns: minmax(0, 1fr); }
.choices--grid .choice { margin: 0; align-items: center; }
@media (min-width: 768px) {
  .choices--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.kpi-pick { margin-block-end: var(--gap-cards); }
.kpi-pick .field__hint { margin-block-start: var(--space-3); }

/* The lifecycle record's two cards each take the height of their own content
   instead of stretching to the taller one. */
#lifecycle .grid--panels { align-items: start; }

/* The queue's counters, then its filter row on one line with the same height:
   search, state and apply. */
.stat-grid + [data-state-host] { margin-block-start: var(--space-5); }
.toolbar--queue { align-items: stretch; }
.toolbar--queue .searchbox { flex: 1 1 16rem; min-block-size: 44px; }
.toolbar--queue .select,
.toolbar--queue .btn { min-block-size: 44px; }
.toolbar--queue .toolbar__spacer { margin-inline-start: 0; display: flex; }
@media (max-width: 767px) {
  .toolbar--queue .searchbox,
  .toolbar--queue .select,
  .toolbar--queue .toolbar__spacer { flex: 1 1 100%; inline-size: 100%; }
  .toolbar--queue .toolbar__spacer .btn { inline-size: 100%; justify-content: center; }
}
.section-card + .data-impact { margin-block-start: var(--space-4); }
#lifecycle .grid--panels .kv__row { grid-template-columns: minmax(0, 7rem) minmax(0, 1fr); gap: var(--space-3); }
@media (max-width: 767px) {
  #lifecycle .grid--panels .kv__row { grid-template-columns: minmax(0, 1fr); gap: var(--space-1); }
}

/* Row actions in a table: a primary small button and a quiet one side by side;
   on a handset they share the card's last row at equal width. */
.row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); }
@media (max-width: 767px) {
  .row-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); inline-size: 100%; }
  .row-actions .btn { inline-size: 100%; min-block-size: 44px; justify-content: center; border: 1px solid var(--border-strong); }
}
