/* Self-hosted webfonts — Geist Sans + Geist Mono (Vercel), Hugeicons stroke-rounded icon font. */

/* @import must be the first rule in a stylesheet (only comments may precede
 * it) or every spec-compliant browser silently drops it — it was below the
 * @font-face rules before, so hgi-stroke-rounded.css never actually loaded
 * and every Icon in the app was rendering blank. Moved to the top; nothing
 * else in this file changed. */
@font-face{font-family:"hgi-stroke-rounded";src:url("/_fonts/hgi-stroke-rounded.b1d5e9e0.woff2") format("woff2");font-display:swap}.hgi-stroke{font-family:"hgi-stroke-rounded" !important;font-style:normal;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;position:relative}.hgi-stroke:after{opacity:.4;position:absolute;left:0}.hgi-stroke.hgi-absolute:before{content:"㪝"}.hgi-stroke.hgi-add-01:before{content:"㪩"}.hgi-stroke.hgi-alert-circle:before{content:"㬃"}.hgi-stroke.hgi-analytics-01:before{content:"㬯"}.hgi-stroke.hgi-anonymous:before{content:"㬺"}.hgi-stroke.hgi-archive:before{content:"㭎"}.hgi-stroke.hgi-arrow-down-01:before{content:"㭚"}.hgi-stroke.hgi-arrow-left-01:before{content:"㭪"}.hgi-stroke.hgi-arrow-right-01:before{content:"㭹"}.hgi-stroke.hgi-arrow-turn-backward:before{content:"㮂"}.hgi-stroke.hgi-arrow-up-01:before{content:"㮆"}.hgi-stroke.hgi-bank:before{content:"㯐"}.hgi-stroke.hgi-blocked:before{content:"㱁"}.hgi-stroke.hgi-briefcase-01:before{content:"㲡"}.hgi-stroke.hgi-building-02:before{content:"㳋"}.hgi-stroke.hgi-calendar-01:before{content:"㳤"}.hgi-stroke.hgi-calendar-03:before{content:"㳦"}.hgi-stroke.hgi-camera-01:before{content:"㴭"}.hgi-stroke.hgi-cancel-01:before{content:"㵂"}.hgi-stroke.hgi-canvas:before{content:"㵉"}.hgi-stroke.hgi-check-list:before{content:"㶝"}.hgi-stroke.hgi-checkmark-circle-02:before{content:"㶧"}.hgi-stroke.hgi-checkmark-circle-04:before{content:"㶩"}.hgi-stroke.hgi-checkmark-square-02:before{content:"㶫"}.hgi-stroke.hgi-chip:before{content:"㶾"}.hgi-stroke.hgi-clipboard:before{content:"㸄"}.hgi-stroke.hgi-clock-01:before{content:"㸅"}.hgi-stroke.hgi-code:before{content:"㸦"}.hgi-stroke.hgi-coins-01:before{content:"㸫"}.hgi-stroke.hgi-command:before{content:"㸾"}.hgi-stroke.hgi-copy-01:before{content:"㹴"}.hgi-stroke.hgi-copy-link:before{content:"㹶"}.hgi-stroke.hgi-credit-card:before{content:"㺚"}.hgi-stroke.hgi-dashboard-square-02:before{content:"㻣"}.hgi-stroke.hgi-delete-02:before{content:"㻺"}.hgi-stroke.hgi-download-01:before{content:"㽰"}.hgi-stroke.hgi-exchange-01:before{content:"㿡"}.hgi-stroke.hgi-eye:before{content:"㿦"}.hgi-stroke.hgi-file-01:before{content:"㿹"}.hgi-stroke.hgi-file-add:before{content:"㿻"}.hgi-stroke.hgi-fingerprint-scan:before{content:"䀹"}.hgi-stroke.hgi-folder-add:before{content:"䁤"}.hgi-stroke.hgi-folder-management:before{content:"䁵"}.hgi-stroke.hgi-function:before{content:"䂢"}.hgi-stroke.hgi-google-sheet:before{content:"䃤"}.hgi-stroke.hgi-grid:before{content:"䃻"}.hgi-stroke.hgi-id:before{content:"䆒"}.hgi-stroke.hgi-image-add-01:before{content:"䆙"}.hgi-stroke.hgi-inbox:before{content:"䆱"}.hgi-stroke.hgi-invoice-01:before{content:"䇕"}.hgi-stroke.hgi-invoice:before{content:"䇙"}.hgi-stroke.hgi-key-01:before{content:"䇾"}.hgi-stroke.hgi-label:before{content:"䈣"}.hgi-stroke.hgi-laptop:before{content:"䈼"}.hgi-stroke.hgi-layout-left:before{content:"䉟"}.hgi-stroke.hgi-line:before{content:"䊇"}.hgi-stroke.hgi-link-01:before{content:"䊉"}.hgi-stroke.hgi-link-02:before{content:"䊊"}.hgi-stroke.hgi-list-view:before{content:"䊘"}.hgi-stroke.hgi-loading-02:before{content:"䊜"}.hgi-stroke.hgi-lock:before{content:"䋄"}.hgi-stroke.hgi-mail-01:before{content:"䋮"}.hgi-stroke.hgi-mail-validation-01:before{content:"䌚"}.hgi-stroke.hgi-map-pin:before{content:"䌡"}.hgi-stroke.hgi-moon:before{content:"䏶"}.hgi-stroke.hgi-more-horizontal:before{content:"䏿"}.hgi-stroke.hgi-move:before{content:"䑃"}.hgi-stroke.hgi-neutral:before{content:"䑥"}.hgi-stroke.hgi-next:before{content:"䑮"}.hgi-stroke.hgi-note:before{content:"䒈"}.hgi-stroke.hgi-notification-01:before{content:"䒌"}.hgi-stroke.hgi-option:before{content:"䒮"}.hgi-stroke.hgi-pencil-edit-01:before{content:"䓿"}.hgi-stroke.hgi-percent:before{content:"䔈"}.hgi-stroke.hgi-play-circle:before{content:"䔲"}.hgi-stroke.hgi-profile:before{content:"䖖"}.hgi-stroke.hgi-profit:before{content:"䖗"}.hgi-stroke.hgi-re::before{content:"䗟"}.hgi-stroke.hgi-react:before{content:"䗠"}.hgi-stroke.hgi-repeat:before{content:"䘁"}.hgi-stroke.hgi-reset-password:before{content:"䘄"}.hgi-stroke.hgi-scroll:before{content:"䚒"}.hgi-stroke.hgi-search-01:before{content:"䚕"}.hgi-stroke.hgi-sent:before{content:"䚶"}.hgi-stroke.hgi-settings-01:before{content:"䛁"}.hgi-stroke.hgi-settings-02:before{content:"䛂"}.hgi-stroke.hgi-share-02:before{content:"䛑"}.hgi-stroke.hgi-shield-01:before{content:"䛠"}.hgi-stroke.hgi-signature:before{content:"䜰"}.hgi-stroke.hgi-sliders-horizontal:before{content:"䝆"}.hgi-stroke.hgi-smart-phone-01:before{content:"䝋"}.hgi-stroke.hgi-star:before{content:"䟣"}.hgi-stroke.hgi-sticky-note-01:before{content:"䟯"}.hgi-stroke.hgi-table-02:before{content:"䡽"}.hgi-stroke.hgi-task-01:before{content:"䢠"}.hgi-stroke.hgi-text:before{content:"䣪"}.hgi-stroke.hgi-tick-02:before{content:"䤈"}.hgi-stroke.hgi-twitter:before{content:"䦉"}.hgi-stroke.hgi-unlink-02:before{content:"䦧"}.hgi-stroke.hgi-upload-01:before{content:"䦭"}.hgi-stroke.hgi-user-add-01:before{content:"䧂"}.hgi-stroke.hgi-user-circle:before{content:"䧊"}.hgi-stroke.hgi-user-group:before{content:"䧍"}.hgi-stroke.hgi-user-multiple:before{content:"䧗"}.hgi-stroke.hgi-view:before{content:"䨃"}.hgi-stroke.hgi-wallet-01:before{content:"䨡"}.hgi-stroke.hgi-work-history:before{content:"䩸"}.hgi-stroke.hgi-zap:before{content:"䪲"}/*# sourceMappingURL=hgi-stroke-rounded.css.map */

@font-face {
  font-family: 'Geist Sans';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-sans-Regular.b497f5ad.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Sans';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-sans-Medium.f9949589.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Sans';
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-sans-SemiBold.45ab21f6.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Sans';
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-sans-Bold.a3b7c213.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-mono-Regular.25ab1a13.woff2') format('woff2');
}
@font-face {
  font-family: 'Geist Mono';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('/_fonts/geist-mono-Medium.9baf6f1a.woff2') format('woff2');
}

/* UAE Dirham sign (U+20C3) — glyph from https://github.com/pooyagolchian/dirham (MIT)
 * unicode-range fallback: primary fonts render text; Dirham-* supplies only ⃃. */

@font-face {
  font-family: 'Dirham-Sans';
  font-weight: normal;
  font-style: normal;
  font-display: swap;
  unicode-range: U+20C3;
  size-adjust: 70%;
  src: url('/_fonts/dirham-sans.457311c6.woff2') format('woff2');
}

@font-face {
  font-family: 'Dirham-Mono';
  font-weight: normal;
  font-style: normal;
  font-display: swap;
  unicode-range: U+20C3;
  size-adjust: 65%;
  src: url('/_fonts/dirham-mono.4ac7acd3.woff2') format('woff2');
}

/* Hugeicons stroke-rounded FREE icon font (5,900+ icons) — https://hugeicons.com/ */
/* Color tokens — dark-first. :root is dark (default); [data-theme="light"] overrides.
   Restrained single accent (reserved for the primary action only) + separate semantic hues. */

:root {
  /* base neutrals — dark */
  --gray-0: #0d0d0d;
  --gray-1: #171717;
  --gray-2: #212121;
  /* COL-01. Raised to 0.36 for WCAG 1.4.11 (3:1 on the card boundary — see git
     history on this line for the full contrast derivation), then reverted to
     the original 0.09 on direct visual review: the compliant weight read as
     too heavy/boxy for the intended look. The accessibility gap this
     re-opens is real (0.09 composites to ~1.22–1.29:1, well under 3:1) — if
     it needs revisiting, don't split the difference blindly; the numbers for
     every surface are in the reverted commit. */
  --gray-border: rgba(245, 243, 239, 0.09);
  --gray-fg: #f5f3ef;
  --gray-muted: #a5a099;
  --gray-muted-2: #8c867c;

  /* base accent + semantic — dark */
  --amber-500: #eab308;
  --green-600: #3ddc73;
  --orange-500: #e78640;
  --red-500: #e64937;

  /* semantic aliases */
  --surface-bg: var(--gray-0);
  --surface-card: var(--gray-1);
  --surface-elevated: var(--gray-2);
  --border-hairline: var(--gray-border);
  --text-primary: var(--gray-fg);
  --text-muted: var(--gray-muted);
  --text-muted-2: var(--gray-muted-2);

  --accent: var(--amber-500);
  --accent-fg: #171717;
  --accent-soft: rgba(234, 179, 8, 0.16);

  --success: var(--green-600);
  --success-soft: rgba(61, 220, 115, 0.14);
  --warning: var(--orange-500);
  --warning-soft: rgba(231, 134, 64, 0.14);
  --destructive: var(--red-500);
  --destructive-soft: rgba(230, 73, 55, 0.14);

  /* COL-18 was tried here too (dropping the inset top highlight as redundant
     once the border below was raised to 3:1) and reverted along with the
     border — restoring the inset highlight instead, at its original alpha,
     since that highlight was the other half of the card's original visual
     edge, not a separate change. It composites to ~1.11:1 (invisible by
     WCAG's own measure) — the same accessibility gap the border revert
     re-opens above. */
  --shadow-card: inset 0 1px 0 rgba(245, 243, 239, 0.045), 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.5);

  color-scheme: dark;
}

:root[data-theme="light"] {
  /* light gray primitives — so direct --gray-* usage is not dark-only */
  --gray-0: #f7f6f4;
  --gray-1: #ffffff;
  --gray-2: #fbfaf8;
  /* COL-01, light. Raised to 0.46 for WCAG 1.4.11, then reverted to the
     original 0.10 on direct visual review — see the dark-theme block above
     for why. 0.10 composites to ~1.23:1, well under the 3:1 the compliant
     value cleared.
     Keep in sync with --border-hairline below (this block states its semantic
     aliases literally rather than via var(), by design). */
  --gray-border: rgba(23, 18, 15, 0.10);
  --gray-fg: #17140f;
  --gray-muted: #6f6a61;
  --gray-muted-2: #7a746a;

  /* semantic aliases (explicit for light) */
  --surface-bg: #f7f6f4;
  --surface-card: #ffffff;
  --surface-elevated: #fbfaf8;
  --border-hairline: rgba(23, 18, 15, 0.10); /* COL-01 — must equal --gray-border above */
  --text-primary: #17140f;
  --text-muted: #6f6a61;
  --text-muted-2: #7a746a;

  --accent: #ca8a04;
  --accent-fg: #17140f;
  --accent-soft: rgba(202, 138, 4, 0.14);

  --success: #16803c;
  --success-soft: rgba(22, 128, 60, 0.12);
  --warning: #c2540c;
  --warning-soft: rgba(194, 84, 12, 0.12);
  --destructive: #c0311a;
  --destructive-soft: rgba(192, 49, 26, 0.12);

  --shadow-card: 0 1px 2px rgba(23, 18, 15, 0.04), 0 8px 24px rgba(23, 18, 15, 0.05);

  color-scheme: light;
}

/* Type tokens — v4 architecture.
   Values unchanged from the original — every size here is pixel-matched to a
   real reference screen (see the original spec comment below), so forcing
   them onto a mathematical ratio would mean breaking that match for no real
   gain. What changed is documentation: every role states its actual consumer,
   and the one genuinely dead token is labeled as dead instead of left silent.
   Reference spec: heading 18px semibold -2% / 100% lh; body 14px medium -2% / 140% lh. */

:root {
  --font-display: 'Geist Sans', 'Dirham-Sans', sans-serif;
  --font-body: 'Geist Sans', 'Dirham-Sans', sans-serif;
  --font-mono: 'Geist Mono', 'Dirham-Mono', ui-monospace, monospace;

  --weight-body: 400;
  --weight-label: 500;
  --weight-display: 600;
  --weight-black: 700; /* defined, zero consumers in _ds_bundle.js, base.css, or real screens today — reserved, not wired. Listed honestly rather than silently dropped or fake-wired. */

  /* role-based scale, hand-tuned per component, not a formula:
     hero 40 / h1 20 / h2 18 / h3 16 / body-lg 16 / body 14 / body-sm 12 /
     button 13 (sits deliberately between body-sm and body — see spacing.css's
     button height math, which depends on this exact ladder) / caption 12 */
  --text-hero: 2.5rem;
  --text-h1: 1.25rem;
  --text-h2: 1.125rem;
  --text-h3: 1rem;
  --text-body-lg: 1rem;
  --text-body: 0.875rem;
  --text-body-sm: 0.75rem;
  --text-button: 0.8125rem;   /* Button size=md only */
  --text-caption: 0.75rem;
  --text-metric: 2.5rem;

  --leading-tight: 1;
  --leading-body: 1.4;
  --leading-relaxed: 1.5;

  --tracking-tight: -0.02em;
  --tracking-caption: 0.06em; /* NOTE: HeroMetric and LedgerTable headers in the compiled bundle hardcode 0.06em inline instead of reading this token — same value today, but they will silently drift if this ever changes. Flagged, not fixed here: that's a component-source fix, not a token fix. */
}

/* Spacing tokens — v4 architecture.
   Three tiers: PRIMITIVES (raw scale, no meaning) -> SEMANTIC (purpose, references
   primitives) -> COMPONENT (the exact custom-property names the compiled React
   bundle, base.css, and the real app screens under screens/ and components/
   read — these names are a fixed public contract and cannot be renamed
   without breaking something live).

   Audited every token in this file against actual consumers in _ds_bundle.js,
   base.css, gallery.html, AND every real screen under screens/ and
   components/ (the first pass only checked the first three and would have
   silently deleted --gap-section, which 10 real screens depend on — caught
   before shipping, kept below). Eleven tokens (--space-2xs, --space-xs/sm/md/
   lg/xl/2xl/3xl, --gap-inline, --padding-page-x/y, --sidebar-gap) had zero
   consumers anywhere, including the real screens — dead aliases nobody could
   safely build on because nothing proved they were live. Removed rather than
   carried forward silently. */

:root {
  /* ---- PRIMITIVES — numeric scale, 4px step, Tailwind-style keys past 8 ---- */
  --space-0: 0;
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-5: 1.25rem;   /* 20px */
  --space-6: 1.5rem;    /* 24px */
  --space-7: 1.75rem;   /* 28px */
  --space-8: 2rem;      /* 32px */
  --space-9: 2.25rem;   /* 36px */
  --space-10: 2.5rem;   /* 40px */
  --space-12: 3rem;     /* 48px — was --space-2xl, unconsumed under that name */
  --space-16: 4rem;     /* 64px — was --space-3xl, unconsumed under that name */

  /* ---- SEMANTIC — purpose-named, each comment states its actual consumer ---- */
  --padding-card: var(--space-4);        /* .card, HeroMetric panel, real screen cards */
  --gap-section: var(--space-4);         /* page-level grid/flex gaps across dashboard/clients/settings/time-tracking screens — real, load-bearing, kept */
  --page-header-spacing: var(--space-4); /* .invios-page-header — tight title row before page content */
  --gap-stack: var(--space-3);           /* SidebarNav outer gap */
  --gap-list: var(--space-1);            /* SidebarNav item-to-item gap */
  --content-max-width: 1360px;           /* product content area, matches the max-width real screens already hardcode */
  --sidebar-width: 15rem;

  /* ---- COMPONENT — sidebar / nav row ---- */
  --sidebar-px: 0.875rem;   /* 14px, pixel-matched to reference */
  --sidebar-pt: 1.1875rem;  /* 19px — matches the concentric radius math (9+10) in radius-motion.css */
  --sidebar-pb: var(--space-4);
  --nav-item-height: 3rem;   /* 48px — the one place we deliberately hit the 44px+ touch minimum: nav is the highest-frequency touch target in the app */
  --nav-item-py: var(--space-3); /* 12px — was 0.75rem restated as a magic number; now references the primitive it already equaled */
  --nav-item-px: var(--space-3); /* 12px — was 0.625rem/10px, off-grid; snapped to the nearest primitive */
  --nav-item-gap: var(--space-1);

  /* ---- COMPONENT — buttons ----
     Height-first, not padding-first: solved for a target rendered height
     (fontSize*1 + paddingY*2 + border*2 = height), because click-target height
     is the actual design constraint, not an arbitrary padding pick. Horizontal
     padding isn't height-bound, so it DOES reference the primitive scale.
     Ladder: sm 24px / md 27px / lg 32px — previously sm and md were byte-for-byte
     identical values under two different names (--button-py-sm and
     --button-py-md both resolved to the same 4px), so "medium" buttons did not
     exist as a distinct size anywhere in the system. */
  --button-py-sm: 0.3125rem; /* 5px  -> 12px text + 5*2 + 2px border = 24px */
  --button-px-sm: 0.875rem;  /* 14px */
  --button-py-md: 0.375rem;  /* 6px  -> 13px text + 6*2 + 2px border = 27px */
  --button-px-md: var(--space-4); /* 16px */
  --button-py-lg: 0.5rem;    /* 8px  -> 14px text + 8*2 + 2px border = 32px */
  --button-px-lg: var(--space-5); /* 20px */

  /* ---- COMPONENT — segmented control track ----
     Item padding intentionally equals --button-py-sm/--button-px-sm (shared
     token, not a coincidence) so segmented items and small buttons stay
     visually interchangeable by construction, not by two people remembering
     to keep two numbers in sync. */
  --segment-track-padding: var(--space-1); /* 4px */
  --segment-track-gap: 0.125rem;           /* 2px */
}

/* Radius, shadow and motion tokens.
   Concentric rule: outer radius = inner radius + padding (9px + 10px = 19px). */

:root {
  --radius-sm: 0.5625rem;  /* 9px  — inner nested element */
  --radius: 0.75rem;       /* 12px — buttons, inputs, nav items */
  --radius-md: 0.75rem;    /* 12px */
  --radius-card: 1.1875rem; /* 19px — card shell (9 + 10) */
  --radius-lg: 1.5rem;     /* 24px */
  --radius-pill: 999px;
  --radius-ledger: 0;      /* intentional break for ledger tables */

  --radius-concentric-padding: 0.625rem; /* 10px */
  --radius-nested-outer: calc(var(--radius-sm) + var(--radius-concentric-padding));

  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

  --duration-micro: 150ms;
  --duration-control: 200ms;
  --duration-slide: 250ms;
}

/* ANI-05. `animation-duration: 0.001ms` on its own is not a killswitch, it is a
   throttle: an `infinite` animation keeps its iteration count, so a 1.4s skeleton
   pulse does not stop — it restarts roughly a thousand times a second, which is
   strictly more motion (and more main-thread work, on a phone) than the effect it
   was meant to suppress. 158 infinite declarations ship in this app.
   `animation-iteration-count: 1` is the line that actually stops them.

   The delays matter for the same reason: a 0.001ms animation that still waits out
   a 600ms `animation-delay` leaves the element parked on its `from` state (often
   `opacity: 0`) for the whole delay, so reduce-motion users get *invisible* content
   rather than instant content. `scroll-behavior: auto` cancels `smooth` scrolling,
   which is motion the media query covers but neither of the two original
   declarations touches.

   `*` alone does not reach generated content, so ::before/::after are listed
   explicitly — the design system paints icon glyphs through them. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}

/* Base element resets — not tokens, but small enough to ship in the global closet.

   Button fix: the plain .ids-btn/.ids-button classes (used across every real
   screen — 56 unqualified usages app-wide) had no real size ladder. The base
   rule and the --sm modifier resolved to the exact same padding tokens, and
   there was no --lg modifier at all — so every unqualified button in the app
   rendered at the "small" size, while the React <Button> component (used
   elsewhere) defaults to "medium." Fixed: base now matches the React default
   (md, ~27px tall instead of ~22px), --sm and --lg are both real, distinct
   modifiers. This changes the rendered height of all 56 unqualified buttons
   across screens/ — flagged clearly, not a silent drift. */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--surface-bg);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-body);
  font-weight: var(--weight-label);
  letter-spacing: var(--tracking-tight);
  -webkit-font-smoothing: antialiased;
  line-height: var(--leading-body);
  transition: background var(--duration-control) var(--ease-out), color var(--duration-control) var(--ease-out);
}
h1, h2, h3, h4 {
  font-weight: var(--weight-display);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  margin: 0;
  text-wrap: balance;
}
h1 { font-size: var(--text-hero); }
h2 { font-size: var(--text-h1); }
h3 { font-size: var(--text-h2); }
h4 { font-size: var(--text-h3); }
p { margin: 0; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent); filter: brightness(1.15); text-decoration: underline; }
::selection { background: var(--accent-soft); }
button { font-family: inherit; cursor: pointer; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.ids-icon { line-height: 1; }

/* Compact pill buttons — Time Tracking reference style */
.ids-button,
.ids-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--text-button);
  line-height: 1;
  padding: var(--button-py-md) var(--button-px-md);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  box-sizing: border-box;
  transition:
    background var(--duration-micro) var(--ease-out),
    border-color var(--duration-micro) var(--ease-out),
    color var(--duration-micro) var(--ease-out),
    filter var(--duration-micro) var(--ease-out);
}
.ids-button:disabled,
.ids-btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}
.ids-button--primary,
.ids-btn--primary {
  background: var(--accent);
  color: var(--accent-fg);
}
.ids-button--primary:not(:disabled):hover,
.ids-btn--primary:not(:disabled):hover {
  filter: brightness(1.08);
}
.ids-button--secondary,
.ids-btn--secondary,
.ids-btn--outline {
  background: var(--surface-card);
  color: var(--text-primary);
  border-color: var(--border-hairline);
}
.ids-button--secondary:not(:disabled):hover,
.ids-btn--secondary:not(:disabled):hover,
.ids-btn--outline:not(:disabled):hover {
  border-color: var(--text-muted-2);
}
.ids-button--ghost,
.ids-btn--ghost {
  background: transparent;
  color: var(--text-muted);
}
.ids-button--ghost:not(:disabled):hover,
.ids-btn--ghost:not(:disabled):hover {
  background: var(--surface-elevated);
  color: var(--text-primary);
}
.ids-button--sm,
.ids-btn--sm {
  font-size: var(--text-body-sm);
  padding: var(--button-py-sm) var(--button-px-sm);
}
.ids-button--lg,
.ids-btn--lg {
  font-size: var(--text-body);
  padding: var(--button-py-lg) var(--button-px-lg);
}

/* Segmented pill toggle — Project / Client / None pattern */
.ids-segmented {
  display: inline-flex;
  align-items: center;
  background: var(--surface-card);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-pill);
  padding: var(--segment-track-padding);
  gap: var(--segment-track-gap);
}
.ids-segmented__item {
  font-family: var(--font-body);
  font-weight: 500;
  font-size: var(--text-body-sm);
  line-height: 1; /* was inheriting body's 1.4, so segmented items didn't actually land on the sm-button height their shared padding tokens were supposed to guarantee */
  padding: var(--button-py-sm) var(--button-px-sm);
  border-radius: var(--radius-pill);
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--duration-micro) var(--ease-out),
    color var(--duration-micro) var(--ease-out);
}
.ids-segmented__item:hover {
  color: var(--text-primary);
}
.ids-segmented__item--active {
  background: var(--accent-soft);
  color: var(--accent);
}
.ids-segmented__item--active:hover {
  color: var(--accent);
}

/* Page title row — title left, actions right, tight space before content */
.invios-page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding-bottom: var(--page-header-spacing);
  box-sizing: border-box;
}
.invios-page-header h1 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-h1);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
  margin: 0;
}
.invios-page-header p {
  font-family: var(--font-body);
  font-size: var(--text-body);
  color: var(--text-muted);
  margin: var(--space-1) 0 0;
}

/* Preload UAE Dirham glyph (U+20C3) so currency amounts never flash a missing-glyph box. */
html::before {
  content: '\20C3';
  font-family: 'Dirham-Mono', 'Dirham-Sans';
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* UAE Dirham sign — glyph runs slightly tall; nudge scale + gap before amount */
.currency-sym {
  font-size: 1em;
  font-weight: 400;
  line-height: 1;
  vertical-align: 0.04em;
  display: inline-block;
  margin-right: 2px;
}







/* Invios list tables — match Expenses — list (sortable header + card shell) */
.ex-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caption);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  text-align: left;
  min-width: 0;
  color: var(--text-muted-2);
}
.ex-sort-btn--right {
  justify-content: flex-end;
  width: 100%;
  text-align: right;
}
.ex-project-pill {
  display: inline-flex;
  max-width: 100%;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size: var(--text-body-sm);
  font-weight: 500;
  color: var(--text-primary);
  background: var(--surface-elevated);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-pill);
  padding: 0.2rem 0.6rem;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.25;
  height: auto;
  align-items: center;
  min-width: 0;
}
