/* ===========================================================================
   HAOS design system
   ---------------------------------------------------------------------------
   Apple's app/support craft, not its marketing language.  Every value traces to
   apple.com's served CSS (docs/haos-apple-redesign/APPLE-MEASURED.md) or to the
   HIG; nothing here is invented.

   SAFETY CONTRACT — read before editing.

   1.  FACE NAMES.  home.html and tracker.html already list `Inter` in their
       --sans stack, so a face called "Inter" would switch them the moment it
       ships.  These are "HAOS Sans" / "HAOS Arabic", verified unused.

   2.  TOKEN NAMES.  Defining a token a page already REFERENCES activates a
       dormant var() fallback branch.  Two dangling references exist and are
       never defined here:
         --panel-2   atlas.js:1833, inside a 3-deep var() chain
         --s-*       tasks.html:1002 builds these at RUNTIME by string
                     concatenation, so they cannot be enumerated statically
       Re-check any new name with:  bin/haos_tests/token-refs.py --ds-<name>

   3.  EVERYTHING VISUAL IS SCOPED TO  html.haos-ds.
       A page opts in by adding that class.  Until it does, this file changes
       nothing about it.  That is how 29 pages migrate one at a time on a
       surface served live with no isolation — a build marker, invisible to
       users, flipped exactly once per page.
       The :root token block is NOT scoped: tokens are inert until consumed.

   4.  TRACKING IS LTR-ONLY.  Negative letter-spacing breaks Arabic cursive
       joining, and the deck carries 64,723 Arabic characters.

   Licences: both faces are SIL OFL 1.1, shipped beside them.  SF Pro is never
   used or redistributed.
   =========================================================================== */

/* ---------------------------------------------------------------- faces --- */

@font-face {
  font-family: "HAOS Sans";
  src: url("HAOSSans.22f0f6602adf.woff2") format("woff2-variations"),
       url("HAOSSans.22f0f6602adf.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC,
                 U+2122, U+2190-21BB, U+2212, U+2215, U+2300-23FF, U+25A0-25FF,
                 U+2600-26FF, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "HAOS Arabic";
  src: url("HAOSArabic.fdc31c1c128a.woff2") format("woff2-variations"),
       url("HAOSArabic.fdc31c1c128a.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF,
                 U+FE70-FEFF, U+200C-200F, U+2010-2011, U+204F, U+2E41;
}

/* ---------------------------------------------------------------- tokens --- */

:root {
  color-scheme: light;
  /* type — 8 roles, 2 weights.  Apple: heading = size+4, reading = size+8.  */
  --ds-font-sans: "HAOS Sans", "HAOS Arabic", -apple-system, BlinkMacSystemFont,
                  "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --ds-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                  "Liberation Mono", monospace;

  --ds-size-display: 32px;    --ds-line-display: 36px;    --ds-track-display: .004em;
  --ds-size-title:   24px;    --ds-line-title:   28px;    --ds-track-title:   .009em;
  --ds-size-heading: 19px;    --ds-line-heading: 27px;    --ds-track-heading: .012em;
  --ds-size-body:    15px;    --ds-line-body:    23px;    --ds-track-body:   -.012em;
  --ds-size-secondary:14px;   --ds-line-secondary:20px;   --ds-track-secondary:-.016em;
  --ds-size-caption: 13px;    --ds-line-caption: 18px;    --ds-track-caption:-.014em;
  --ds-size-label:   12px;    --ds-line-label:   16px;    --ds-track-label:  -.010em;

  --ds-weight-regular: 400;
  --ds-weight-strong:  600;

  /* colour — Apple's grey ladder.  The ROLES invert between themes:
     #6E6E73 is secondary in light and tertiary in dark, #86868B the reverse. */
  /* An APP ladder, not a document one. apple.com puts white on the page and
     grey on the card; a dense deck inverts that so content separates by
     SURFACE instead of by a border -- which is what macOS itself does, and
     what HAOS already did. --ds-bg stays as an alias so nothing breaks. */
  /* Owner, 2026-09-24: NO GREY SECTIONS IN LIGHT MODE. The ladder is tinted
     with the accent hue (192) instead of Apple's neutral grey: the ground is a
     near-white with a breath of teal, an inset is a teal wash, and the
     hairlines carry the same hue. Contrast was COMPUTED, not eyeballed (the
     ink-2/ink-3 values moved down so they stay AA on the tinted inset):
       ink   #1D1D1F on canvas 16.05 · on surface-2 15.08
       ink-2 #63636A on canvas  5.68 · on surface-2  5.34 · on white 5.96
       ink-3 #6B6B70 on canvas  5.05 · on surface-2  4.75 · on white 5.30
       accent-text #07728D on surface-2 4.95; ok/warn/danger/info ≥ 5.31    */
  --ds-canvas:    #F7FAFB;   /* the page ground: white with a breath of teal */
  --ds-surface:   #FFFFFF;   /* a raised card                               */
  --ds-surface-2: #EBF4F7;   /* an inset: a teal wash -- differs from the    */
                             /* canvas, so nothing using it vanishes         */
  --ds-bg:        var(--ds-canvas);
  --ds-ink:       #1D1D1F;
  --ds-ink-2:     #63636A;   /* AA on every light surface above              */
  --ds-ink-3:     #6B6B70;   /* AA on every light surface above              */
  --ds-hairline:  rgba(8,129,160,.22);    /* the accent hue, alpha: any ground */
  --ds-hairline-soft: rgba(8,129,160,.11);

  /* accent — Signal Teal, owner ruling 2026-09-21.  Hue 192: 18 deg from
     Apple blue, 51 from Tailwind indigo, near-complementary to HEMFA's brand
     red #D51C28 so the two sharpen each other — and it LEAVES RED FREE TO
     MEAN DANGER, which is the whole reason it is not the brand colour.      */
  --ds-accent:      #0881A0;   /* white on it 4.51:1 — fills, one role only  */
  --ds-accent-text: #07728D;   /* on white   5.53:1 — links, icons           */
  --ds-accent-tint: rgba(8,129,160,.12);

  /* separate tokens per role, so the accent stops doing five jobs at once
     (council R2-6: the ">=5 roles" finding is about the TOKEN MODEL).       */
  --ds-focus:     #0881A0;
  --ds-selection: rgba(8,129,160,.20);

  /* signals — the --haos-* set already injected into every page by
     haos-session.js:202-220, adopted rather than reinvented.  Text sits at
     full strength on an OPAQUE tint, never coloured text on a low-alpha wash. */
  --ds-ok:     #1B6B45;  --ds-ok-bg:     #E4F2EA;  --ds-ok-line:     #9CCBB4;
  --ds-warn:   #8A5A00;  --ds-warn-bg:   #FAEFD9;  --ds-warn-line:   #D8B978;
  --ds-danger: #A82A1F;  --ds-danger-bg: #FAE6E3;  --ds-danger-line: #DBA097;
  --ds-info:   #15568F;  --ds-info-bg:   #E2EDF8;  --ds-info-line:   #9BBEDD;

  /* shape — 5 radii + a pill, down from 44 declarations */
  --ds-radius-badge:   6px;
  --ds-radius-control: 8px;
  --ds-radius-field:  12px;
  --ds-radius-card:   18px;
  --ds-radius-modal:  28px;
  --ds-radius-pill:  980px;

  /* depth — offset, not centred, as Apple does it. 3 values, down from 60. */
  --ds-shadow-1: 0 1px 2px rgba(0,0,0,.04);
  --ds-shadow-2: 8px 8px 16px rgba(0,0,0,.08);
  --ds-shadow-3: 0 12px 32px rgba(0,0,0,.12);

  /* space — a 4pt grid, down from 61 values.  Hairlines and optical
     corrections are EXEMPT: these are defaults, not a straitjacket.         */
  --ds-space-1:  4px;  --ds-space-2:  8px;  --ds-space-3: 12px;
  --ds-space-4: 16px;  --ds-space-5: 20px;  --ds-space-6: 24px;
  --ds-space-7: 32px;  --ds-space-8: 40px;  --ds-space-9: 48px;
  --ds-space-10:64px;

  /* motion — Apple's two curves are 78% of its own usage */
  --ds-ease:      cubic-bezier(.4, 0, .6, 1);
  --ds-ease-in:   cubic-bezier(0, 0, .2, 1);
  --ds-dur-fast:  100ms;
  --ds-dur-base:  160ms;
  --ds-dur-slow:  240ms;
  --ds-dur-enter: 400ms;

  /* glass — FLOATING LAYER ONLY (liquid-glass.md > The two layers).
     Apple's own nav values.                                                 */
  --ds-glass-blur: blur(20px) saturate(180%);
  --ds-glass-bg:   rgba(247,250,251,.92);   /* the canvas, translucent      */
  --ds-nav-h:      44px;

  /* targets */
  --ds-target:     44px;
  --ds-target-min: 28px;
}

/* dark — pure black ground, and text is #F5F5F7, never #FFF (Apple's own
   softened white).  Declared for BOTH the media query and the explicit stamp,
   so an OS preference and a user choice each win in their own direction.    */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --ds-canvas:    #000000;
    --ds-surface:   #161617;
    --ds-surface-2: #1D1D1F;
    --ds-bg:        var(--ds-canvas);
    --ds-ink:       #F5F5F7;
    --ds-ink-2:     #86868B;
    --ds-ink-3:     #8E8E93;
    --ds-hairline:  rgba(255,255,255,.24);
    --ds-hairline-soft: rgba(255,255,255,.10);
    --ds-accent:      #087F9C;
    --ds-accent-text: #6FC5DC;
    --ds-accent-tint: rgba(111,197,220,.16);
    --ds-focus:     #6FC5DC;
    --ds-selection: rgba(111,197,220,.24);
    --ds-ok:     #79D3A2;  --ds-ok-bg:     #14291F;  --ds-ok-line:     #2F6B4C;
    --ds-warn:   #E3B667;  --ds-warn-bg:   #2C2415;  --ds-warn-line:   #6E5B2E;
    --ds-danger: #F0907A;  --ds-danger-bg: #3B211B;  --ds-danger-line: #7C4235;
    --ds-info:   #87BCF0;  --ds-info-bg:   #17293A;  --ds-info-line:   #395E80;
    --ds-shadow-1: 0 1px 2px rgba(0,0,0,.4);
    --ds-shadow-2: 8px 8px 24px rgba(0,0,0,.5);
    --ds-shadow-3: 0 12px 40px rgba(0,0,0,.6);
    --ds-glass-bg: rgba(22,22,23,.88);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --ds-canvas:    #000000;
  --ds-surface:   #161617;
  --ds-surface-2: #1D1D1F;
  --ds-bg:        var(--ds-canvas);
  --ds-ink:       #F5F5F7;
  --ds-ink-2:     #86868B;
  --ds-ink-3:     #8E8E93;
  --ds-hairline:  rgba(255,255,255,.24);
  --ds-hairline-soft: rgba(255,255,255,.10);
  --ds-accent:      #087F9C;
  --ds-accent-text: #6FC5DC;
  --ds-accent-tint: rgba(111,197,220,.16);
  --ds-focus:     #6FC5DC;
  --ds-selection: rgba(111,197,220,.24);
  --ds-ok:     #79D3A2;  --ds-ok-bg:     #14291F;  --ds-ok-line:     #2F6B4C;
  --ds-warn:   #E3B667;  --ds-warn-bg:   #2C2415;  --ds-warn-line:   #6E5B2E;
  --ds-danger: #F0907A;  --ds-danger-bg: #3B211B;  --ds-danger-line: #7C4235;
  --ds-info:   #87BCF0;  --ds-info-bg:   #17293A;  --ds-info-line:   #395E80;
  --ds-shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --ds-shadow-2: 8px 8px 24px rgba(0,0,0,.5);
  --ds-shadow-3: 0 12px 40px rgba(0,0,0,.6);
  --ds-glass-bg: rgba(22,22,23,.88);
}

/* =========================================================================
   Everything below is scoped to html.haos-ds and applies ONLY to a page that
   has opted in.  Nothing here reaches an un-migrated page.
   ========================================================================= */

/* ------------------------------------------------------------- typography --- */

html.haos-ds {
  background: var(--ds-canvas);     /* the root paints too: a view transition never shows the UA default */
  font-family: var(--ds-font-sans);
  font-optical-sizing: auto;
  font-size: var(--ds-size-body);
  line-height: var(--ds-line-body);
  -webkit-text-size-adjust: 100%;
}
html.haos-ds body {
  margin: 0;
  background: var(--ds-canvas);
  color: var(--ds-ink);
  font-family: var(--ds-font-sans);
  font-size: var(--ds-size-body);
  line-height: var(--ds-line-body);
  font-weight: var(--ds-weight-regular);
  -webkit-font-smoothing: antialiased;
}

/* Tracking is LTR-only: negative letter-spacing breaks Arabic joining. */
html.haos-ds:not([dir="rtl"]) body { letter-spacing: var(--ds-track-body); }
html.haos-ds[dir="rtl"] body { letter-spacing: normal; }

html.haos-ds ::selection { background: var(--ds-selection); }

html.haos-ds .ds-display   { font-size: var(--ds-size-display);   line-height: var(--ds-line-display);   font-weight: var(--ds-weight-strong); }
html.haos-ds .ds-title     { font-size: var(--ds-size-title);     line-height: var(--ds-line-title);     font-weight: var(--ds-weight-strong); }
html.haos-ds .ds-heading   { font-size: var(--ds-size-heading);   line-height: var(--ds-line-heading);   font-weight: var(--ds-weight-strong); }
html.haos-ds .ds-body      { font-size: var(--ds-size-body);      line-height: var(--ds-line-body); }
html.haos-ds .ds-secondary { font-size: var(--ds-size-secondary); line-height: var(--ds-line-secondary); color: var(--ds-ink-2); }
html.haos-ds .ds-caption   { font-size: var(--ds-size-caption);   line-height: var(--ds-line-caption);   color: var(--ds-ink-2); }
html.haos-ds .ds-label     { font-size: var(--ds-size-label);     line-height: var(--ds-line-label);     font-weight: var(--ds-weight-strong); color: var(--ds-ink-2); }

html.haos-ds:not([dir="rtl"]) .ds-display   { letter-spacing: var(--ds-track-display); }
html.haos-ds:not([dir="rtl"]) .ds-title     { letter-spacing: var(--ds-track-title); }
html.haos-ds:not([dir="rtl"]) .ds-heading   { letter-spacing: var(--ds-track-heading); }
html.haos-ds:not([dir="rtl"]) .ds-secondary { letter-spacing: var(--ds-track-secondary); }
html.haos-ds:not([dir="rtl"]) .ds-caption   { letter-spacing: var(--ds-track-caption); }
html.haos-ds:not([dir="rtl"]) .ds-label     { letter-spacing: var(--ds-track-label); }

/* Bare headings are a DEFAULT, not an override — note :where(), which has
   specificity ZERO.  Styling them as `html.haos-ds h1` (0,1,2) outranked every
   page's own heading rule (a bare `h1{}` is (0,0,1), and even `.card h2` is
   only (0,1,1)), which is the same mechanism that inverted the canvas.

   Measured damage before this fix, on versions.html:
     .card h2   a 14px uppercase eyebrow rendered at 24px, still uppercase,
                with its own .05em now scaled against 24px = 1.2px tracking.
     h1         took the DS's 32px but kept the page's own -.02em, which is
                relative to the NEW size: -0.64px, where this scale intends
                +0.13px at 32px. Wrong magnitude and wrong sign.

   At specificity zero these give a page with no heading rules the full correct
   treatment, and never argue with a page that has its own. Tracking ships
   alongside the size so the two can never be set by different rules again.

   The line-height here is UNITLESS on purpose. A px leading paired with a size
   that a page overrides is the same defect one level down: versions.html kept
   its own 22px h1 and took the system's 36px leading, a 1.64 ratio on a
   heading. Unitless scales with whichever size wins. Apple's rule is size+4px,
   which is 1.125 at 32px and 1.18 at 22px -- 1.15 holds across that range. */
:where(html.haos-ds) h1 {
  font-size: var(--ds-size-display); line-height: 1.15;
  font-weight: var(--ds-weight-strong); margin: 0 0 var(--ds-space-3);
  text-wrap: balance;
}
:where(html.haos-ds) h2 {
  font-size: var(--ds-size-title); line-height: 1.16;
  font-weight: var(--ds-weight-strong); margin: 0 0 var(--ds-space-3);
  text-wrap: balance;
}
:where(html.haos-ds) h3 {
  font-size: var(--ds-size-heading); line-height: 1.2;
  font-weight: var(--ds-weight-strong); margin: 0 0 var(--ds-space-2);
}
:where(html.haos-ds:not([dir="rtl"])) h1 { letter-spacing: var(--ds-track-display); }
:where(html.haos-ds:not([dir="rtl"])) h2 { letter-spacing: var(--ds-track-title); }
:where(html.haos-ds:not([dir="rtl"])) h3 { letter-spacing: var(--ds-track-heading); }

/* The UA default for <b>/<strong> is 700, which is off a two-weight scale and
   is where the last stray weights on the deck came from -- no stylesheet
   declares them, so no sweep can find them. */
html.haos-ds b,
html.haos-ds strong,
html.haos-ds th { font-weight: var(--ds-weight-strong); }

/* Identifiers: tabular figures align the DIGITS; a grid column aligns the
   variable-length prefixes.  bdi keeps QA-84 and 19.0.5.4.15 LTR under RTL. */
html.haos-ds .ds-id {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  unicode-bidi: isolate;
  direction: ltr;
}
html.haos-ds code, html.haos-ds kbd, html.haos-ds samp, html.haos-ds .ds-mono {
  font-family: var(--ds-font-mono);
  font-size: .95em;
}

/* --------------------------------------------------------------- focus --- */
/* One ring, everywhere, keyboard-only — replacing three different treatments
   and 20 `outline:none` rules.                                              */
html.haos-ds :focus-visible {
  outline: 2px solid var(--ds-focus);
  outline-offset: 2px;
  border-radius: var(--ds-radius-control);
}
html.haos-ds :focus:not(:focus-visible) { outline: none; }

/* --------------------------------------------------------------- layout --- */
html.haos-ds .ds-wrap {
  width: 100%;
  max-width: 1180px;
  margin-inline: auto;
  padding-inline: var(--ds-space-5);
}
html.haos-ds .ds-stack  { display: flex; flex-direction: column; gap: var(--ds-space-4); }
html.haos-ds .ds-row    { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ds-space-3); }
html.haos-ds .ds-spacer { flex: 1 1 auto; }

/* ------------------------------------------------------------------ nav --- */
/* THE ONLY GLASS ON THE DECK.  Content scrolls under it; cards and tables are
   opaque.  Apple's exact nav values.                                        */
html.haos-ds .ds-nav {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  min-height: var(--ds-nav-h);
  padding: 0 var(--ds-space-5);
  background: var(--ds-glass-bg);
  -webkit-backdrop-filter: var(--ds-glass-blur);
  backdrop-filter: var(--ds-glass-blur);
  border-bottom: 1px solid var(--ds-hairline-soft);
}
html.haos-ds .ds-nav a,
html.haos-ds .ds-nav .ds-nav-item {
  display: inline-flex;
  align-items: center;
  min-height: var(--ds-target-min);
  padding: var(--ds-space-1) var(--ds-space-3);
  border-radius: var(--ds-radius-pill);
  font-size: var(--ds-size-secondary);
  line-height: var(--ds-line-secondary);
  color: var(--ds-ink);
  opacity: .72;                       /* Apple changes OPACITY, not colour */
  text-decoration: none;
  white-space: nowrap;
  transition: opacity var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .ds-nav a:hover { opacity: 1; }
html.haos-ds .ds-nav a[aria-current],
html.haos-ds .ds-nav .is-current {
  opacity: 1;
  font-weight: var(--ds-weight-strong);
  background: var(--ds-accent-tint);
  color: var(--ds-accent-text);
}

/* The shared chrome's own controls must clear the floor the system sets:
   the two skip links measured 20px and the brand wordmark 23px. */
html.haos-ds .haos-skip,
html.haos-ds .haos-brand {
  min-height: var(--ds-target-min);
  display: inline-flex;
  align-items: center;
}

/* --------------------------------------------- legacy page headers --- */
/* Seven pages (home, tracker, hq, floor, mission, brain, council) predate the
   shared nav and carry their own `header.bar` + `nav.nav` markup. Their LINKS
   are already rebuilt from the shared list by HAOS.navLinks(), so the menu
   cannot drift -- but their CHROME still had its own look: a 61-64px bar with
   blur(12px) saturate(1.2), against the shared bar's 44px and Apple's
   blur(20px) saturate(1.8). That is the 2026-07-16 "two separate systems" bug
   showing through as appearance rather than as content.

   Styled here rather than by rewriting seven pages' markup:
   `html.haos-ds header.bar` is (0,1,2) and a page's own `header.bar` is (0,1,1),
   so these win without touching the pages at all.                           */
html.haos-ds header.bar,
html.haos-ds header.topbar {
  position: sticky;
  top: 0;
  z-index: 60;
  gap: var(--ds-space-4);
  /* The height patch above was never going to hold on its own: these headers
     keep flex-wrap:wrap from their own CSS, so at 390px they grew to 75-125px
     against a 48px spec -- up to 2.8x. min-height cannot shrink a bar that has
     wrapped onto three rows. The tab strip scrolls instead (nav.nav below). */
  flex-wrap: nowrap;
  min-height: var(--ds-nav-h);
  padding: 0 var(--ds-space-5);
  background: var(--ds-glass-bg);
  -webkit-backdrop-filter: var(--ds-glass-blur);
  backdrop-filter: var(--ds-glass-blur);
  border-bottom: 1px solid var(--ds-hairline-soft);
  align-items: center;
}
/* ---- bar layout: correct at EVERY width -------------------------------
   flex-wrap only engages when a row genuinely cannot fit, so a roomy desktop
   bar is still one 44px row -- but a cramped one wraps into clean rows instead
   of painting its children on top of each other. Locking these behind a phone
   breakpoint left 834-1100px with nowrap and nothing to give, which is where
   the densest page still collided. */
html.haos-ds header.bar,
html.haos-ds header.topbar,
html.haos-ds .haos-nav {
  flex-wrap: wrap;
  row-gap: var(--ds-space-2);
  align-items: center;
}
/* brand first and pinned left; it yields first, truncating rather than
   pushing the controls onto a row of their own */
html.haos-ds header.bar > .wordmark,
html.haos-ds header.bar > .brand,
html.haos-ds header.topbar > .wordmark,
html.haos-ds header.topbar > .brand,
html.haos-ds .haos-nav > .haos-brand {
  order: 1;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
/* the controls cluster second, pinned RIGHT -- never stranded mid-strip, which
   is how the bell and avatar ended up between the brand and the tabs */
html.haos-ds header.bar > .bar-right,
html.haos-ds header.topbar > .topbar-right,
html.haos-ds header.bar > .haos-user,
html.haos-ds header.topbar > .haos-user,
html.haos-ds .haos-nav > .haos-user {
  order: 2;
  margin-inline-start: auto;
  flex: 0 0 auto;
}
/* the tab strip last; when it wraps it takes a whole row and scrolls */
html.haos-ds header.bar > nav.nav,
html.haos-ds header.topbar > nav.nav,
html.haos-ds .haos-nav > .haos-tabs {
  order: 3;
  min-width: 0;
  overflow-x: auto;
}

@media (max-width: 1100px) {
  html.haos-ds header.bar,
  html.haos-ds header.topbar,
  html.haos-ds .haos-nav { padding-block: var(--ds-space-2); }

  /* A SPAN chip in the bar is a status READOUT ("Saved - 19d ago"); a BUTTON
     chip is an ACTION. Below this width the actions stay and the readouts go:
     the readout's information is on the page itself, and a brand plus five
     controls cannot share one row.
     DESCENDANT, not child -- the clock sits INSIDE .bar-right, so a `>`
     selector misses it entirely, which is why the first attempt still wrapped
     to three rows. */
  html.haos-ds header.bar .clock,
  html.haos-ds header.topbar .clock,
  html.haos-ds header.bar .live,
  html.haos-ds header.topbar .live,
  html.haos-ds header.bar .haos-clock,
  html.haos-ds header.topbar .haos-clock,
  html.haos-ds .haos-nav .haos-clock,
  html.haos-ds header.bar span.chip,
  html.haos-ds header.topbar span.chip,
  html.haos-ds .haos-nav span.chip { display: none; }
}

@media (max-width: 833px) {
  html.haos-ds header.bar,
  html.haos-ds header.topbar { min-height: 48px; }
  /* once it has wrapped, the strip owns its row outright */
  html.haos-ds header.bar > nav.nav,
  html.haos-ds header.topbar > nav.nav,
  html.haos-ds .haos-nav > .haos-tabs { flex: 1 0 100%; }
}

/* A flex child's default min-width is `auto`, which means "never shrink below
   your content". tracker.html's right-hand cluster carries one more control
   than home.html's, so at 390px it measured 429px wide and pushed the page to
   a 449px scrollWidth -- a real horizontal scrollbar. home.html, on the exact
   same code path, stayed at 390, which is what proves it is shrinkability and
   not the breakpoint. */
html.haos-ds .bar-right,
html.haos-ds .topbar-right,
html.haos-ds .haos-user {
  min-width: 0;
  flex-shrink: 1;
  /* ⛔ NEVER `overflow: hidden` here. It was added as belt-and-braces with the
     shrink fix and it CLIPPED the notification bell's dropdown, which is
     position:absolute inside this cluster -- the panel opened and was cut off
     at the header's edge, reading as "it opens behind the page". min-width:0
     and flex-shrink do all the shrinking on their own; the clip did nothing
     except break every popover anchored in here. */
}

/* The header carries backdrop-filter, which CREATES A STACKING CONTEXT -- so a
   panel inside it can never escape above the header's own z-index no matter
   how large its own is. Keep the header's context above page content, and give
   the popovers a z-index that wins inside it. */
html.haos-ds .hbell-dd,
html.haos-ds .haos-nav-menu { z-index: 90; }
html.haos-ds .bar-right > *,
html.haos-ds .topbar-right > * { flex: none; }

html.haos-ds nav.nav {
  display: flex;
  gap: var(--ds-space-1);
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10px,
                      #000 calc(100% - 14px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 10px,
                      #000 calc(100% - 14px), transparent 100%);
}
html.haos-ds nav.nav::-webkit-scrollbar { display: none; }

html.haos-ds nav.nav a,
html.haos-ds nav.nav .nav-link {
  scroll-snap-align: start;
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: var(--ds-target-min);
  padding: var(--ds-space-1) var(--ds-space-3);
  border-radius: var(--ds-radius-pill);
  font-size: var(--ds-size-secondary);
  line-height: var(--ds-line-secondary);
  font-weight: var(--ds-weight-regular);
  letter-spacing: normal;
  color: var(--ds-ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--ds-dur-base) var(--ds-ease),
              color var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds nav.nav a:hover,
html.haos-ds nav.nav .nav-link:hover { color: var(--ds-ink); }
html.haos-ds nav.nav a.active,
html.haos-ds nav.nav .nav-link.active,
html.haos-ds nav.nav a[aria-current] {
  font-weight: var(--ds-weight-strong);
  color: var(--ds-accent-text);
  background: var(--ds-accent-tint);
}

@media (prefers-reduced-transparency: reduce) {
  html.haos-ds header.bar,
  html.haos-ds header.topbar {
    background: var(--ds-canvas);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* --------------------------------------------------------------- button --- */
html.haos-ds .ds-btn {
  --_bw: 1px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  min-height: 36px;
  /* Apple's mechanic: bordered and borderless buttons match optically. */
  padding-block: calc(var(--ds-space-2) - var(--_bw));
  padding-inline: calc(var(--ds-space-4) - var(--_bw));
  border: var(--_bw) solid transparent;
  border-radius: var(--ds-radius-pill);      /* pill ONLY when auto-width */
  font-family: inherit;
  font-size: var(--ds-size-secondary);
  line-height: var(--ds-line-secondary);
  font-weight: var(--ds-weight-regular);
  cursor: pointer;
  background: var(--ds-surface-2);
  color: var(--ds-ink);
  transition: opacity var(--ds-dur-base) var(--ds-ease),
              background-color var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .ds-btn:hover  { opacity: .84; }
html.haos-ds .ds-btn:active { opacity: .68; }
html.haos-ds .ds-btn[disabled],
html.haos-ds .ds-btn[aria-disabled="true"] { opacity: .4; cursor: default; }
html.haos-ds .ds-btn--primary { background: var(--ds-accent); color: #fff; }
html.haos-ds .ds-btn--quiet   { background: transparent; color: var(--ds-accent-text); }
html.haos-ds .ds-btn--danger  { background: var(--ds-danger); color: #fff; }
html.haos-ds .ds-btn--large   { min-height: var(--ds-target); }
/* Full-width buttons are rounded rects, not pills — Apple's own rule. */
html.haos-ds .ds-btn--block   { display: flex; width: 100%; border-radius: var(--ds-radius-control); }

/* Icon-only controls still reach a 44px HIT REGION even when the visual box is
   smaller — the deck has 3 buttons under 28px on every page today.          */
html.haos-ds .ds-icon-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ds-target-min);
  height: var(--ds-target-min);
  border: 0;
  background: transparent;
  color: var(--ds-ink-2);
  border-radius: var(--ds-radius-control);
  cursor: pointer;
}
html.haos-ds .ds-icon-btn::before {
  content: "";
  position: absolute;
  inset: calc((var(--ds-target-min) - var(--ds-target)) / 2);
}
html.haos-ds .ds-icon-btn:hover { color: var(--ds-ink); background: var(--ds-surface-2); }

/* ------------------------------------------- shape, bridged --------------
   The component rules below this point are named .ds-card / .ds-badge / .ds-nav,
   and the pages do not use those class names -- they use .card, .kpi, .badge,
   .chip, .pill, .detail, built by each page's own JS. So the COLOUR half of the
   migration reached them (their tokens were re-pointed at --ds-*) and the SHAPE
   half reached nothing: cards still carried 14px, 12px and 8px radii, badges a
   20px pill, against a declared scale of 6/8/12/18/28.

   These rules key on the classes that actually exist. They deliberately OUTRANK
   a page's own rule -- html.haos-ds .card is (0,2,1) against a bare .card at
   (0,1,0) -- which is the opposite of how headings are handled a few hundred
   lines above, and the difference is intentional:

     a heading size is a per-page DESIGN DECISION, often calibrated against that
     page's content, so the system offers it as a :where() default;
     a card radius of 14 vs 12 vs 8 is DRIFT, not intent -- 44 radius
     declarations across 29 pages with no system behind them -- and collapsing
     it is the entire point.                                                  */

html.haos-ds .card,
html.haos-ds .kpi,
html.haos-ds .panel,
html.haos-ds .detail { border-radius: var(--ds-radius-card); }

html.haos-ds .badge,
html.haos-ds .chip,
html.haos-ds .pill,
html.haos-ds .tag { border-radius: var(--ds-radius-badge); }

html.haos-ds .btn,
html.haos-ds .icon-btn,
html.haos-ds .seg-btn { border-radius: var(--ds-radius-control); }

html.haos-ds input,
html.haos-ds select,
html.haos-ds textarea { border-radius: var(--ds-radius-field); }

html.haos-ds .modal,
html.haos-ds .sheet,
html.haos-ds .drawer,
html.haos-ds .modalcard { border-radius: var(--ds-radius-modal); }

/* A hairline OR a shadow, never both -- the Calm generation ships both. */
html.haos-ds .card { box-shadow: var(--ds-shadow-1); }

/* ---------------------------------------------------------------- card --- */
/* A hairline OR a shadow — never both.  The Calm generation ships both. */
html.haos-ds .ds-card {
  background: var(--ds-surface);   /* white on a grey canvas */
  border-radius: var(--ds-radius-card);
  padding: var(--ds-space-5);
  box-shadow: var(--ds-shadow-1);
}
html.haos-ds .ds-card--flat { box-shadow: none; border: 1px solid var(--ds-hairline-soft); }

/* --------------------------------------------------------------- badge --- */
/* One component.  Replaces .chip/.pill/.badge — three names, 29 definitions. */
html.haos-ds .ds-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  padding: 3px 9px;
  border-radius: var(--ds-radius-badge);
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  background: var(--ds-surface-2);
  color: var(--ds-ink-2);
  white-space: nowrap;
}
html.haos-ds .ds-badge--ok     { background: var(--ds-ok-bg);     color: var(--ds-ok); }
html.haos-ds .ds-badge--warn   { background: var(--ds-warn-bg);   color: var(--ds-warn); }
html.haos-ds .ds-badge--danger { background: var(--ds-danger-bg); color: var(--ds-danger); }
html.haos-ds .ds-badge--info   { background: var(--ds-info-bg);   color: var(--ds-info); }

/* ---------------------------------------------------- the ledger row ------ */
/* The signature.  Reserved for REGISTERS — queues, audit trails, QA records,
   versions, promotions.  Never forms, never prose.
   The dot NEVER carries the state alone: the written state is always present
   (accessibility.md — nothing is conveyed by colour alone).                 */
html.haos-ds .ds-ledger { display: flex; flex-direction: column; }
html.haos-ds .ds-ledger-row {
  display: grid;
  grid-template-columns: 8px minmax(72px, auto) 1fr auto;
  align-items: center;
  gap: var(--ds-space-3);
  min-height: var(--ds-target);
  padding: var(--ds-space-2) var(--ds-space-3);
  border-bottom: 1px solid var(--ds-hairline-soft);
  color: inherit;
  text-decoration: none;
}
html.haos-ds .ds-ledger-row:last-child { border-bottom: 0; }
html.haos-ds .ds-ledger-row:hover { background: var(--ds-surface-2); }
html.haos-ds .ds-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ds-ink-3); }
html.haos-ds .ds-dot--ok     { background: var(--ds-ok); }
html.haos-ds .ds-dot--warn   { background: var(--ds-warn); }
html.haos-ds .ds-dot--danger { background: var(--ds-danger); }
html.haos-ds .ds-dot--info   { background: var(--ds-info); }
html.haos-ds .ds-ledger-what {
  color: var(--ds-ink-2);
  font-size: var(--ds-size-caption);
  line-height: var(--ds-line-caption);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------- table --- */
/* The deck is mostly tables, already its most consistent component.
   No striping — hairlines do the work.                                      */
html.haos-ds .ds-table { width: 100%; border-collapse: collapse; }
html.haos-ds .ds-table th {
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  text-transform: none;              /* the deck shouts on 23 of 29 pages */
  color: var(--ds-ink-2);
  text-align: start;
  padding: var(--ds-space-2) var(--ds-space-4);
  border-bottom: 1px solid var(--ds-hairline);
  white-space: nowrap;
}
html.haos-ds .ds-table td {
  font-size: var(--ds-size-body);
  line-height: var(--ds-line-body);
  padding: var(--ds-space-3) var(--ds-space-4);
  border-bottom: 1px solid var(--ds-hairline-soft);
}
html.haos-ds .ds-table tbody tr:hover { background: var(--ds-surface-2); }
html.haos-ds .ds-table thead th[aria-sort] { cursor: pointer; }
/* Sticky header gains a scroll edge effect — 12 sticky pages lack one. */
html.haos-ds .ds-table--sticky thead th {
  position: sticky;
  top: var(--ds-nav-h);
  z-index: 2;
  background: var(--ds-surface);
  box-shadow: 0 1px 0 var(--ds-hairline);
}
html.haos-ds .ds-table-scroll { overflow-x: auto; }

/* ---------------------------------------------------------------- field --- */
html.haos-ds .ds-field input,
html.haos-ds .ds-field select,
html.haos-ds .ds-field textarea {
  font-family: inherit;
  font-size: var(--ds-size-body);
  line-height: var(--ds-line-body);
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-field);
  padding: var(--ds-space-2) var(--ds-space-3);
  min-height: var(--ds-target);
  width: 100%;
  box-sizing: border-box;
}
html.haos-ds .ds-field select { appearance: none; padding-inline-end: var(--ds-space-7); }
html.haos-ds .ds-field textarea { min-height: calc(var(--ds-target) * 2); resize: vertical; }
html.haos-ds .ds-field input::placeholder,
html.haos-ds .ds-field textarea::placeholder { color: var(--ds-ink-3); }
html.haos-ds .ds-field input[aria-invalid="true"],
html.haos-ds .ds-field textarea[aria-invalid="true"] {
  border-color: var(--ds-danger);
  background: var(--ds-danger-bg);
}
/* iOS Safari auto-zooms any focused field under 16px. */
@media (max-width: 767px) {
  html.haos-ds .ds-field input,
  html.haos-ds .ds-field select,
  html.haos-ds .ds-field textarea { font-size: 16px; }
}

/* --------------------------------------------------------------- modal --- */
html.haos-ds .ds-scrim {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(0,0,0,.28);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  display: grid;
  place-items: center;
  padding: var(--ds-space-4);
}
html.haos-ds .ds-modal {
  background: var(--ds-surface);
  border-radius: var(--ds-radius-modal);
  box-shadow: var(--ds-shadow-3);
  padding: var(--ds-space-6);
  width: min(560px, 100%);
  max-height: calc(100dvh - var(--ds-space-8));
  overflow: auto;
}

/* ----------------------------------------------------------- tab strip --- */
html.haos-ds .ds-tabs {
  display: flex;
  gap: var(--ds-space-1);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  /* fade the edge so labels stop clipping mid-word with no affordance */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 12px,
                      #000 calc(100% - 12px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 12px,
                      #000 calc(100% - 12px), transparent 100%);
}
html.haos-ds .ds-tab {
  scroll-snap-align: start;
  flex: none;
  min-height: var(--ds-target-min);
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 0;
  background: transparent;
  font: inherit;
  font-size: var(--ds-size-secondary);
  font-weight: var(--ds-weight-regular);
  color: var(--ds-ink-2);
  cursor: pointer;
  border-radius: var(--ds-radius-control);
  white-space: nowrap;
}
html.haos-ds .ds-tab[aria-selected="true"] { color: var(--ds-ink); background: var(--ds-surface-2); }

/* --------------------------------------------------- toast / empty --- */
html.haos-ds .ds-toast {
  position: fixed;
  inset-block-end: var(--ds-space-5);
  inset-inline-end: var(--ds-space-5);
  z-index: 300;
  background: var(--ds-glass-bg);
  -webkit-backdrop-filter: var(--ds-glass-blur);
  backdrop-filter: var(--ds-glass-blur);
  border: 1px solid var(--ds-hairline-soft);
  border-radius: var(--ds-radius-field);
  box-shadow: var(--ds-shadow-2);
  padding: var(--ds-space-3) var(--ds-space-4);
  font-size: var(--ds-size-secondary);
  max-width: min(420px, calc(100vw - var(--ds-space-8)));
}
html.haos-ds .ds-empty {
  padding: var(--ds-space-8) var(--ds-space-5);
  text-align: center;
  color: var(--ds-ink-2);
  font-size: var(--ds-size-body);
}
html.haos-ds .ds-empty .ds-btn { margin-top: var(--ds-space-4); }

/* ------------------------------------------------- accessibility states --- */
@media (prefers-reduced-motion: reduce) {
  html.haos-ds *,
  html.haos-ds *::before,
  html.haos-ds *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
/* Glass gets an opaque fallback — 0 of 29 pages implement this today, yet 9
   already use backdrop-filter.                                              */
@media (prefers-reduced-transparency: reduce) {
  html.haos-ds .ds-nav,
  html.haos-ds .ds-toast {
    background: var(--ds-bg);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  html.haos-ds .ds-scrim {
    background: rgba(0,0,0,.72);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
@media (prefers-contrast: more) {
  html.haos-ds { --ds-hairline: rgba(0,0,0,.42); --ds-hairline-soft: rgba(0,0,0,.24); }
  html.haos-ds .ds-nav,
  html.haos-ds .ds-toast { background: var(--ds-bg); backdrop-filter: none; }
  html.haos-ds .ds-badge { outline: 1px solid currentColor; }
}
@media (forced-colors: active) {
  html.haos-ds .ds-nav,
  html.haos-ds .ds-card,
  html.haos-ds .ds-modal { border: 1px solid CanvasText; }
  html.haos-ds .ds-dot { forced-color-adjust: none; }
}

/* =========================================================================
   COMPOSITION — what makes a page read as Apple rather than as a clean admin
   panel. The tokens were never the gap; this is.

   Measured on the OR-000017 detail page: a grey client bar INSIDE a white
   card WITH a 1px border, FOUR equal-weight buttons in a row (two red-outlined),
   metadata set as a table, and the title repeated three times. Every one of
   those is a composition choice, and each is the opposite of what Apple does:

     Apple separates with SURFACE AND SPACE, never a border inside a border.
     Apple has ONE filled action; everything else is quiet; destructive is text.
     Apple sets metadata as a definition list with air around it.

   These rules deliberately outrank a page's own -- composition drift is the
   defect, exactly as radius drift was. Display and cursor are never touched,
   because on some pages a .card is a link.
   ========================================================================= */

/* ---- cards: a surface, not a box ----------------------------------------- */
html.haos-ds .card,
html.haos-ds .panel,
html.haos-ds .detail {
  border: 0 !important;                    /* the 1px box is the admin-panel tell */
  box-shadow: var(--ds-shadow-1);
  background: var(--ds-surface);
  padding: var(--ds-space-6);
}
/* a box inside a box becomes an inset: a tone, never a second border */
html.haos-ds .card .card,
html.haos-ds .card .box,
html.haos-ds .card .cli,
html.haos-ds .card .client-bar,
html.haos-ds .card .sub-card {
  border: 0 !important;
  box-shadow: none;
  background: var(--ds-surface-2);
  border-radius: var(--ds-radius-field);
  padding: var(--ds-space-3) var(--ds-space-4);
}
html.haos-ds .card > * + * { margin-top: 0; }

/* ---- section labels inside a card: a caption, not a heading ------------- */
html.haos-ds .card > .grp,
html.haos-ds .card > h4,
html.haos-ds .card > .section-title,
html.haos-ds .card .label {
  font-size: var(--ds-size-caption);
  line-height: var(--ds-line-caption);
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink-2);
  text-transform: none;
  letter-spacing: normal;
  margin: 0 0 var(--ds-space-3);
}

/* ---- metadata: a definition list with air, never a table ---------------- */
html.haos-ds dl.kv,
html.haos-ds .kv {
  display: grid;
  grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
  column-gap: var(--ds-space-6);
  row-gap: var(--ds-space-3);
  align-items: baseline;
  margin: var(--ds-space-4) 0 0;
}
html.haos-ds dl.kv dt,
html.haos-ds .kv dt {
  font-size: var(--ds-size-caption);
  line-height: var(--ds-line-body);
  font-weight: var(--ds-weight-regular);
  color: var(--ds-ink-2);
  margin: 0;
}
html.haos-ds dl.kv dd,
html.haos-ds .kv dd {
  font-size: var(--ds-size-body);
  line-height: var(--ds-line-body);
  color: var(--ds-ink);
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
html.haos-ds dl.kv dd code,
html.haos-ds .kv dd code { font-size: .9em; color: var(--ds-ink-2); }

/* ---- actions: one filled primary, quiet secondaries, destructive as text - */
html.haos-ds .actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-2);
  margin-top: var(--ds-space-6);
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-hairline-soft);   /* the ONE hairline a card gets */
}
html.haos-ds .actions .btn,
html.haos-ds .btn {
  min-height: 36px;
  padding: var(--ds-space-2) var(--ds-space-4);
  border: 0;
  border-radius: var(--ds-radius-pill);
  background: var(--ds-surface-2);
  color: var(--ds-ink);
  font-size: var(--ds-size-secondary);
  font-weight: 500;
  box-shadow: none;
  transition: opacity var(--ds-dur-base) var(--ds-ease),
              background-color var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .btn:hover { opacity: .84; }
html.haos-ds .btn.primary,
html.haos-ds .btn.pri,
html.haos-ds .btn.ok,
html.haos-ds .btn[data-primary],
html.haos-ds button.primary {
  background: var(--ds-accent);
  color: #fff;
  font-weight: var(--ds-weight-strong);
}
/* Destructive is text, pushed to the far edge, and only turns red on hover.
   Two red-outlined buttons beside the primary is a fire alarm, not a UI. */
html.haos-ds .btn.danger,
html.haos-ds .btn.err,
html.haos-ds .btn.warn,
html.haos-ds .btn.ghost,
html.haos-ds .btn.cancel {
  background: transparent;
  color: var(--ds-ink-2);
  border: 0;
}
html.haos-ds .actions .btn.danger:first-of-type { margin-inline-start: auto; }
html.haos-ds .btn.sm { min-height: 28px; padding: var(--ds-space-1) var(--ds-space-3); font-size: var(--ds-size-caption); }
html.haos-ds .btn.danger:hover,
html.haos-ds .btn.err:hover,
html.haos-ds .btn.cancel:hover { color: var(--ds-danger); background: var(--ds-danger-bg); opacity: 1; }

/* ---- drop zones: an invitation, not a fenced lot ------------------------- */
html.haos-ds .drop,
html.haos-ds .dropzone {
  border: 0 !important;
  background: var(--ds-surface-2);
  border-radius: var(--ds-radius-field);
  padding: var(--ds-space-6);
  text-align: center;
  color: var(--ds-ink-2);
  font-size: var(--ds-size-secondary);
  transition: background-color var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .drop:hover,
html.haos-ds .drop.over,
html.haos-ds .dropzone.over { background: var(--ds-accent-tint); color: var(--ds-accent-text); }

/* ---- page title block ---------------------------------------------------- */
html.haos-ds .page-head,
html.haos-ds .head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ds-space-4);
  margin-bottom: var(--ds-space-6);
}
html.haos-ds .page-head .sub,
html.haos-ds .head .sub,
html.haos-ds h1 + .sub,
html.haos-ds h1 + p.sub {
  color: var(--ds-ink-2);
  font-size: var(--ds-size-secondary);
  margin: var(--ds-space-1) 0 0;
}

/* ---- list rows that are links: hover as a surface, never a lift --------- */
html.haos-ds a.card:hover,
html.haos-ds .card[href]:hover { background: var(--ds-surface); box-shadow: var(--ds-shadow-2); }

/* ---- forms inside cards -------------------------------------------------- */
html.haos-ds .card label {
  display: block;
  font-size: var(--ds-size-caption);
  color: var(--ds-ink-2);
  margin-bottom: var(--ds-space-1);
}
html.haos-ds .card input:not([type=checkbox]):not([type=radio]),
html.haos-ds .card select,
html.haos-ds .card textarea {
  width: 100%;
  min-height: var(--ds-target);
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-field);
  background: var(--ds-surface);
  color: var(--ds-ink);
  font: inherit;
  font-size: var(--ds-size-body);
}
html.haos-ds .card textarea { min-height: 96px; resize: vertical; }
html.haos-ds .card input:focus-visible,
html.haos-ds .card select:focus-visible,
html.haos-ds .card textarea:focus-visible {
  outline: none;
  border-color: var(--ds-accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ds-accent) 24%, transparent);
}

/* =========================================================================
   SIGN-IN SURFACES
   -------------------------------------------------------------------------
   The one place on this product where apple.com's MARKETING language is the
   right answer. A sign-in page has a single job and no data, so it can afford
   the hero scale, the depth field and the motion that would be actively wrong
   on a QA queue. Every working screen stays calm; this is the arrival.

   Type here breaks the 32px display ceiling on purpose -- Apple's own scale
   runs to 80px, and its tracking is NEGATIVE above 40px, which is why the
   clamp below carries -.015em rather than the +.004em the 32px role uses.
   ========================================================================= */

html.haos-ds body.haos-signin,
html.haos-ds body.portal-login {
  min-height: 100dvh;
  box-sizing: border-box;           /* or the padding adds a 48px scroll */
  display: grid;
  place-items: center;
  padding: var(--ds-space-6) var(--ds-space-5);
  padding-block-start: max(var(--ds-space-6), env(safe-area-inset-top, 0px));
  padding-block-end: max(var(--ds-space-6), env(safe-area-inset-bottom, 0px));
  overflow-x: hidden;
}

/* ---- the depth field ---------------------------------------------------- */
/* Fixed, behind everything, pointer-transparent. Each layer is oversized so a
   parallax translate never reveals an edge.                                  */
.haos-depth {
  position: fixed;
  inset: -12vmax;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.haos-depth-layer { position: absolute; inset: 0; will-change: transform; }

/* Far: one broad wash that sets the mood and never competes with the card. */
.haos-depth-far {
  background:
    radial-gradient(48vmax 40vmax at 18% 12%,
      color-mix(in srgb, var(--ds-accent) 9%, transparent), transparent 62%),
    radial-gradient(42vmax 38vmax at 86% 82%,
      color-mix(in srgb, var(--ds-info) 7%, transparent), transparent 64%);
}
/* Mid: HEMFA's brand red, present but not in charge. The mark's colour belongs
   on the arrival surface even though it is deliberately not the accent. */
.haos-depth-mid {
  background:
    radial-gradient(30vmax 26vmax at 76% 16%,
      color-mix(in srgb, #D51C28 6%, transparent), transparent 66%),
    radial-gradient(26vmax 24vmax at 12% 78%,
      color-mix(in srgb, var(--ds-accent) 7%, transparent), transparent 68%);
}
/* The fleet canvas: the one object. Above the wash, below the content. */
canvas.haos-fleet {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
/* Near layer retired: the lattice IS the thing the eye measures motion
   against now, and a grid under a lattice is noise. */
.haos-depth-near { display: none; }
.haos-depth-near-retired {
  background-image:
    linear-gradient(to right, var(--ds-hairline-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--ds-hairline-soft) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(70vmax 60vmax at 50% 40%, #000, transparent 72%);
          mask-image: radial-gradient(70vmax 60vmax at 50% 40%, #000, transparent 72%);
  opacity: .55;
}

/* ---- the stage ----------------------------------------------------------- */
/* ONE owner. The portal's own sheet used to lay this screen out as well, and
   its #main rule outranked these by id: the title rendered at this sheet's
   64px inside that sheet's 10ch box, four lines deep, beside a 300px card,
   and on a phone the title box sat 38px off the centre its siblings shared.
   Geometry that matches NEITHER sheet's numbers is the tell for two owners.
   The portal keeps its components (status dot, trust line); the layout is
   here, in px -- the portal root is 15px, so its rem was never 16.        */
html.haos-ds .haos-signin-stage,
html.haos-ds .login-stage {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1060px;
  min-height: 0;
  margin: 0 auto;
  padding: 0;                       /* the body gutter is the gutter */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  /* one column: hero, then the object (drawn into this gap), then the card --
     the phone hero pattern: headline, product, action */
  row-gap: min(64vw, 420px);
}
/* Two columns only where there is genuinely room. The card column is a fixed
   420 -- a sign-in form does not get wider on a wider screen -- and the hero
   takes the rest. */
@media (min-width: 900px) {
  html.haos-ds .haos-signin-stage,
  html.haos-ds .login-stage {
    grid-template-columns: minmax(0, 1fr) 420px;
    column-gap: var(--ds-space-10);
    row-gap: 0;
  }
}

/* The portal has a header row above the stage. Pin it: an auto row for the
   header and the rest for the stage, so free space never splits across the
   two rows and floats the brand down the page. The header shares the stage's
   1060px column, so the wordmark and the headline start on one x. */
html.haos-ds body.portal-login {
  grid-template-rows: auto minmax(0, 1fr);
  align-content: stretch;
}
html.haos-ds body.portal-login > header {
  width: 100%;
  max-width: 1060px;
  justify-self: center;
  align-self: start;
  border: 0;
  background: transparent;
  position: relative;
  z-index: 2;
}
html.haos-ds body.portal-login > header .wrap {
  max-width: none;
  margin: 0;
  padding: 0 0 var(--ds-space-6);
}
html.haos-ds body.portal-login > header .brand {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  color: var(--ds-ink);
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  letter-spacing: normal;
  text-transform: none;
}
html.haos-ds body.portal-login > header .brand::before {
  content: "";
  width: 8px; height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--ds-accent);
  animation: none;
  box-shadow: none;
}
html.haos-ds #main.login-stage { align-self: center; }

/* The portal's Arabic mode set Tahoma on body after this sheet loaded; the
   Arabic face ships with the system and is the one the owner chose. */
html.haos-ds[dir="rtl"] body { font-family: var(--ds-font-sans); }

/* ---- the hero ----------------------------------------------------------- */
html.haos-ds .haos-signin .hero,
html.haos-ds .login-hero {
  max-width: 520px;
  min-width: 0;
  padding: 0;
  text-shadow: none;
}
html.haos-ds .haos-signin h1,
html.haos-ds .login-hero h1 {
  font-size: clamp(40px, 4.4vw, 56px);
  line-height: 1.08;
  font-weight: var(--ds-weight-strong);
  max-width: none;
  margin: 0 0 var(--ds-space-4);
  text-wrap: balance;
}
html.haos-ds:not([dir="rtl"]) .haos-signin h1,
html.haos-ds:not([dir="rtl"]) .login-hero h1 { letter-spacing: -.02em; }

html.haos-ds .haos-signin .tag,
html.haos-ds .login-lede {
  font-size: clamp(17px, 1.4vw, 19px);
  line-height: 1.45;
  color: var(--ds-ink-2);
  margin: 0;
  max-width: 42ch;
  text-wrap: pretty;
}
html.haos-ds .login-ar {
  width: fit-content;
  margin: var(--ds-space-5) 0 0;
  font-family: var(--ds-font-sans);
  font-size: 17px;
  line-height: 1.5;
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink);
}
html.haos-ds .login-status {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  margin: var(--ds-space-7) 0 0;
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink-2);
  letter-spacing: normal;
}
html.haos-ds .haos-signin .brand,
html.haos-ds .login-kicker {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink-2);
  letter-spacing: normal;
  margin: 0 0 var(--ds-space-4);
}
html.haos-ds .haos-signin .brand .mark,
html.haos-ds .login-kicker::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--ds-accent);
  flex: none;
}

/* ---- the card ----------------------------------------------------------- */
/* The one glass surface here, and it is the floating functional layer the
   guideline permits: the depth field and the object are content behind it,
   which is exactly the relationship glass exists for. */
html.haos-ds .haos-signin .card,
html.haos-ds .portal-login .card.login {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 420px;
  min-width: 0;
  box-sizing: border-box;
  margin: 0;
  justify-self: end;
  background: var(--ds-glass-bg);
  -webkit-backdrop-filter: var(--ds-glass-blur);
  backdrop-filter: var(--ds-glass-blur);
  border: 1px solid var(--ds-hairline-soft);
  border-radius: var(--ds-radius-modal);
  box-shadow: var(--ds-shadow-3);
  padding: var(--ds-space-7);
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}
/* ---- the sheen: the light on the glass follows the pointer -------------- */
/* The card is a fixed plane; what moves is the highlight on it. An oversized
   gradient moved by transform only (compositor work, no repaint), written once
   per frame by haos-fleet.js while the pointer moves. It carries no meaning and
   takes no pointer; there is nothing to announce. Fine pointers only, and
   haos-fleet.js never installs it under reduced motion. */
html.haos-ds .card.haos-sheen { overflow: hidden; }
html.haos-ds .card.haos-sheen::after {
  content: "";
  position: absolute;
  inset: -60%;
  pointer-events: none;
  background: radial-gradient(closest-side,
    color-mix(in srgb, var(--ds-accent) 9%, transparent), transparent 72%);
  transform: translate(var(--sheen-x, 0px), var(--sheen-y, 0px));
  opacity: var(--sheen-o, 0);
  transition: opacity var(--ds-dur-enter) var(--ds-ease-in);
  will-change: transform, opacity;
}
html.haos-ds[data-theme="dark"] .card.haos-sheen::after {
  background: radial-gradient(closest-side, rgba(255, 255, 255, .10), transparent 72%);
}
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) .card.haos-sheen::after {
    background: radial-gradient(closest-side, rgba(255, 255, 255, .10), transparent 72%);
  }
}

/* the portal builds its fields inside a <form> inside the card */
html.haos-ds .portal-login .card.login form {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  margin: 0;
}
html.haos-ds .portal-login .card.login .fld {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  margin: 0;
}
html.haos-ds .login-card-kicker {
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink-3);
  letter-spacing: normal;
  margin: 0;
}
html.haos-ds .portal-login .card.login h1,
html.haos-ds .portal-login .card.login h2,
html.haos-ds .haos-signin .card h2 {
  font-size: 28px;
  line-height: 32px;
  font-weight: var(--ds-weight-strong);
  letter-spacing: var(--ds-track-title);
  color: var(--ds-ink);
  margin: 0;
}
html.haos-ds .portal-login .card.login .sub {
  font-size: var(--ds-size-secondary);
  line-height: var(--ds-line-secondary);
  color: var(--ds-ink-2);
  margin: 0 0 var(--ds-space-2);
}
/* One filled action, full width; the secondary is a text link beneath it;
   no hairline above -- the detail-page composition rule does not apply to a
   sign-in form. */
html.haos-ds .portal-login .card.login .actions {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--ds-space-2);
  margin-top: var(--ds-space-2);
  padding-top: 0;
  border-top: 0;
}
html.haos-ds .portal-login .card.login .actions .btn {
  min-height: var(--ds-target);
  border-radius: var(--ds-radius-pill);
  font-size: var(--ds-size-body);
  margin: 0;
}
html.haos-ds .portal-login .card.login .actions .btn:not(.pri) {
  background: transparent;
  color: var(--ds-accent-text);
  font-weight: var(--ds-weight-regular);
  min-height: 36px;
}
html.haos-ds .portal-login .login-trust {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--ds-space-2);
  margin: var(--ds-space-2) 0 0;
  font-size: var(--ds-size-caption);
  line-height: var(--ds-line-caption);
  color: var(--ds-ink-3);
}
html.haos-ds .portal-login .login-trust > :first-child { color: var(--ds-accent); font-size: 8px; }

/* ---- one column ---------------------------------------------------------- */
/* Read what this is, then sign in. Every width-capped block centres by its
   own margins: a max-width inside text-align:center centres the TEXT, not the
   box, which is how the title sat 38px off its siblings. */
@media (max-width: 899px) {
  html.haos-ds body.haos-signin,
  html.haos-ds body.portal-login { position: relative; }
  /* the object becomes a page element here, drawn into the gap between hero
     and card, and scrolls with them */
  canvas.haos-fleet.is-page { position: absolute; top: 0; left: 0; }

  html.haos-ds .haos-signin .hero,
  html.haos-ds .login-hero {
    max-width: 544px;
    justify-self: center;
    text-align: center;
  }
  html.haos-ds .haos-signin h1,
  html.haos-ds .login-hero h1 {
    font-size: clamp(34px, 9vw, 44px);
    margin-inline: auto;
  }
  html.haos-ds .haos-signin .tag,
  html.haos-ds .login-lede { margin-inline: auto; max-width: 36ch; }
  html.haos-ds .login-ar { margin-inline: auto; }
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login { justify-self: center; max-width: 480px; }
}
@media (max-width: 479px) {
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login { padding: var(--ds-space-6) var(--ds-space-5); }
}

html.haos-ds .haos-signin label,
html.haos-ds .portal-login .card.login label {
  font-size: var(--ds-size-label);
  line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
  color: var(--ds-ink-2);
  margin-bottom: calc(var(--ds-space-1) * -1);
}
html.haos-ds .haos-signin input,
html.haos-ds .haos-signin select,
html.haos-ds .portal-login .card.login input {
  width: 100%;
  min-height: var(--ds-target);
  box-sizing: border-box;
  /* 16px, or iOS Safari zooms the whole page on focus -- on the one screen
     where that is most jarring, because the keyboard is already up. */
  font-size: 16px;
  font-family: inherit;
  color: var(--ds-ink);
  background: var(--ds-surface);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-field);
  padding: var(--ds-space-3);
  transition: border-color var(--ds-dur-base) var(--ds-ease),
              box-shadow var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .haos-signin input:focus-visible,
html.haos-ds .portal-login .card.login input:focus-visible {
  outline: none;
  border-color: var(--ds-accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ds-accent) 28%, transparent);
}
html.haos-ds .haos-signin button[type=submit],
html.haos-ds .portal-login .card.login button[type=submit] {
  min-height: var(--ds-target);
  margin-top: var(--ds-space-2);
  border: 0;
  border-radius: var(--ds-radius-pill);
  background: var(--ds-accent);
  color: #fff;
  font-family: inherit;
  font-size: var(--ds-size-body);
  font-weight: var(--ds-weight-strong);
  cursor: pointer;
  transition: opacity var(--ds-dur-base) var(--ds-ease);
}
html.haos-ds .haos-signin button[type=submit]:hover,
html.haos-ds .portal-login .card.login button[type=submit]:hover { opacity: .88; }

html.haos-ds .haos-signin .foot,
html.haos-ds .portal-login .card.login .foot {
  font-size: var(--ds-size-caption);
  line-height: var(--ds-line-caption);
  color: var(--ds-ink-2);
  margin-top: var(--ds-space-2);
}
html.haos-ds .haos-signin .foot a,
html.haos-ds .portal-login .card.login .foot a { color: var(--ds-accent-text); }
/* the recovery frames put a status card here: a paragraph and a link */
html.haos-ds .haos-signin .card p {
  margin: 0;
  font-size: var(--ds-size-secondary);
  line-height: var(--ds-line-secondary);
  color: var(--ds-ink-2);
}
html.haos-ds .haos-signin .card > a { color: var(--ds-accent-text); font-weight: var(--ds-weight-strong); }

html.haos-ds .haos-signin .err,
html.haos-ds .portal-login .card.login .err {
  background: var(--ds-danger-bg);
  color: var(--ds-danger);
  border: 1px solid var(--ds-danger-line);
  border-radius: var(--ds-radius-control);
  padding: var(--ds-space-3);
  font-size: var(--ds-size-secondary);
}

/* The portal built its own drifting mesh before this system existed
   (body:has(.card.login)::before/::after) and its own glass at blur(22-24px).
   With the shared depth field mounted there would be TWO background systems
   moving against each other on one screen. The shared one wins; the older
   pair is retired here rather than deleted from portal.css, so the portal
   still renders correctly on its own if this sheet ever fails to load. */
html.haos-ds body:has(.card.login)::before,
html.haos-ds body:has(.card.login)::after { content: none; }

html.haos-ds body.portal-login .card.login,
html.haos-ds #main .card.login {
  background: var(--ds-glass-bg);
  -webkit-backdrop-filter: var(--ds-glass-blur);
  backdrop-filter: var(--ds-glass-blur);
  border-radius: var(--ds-radius-modal);
  box-shadow: var(--ds-shadow-3);
}


/* ---- sign-in: review pass (2026-09-28) ------------------------------------
   An independent visual critique of six frames (both products, both themes,
   phone to laptop) found: the object read as a loud foreground graphic, not
   atmosphere; the card was opaque, so "glass over the object" never
   happened; the two products' cards had different anatomies (title, field
   height, spacing, footnote, button glow); a red smear in the top corner;
   and on a phone the Sign in button sat below the fold. This block is the
   answer, and it comes after the rules it refines (same or higher
   specificity), before the reduced-transparency fallback that must still win.
   ------------------------------------------------------------------------- */
html.haos-ds {
  --si-glass:  rgba(255, 255, 255, .72);
  --si-edge:   rgba(0, 0, 0, .06);
  --si-shadow: 0 8px 24px rgba(0, 0, 0, .06);
  --si-ring:   color-mix(in srgb, var(--ds-accent-text) 40%, transparent);
}
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) {
    --si-glass:  rgba(28, 28, 30, .72);
    --si-edge:   rgba(255, 255, 255, .10);
    --si-shadow: 0 8px 24px rgba(0, 0, 0, .45);
  }
}
html.haos-ds[data-theme="dark"] {
  --si-glass:  rgba(28, 28, 30, .72);
  --si-edge:   rgba(255, 255, 255, .10);
  --si-shadow: 0 8px 24px rgba(0, 0, 0, .45);
}

/* air at the sides on a laptop: 20px was a stretched phone layout at 1024 */
html.haos-ds body.haos-signin,
html.haos-ds body.portal-login { padding-inline: clamp(20px, 5vw, 64px); }

/* one brand position for both products: a header row, top left, and the
   header's right edge on the card's right edge */
html.haos-ds body.haos-signin { grid-template-rows: auto minmax(0, 1fr); align-content: stretch; }
html.haos-ds body.haos-signin > header,
html.haos-ds body.portal-login > header {
  width: 100%; max-width: 1060px; justify-self: center; align-self: start;
  border: 0; background: transparent; position: relative; z-index: 2;
}
html.haos-ds body.haos-signin > header .wrap {
  max-width: none; margin: 0; padding: 0 0 var(--ds-space-6);
  display: flex; align-items: center; gap: var(--ds-space-3);
}
html.haos-ds body.haos-signin > header .brand {
  display: inline-flex; align-items: center; gap: var(--ds-space-2);
  color: var(--ds-ink); font-size: var(--ds-size-label); line-height: var(--ds-line-label);
  font-weight: var(--ds-weight-strong);
}
html.haos-ds body.haos-signin > header .brand::before {
  content: ""; width: 8px; height: 8px; flex: none; border-radius: 50%; background: var(--ds-accent);
}
html.haos-ds body.portal-login > header .who:empty { display: none; }  /* its flex gap pushed the controls 12px in */
html.haos-ds body.portal-login .portal-controls { gap: var(--ds-space-2); }
html.haos-ds body.portal-login .pref-btn {
  min-height: 36px; min-width: 36px; padding: 0 var(--ds-space-3);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 0; border-radius: var(--ds-radius-pill);
  background: color-mix(in srgb, var(--ds-ink) 6%, transparent);
  color: var(--ds-ink-2); font-size: var(--ds-size-label); font-weight: var(--ds-weight-strong);
}
html.haos-ds body.portal-login .pref-btn:hover { color: var(--ds-ink); background: color-mix(in srgb, var(--ds-ink) 10%, transparent); }
html.haos-ds body.portal-login .pref-btn svg { flex: none; }

/* one hero: headline, a quiet lede; the portal's eyebrow and card eyebrow
   repeated the header's brand -- three labels saying one thing */
html.haos-ds .login-kicker,
html.haos-ds .login-card-kicker { display: none; }
html.haos-ds:not([dir="rtl"]) .haos-signin h1,
html.haos-ds:not([dir="rtl"]) .login-hero h1 { letter-spacing: -.015em; }
html.haos-ds .login-ar {
  margin-top: var(--ds-space-3); font-size: 17px; font-weight: var(--ds-weight-regular); color: var(--ds-ink-2);
}
html.haos-ds .login-status { margin-top: var(--ds-space-6); }
html.haos-ds .login-status .status-dot { margin-inline-start: 5px; }  /* its halo stays inside the text edge */

/* the card: real glass. .72 lets the object through, blurred, so the glass
   has something to do; the edge and a soft shadow separate it -- no glow */
html.haos-ds .haos-signin .card,
html.haos-ds .portal-login .card.login,
html.haos-ds #main .card.login {
  background: var(--si-glass);
  -webkit-backdrop-filter: blur(30px) saturate(160%);
  backdrop-filter: blur(30px) saturate(160%);
  border: 1px solid var(--si-edge);
  box-shadow: var(--si-shadow);
}
/* one anatomy: a title, then fields 8px under their label and 20px apart */
html.haos-ds .haos-signin .card > h2 { margin: 0 0 var(--ds-space-2); }
html.haos-ds .haos-signin .card input + label { margin-top: var(--ds-space-2); }
html.haos-ds .portal-login .card.login form { gap: var(--ds-space-5); }
html.haos-ds .portal-login .card.login .fld { gap: var(--ds-space-2); }
html.haos-ds .haos-signin input,
html.haos-ds .haos-signin select,
html.haos-ds .portal-login .card.login input {
  height: var(--ds-target); min-height: var(--ds-target);
  padding: 0 var(--ds-space-3);
}
html.haos-ds .haos-signin input:focus-visible,
html.haos-ds .haos-signin select:focus-visible,
html.haos-ds .portal-login .card.login input:focus-visible {
  outline: none; border-color: var(--ds-accent); box-shadow: 0 0 0 3px var(--si-ring);
}
html.haos-ds .haos-signin button[type=submit],
html.haos-ds .portal-login .card.login button[type=submit],
html.haos-ds #main .card.login .btn.pri {
  height: var(--ds-target); min-height: var(--ds-target); box-shadow: none; transform: none;
}
/* the link on its own line, the fine print under it */
html.haos-ds .haos-signin .foot {
  text-align: center; font-size: var(--ds-size-caption); line-height: var(--ds-line-caption); color: var(--ds-ink-3);
}
html.haos-ds .haos-signin .foot br { display: none; }
html.haos-ds .haos-signin .foot a {
  display: block; margin: 0 0 var(--ds-space-5); font-size: var(--ds-size-secondary);
  font-weight: var(--ds-weight-regular); text-decoration: none;
}
html.haos-ds .haos-signin .foot a:hover { text-decoration: underline; }
html.haos-ds .portal-login .login-trust { justify-content: center; color: var(--ds-ink-3); }
html.haos-ds .portal-login .login-trust > [aria-hidden="true"] { display: none; }

/* the atmosphere: the red wash read as a smudge (maroon in dark); one faint
   accent wash is enough behind an object that is itself the atmosphere */
html.haos-ds .haos-signin .haos-depth-mid,
html.haos-ds body.portal-login .haos-depth-mid { display: none; }
html.haos-ds .haos-signin .haos-depth-far,
html.haos-ds body.portal-login .haos-depth-far {
  background: radial-gradient(48vmax 40vmax at 18% 12%,
    color-mix(in srgb, var(--ds-accent) 5%, transparent), transparent 62%);
}

/* a phone: the whole form above the fold. The object sits BEHIND the card
   (haos-fleet.js), so no gap is reserved for it; the Arabic line (the header
   toggle covers it) and the status line go */
@media (max-width: 899px) {
  html.haos-ds .haos-signin-stage,
  html.haos-ds .login-stage { row-gap: var(--ds-space-6); }
  html.haos-ds .haos-signin h1,
  html.haos-ds .login-hero h1 { font-size: clamp(30px, 8vw, 40px); }
  html.haos-ds .haos-signin .tag,
  html.haos-ds .login-lede { font-size: 16px; }
  html.haos-ds .login-ar,
  html.haos-ds .login-status { display: none; }
  html.haos-ds body.portal-login .pref-btn { min-width: 44px; min-height: 44px; padding: 0; }
  html.haos-ds body.portal-login .pref-btn .pref-label { display: none; }
  html.haos-ds body.portal-login > header .wrap { flex-wrap: nowrap; }
}

@media (prefers-reduced-transparency: reduce) {
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login,
  html.haos-ds #main .card.login {
    background: var(--ds-surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}


/* ---- sign-in: review round 2 (2026-09-28) ---------------------------------
   Round 2 confirmed most of the first pass and named what was left: the glass
   still did not show the object through it (the object sat BESIDE the card),
   the pointer sheen painted over the button, the focus ring was under 3:1,
   the phone object was reduced to edge dust, and the products still differed
   (header controls, card lede, field-to-button gap, brand height). */

/* real glass: less opaque and less blur, over an object that is now centred
   BEHIND the card (haos-fleet.js), so blurred structure shows through */
html.haos-ds { --si-glass: rgba(255, 255, 255, .66); }
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) { --si-glass: rgba(28, 28, 30, .66); }
}
html.haos-ds[data-theme="dark"] { --si-glass: rgba(28, 28, 30, .66); }
html.haos-ds .haos-signin .card,
html.haos-ds .portal-login .card.login,
html.haos-ds #main .card.login {
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
}
/* the sheen lies UNDER the card's content: as the card's last box it had
   been painting over the button (read as a gradient fill in dark mode) */
html.haos-ds .card.haos-sheen::after { z-index: -1; }

/* the depth washes are retired here: the object IS the atmosphere, and the
   one remaining wash showed a banded arc edge in light mode */
html.haos-ds body.haos-signin .haos-depth,
html.haos-ds body.portal-login .haos-depth { display: none; }

/* one header for both products: the same height whether or not it holds
   controls, so the brand sits at the same y on both */
html.haos-ds body.haos-signin > header .wrap,
html.haos-ds body.portal-login > header .wrap { min-height: 44px; align-items: center; }
html.haos-ds body.haos-signin .pref-btn,
html.haos-ds body.portal-login .pref-btn {
  position: relative;
  min-height: 36px; min-width: 36px; padding: 0 var(--ds-space-3);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: 0; border-radius: var(--ds-radius-pill);
  background: color-mix(in srgb, var(--ds-ink) 6%, transparent);
  color: var(--ds-ink-2); font: var(--ds-weight-strong) var(--ds-size-label)/1 var(--ds-font-sans);
  cursor: pointer;
}
html.haos-ds body.haos-signin .pref-btn:hover,
html.haos-ds body.portal-login .pref-btn:hover { color: var(--ds-ink); background: color-mix(in srgb, var(--ds-ink) 10%, transparent); }
/* 36px to the eye, 44px to the finger */
html.haos-ds body.haos-signin .pref-btn::after,
html.haos-ds body.portal-login .pref-btn::after { content: ""; position: absolute; inset: -4px; }
html.haos-ds body.haos-signin > header .pref-btn { margin-inline-start: auto; }

/* one card anatomy: title, a one-line lede, fields, 28px to the button */
html.haos-ds .haos-signin .card > .sub {
  margin: calc(var(--ds-space-2) * -1) 0 var(--ds-space-2);
  font-size: var(--ds-size-secondary); line-height: var(--ds-line-secondary); color: var(--ds-ink-2);
}
html.haos-ds .haos-signin button[type=submit] { margin-top: var(--ds-space-4); }
/* fields: neutral at rest in both themes; the accent is spent on focus only,
   as a 2px solid ring (>= 3:1 against the card in both themes) */
html.haos-ds .haos-signin input,
html.haos-ds .haos-signin select,
html.haos-ds .portal-login .card.login input {
  border-color: color-mix(in srgb, var(--ds-ink) 16%, transparent);
}
html.haos-ds .haos-signin input:focus-visible,
html.haos-ds .haos-signin select:focus-visible,
html.haos-ds .portal-login .card.login input:focus-visible {
  border-color: var(--ds-accent-text);
  box-shadow: 0 0 0 2px var(--ds-accent-text);
}
/* fine print: centred and balanced, no orphans */
html.haos-ds .haos-signin .foot,
html.haos-ds .portal-login .login-trust,
html.haos-ds .portal-login .login-trust > span { text-align: center; text-wrap: balance; }

/* a phone: the object gets the gap between the headline and the card (its
   lower part runs behind the card's glass); the page starts at the top
   rather than floating in the middle of the viewport */
@media (max-width: 899px) {
  html.haos-ds .haos-signin-stage,
  html.haos-ds .login-stage { row-gap: 110px; align-self: start; }
  html.haos-ds #main.login-stage { align-self: start; }
  html.haos-ds .haos-signin .tag,
  html.haos-ds .login-lede { max-width: 34ch; text-wrap: balance; }
  html.haos-ds body.haos-signin .pref-btn,
  html.haos-ds body.portal-login .pref-btn { min-width: 44px; min-height: 44px; padding: 0; }
  html.haos-ds body.haos-signin .pref-btn::after,
  html.haos-ds body.portal-login .pref-btn::after { inset: 0; }
}


/* ---- sign-in: review round 3 (2026-09-28) ----------------------------------
   Round 3 found: the HAOS theme control stretched into a full-width empty
   capsule (the page's no-sheet floor sets button{width:100%} for its form);
   the two brands at different heights (the portal's .brand has a legacy
   min-height, HAOS's has none, and min-height counted the row's padding);
   the lattice still not traceable through the glass; resting field borders
   tinted in light and at 1.7:1 in dark; the sheen too strong at 1024. */

/* a header control is never full-width */
html.haos-ds body.haos-signin .pref-btn,
html.haos-ds body.portal-login .pref-btn { width: auto; flex: none; align-self: center; }
/* one header row on both products: 44px of CONTENT, brand and controls
   centred in it, 24px under it */
html.haos-ds body.haos-signin > header .wrap,
html.haos-ds body.portal-login > header .wrap {
  box-sizing: content-box; height: 44px; min-height: 0;
  padding: 0 0 var(--ds-space-6); align-items: center;
}
html.haos-ds body.haos-signin > header .brand,
html.haos-ds body.portal-login > header .brand {
  height: 44px; min-height: 0; display: inline-flex; align-items: center;
  margin: 0;   /* the old hero-brand rule's 16px bottom margin lifted a centred brand 8px */
}

/* fields: a neutral edge at >= 3:1 in both themes (a 1px boundary the eye
   needs to find the field), the accent only on focus */
html.haos-ds { --si-field-edge: #8E8E93; }
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) { --si-field-edge: #636366; }
}
html.haos-ds[data-theme="dark"] { --si-field-edge: #636366; }
html.haos-ds .haos-signin input,
html.haos-ds .haos-signin select,
html.haos-ds .portal-login .card.login input { border-color: var(--si-field-edge); }
html.haos-ds .haos-signin input:focus-visible,
html.haos-ds .haos-signin select:focus-visible,
html.haos-ds .portal-login .card.login input:focus-visible { border-color: var(--ds-accent-text); }

/* glass you can see through: less blur and a little less fill, so the soft
   light behind the card has shape. Deliberately NOT clear enough to trace the
   lattice's lines: measured, that needs a ~2px blur, and a line passing under
   the card's 12px labels then takes them below 4.5:1. Frosted light keeps
   every label AA; glass_check.js measures the spread (2.2-3.2 vs 0 opaque). */
html.haos-ds { --si-glass: rgba(255, 255, 255, .62); }
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) { --si-glass: rgba(28, 28, 30, .60); }
}
html.haos-ds[data-theme="dark"] { --si-glass: rgba(28, 28, 30, .60); }
html.haos-ds .haos-signin .card,
html.haos-ds .portal-login .card.login,
html.haos-ds #main .card.login {
  -webkit-backdrop-filter: blur(10px) saturate(180%);
  backdrop-filter: blur(10px) saturate(180%);
}
/* the sheen: a hint, not a wash */
html.haos-ds .card.haos-sheen::after {
  inset: -30%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--ds-accent) 5%, transparent), transparent 72%);
}
html.haos-ds[data-theme="dark"] .card.haos-sheen::after {
  background: radial-gradient(closest-side, rgba(255, 255, 255, .06), transparent 72%);
}
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) .card.haos-sheen::after {
    background: radial-gradient(closest-side, rgba(255, 255, 255, .06), transparent 72%);
  }
}

/* the HAOS hero carries what the portal's does: a lede and an Arabic line */
html.haos-ds .haos-signin .hero .ar {
  margin: var(--ds-space-3) 0 0; font-size: 17px; line-height: 1.5;
  font-weight: var(--ds-weight-regular); color: var(--ds-ink-2); width: fit-content;
}
html.haos-ds .haos-signin .foot { letter-spacing: normal; }

@media (max-width: 899px) {
  html.haos-ds .haos-signin-stage,
  html.haos-ds .login-stage { row-gap: 140px; }
  html.haos-ds .haos-signin .hero .ar { display: none; }
  html.haos-ds .haos-signin .hero .ar,
  html.haos-ds .haos-signin .tag { margin-inline: auto; }
}


/* ---- sign-in: review round 4 (2026-09-28) ----------------------------------
   "Close to premium -- unify the field border token, fit the phone globe in
   its gap, fix the sign-/out break." Two of the gaps were the cascade:
   - the design system's card-form rule
     (html.haos-ds .card input:not([type=checkbox]):not([type=radio]), 0,4,2)
     outranked the HAOS field rule, so HAOS fields kept the tinted hairline
     while the portal's -- whose selector ties it and comes later -- got the
     neutral edge. Measured: HAOS rgba(8,129,160,.22) vs portal #8E8E93.
   - the composition layer's `.card { border: 0 !important }` removed the
     card's edge, so the glass had only a shadow. The edge is a ring now. */
html.haos-ds .haos-signin .card input:not([type=checkbox]):not([type=radio]),
html.haos-ds .haos-signin .card select { border-color: var(--si-field-edge); }
html.haos-ds .haos-signin .card input:not([type=checkbox]):not([type=radio]):focus-visible,
html.haos-ds .haos-signin .card select:focus-visible {
  outline: none; border-color: var(--ds-accent-text); box-shadow: 0 0 0 2px var(--ds-accent-text);
}

html.haos-ds { --si-hi: rgba(255, 255, 255, .7); }
@media (prefers-color-scheme: dark) {
  html.haos-ds:not([data-theme="light"]) { --si-hi: rgba(255, 255, 255, .08); }
}
html.haos-ds[data-theme="dark"] { --si-hi: rgba(255, 255, 255, .08); }
html.haos-ds .haos-signin .card,
html.haos-ds .portal-login .card.login,
html.haos-ds #main .card.login {
  box-shadow: 0 0 0 1px var(--si-edge), inset 0 1px 0 var(--si-hi), var(--si-shadow);
}

/* the theme control: one form on both products -- icon + label on a desk,
   icon only on a phone */
@media (max-width: 899px) {
  html.haos-ds body.haos-signin .pref-btn .pref-label { display: none; }
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login { padding: var(--ds-space-6); }
}
/* the sheen: half as strong where the card fills more of the screen */
@media (max-width: 1100px) {
  html.haos-ds .card.haos-sheen::after {
    background: radial-gradient(closest-side, color-mix(in srgb, var(--ds-accent) 2.5%, transparent), transparent 72%);
  }
}


/* ---- sign-in: review round 5 polish (2026-09-29) --------------------------- */
html.haos-ds .haos-signin .card > .sub,
html.haos-ds .portal-login .card.login .sub { text-wrap: balance; }
html.haos-ds .haos-signin .foot a { margin-bottom: var(--ds-space-4); }
html.haos-ds .portal-login .login-trust { margin-top: 0; }
@media (max-width: 899px) {
  html.haos-ds body.haos-signin > header .wrap,
  html.haos-ds body.portal-login > header .wrap { padding-bottom: var(--ds-space-7); }
}

/* One orchestrated arrival rather than scattered effects: hero and card rise
   together, 80ms apart -- Apple's own primary stagger. */
@media (prefers-reduced-motion: no-preference) {
  html.haos-ds .haos-signin .hero,
  html.haos-ds .login-hero,
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login {
    animation: haosSigninIn var(--ds-dur-enter) var(--ds-ease-in) both;
  }
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login { animation-delay: 80ms; }
  @keyframes haosSigninIn {
    from { opacity: 0; transform: translate3d(0, 12px, 0); }
    to   { opacity: 1; transform: none; }
  }
}

@media (prefers-reduced-transparency: reduce) {
  html.haos-ds .haos-signin .card,
  html.haos-ds .portal-login .card.login {
    background: var(--ds-surface);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .haos-depth-near { display: none; }
}

/* =========================================================================
   MOTION GRAMMAR — three families on the three duration tokens, so a motion
   says what KIND of thing just happened (council 057eaa8c4ed7, 2026-09-21):
     navigation          --ds-dur-slow  240ms  --ds-ease     the place changed
     direct manipulation --ds-dur-base  160ms  --ds-ease     you did that
     status              --ds-dur-enter 400ms  --ds-ease-in  the system did that
   Anything decorative is outside the grammar and must not borrow its tokens.
   ========================================================================= */

/* ---- cross-document view transitions: OFF (owner, 2026-09-24) ------------
   The 2026-09-21 fade froze the OLD page until the new one could paint, then
   ran a 240ms fade during which the new page was not interactive -- on a
   real link that read as lag on every page switch, and the owner asked for
   every transition that slows navigation to go. The rail no longer needs a
   hold: haos-boot.js reserves it and paints the cached nav before the first
   paint, so a plain document swap is already still. Declared here (not just
   absent) because the sign-in family and any page sheet must not re-enable it. */
@view-transition { navigation: none; }

/* The bar's mount reserves the bar's height before the session answers, so
   the page never shifts 44px when the bar arrives -- with or without a
   transition. */
html.haos-ds:not([data-haos-side]) #nav:not([data-haos-nav="ready"]) { min-height: 44px; }

/* ---- the sidebar's sheet (moved here from haos-session.js SIDE_CSS, 2026-09-24 pm) ----
   The rail is painted BEFORE haos-session.js runs: the inline boot script paints
   the tab's cached nav pre-paint. Its styling therefore has to live in THIS
   render-blocking sheet, not in a string that script injects from the end of
   the body -- for as long as the 153 KB script took to arrive, the nav painted
   as raw links (the owner's photo, 2026-09-24). ONE source now: haos-session.js
   injects only NAV_CSS. Tokens keep their fallbacks (29 page vocabularies);
   test_sidebar pins that. Kept minified, one rule per line: the contract tests
   match these strings verbatim. */
/* @sidebar-sheet */
html[data-haos-side]{--haos-side-w:264px}
html[data-haos-side=compact]{--haos-side-w:88px}
html[data-haos-side=drawer]{--haos-side-w:0px}
html[data-haos-side]{padding-inline-start:var(--haos-side-w);--haos-top-offset:0px}
html[data-haos-side].haos-side-anim{transition:padding-inline-start var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1))}
html[data-haos-side=drawer]{padding-top:48px;--haos-top-offset:48px}
/* R-048 (review 2026-09-25, UXIA-05): 13 pages opt into viewport-fit=cover
   (the PWA head already sets apple-mobile-web-app-status-bar-style:
   black-translucent) with no safe-area padding anywhere but sign-in -- the
   fixed mobile top bar sat under the notch/status-bar cutout on those devices.
   ONE central fix on the bar itself, added as an override AFTER the pinned
   rule above (test_sidebar matches that string verbatim; this line changes
   the cascade's WINNING value for padding-top/--haos-top-offset without
   touching it) -- every page that loads this sheet is fixed, none per-page. */
html[data-haos-side=drawer]{padding-top:calc(48px + env(safe-area-inset-top,0px));--haos-top-offset:calc(48px + env(safe-area-inset-top,0px))}
html[data-haos-side] #nav .haos-nav,html[data-haos-side] header.bar,html[data-haos-side] header.topbar,html[data-haos-side] .atlas-chats-tab{display:none!important}
.haos-side{position:fixed;top:0;bottom:0;inset-inline-start:0;width:var(--haos-side-w);z-index:900;display:flex;flex-direction:column;box-sizing:border-box;background:var(--ds-glass-bg,color-mix(in srgb,var(--bg,#101014) 84%,transparent));backdrop-filter:var(--ds-glass-blur,saturate(180%) blur(20px));-webkit-backdrop-filter:var(--ds-glass-blur,saturate(180%) blur(20px));border-inline-end:1px solid var(--ds-hairline-soft,var(--border,var(--line,rgba(128,128,128,.25))));color:var(--ds-ink,var(--ink,var(--text,inherit)));transition:width var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1)),transform var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1))}
@supports not (backdrop-filter:blur(1px)){.haos-side,.haos-mtop,.haos-side-peek{background:var(--ds-canvas,var(--bg,#101014))}
}
@media(prefers-reduced-transparency:reduce){.haos-side,.haos-mtop,.haos-side-peek{background:var(--ds-canvas,var(--bg,#101014));backdrop-filter:none;-webkit-backdrop-filter:none}
}
.haos-side-head{display:flex;align-items:center;gap:8px;min-height:56px;padding:0 10px 0 16px;flex:none}
.haos-side-brand{display:flex;align-items:center;gap:8px;font:inherit;font-weight:600;font-size:15px;letter-spacing:-.012em;color:inherit;text-decoration:none;white-space:nowrap;min-width:0;border:0;background:transparent;cursor:pointer;padding:6px 8px;border-radius:10px}
.haos-side-brand:hover{background:color-mix(in srgb,var(--ds-accent,var(--accent,#0881A0)) 10%,transparent)}
.haos-side-brand .haos-dot{flex:none}
.haos-side-toggle{display:flex;align-items:center;gap:10px;width:100%;min-height:38px;padding:0 10px;border-radius:10px;border:0;background:transparent;color:var(--ds-ink-2,var(--ink-2,var(--text-2,var(--muted,inherit))));cursor:pointer;font:inherit;font-size:13px;text-align:start;box-sizing:border-box}
.haos-side-toggle .haos-side-ico{flex:none;width:20px;display:inline-flex;justify-content:center}
.haos-side-toggle:hover,.haos-side-peek:hover{background:color-mix(in srgb,var(--ds-accent,var(--accent,#0881A0)) 10%,transparent)}
[dir=rtl] .haos-side-toggle svg,[dir=rtl] .haos-side-peek svg,[dir=rtl] .haos-side-item .haos-side-ico svg.chev{transform:scaleX(-1)}
.haos-side-nav{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:6px 8px;display:flex;flex-direction:column;gap:2px;scrollbar-width:thin}
.haos-side-item{display:flex;align-items:center;gap:10px;min-height:38px;padding:0 10px;border-radius:10px;color:var(--ds-ink-2,var(--ink-2,var(--text-2,var(--muted,inherit))));text-decoration:none;font:inherit;font-size:13.5px;white-space:nowrap;background:transparent;border:0;cursor:pointer;width:100%;text-align:start;box-sizing:border-box;transition:background var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1)),color var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1))}
.haos-side-item:hover{color:var(--ds-ink,var(--ink,var(--text,inherit)));background:color-mix(in srgb,var(--ds-accent,var(--accent,#0881A0)) 10%,transparent)}
.haos-side-item.active{font-weight:600;color:var(--ds-accent-text,var(--accent,#4F46E5));background:var(--ds-accent-tint,color-mix(in srgb,var(--accent,#4F46E5) 14%,transparent))}
.haos-side-item:focus-visible,.haos-side-toggle:focus-visible,.haos-side-peek:focus-visible,.haos-mtop-menu:focus-visible{outline:2px solid var(--ds-accent,var(--accent,#4F46E5));outline-offset:2px}
.haos-side-item[hidden],.haos-side-scrim[hidden]{display:none!important}
.haos-side-ico{flex:none;width:20px;display:inline-flex;align-items:center;justify-content:center}
.haos-side-item .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.haos-side-caret{flex:none;font-size:11px;opacity:.6;transition:transform var(--ds-dur-base,160ms) var(--ds-ease,cubic-bezier(.4,0,.6,1))}
.haos-side-group[aria-expanded=true] .haos-side-caret{transform:rotate(180deg)}
.haos-side-sub{display:none;flex-direction:column;gap:1px;margin:2px 0 4px 19px;padding-inline-start:12px;border-inline-start:1px solid var(--ds-hairline-soft,var(--border,var(--line,rgba(128,128,128,.25))))}
[dir=rtl] .haos-side-sub{margin:2px 19px 4px 0}
.haos-side-group[aria-expanded=true] .haos-side-sub{display:flex}
/* A sub-item has its OWN prefixed class (2026-09-24): pages style a generic `.sub` — their subtitle —
   and that rule reached these items (tracker.html centred them, 24 other pages added 14-18px gaps and
   their own colour and size). Labels are centred in the space beside the guide line, on every page.
   The [data-boot] form styles a pre-paint shell painted from a cache written before the rename. */
.haos-side-item.haos-side-subitem,.haos-side[data-boot] .haos-side-item.sub{min-height:32px;font-size:13px;padding:0 10px;justify-content:center;text-align:center}
.haos-side-item.haos-side-subitem .lbl,.haos-side[data-boot] .haos-side-item.sub .lbl{flex:0 1 auto;text-align:center}
/* the on-colour belongs to the SHEET's accent, never a page's --on-accent: several pages set that to a dark ink
   for their own lighter dark-mode accent, which put near-black text on this teal (3.86:1) — 2026-09-24 */
.haos-side-item.haos-side-subitem.active,.haos-side-item.haos-side-subitem.active:hover,.haos-side[data-boot] .haos-side-item.sub.active{background:var(--ds-accent,var(--accent,#4F46E5));color:var(--ds-on-accent,#fff)}
.haos-side-foot{flex:none;padding:8px;border-top:1px solid var(--ds-hairline-soft,var(--border,var(--line,rgba(128,128,128,.25))));display:flex;flex-direction:column;gap:6px}
.haos-side-tools{display:flex;flex-wrap:wrap;gap:6px;align-items:center;font-size:12px;padding:0 6px}
.haos-side-tools:empty{display:none}
.haos-side .haos-user{display:flex;align-items:center;gap:10px;margin:0;padding:4px 6px;font-size:12px;flex:none;color:var(--ds-ink-2,var(--ink-2,var(--text-2,var(--muted,inherit))))}
.haos-side .haos-user .haos-clock{flex:1;min-width:0;text-align:center}
.haos-side .hbell-dd{top:auto;bottom:0;right:auto;left:calc(100% + 12px)}
[dir=rtl] .haos-side .hbell-dd{left:auto;right:calc(100% + 12px)}
html[data-haos-side=compact] .haos-side-head{justify-content:center;padding:10px 0 4px;min-height:0}
html[data-haos-side=compact] .haos-side-brand{flex-direction:column;gap:3px;font-size:11px;padding:6px 4px}
html[data-haos-side=compact] .haos-side-toggle{justify-content:center;padding:6px 2px}
html[data-haos-side=compact] .haos-side-toggle .lbl{display:none}
html[data-haos-side=compact] .haos-side-nav{padding:6px 6px}
html[data-haos-side=compact] .haos-side-item{flex-direction:column;gap:3px;padding:6px 2px;min-height:52px;justify-content:center;white-space:normal}
html[data-haos-side=compact] .haos-side-item .lbl{flex:none;font-size:10.5px;line-height:1.15;text-align:center;max-width:76px;max-height:2.3em;overflow:hidden;text-overflow:ellipsis}
html[data-haos-side=compact] .haos-side-caret,html[data-haos-side=compact] .haos-side-sub{display:none!important}
html[data-haos-side=compact] .haos-side-tools{display:none}
html[data-haos-side=compact] .haos-side .haos-user{flex-direction:column;gap:8px;padding:6px 0}
html[data-haos-side=compact] .haos-side .haos-user .haos-clock{font-size:10px}
.haos-mtop{display:none;position:fixed;top:0;left:0;right:0;height:calc(48px + env(safe-area-inset-top,0px));z-index:880;align-items:center;gap:10px;padding:env(safe-area-inset-top,0px) 12px 0;box-sizing:border-box;background:var(--ds-glass-bg,color-mix(in srgb,var(--bg,#101014) 84%,transparent));backdrop-filter:var(--ds-glass-blur,saturate(180%) blur(20px));-webkit-backdrop-filter:var(--ds-glass-blur,saturate(180%) blur(20px));border-bottom:1px solid var(--ds-hairline-soft,var(--border,var(--line,rgba(128,128,128,.25))));color:var(--ds-ink,var(--ink,var(--text,inherit)))}
.haos-mtop .haos-side-brand{font-size:15px}
.haos-mtop .haos-user{margin-inline-start:auto;display:flex;align-items:center;gap:8px;font-size:12px}
.haos-mtop-menu{flex:none;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;border:1px solid var(--ds-hairline-soft,var(--border,var(--line,rgba(128,128,128,.25))));background:transparent;color:inherit;cursor:pointer;padding:0}
.haos-side-scrim{position:fixed;inset:0;z-index:890;background:rgba(0,0,0,.28);border:0;padding:0;margin:0;cursor:default}
html[data-haos-side=drawer] .haos-mtop{display:flex}
html[data-haos-side=drawer] .haos-side{width:min(288px,86vw);transform:translateX(-100%);box-shadow:none;visibility:hidden;pointer-events:none}
html[dir=rtl][data-haos-side=drawer] .haos-side{transform:translateX(100%)}
html[data-haos-side=drawer][data-haos-side-open] .haos-side{transform:none;box-shadow:0 0 44px rgba(0,0,0,.28);visibility:visible;pointer-events:auto}
html[data-haos-side=drawer] .haos-side-toggle{display:none}
html[data-haos-side-open] .atlas-root,html[data-haos-side-open] .haos-a2hs{display:none!important}
@media(max-width:420px){.haos-mtop .haos-clock{font-size:11px}
}
@media(pointer:coarse){.haos-side-item{min-height:44px}
.haos-side-item.haos-side-subitem{min-height:40px}
.haos-side-toggle{width:40px;height:40px}
}
@media(prefers-reduced-motion:reduce){.haos-side,html[data-haos-side],.haos-side-item,.haos-side-caret{transition:none}
}
@media print{.haos-side,.haos-side-peek,.haos-mtop,.haos-side-scrim{display:none!important}
html[data-haos-side]{padding:0}
}
.haos-dot{width:5px;height:5px;border-radius:50%;background:var(--ds-accent,var(--accent,#4F46E5))}
/* @end-sidebar-sheet */

@media (prefers-reduced-motion: reduce) {
  /* no transition object is created at all; the pseudo rules are the floor
     for an engine that does not honour a nested @view-transition */
  @view-transition { navigation: none; }
  ::view-transition-group(*),
  ::view-transition-image-pair(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
@media print {
  ::view-transition { display: none; }
}

/* The portal's own sheet still paints body from its legacy --bg; the canvas
   is the system's, on both surfaces. */
html.haos-ds body.portal-login,
html.haos-ds body:has(> #main.wrap) { background: var(--ds-canvas); }


/* ---------------------------------------------------------------- print --- */
@media print {
  html.haos-ds .ds-nav,
  html.haos-ds .ds-toast,
  html.haos-ds .ds-scrim { display: none !important; }
  html.haos-ds body { background: #fff; color: #000; }
}
