/* =============================================================================
   Win-DSL ISP Billing: components  (components.css, spec v1.0, 2026-10-17)
   Requires tokens.css. Vanilla CSS, no build step. Arabic RTL first; every rule
   uses logical properties so LTR (English) works by flipping <html dir> only.

   Class prefix "fn-" so new components can coexist with legacy style.css classes
   (.btn, .card, .tabs, .modal…) during the phased migration (SPEC §11).
   ARIA contract for each component is in the comment above it and in SPEC §6.

   Sections
     0  Base: focus-visible, utilities, icons, numbers/bidi, reduced motion
     1  Buttons            2  Badges, tags, counters     3  Form fields
     4  Tables (+ mobile 2-line rows, pager)            5  Cards, KPI, alerts, empty, skeleton, banner, meter
     6  Tabs & segmented   7  Dialog / bottom sheet / confirm   8  Menu (More / ⋯)
     9  Toasts             10 App shell: sidebar groups, rail, top bar, command bar
     11 Bottom bar + More sheet                         12 Page header, entity header, action bar
   ========================================================================== */

/* ---------------------------------------------------------------- 0 BASE */
html { -webkit-text-size-adjust: 100%; }
body.fn-ui {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-body);
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}
.fn-ui *, .fn-ui *::before, .fn-ui *::after { box-sizing: border-box; }

/* Focus: one ring for every interactive element (audit #23: 0 :focus-visible today).
   Applies to legacy markup too, so it ships in phase 1. Never remove without replacement. */
:where(a[href], button, input, select, textarea, summary, [tabindex], [contenteditable="true"], [role="button"], [role="tab"], [role="menuitem"], [role="option"], [role="switch"]):focus-visible {
  outline: var(--focus-outline);
  outline-offset: var(--focus-offset);
}
/* Inputs: show the ring on any focus (keyboard or pointer) plus the brand border. */
:where(input:not([type="checkbox"], [type="radio"]), select, textarea):focus {
  outline: var(--focus-outline);
  outline-offset: 1px;
  border-color: var(--color-brand);
}
/* Elements inside overflow:hidden containers use the box-shadow ring instead. */
.fn-focus-inset:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--color-focus); }

.fn-visually-hidden:not(:focus):not(:active) {
  position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.fn-skip-link { position: absolute; inset-inline-start: var(--space-2); top: var(--space-2); z-index: var(--z-tooltip);
  background: var(--color-surface); color: var(--color-brand-text); padding: var(--space-2) var(--space-4); border-radius: var(--radius-md); box-shadow: var(--shadow-2); }
.fn-skip-link:not(:focus) { transform: translateY(-200%); }

/* Numbers / IDs / IP / MAC / login / phone / money: LTR-isolated, tabular. Use <bdi class="fn-num"> or <span class="fn-num">. */
.fn-num { font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate; display: inline-block; }
.fn-ltr { direction: ltr; unicode-bidi: isolate; }
.fn-mono { font-family: var(--font-mono); font-size: .92em; }

/* Icons: inline SVG from the single 24-px stroke set (mockups/assets/icons.js).
   Decorative by default: <svg class="fn-icon" aria-hidden="true" focusable="false">.
   Directional ones (chevrons, arrows, back/next) add .fn-icon--dir. */
.fn-icon { width: var(--icon-md); height: var(--icon-md); flex: 0 0 auto; stroke: currentColor; fill: none;
  stroke-width: var(--icon-stroke); stroke-linecap: round; stroke-linejoin: round; vertical-align: middle; }
.fn-icon--sm { width: var(--icon-sm); height: var(--icon-sm); }
.fn-icon--lg { width: var(--icon-lg); height: var(--icon-lg); }
[dir="rtl"] .fn-icon--dir { scale: -1 1; }

.fn-text-muted { color: var(--color-text-muted); }
.fn-text-xs { font-size: var(--text-xs); } .fn-text-sm { font-size: var(--text-sm); }
.fn-stack { display: flex; flex-direction: column; gap: var(--space-4); }
.fn-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.fn-spacer { flex: 1; }
@media (max-width: 760px) { .fn-hide-mobile { display: none !important; } }
.fn-only-mobile { display: none !important; } @media (max-width: 760px) { .fn-only-mobile { display: revert !important; } }
.fn-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)); }
@media (max-width: 1180px) { .fn-grid { grid-template-columns: repeat(min(var(--cols, 4), 2), minmax(0, 1fr)); } }
@media (max-width: 760px)  { .fn-grid--collapse { grid-template-columns: 1fr; } }

.fn-kbd { font: var(--weight-semibold) var(--text-xs)/1.6 var(--font-sans); direction: ltr; padding: 0 var(--space-1);
  border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text-muted); }
@media (pointer: coarse) { .fn-kbd { display: none; } }

.fn-avatar { inline-size: 36px; block-size: 36px; border-radius: var(--radius-full); display: grid; place-items: center;
  font-weight: var(--weight-bold); font-size: var(--text-sm); background: var(--color-brand-bg); color: var(--color-brand-on); flex: 0 0 auto; }
.fn-avatar--lg { inline-size: 56px; block-size: 56px; font-size: var(--text-xl); }

@media (prefers-reduced-motion: reduce) {
  .fn-ui *, .fn-ui *::before, .fn-ui *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}

/* ------------------------------------------------------------- 1 BUTTONS
   <button class="fn-btn fn-btn--primary" type="button">…</button>
   - ONE primary per view (per screen on mobile).
   - Icon-only: .fn-btn--icon + aria-label (required). Icon inside gets aria-hidden.
   - Destructive (--danger / --danger-outline) never fires directly: opens a confirm dialog (§7).
   - Disabled: prefer aria-disabled="true" + a reason (title/hint) so it stays focusable;
     [disabled] is allowed for form submit while busy.
   - Busy: aria-busy="true" shows a spinner; keep the label (screen readers announce via the toast). */
.fn-btn {
  --_bg: var(--color-surface); --_fg: var(--color-brand-text); --_bd: var(--color-border-strong); --_bg-hover: var(--color-brand-soft);
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  min-block-size: var(--control-h-md); padding-inline: var(--space-4); padding-block: 0;
  border: 1px solid var(--_bd); border-radius: var(--radius-md); background: var(--_bg); color: var(--_fg);
  font: inherit; font-size: var(--text-sm); font-weight: var(--weight-semibold); line-height: var(--leading-tight);
  white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none; position: relative;
  transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.fn-btn:hover { background: var(--_bg-hover); }
.fn-btn:active { translate: 0 1px; }
.fn-btn--primary   { --_bg: var(--color-brand); --_fg: var(--color-text-on-brand); --_bd: transparent; --_bg-hover: var(--color-brand-hover); }
.fn-btn--secondary { /* = default */ }
.fn-btn--tertiary  { --_bg: transparent; --_bd: transparent; --_bg-hover: var(--color-brand-soft); padding-inline: var(--space-3); }
.fn-btn--danger    { --_bg: var(--color-bad-solid); --_fg: var(--color-text-on-solid); --_bd: transparent; --_bg-hover: var(--color-bad-fg); }
.fn-btn--danger-outline { --_fg: var(--color-bad-fg); --_bd: var(--color-bad-border); --_bg-hover: var(--color-bad-bg); }
.fn-btn--accent    { --_bg: var(--color-warn-solid); --_fg: var(--color-warn-solid-on); --_bd: transparent; --_bg-hover: var(--color-warn-solid-hover); }
.fn-btn--success   { --_bg: var(--color-ok-solid); --_fg: var(--color-text-on-solid); --_bd: transparent; --_bg-hover: var(--color-ok-on); }
.fn-btn--sm { min-block-size: var(--control-h-sm); padding-inline: var(--space-3); font-size: var(--text-sm); }
.fn-btn--lg { min-block-size: var(--control-h-lg); padding-inline: var(--space-5); font-size: var(--text-md); border-radius: 12px; }
.fn-btn--xl { min-block-size: var(--control-h-xl); padding-inline: var(--space-6); font-size: var(--text-lg); border-radius: var(--radius-lg); }
.fn-btn--block { inline-size: 100%; }
.fn-btn--icon { inline-size: var(--control-h-md); padding-inline: 0; }
.fn-btn--icon.fn-btn--sm { inline-size: var(--control-h-sm); }
.fn-btn--icon.fn-btn--lg { inline-size: var(--control-h-lg); }
.fn-btn:is([disabled], [aria-disabled="true"]) { --_bg: var(--color-neutral-bg); --_fg: var(--color-text-disabled); --_bd: var(--color-border); --_bg-hover: var(--color-neutral-bg); cursor: not-allowed; translate: none; }
.fn-btn[aria-busy="true"] { pointer-events: none; color: transparent; }
.fn-btn[aria-busy="true"]::after { content: ""; position: absolute; inset: 0; margin: auto; inline-size: 18px; block-size: 18px;
  border-radius: 50%; border: 2px solid var(--_fg); border-inline-end-color: transparent; animation: fn-spin .7s linear infinite; }
@keyframes fn-spin { to { rotate: 1turn; } }
/* Touch devices: every control reaches the 44/48-px target (audit #25: .btn.sm was ~28px). */
@media (pointer: coarse) {
  .fn-btn { min-block-size: var(--target-touch); }
  .fn-btn--sm { min-block-size: var(--target-min); }
  .fn-btn--icon { inline-size: var(--target-touch); } .fn-btn--icon.fn-btn--sm { inline-size: var(--target-min); }
}
.fn-btn-group { display: inline-flex; gap: var(--space-2); flex-wrap: wrap; align-items: center; }

/* ---------------------------------------------- 2 BADGES, TAGS, COUNTERS
   Status is NEVER colour-only: every badge has text, and subscription/money badges an icon.
   <span class="fn-badge fn-badge--ok"><svg class="fn-icon" aria-hidden="true">…</svg>نشط</span>
   Dot variant for connection state: .fn-badge--dot (the dot is decorative; text carries meaning).
   Counters in nav/tabs: <span class="fn-count fn-count--bad">3<span class="fn-visually-hidden"> تذاكر متأخرة</span></span> */
.fn-badge { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: var(--radius-full);
  font-size: var(--text-xs); font-weight: var(--weight-semibold); line-height: 1; white-space: nowrap;
  background: var(--color-neutral-bg); color: var(--color-neutral-on); }
.fn-badge .fn-icon { inline-size: 14px; block-size: 14px; stroke-width: 2.2; }
.fn-badge--ok { background: var(--color-ok-bg); color: var(--color-ok-on); }
.fn-badge--warn { background: var(--color-warn-bg); color: var(--color-warn-on); }
.fn-badge--bad { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-badge--info { background: var(--color-info-bg); color: var(--color-info-on); }
.fn-badge--brand { background: var(--color-brand-bg); color: var(--color-brand-on); }
.fn-badge--violet { background: var(--color-violet-bg); color: var(--color-violet-on); }
.fn-badge--neutral { background: var(--color-neutral-bg); color: var(--color-neutral-on); }
.fn-badge--dot::before { content: ""; inline-size: 7px; block-size: 7px; border-radius: 50%; background: currentColor; }
.fn-badge--md { font-size: var(--text-sm); padding: 6px 12px; }
/* Secondary technical code next to a plain-language label, e.g. "راوتر غير مسجّل" + mac_mismatch */
.fn-code-tag, .fn-tag { display: inline-block; font-size: var(--text-xs); font-weight: var(--weight-semibold); line-height: 1.5;
  padding: 0 7px; border: 1px solid var(--color-border-strong); border-radius: var(--radius-sm); color: var(--color-text-secondary); background: var(--color-surface); }
.fn-code-tag { direction: ltr; unicode-bidi: isolate; font-family: var(--font-mono); }
.fn-count { display: inline-grid; place-items: center; min-inline-size: 20px; block-size: 20px; padding-inline: 6px; border-radius: var(--radius-full);
  font-size: var(--text-xs); font-weight: var(--weight-bold); line-height: 1; font-variant-numeric: tabular-nums;
  background: var(--color-neutral-bg); color: var(--color-neutral-on); }
.fn-count--bad { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-count--warn { background: var(--color-warn-bg); color: var(--color-warn-on); }
.fn-count--solid { background: var(--color-bad-solid); color: var(--color-text-on-solid); box-shadow: 0 0 0 2px var(--color-surface); }

/* ------------------------------------------------------- 3 FORM FIELDS
   <div class="fn-field">
     <label class="fn-field__label" for="amt">المبلغ المدفوع <span class="fn-field__req" aria-hidden="true">*</span></label>
     <input class="fn-input" id="amt" inputmode="decimal" required aria-describedby="amt-hint amt-err" aria-invalid="true">
     <p class="fn-field__hint" id="amt-hint">…</p>
     <p class="fn-field__error" id="amt-err">…</p>   (rendered only when invalid)
   </div>
   - Visible label always (placeholder is never the label; search fields get a visually-hidden label).
   - Required = native `required` + the visual asterisk; a legend "* إلزامي" once per form.
   - Errors: text + icon, aria-invalid on the control, focus moves to the first invalid field on submit. */
.fn-field { display: flex; flex-direction: column; gap: 6px; min-inline-size: 0; }
.fn-field__label { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text-secondary); }
.fn-field__req { color: var(--color-bad-fg); margin-inline-start: 2px; }
.fn-field__hint { margin: 0; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-field__error { margin: 0; display: flex; gap: var(--space-1); align-items: center; font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-bad-fg); }
.fn-input, .fn-select > select, .fn-textarea {
  inline-size: 100%; min-block-size: var(--input-h); padding: 0 var(--space-3);
  font: inherit; font-size: var(--text-md); color: var(--color-text); background: var(--color-surface);
  border: 1px solid var(--color-border-control); border-radius: var(--radius-md);
}
.fn-textarea { padding-block: var(--space-2); min-block-size: 96px; resize: vertical; line-height: var(--leading-body); }
.fn-input::placeholder, .fn-textarea::placeholder { color: var(--color-text-subtle); opacity: 1; }
.fn-input[aria-invalid="true"], .fn-textarea[aria-invalid="true"], .fn-select > select[aria-invalid="true"] { border-color: var(--color-bad-fg); box-shadow: 0 0 0 3px var(--color-bad-bg); }
.fn-input:disabled, .fn-textarea:disabled, .fn-select > select:disabled { background: var(--color-surface-sunken); color: var(--color-text-muted); cursor: not-allowed; }
.fn-input[readonly] { background: var(--color-surface-sunken); }
.fn-input--num { font-variant-numeric: tabular-nums; direction: ltr; text-align: end; }  /* amounts, IPs, MACs; [dir=rtl] keeps it right-aligned */
/* Select with a logical chevron (background-position is physical, so use a pseudo-element). */
.fn-select { position: relative; display: block; }
.fn-select > select { appearance: none; padding-inline-end: var(--space-10); }
.fn-select::after { content: ""; position: absolute; inset-inline-end: var(--space-3); top: 50%; inline-size: 8px; block-size: 8px;
  border: solid var(--color-text-muted); border-width: 0 2px 2px 0; rotate: 45deg; translate: 0 -70%; pointer-events: none; }
/* Input with leading icon / trailing unit or kbd hint */
.fn-input-group { display: flex; align-items: center; gap: var(--space-2); min-block-size: var(--input-h); padding-inline: var(--space-3);
  border: 1px solid var(--color-border-control); border-radius: var(--radius-md); background: var(--color-surface); }
.fn-input-group:focus-within { outline: var(--focus-outline); outline-offset: 1px; border-color: var(--color-brand); }
.fn-input-group > input { flex: 1; min-inline-size: 0; border: 0; background: transparent; font: inherit; font-size: var(--text-md); color: inherit; min-block-size: calc(var(--input-h) - 2px); padding: 0; }
.fn-input-group > input:focus { outline: none; }
.fn-input-group > .fn-icon { color: var(--color-text-muted); }
.fn-input-group__unit { font-size: var(--text-sm); color: var(--color-text-muted); }
/* Checkbox / radio rows: 20px control inside a ≥44px tappable label */
.fn-check { display: flex; align-items: center; gap: var(--space-3); min-block-size: var(--target-min); font-size: var(--text-sm); cursor: pointer; }
.fn-check > input:not(.fn-switch) { inline-size: 20px; block-size: 20px; margin: 0; accent-color: var(--color-brand); flex: 0 0 auto; }
/* Switch: <input type="checkbox" role="switch" class="fn-switch"> */
.fn-switch { appearance: none; inline-size: 44px; block-size: 26px; border-radius: var(--radius-full); background: var(--color-border-control);
  position: relative; cursor: pointer; margin: 0; flex: 0 0 auto; transition: background-color var(--dur-fast); }
.fn-switch::before { content: ""; position: absolute; inset-block-start: 3px; inset-inline-start: 3px; inline-size: 20px; block-size: 20px; border-radius: 50%; background: var(--color-surface); box-shadow: var(--shadow-1); transition: inset-inline-start var(--dur-fast) var(--ease-out); }
.fn-switch:checked { background: var(--color-brand); }
.fn-switch:checked::before { inset-inline-start: 21px; }
.fn-form-grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.fn-form-legend { font-size: var(--text-xs); color: var(--color-text-muted); }

/* ------------------------------------------------------------- 4 TABLES
   Desktop: essential columns only, ONE visible action + ⋯ menu, paging (no 300-5000-row client lists).
   <div class="fn-table-wrap" role="region" aria-labelledby="t-cap" tabindex="0">
     <table class="fn-table fn-table--list">
       <caption id="t-cap" class="fn-visually-hidden">المشتركون</caption>
       <thead><tr><th scope="col" aria-sort="ascending"><button class="fn-th-sort">الاسم …</button></th>…
       <tbody><tr><td data-m="lead">…</td><td data-m="primary"><a class="fn-row-link" href="#/subs/S-1042/overview">سامر</a></td>…
   Sortable headers: a <button> inside <th>; aria-sort only on the currently sorted column.
   Mobile (≤760px) .fn-table--list → 2-line list rows (replaces cardTables() 10-field cards, audit #8/#28):
       ┌ lead ┐ primary ………………………… end1 ┐   line 1: name (+tag)            | amount or status
       └──────┘ meta    ………………………… end2 ┘   line 2: login/address + badge  | one action
     Cells WITHOUT data-m are hidden on mobile (still in the detail page). Sort and filters move
     to the "Filters" sheet; bulk select uses data-m="lead" checkboxes with aria-label per row. */
.fn-table-wrap { overflow-x: auto; border-radius: inherit; }
.fn-table-wrap:focus-visible { outline-offset: -2px; }
.fn-table { inline-size: 100%; border-collapse: collapse; font-size: var(--text-sm); line-height: var(--leading-compact); }
.fn-table th { text-align: start; font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-text-muted);
  background: var(--color-surface-sunken); padding: var(--space-2) var(--space-3); border-block-end: 1px solid var(--color-border); white-space: nowrap;
  position: sticky; top: 0; z-index: var(--z-raised); }
.fn-table td { padding: var(--space-3); border-block-end: 1px solid var(--color-border); vertical-align: middle; }
.fn-table tbody tr { position: relative; }
.fn-table tbody tr:hover td { background: var(--color-surface-hover); }
.fn-table tr[aria-selected="true"] td, .fn-table tr.is-highlight td { background: var(--color-brand-soft); }
.fn-table tfoot td { font-weight: var(--weight-bold); border-block-start: 2px solid var(--color-border-strong); }
.fn-table .fn-cell-num { text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.fn-table .fn-cell-actions { text-align: end; white-space: nowrap; inline-size: 1%; }
.fn-table .fn-cell-actions .fn-btn-group { flex-wrap: nowrap; justify-content: flex-end; position: relative; z-index: var(--z-raised); }
.fn-table .fn-cell-check { inline-size: 1%; }
.fn-th-sort { all: unset; display: inline-flex; align-items: center; gap: var(--space-1); cursor: pointer; border-radius: var(--radius-sm); min-block-size: 28px; }
.fn-th-sort:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }
.fn-th-sort::after { content: "↕"; font-size: var(--text-xs); color: var(--color-text-subtle); }
th[aria-sort="ascending"] .fn-th-sort::after { content: "↑"; color: var(--color-brand-text); }
th[aria-sort="descending"] .fn-th-sort::after { content: "↓"; color: var(--color-brand-text); }
/* Whole-row click target via the primary link (keyboard reaches it as a normal link). */
.fn-row-link { color: inherit; font-weight: var(--weight-semibold); text-decoration: none; }
.fn-row-link::after { content: ""; position: absolute; inset: 0; }
.fn-row-link:focus-visible { outline: none; }
.fn-row-link:focus-visible::after { outline: var(--focus-outline); outline-offset: -3px; border-radius: var(--radius-sm); }
.fn-row-link:hover { text-decoration: underline; }
.fn-cell-sub { display: block; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-table-empty td { text-align: center; padding: var(--space-8) var(--space-4); }
.fn-table-toolbar { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--color-border); }
.fn-pager { display: flex; align-items: center; gap: var(--space-2); justify-content: space-between; padding: var(--space-3) var(--space-4); font-size: var(--text-sm); color: var(--color-text-muted); }

@media (max-width: 760px) {
  .fn-table--list, .fn-table--list tbody { display: block; }
  .fn-table--list thead, .fn-table--list caption + thead { display: none; }
  .fn-table--list tbody tr {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "lead primary end1" "lead meta end2";
    column-gap: var(--space-3); row-gap: 2px; align-items: center;
    padding: var(--space-3); min-block-size: 64px;
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); margin-block-end: var(--space-2);
  }
  .fn-table--list td, .fn-table--list td.fn-cell-actions, .fn-table--list td.fn-cell-check { display: none; padding: 0; border: 0; min-inline-size: 0; inline-size: auto; }
  .fn-table--list td[data-m="primary"] .fn-cell-sub { display: none; } /* line 2 is the meta cell: put the ONE secondary datum there */
  .fn-table--list td[data-m] { display: block; }
  .fn-table--list td[data-m="lead"] { grid-area: lead; align-self: center; }
  .fn-table--list td[data-m="primary"] { grid-area: primary; font-size: var(--text-md); font-weight: var(--weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .fn-table--list td[data-m="meta"] { grid-area: meta; font-size: var(--text-sm); color: var(--color-text-muted); display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
  .fn-table--list td[data-m="end1"] { grid-area: end1; justify-self: end; font-weight: var(--weight-bold); font-variant-numeric: tabular-nums; }
  .fn-table--list td[data-m="end2"] { grid-area: end2; justify-self: end; position: relative; z-index: var(--z-raised); }
  .fn-table--list tbody tr:hover td { background: none; }
  .fn-table--list tfoot { display: block; }
  .fn-table--list tfoot tr { display: flex; justify-content: space-between; padding: var(--space-3); background: var(--color-brand-soft); border-radius: var(--radius-lg); }
  .fn-table--list tfoot td { border: 0; padding: 0; }
  .fn-table--list .fn-table-empty { display: block; }
  .fn-table--list .fn-table-empty td { display: block; }
  .fn-pager { flex-wrap: wrap; justify-content: center; }
}

/* ---------------------- 5 CARDS, KPI, ALERT ITEMS, EMPTY, SKELETON, BANNER, METER
   Card = <section class="fn-card" aria-labelledby="h-id"> with an <h2|h3 class="fn-card__title" id="h-id">.
   KPI tiles that navigate are <a class="fn-kpi" href="#/money/debts?state=overdue"> (never div onclick, audit #22).
   Empty state = reason + next step + one CTA (audit #15). Loading = skeleton + aria-busy on the region. */
.fn-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: var(--shadow-1); min-inline-size: 0; }
.fn-card__header { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--color-border); min-block-size: 52px; }
.fn-card__title { margin: 0; font-size: var(--text-md); font-weight: var(--weight-semibold); line-height: var(--leading-heading); }
.fn-card__body { padding: var(--space-4); }
.fn-card__footer { padding: var(--space-3) var(--space-4); border-block-start: 1px solid var(--color-border); display: flex; gap: var(--space-2); justify-content: flex-end; }
.fn-card--flush .fn-card__body { padding: 0; }
.fn-card--bad { border-color: var(--color-bad-border); }
.fn-kv { display: flex; justify-content: space-between; gap: var(--space-3); padding-block: var(--space-2); border-block-end: 1px solid var(--color-border); font-size: var(--text-sm); }
.fn-kv:last-child { border-block-end: 0; }
.fn-kv > dt, .fn-kv > :first-child { color: var(--color-text-muted); margin: 0; }
.fn-kv > dd, .fn-kv > :last-child { margin: 0; font-weight: var(--weight-semibold); text-align: end; }
.fn-kpi { display: flex; flex-direction: column; gap: 6px; padding: var(--space-4); color: inherit; text-decoration: none;
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: var(--shadow-1); }
a.fn-kpi:hover { border-color: var(--color-brand); }
.fn-kpi__label { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text-muted); }
.fn-kpi__value { font-size: var(--text-kpi); font-weight: var(--weight-bold); line-height: var(--leading-tight); font-variant-numeric: tabular-nums; }
.fn-kpi__delta { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-text-muted); }
.fn-kpi--bad { border-color: var(--color-bad-border); } .fn-kpi--bad .fn-kpi__value { color: var(--color-bad-fg); }
.fn-up { color: var(--color-ok-fg); } .fn-down { color: var(--color-bad-fg); }
.fn-icon-tile { inline-size: 36px; block-size: 36px; border-radius: var(--radius-md); display: grid; place-items: center; flex: 0 0 auto; background: var(--color-brand-soft); color: var(--color-brand-text); }
.fn-icon-tile--ok { background: var(--color-ok-bg); color: var(--color-ok-on); }
.fn-icon-tile--warn { background: var(--color-warn-bg); color: var(--color-warn-on); }
.fn-icon-tile--bad { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-icon-tile--info { background: var(--color-info-bg); color: var(--color-info-on); }
.fn-icon-tile--violet { background: var(--color-violet-bg); color: var(--color-violet-on); }
.fn-icon-tile--neutral { background: var(--color-neutral-bg); color: var(--color-neutral-on); }
/* Actionable alert / task item: icon + text + ONE verb button (home "Needs action", Today inbox) */
.fn-alert-list { list-style: none; margin: 0; padding: 0; }
.fn-alert-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-block-end: 1px solid var(--color-border); min-block-size: 64px; position: relative; }
.fn-alert-item:last-child { border-block-end: 0; }
.fn-alert-item__text { flex: 1; min-inline-size: 0; }
.fn-alert-item__title { display: block; font-weight: var(--weight-semibold); font-size: var(--text-sm); }
.fn-alert-item__meta { display: block; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-2); padding: var(--space-8) var(--space-4); }
.fn-empty__icon { inline-size: 44px; block-size: 44px; border-radius: var(--radius-md); display: grid; place-items: center; background: var(--color-ok-bg); color: var(--color-ok-on); }
.fn-empty__title { margin: 0; font-size: var(--text-md); font-weight: var(--weight-semibold); }
.fn-empty__text { margin: 0 0 var(--space-2); font-size: var(--text-sm); color: var(--color-text-muted); max-inline-size: 40ch; }
.fn-skeleton { display: block; block-size: 1em; border-radius: var(--radius-sm); background: linear-gradient(90deg, var(--color-neutral-bg) 25%, var(--color-surface-sunken) 50%, var(--color-neutral-bg) 75%); background-size: 200% 100%; animation: fn-shimmer 1.2s linear infinite; }
@keyframes fn-shimmer { to { background-position: -200% 0; } }
/* Banner: inline message in page flow. role="status" (info/ok/warn) or role="alert" (blocking error). */
.fn-banner { display: flex; gap: var(--space-3); align-items: flex-start; padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  background: var(--color-info-bg); color: var(--color-info-on); font-size: var(--text-sm); border-inline-start: 4px solid currentColor; }
.fn-banner--ok { background: var(--color-ok-bg); color: var(--color-ok-on); }
.fn-banner--warn { background: var(--color-warn-bg); color: var(--color-warn-on); }
.fn-banner--bad { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-banner__body { flex: 1; } .fn-banner__title { font-weight: var(--weight-bold); display: block; }
/* Meter: <div class="fn-meter" role="meter" aria-valuemin="0" aria-valuemax="300" aria-valuenow="182" aria-valuetext="182 من 300 GB"><i style="--v:61%"></i></div> */
.fn-meter { block-size: 8px; border-radius: var(--radius-full); background: var(--color-neutral-bg); overflow: hidden; }
.fn-meter > i { display: block; block-size: 100%; inline-size: var(--v, 0%); border-radius: inherit; background: var(--color-brand); }
.fn-meter--warn > i { background: var(--color-warn-solid); } .fn-meter--bad > i { background: var(--color-bad-solid); }
/* Timeline (recent activity, ledger) */
.fn-timeline { list-style: none; margin: 0; padding: 0; }
.fn-timeline > li { display: flex; gap: var(--space-3); align-items: center; padding-block: var(--space-2); border-block-end: 1px solid var(--color-border); font-size: var(--text-sm); flex-wrap: wrap; }
.fn-timeline > li:last-child { border-block-end: 0; }
.fn-timeline time { inline-size: 92px; flex: 0 0 auto; color: var(--color-text-muted); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------- 6 TABS, SEGMENTED
   URL-addressable tabs (SPEC §5): each tab is a real link to its own hash.
   <div class="fn-tabs" role="tablist" aria-label="أقسام المشترك">
     <a class="fn-tab" role="tab" id="tab-overview" href="#/subs/S-1042/overview" aria-selected="true" aria-controls="panel" tabindex="0">نظرة عامة</a>
     <a class="fn-tab" role="tab" id="tab-usage" href="#/subs/S-1042/usage" aria-selected="false" aria-controls="panel" tabindex="-1">الاستخدام</a>
   </div>
   <section id="panel" class="fn-tabpanel" role="tabpanel" aria-labelledby="tab-overview" tabindex="0">…</section>
   Keyboard: ←/→ move focus (direction-aware: in RTL, ArrowLeft = next), Home/End, Enter/Space activates
   (= navigates to the hash; manual activation, because activation fetches data). Roving tabindex.
   Segmented control = native radio group (filters/currency/period inside one view, NOT navigation). */
.fn-tabs { display: flex; gap: var(--space-1); border-block-end: 1px solid var(--color-border); padding-inline: var(--space-4);
  overflow-x: auto; scrollbar-width: none; scroll-snap-type: x proximity; }
.fn-tabs::-webkit-scrollbar { display: none; }
.fn-tab { display: inline-flex; align-items: center; gap: 6px; min-block-size: var(--target-touch); padding-inline: var(--space-3);
  font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text-muted); text-decoration: none; white-space: nowrap;
  border-block-end: 2px solid transparent; margin-block-end: -1px; scroll-snap-align: start; border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
.fn-tab:hover { color: var(--color-text); background: var(--color-surface-hover); }
.fn-tab[aria-selected="true"], .fn-tab[aria-current="page"] { color: var(--color-brand-text); border-block-end-color: var(--color-brand); }
.fn-tab:focus-visible { outline-offset: -2px; }
.fn-tab .fn-count { min-inline-size: 18px; block-size: 18px; }
.fn-tabpanel:focus-visible { outline-offset: 4px; }
.fn-seg { display: inline-flex; align-self: flex-start; gap: 2px; padding: 3px; border-radius: var(--radius-md); background: var(--color-surface-sunken); border: 0; margin: 0; }
.fn-seg > legend { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.fn-seg label { position: relative; display: inline-flex; align-items: center; justify-content: center; min-block-size: 36px; padding-inline: var(--space-3);
  border-radius: 8px; font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--color-text-muted); cursor: pointer; flex: 1; white-space: nowrap; }
.fn-seg input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.fn-seg label:has(input:checked) { background: var(--color-surface); color: var(--color-brand-text); box-shadow: var(--shadow-1); }
.fn-seg label:has(input:focus-visible) { outline: var(--focus-outline); outline-offset: 1px; }
.fn-seg--block { display: flex; inline-size: 100%; align-self: stretch; }
@media (pointer: coarse) { .fn-seg label { min-block-size: var(--target-min); } }

/* ---------------------------------------- 7 DIALOG / BOTTOM SHEET / CONFIRM
   Use the native <dialog> + showModal(): top layer, inert background, Esc → "cancel" event.
   <dialog class="fn-dialog" aria-labelledby="dlg-t" aria-describedby="dlg-d">
     <form method="dialog" class="fn-dialog__frame">
       <header class="fn-dialog__header"><h2 id="dlg-t" class="fn-dialog__title">…</h2>
         <button class="fn-btn fn-btn--tertiary fn-btn--icon" value="cancel" aria-label="إغلاق">×svg</button></header>
       <div class="fn-dialog__body" id="dlg-d">…</div>
       <footer class="fn-dialog__footer">…secondary… …primary…</footer>
     </form>
   </dialog>
   Behaviour (SPEC §6.7): focus first field (or the title for read-only), restore focus to the opener on close,
   dirty-form guard on Esc / backdrop / close (audit #9), results stay visible after close via a toast.
   ≤760px every dialog becomes a bottom sheet; .fn-dialog--sheet forces sheet at all widths (More, Filters). */
.fn-dialog { padding: 0; border: 0; border-radius: var(--radius-xl); background: var(--color-surface); color: var(--color-text);
  box-shadow: var(--shadow-3); inline-size: min(var(--dialog-w-md), calc(100vw - 2 * var(--space-4))); max-block-size: min(88dvh, 900px); overflow: hidden; }
.fn-dialog--sm { inline-size: min(var(--dialog-w-sm), calc(100vw - 2 * var(--space-4))); }
.fn-dialog--lg { inline-size: min(var(--dialog-w-lg), calc(100vw - 2 * var(--space-4))); }
.fn-dialog::backdrop { background: var(--color-scrim); }
.fn-dialog[open] { animation: fn-dialog-in var(--dur-base) var(--ease-out); }
@keyframes fn-dialog-in { from { opacity: 0; translate: 0 8px; } }
.fn-dialog__frame { display: flex; flex-direction: column; max-block-size: inherit; margin: 0; }
.fn-dialog__header { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-4) var(--space-5) var(--space-3); border-block-end: 1px solid var(--color-border); }
.fn-dialog__title { flex: 1; margin: 0; font-size: var(--text-lg); font-weight: var(--weight-bold); line-height: var(--leading-heading); }
.fn-dialog__body { padding: var(--space-5); overflow-y: auto; overscroll-behavior: contain; flex: 1; }
.fn-dialog__footer { display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; padding: var(--space-3) var(--space-5);
  border-block-start: 1px solid var(--color-border); background: var(--color-surface); }
.fn-sheet__grab { display: none; inline-size: 40px; block-size: 5px; border-radius: 9px; background: var(--color-border-strong); margin: var(--space-2) auto 0; border: 0; padding: 0; }
@media (max-width: 760px) {
  .fn-dialog, .fn-dialog--sm, .fn-dialog--lg { inline-size: 100%; max-inline-size: 100%; margin: auto 0 0; border-radius: var(--radius-xl) var(--radius-xl) 0 0; max-block-size: 92dvh; }
  .fn-dialog[open] { animation-name: fn-sheet-in; }
  .fn-dialog__footer { padding-block-end: calc(var(--space-3) + env(safe-area-inset-bottom)); }
  .fn-dialog__footer > .fn-btn { flex: 1; min-block-size: var(--target-touch); }
  .fn-dialog__footer > .fn-btn--primary, .fn-dialog__footer > .fn-btn--danger { order: -1; flex-basis: 100%; min-block-size: var(--target-primary-mobile); font-size: var(--text-lg); }
  .fn-sheet__grab { display: block; }
}
.fn-dialog--sheet { inline-size: min(560px, 100%); margin: auto auto 0; border-radius: var(--radius-xl) var(--radius-xl) 0 0; max-block-size: 92dvh; }
.fn-dialog--sheet[open] { animation-name: fn-sheet-in; }
.fn-dialog--sheet .fn-sheet__grab { display: block; }
@keyframes fn-sheet-in { from { translate: 0 100%; } }
/* Confirm (destructive): states consequence, asks for a reason when the action is audited. */
.fn-dialog--confirm .fn-dialog__body { display: flex; flex-direction: column; gap: var(--space-3); }
.fn-confirm__consequence { display: flex; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-bad-bg); color: var(--color-bad-on); font-size: var(--text-sm); }

/* ------------------------------------------------------------- 8 MENU
   Action menu ("المزيد" on the subscriber hub, row ⋯ menus).
   <button class="fn-btn" aria-haspopup="menu" aria-expanded="false" aria-controls="m1">المزيد</button>
   <div class="fn-menu" id="m1" role="menu" aria-label="إجراءات المشترك" hidden>
     <div role="group" aria-labelledby="g1"><div class="fn-menu__label" id="g1" role="presentation">الشبكة</div>
       <button class="fn-menu__item" role="menuitem" tabindex="-1">اختبار Ping <kbd class="fn-kbd" aria-hidden="true">P</kbd></button></div>
     <hr class="fn-menu__sep" role="separator">
     <button class="fn-menu__item fn-menu__item--danger" role="menuitem">حظر المشترك…</button>
   </div>
   Keyboard: Enter/Space/↓ opens and focuses first item; ↑/↓ cycle; Home/End; type-ahead; Esc closes and returns focus;
   Tab closes. Destructive items last, red, label ends with "…" (opens a confirm dialog). On ≤760px render as a sheet. */
.fn-menu { position: absolute; z-index: var(--z-dropdown); min-inline-size: 260px; max-inline-size: 320px; padding: 6px;
  background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: var(--shadow-3); }
.fn-menu[hidden] { display: none; }
.fn-menu__label { margin: var(--space-2) var(--space-3) var(--space-1); font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-text-muted); }
.fn-menu__item { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: var(--space-3); inline-size: 100%; min-block-size: 40px;
  padding-inline: var(--space-3); border-radius: 8px; font-size: var(--text-sm); color: var(--color-text-secondary); cursor: pointer; }
.fn-menu__item:hover, .fn-menu__item:focus-visible { background: var(--color-brand-soft); color: var(--color-brand-on); }
.fn-menu__item:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--color-focus); }
.fn-menu__item .fn-kbd { margin-inline-start: auto; }
.fn-menu__item--danger { color: var(--color-bad-fg); }
.fn-menu__item--danger:hover, .fn-menu__item--danger:focus-visible { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-menu__item[aria-disabled="true"] { color: var(--color-text-disabled); cursor: not-allowed; }
.fn-menu__sep { border: 0; border-block-start: 1px solid var(--color-border); margin: 6px 4px; }
@media (pointer: coarse) { .fn-menu__item { min-block-size: var(--target-touch); } }

/* ------------------------------------------------------------- 9 TOASTS
   Two live regions exist in index.html from page load (never injected later):
     <div class="fn-toaster" id="toast-polite" role="status" aria-live="polite" aria-atomic="false"></div>
     <div class="fn-toaster" id="toast-alert" role="alert" aria-live="assertive"></div>   (errors only)
   Toast: icon + message + optional action ("تراجع", "فتح الإيصال"). Success auto-dismiss 5s (pause on hover/focus);
   errors persist until dismissed. Max 3 visible. Above the bottom bar on mobile. Never the only place a result lives. */
.fn-toaster { position: fixed; z-index: var(--z-toast); inset-inline: 0; inset-block-end: var(--space-5); display: flex; flex-direction: column; align-items: center; gap: var(--space-2); pointer-events: none; padding-inline: var(--space-4); }
.fn-toast { pointer-events: auto; display: flex; align-items: center; gap: var(--space-3); max-inline-size: min(480px, 100%); padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md); background: var(--color-toast-bg); color: var(--color-text-on-solid); box-shadow: var(--shadow-3); font-size: var(--text-sm); animation: fn-toast-in var(--dur-base) var(--ease-out); }
.fn-toast--ok { background: var(--color-ok-solid); } .fn-toast--bad { background: var(--color-bad-solid); } .fn-toast--info { background: var(--color-info-solid); }
.fn-toast--warn { background: var(--color-warn-solid); color: var(--color-warn-solid-on); }
.fn-toast__msg { flex: 1; }
.fn-toast__action { all: unset; font-weight: var(--weight-bold); text-decoration: underline; cursor: pointer; padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); min-block-size: 32px; display: inline-flex; align-items: center; }
.fn-toast__action:focus-visible, .fn-toast__close:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.fn-toast__close { all: unset; display: grid; place-items: center; inline-size: 32px; block-size: 32px; border-radius: var(--radius-sm); cursor: pointer; }
@keyframes fn-toast-in { from { opacity: 0; translate: 0 8px; } }
@media (max-width: 760px) { .fn-toaster { inset-block-end: calc(var(--bottombar-h) + env(safe-area-inset-bottom) + var(--space-3)); } }

/* --------------------------------------------- 10 APP SHELL: SIDEBAR, TOP BAR
   <a class="fn-skip-link" href="#main">تخطَّ إلى المحتوى</a>
   <div class="fn-shell">
     <aside class="fn-sidebar" aria-label="القائمة الرئيسية"> brand · <nav class="fn-nav" aria-label="الأقسام"> · me block </aside>
     <div class="fn-main-col"><header class="fn-topbar">…</header><main id="main" class="fn-content" tabindex="-1">…</main></div>
   </div>
   Group = disclosure (not a menu):
     <div class="fn-nav__group" data-open>
       <button class="fn-nav__group-toggle" aria-expanded="true" aria-controls="g-cust">icon المشتركون <span class="fn-count">…</span> chevron</button>
       <ul class="fn-nav__group-items" id="g-cust"><li><a class="fn-nav__item" href="#/subs" aria-current="page">…</a></li></ul>
     </div>
   Collapsed group shows its worst counter (bad > warn > neutral). Open/closed state persisted per user (localStorage fn-nav).
   Tablet rail (761-1180): icons only, labels become tooltips (title + aria-label), group items open as a flyout. */
.fn-shell { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-block-size: 100dvh; background: var(--color-bg); }
.fn-sidebar { position: sticky; top: 0; block-size: 100dvh; overflow-y: auto; display: flex; flex-direction: column; z-index: var(--z-sidebar);
  background: var(--color-surface); border-inline-end: 1px solid var(--color-border); padding: var(--space-3); }
.fn-brand { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-1) var(--space-2) var(--space-3); border-block-end: 1px solid var(--color-border); text-decoration: none; color: inherit; }
.fn-brand__logo { inline-size: 38px; block-size: 38px; border-radius: 11px; display: grid; place-items: center; flex: 0 0 auto; font-weight: var(--weight-bold); font-size: var(--text-xl);
  color: var(--color-text-on-brand); background: var(--gradient-brand); overflow: hidden; }
.fn-brand__logo img { max-inline-size: 100%; max-block-size: 100%; object-fit: contain; }
.fn-brand__name { display: block; font-size: var(--text-md); font-weight: var(--weight-bold); line-height: 1.2; }
.fn-brand__sub { display: block; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-nav { display: flex; flex-direction: column; gap: 2px; margin-block-start: var(--space-3); flex: 1; }
.fn-nav ul { list-style: none; margin: 0; padding: 0; }
.fn-nav__item, .fn-nav__group-toggle {
  all: unset; box-sizing: border-box; display: flex; align-items: center; gap: var(--space-3); inline-size: 100%; min-block-size: 40px;
  padding-inline: var(--space-3); border-radius: var(--radius-md); font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--color-text-secondary); cursor: pointer; position: relative; }
.fn-nav__item:hover, .fn-nav__group-toggle:hover { background: var(--color-surface-hover); }
.fn-nav__item:focus-visible, .fn-nav__group-toggle:focus-visible { outline: var(--focus-outline); outline-offset: -2px; }
.fn-nav__item[aria-current="page"] { background: var(--color-brand-soft); color: var(--color-brand-text); font-weight: var(--weight-semibold); }
.fn-nav__item[aria-current="page"]::before { content: ""; position: absolute; inset-inline-start: 0; inset-block: 6px; inline-size: 3px; border-radius: 3px; background: var(--color-brand); }
.fn-nav__item .fn-icon, .fn-nav__group-toggle .fn-icon { color: var(--color-text-muted); }
.fn-nav__item[aria-current="page"] .fn-icon { color: var(--color-brand-text); }
.fn-nav__group-toggle { font-weight: var(--weight-semibold); color: var(--color-text); margin-block-start: var(--space-1); }
.fn-nav__count, .fn-nav__chev { margin-inline-start: auto; }
.fn-nav__count + .fn-nav__chev { margin-inline-start: 0; }
.fn-nav__chev { transition: rotate var(--dur-fast); }
.fn-nav__group-toggle[aria-expanded="true"] .fn-nav__chev { rotate: 180deg; }
.fn-nav__group-toggle[aria-expanded="true"] .fn-nav__count--collapsed { display: none; }
.fn-nav__group-items { display: flex; flex-direction: column; gap: 1px; margin-inline-start: 19px; padding-inline-start: var(--space-3); border-inline-start: 1px solid var(--color-border); }
.fn-nav__group-items[hidden] { display: none; }
.fn-nav__group-items .fn-nav__item { min-block-size: 36px; }
.fn-nav__me { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-2) 0; border-block-start: 1px solid var(--color-border); }
.fn-nav__me-name { display: block; font-size: var(--text-sm); font-weight: var(--weight-semibold); line-height: 1.2; }
.fn-nav__me-role { display: block; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-main-col { min-inline-size: 0; display: flex; flex-direction: column; }
.fn-topbar { position: sticky; top: 0; z-index: var(--z-sticky); display: flex; align-items: center; gap: var(--space-3); min-block-size: var(--topbar-h);
  padding: var(--space-3) var(--page-pad-x); background: var(--color-surface); border-block-end: 1px solid var(--color-border); }
.fn-topbar > :not(.fn-cmdk) { flex: 0 0 auto; }
.fn-topbar__title { display: none; margin: 0; font-size: var(--text-xl); font-weight: var(--weight-bold); line-height: var(--leading-heading); flex: 1 1 auto !important; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fn-content { padding: var(--space-5) var(--page-pad-x) var(--space-8); display: flex; flex-direction: column; gap: var(--space-4); max-inline-size: var(--content-max); inline-size: 100%; }
.fn-content:focus { outline: none; }
/* Command bar trigger (opens the command palette dialog; role="combobox" lives on the dialog input) */
.fn-cmdk { all: unset; box-sizing: border-box; flex: 1; max-inline-size: 620px; min-inline-size: 0; display: flex; align-items: center; gap: var(--space-3);
  min-block-size: var(--input-h); padding-inline: var(--space-4); border: 1px solid var(--color-border-strong); border-radius: 12px; background: var(--color-surface-sunken);
  color: var(--color-text-muted); font-size: var(--text-sm); cursor: text; }
.fn-cmdk:hover { border-color: var(--color-border-control); }
.fn-cmdk:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }
.fn-cmdk__text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fn-cmdk__keys { display: flex; gap: var(--space-1); }
/* Command palette dialog contents: input role="combobox" aria-expanded aria-controls="cmd-list" aria-activedescendant;
   <ul id="cmd-list" role="listbox"> with role="group" sections (مشتركون · جلسات/IP · تذاكر · إجراءات) and role="option" rows. */
.fn-cmdp__list { list-style: none; margin: 0; padding: var(--space-2); max-block-size: 60dvh; overflow-y: auto; }
.fn-cmdp__group-label { padding: var(--space-2) var(--space-3) var(--space-1); font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-text-muted); }
.fn-cmdp__option { display: flex; align-items: center; gap: var(--space-3); min-block-size: var(--target-touch); padding-inline: var(--space-3); border-radius: var(--radius-md); cursor: pointer; }
.fn-cmdp__option[aria-selected="true"] { background: var(--color-brand-soft); color: var(--color-brand-on); box-shadow: inset 0 0 0 2px var(--color-focus); }
/* Exchange-rate chip: ok vs stale (stale = text + icon, not colour alone) */
.fn-rate { display: inline-flex; align-items: center; gap: 6px; min-block-size: 36px; padding-inline: var(--space-3); border-radius: var(--radius-full);
  background: var(--color-ok-bg); color: var(--color-ok-on); font-size: var(--text-xs); font-weight: var(--weight-semibold); border: 0; font-family: inherit; }
button.fn-rate { cursor: pointer; }
.fn-rate--stale { background: var(--color-bad-bg); color: var(--color-bad-on); }
.fn-lang { display: inline-flex; align-items: center; gap: 6px; min-block-size: 36px; padding-inline: var(--space-3); border: 1px solid var(--color-border-strong); border-radius: var(--radius-md);
  background: var(--color-surface); color: var(--color-text-secondary); font: inherit; font-size: var(--text-xs); font-weight: var(--weight-semibold); cursor: pointer; }
.fn-iconbtn-badge { position: absolute; inset-block-start: 2px; inset-inline-end: 2px; }

@media (max-width: 1180px) and (min-width: 761px) {
  .fn-shell { grid-template-columns: var(--sidebar-rail-w) minmax(0, 1fr); }
  .fn-sidebar { padding: var(--space-3) var(--space-2); overflow: visible; }
  .fn-brand { justify-content: center; padding-inline: 0; }
  .fn-brand__text, .fn-nav__label, .fn-nav__chev, .fn-nav__me-text { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .fn-nav__item, .fn-nav__group-toggle { justify-content: center; padding-inline: 0; min-block-size: var(--target-min); }
  .fn-nav__group { position: relative; }
  .fn-nav__count { position: absolute; inset-block-start: 2px; inset-inline-end: 4px; min-inline-size: 16px; block-size: 16px; font-size: 11px; padding-inline: 4px; }
  /* group items become a flyout next to the rail (headings hidden: fixes audit #6) */
  .fn-nav__group-items { position: absolute; inset-inline-start: calc(100% + var(--space-2)); inset-block-start: 0; z-index: var(--z-dropdown); min-inline-size: 220px;
    margin: 0; padding: 6px; border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-3); }
  .fn-nav__group-items .fn-nav__item { justify-content: flex-start; padding-inline: var(--space-3); }
  .fn-nav__group-items .fn-nav__label { position: static; width: auto; height: auto; overflow: visible; clip-path: none; }
  .fn-nav__me { justify-content: center; padding-inline: 0; }
}
@media (max-width: 760px) {
  :root { --page-pad-x: var(--space-4); }
  .fn-shell { grid-template-columns: 1fr; }
  .fn-sidebar { display: none; }
  .fn-topbar { min-block-size: 56px; padding-block: var(--space-2); }
  .fn-topbar .fn-cmdk { display: none; }           /* search lives in the bottom bar / top-bar icon on mobile */
  .fn-topbar__title { display: block; }
  .fn-content { padding: var(--space-3) var(--page-pad-x) calc(var(--bottombar-h) + env(safe-area-inset-bottom) + var(--space-6)); gap: var(--space-3); }
}

/* ------------------------------------------------ 11 BOTTOM BAR + MORE SHEET
   ≤760px only. Exactly 5 slots: 4 role-specific tasks + "المزيد" (SPEC §4).
   <nav class="fn-bottombar" aria-label="التنقل الرئيسي">
     <a class="fn-bottombar__item" href="#/collect" aria-current="page"><span class="fn-bottombar__pill">svg</span><span>تحصيل</span><span class="fn-count fn-count--solid">5<span class="fn-visually-hidden"> مستحق</span></span></a>
     …
     <button class="fn-bottombar__item" aria-haspopup="dialog" aria-expanded="false" aria-controls="more-sheet">…المزيد</button>
   </nav>
   Labels always visible (no icon-only). Active = filled pill + aria-current="page". */
.fn-bottombar { display: none; }
@media (max-width: 760px) {
  .fn-bottombar { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); position: fixed; inset-inline: 0; inset-block-end: 0; z-index: var(--z-bottombar);
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom)); background: var(--color-surface); border-block-start: 1px solid var(--color-border); box-shadow: 0 -1px 3px rgba(16, 24, 40, .04); }
}
.fn-bottombar__item { all: unset; box-sizing: border-box; position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  min-block-size: 52px; border-radius: 12px; font-size: var(--text-xs); font-weight: var(--weight-semibold); line-height: 1.25; color: var(--color-text-muted);
  text-align: center; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.fn-bottombar__item:focus-visible { outline: var(--focus-outline); outline-offset: -2px; }
.fn-bottombar__pill { inline-size: 56px; block-size: 30px; border-radius: var(--radius-full); display: grid; place-items: center; }
.fn-bottombar__pill .fn-icon { inline-size: var(--icon-lg); block-size: var(--icon-lg); }
.fn-bottombar__item[aria-current="page"], .fn-bottombar__item[aria-expanded="true"] { color: var(--color-brand-text); }
.fn-bottombar__item[aria-current="page"] .fn-bottombar__pill, .fn-bottombar__item[aria-expanded="true"] .fn-bottombar__pill { background: var(--color-brand-bg); color: var(--color-brand-on); }
.fn-bottombar__item .fn-count { position: absolute; inset-block-start: 2px; inset-inline-end: calc(50% - 26px); min-inline-size: 18px; block-size: 18px; font-size: 11px; }
/* More sheet (a .fn-dialog.fn-dialog--sheet): searchable, grouped tiles, account block always present */
.fn-more__tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; list-style: none; margin: 0; padding: 0; }
.fn-more__group-title { margin: var(--space-3) var(--space-1) var(--space-2); font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--color-text-muted); }
.fn-more__tile { display: flex; flex-direction: column; align-items: center; gap: 6px; min-block-size: 84px; padding: var(--space-2) 2px; border-radius: 12px;
  font-size: var(--text-xs); font-weight: var(--weight-semibold); line-height: 1.3; color: var(--color-text-secondary); text-align: center; text-decoration: none; position: relative; }
.fn-more__tile:hover { background: var(--color-surface-hover); }
.fn-more__tile:focus-visible { outline-offset: -2px; }
.fn-more__tile .fn-icon-tile { inline-size: 44px; block-size: 44px; border-radius: var(--radius-lg); }
.fn-more__tile[aria-current="page"] { color: var(--color-brand-text); }
.fn-more__account { list-style: none; margin: var(--space-3) 0 0; padding: 0; border: 1px solid var(--color-border); border-radius: var(--radius-lg); }
.fn-more__account > li { border-block-end: 1px solid var(--color-border); }
.fn-more__account > li:last-child { border-block-end: 0; }
.fn-more__row { all: unset; box-sizing: border-box; display: flex; align-items: center; gap: var(--space-3); inline-size: 100%; min-block-size: 52px; padding-inline: var(--space-4);
  font-size: var(--text-md); font-weight: var(--weight-medium); color: var(--color-text); cursor: pointer; }
.fn-more__row:focus-visible { outline: var(--focus-outline); outline-offset: -2px; border-radius: var(--radius-lg); }
.fn-more__row--danger { color: var(--color-bad-fg); }
.fn-more__row > .fn-spacer + * { flex: 0 0 auto; }

/* ------------------------------------- 12 PAGE HEADER, ENTITY HEADER, ACTION BAR */
.fn-breadcrumb ol { list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; padding: 0; font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-breadcrumb a { color: inherit; } .fn-breadcrumb [aria-current="page"] { color: var(--color-text); font-weight: var(--weight-semibold); }
.fn-page-header { display: flex; align-items: flex-end; gap: var(--space-3); flex-wrap: wrap; }
.fn-page-header__title { margin: 0; font-size: var(--text-2xl); font-weight: var(--weight-bold); line-height: var(--leading-heading); }
.fn-page-header__sub { margin: 0; font-size: var(--text-sm); color: var(--color-text-muted); }
@media (max-width: 760px) { .fn-page-header__title { font-size: var(--text-xl); } }
/* Entity header: subscriber / reseller / OLT hubs (mockup 05) */
.fn-entity { display: flex; align-items: flex-start; gap: var(--space-4); padding: var(--space-5); }
.fn-entity__main { flex: 1; min-inline-size: 0; }
.fn-entity__title { margin: 0; font-size: var(--text-2xl); font-weight: var(--weight-bold); line-height: var(--leading-heading); }
.fn-entity__meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: var(--space-1) 0 0; padding: 0; list-style: none; font-size: var(--text-sm); color: var(--color-text-muted); }
.fn-entity__meta > li { display: inline-flex; align-items: center; gap: 5px; }
.fn-entity__actions { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; position: relative; }
.fn-hub { display: grid; grid-template-columns: minmax(0, 1fr) 330px; gap: var(--space-4); align-items: start; }
@media (max-width: 1180px) { .fn-hub { grid-template-columns: 1fr; } }
@media (max-width: 760px) {
  .fn-entity { flex-wrap: wrap; padding: var(--space-4); gap: var(--space-3); }
  .fn-entity__main { flex-basis: calc(100% - 56px - var(--space-3)); }
  .fn-entity__actions { flex-basis: 100%; }
  .fn-entity__actions > .fn-btn:not(.fn-btn--primary) { flex: 1; }
  .fn-entity__title { font-size: var(--text-xl); }
  .fn-entity__actions .fn-btn--primary { display: none; } /* moves to the sticky action bar */
}
/* Sticky mobile action bar: holds the ONE primary action (56px) in thumb reach, above the bottom bar */
.fn-action-bar { display: none; }
@media (max-width: 760px) {
  .fn-action-bar { display: flex; gap: var(--space-2); position: fixed; inset-inline: 0; z-index: var(--z-sticky);
    inset-block-end: calc(var(--bottombar-h) + env(safe-area-inset-bottom)); padding: var(--space-2) var(--space-4); background: var(--color-surface); border-block-start: 1px solid var(--color-border); box-shadow: var(--shadow-2); }
  .fn-action-bar .fn-btn--primary { flex: 1; min-block-size: var(--target-primary-mobile); font-size: var(--text-lg); border-radius: var(--radius-lg); }
  .fn-has-action-bar .fn-content { padding-block-end: calc(var(--bottombar-h) + var(--target-primary-mobile) + env(safe-area-inset-bottom) + var(--space-8)); }
}
/* Live-refresh indicator ("مباشر · يتحدّث كل 30 ث · يتوقف أثناء الكتابة") */
.fn-live { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); color: var(--color-text-muted); }
.fn-live[data-paused] .fn-badge { background: var(--color-neutral-bg); color: var(--color-neutral-on); }

@media print {
  .fn-sidebar, .fn-topbar, .fn-bottombar, .fn-action-bar, .fn-toaster, .no-print { display: none !important; }
  .fn-shell { display: block; } .fn-content { padding: 0; max-inline-size: none; }
  .fn-card { box-shadow: none; break-inside: avoid; }
}

/* Migrate from Jaze (desktop only): an unmapped plan row stands out */
.jz-plans tr.jz-need td { background: color-mix(in srgb, var(--bad, #dc2626) 8%, transparent); }
.jz-plans select { max-width: 100%; }
/* a customer's Billing sub-views can outgrow a phone screen: the row scrolls on its own, the page does not */
#hubpanel .fn-seg { min-width: 0; max-width: 100%; overflow-x: auto; scrollbar-width: thin; }   /* a fieldset will not shrink without min-width: 0 */
#hubpanel .fn-seg > label { flex: 0 0 auto; }
