/* ============================================================================
   dswd-ux-rules.css — the UX/UI Do's and Don'ts, applied system-wide

   Rather than editing forty pages by hand, the rules that are *systemic*
   (colour, spacing, tap targets, hierarchy, typography, affordance) are
   enforced here in one place. Rule numbers refer to the reference guide.

   Loaded last, after every other stylesheet, so it wins on cascade order
   without needing !important on everything.
   ========================================================================= */

/* ═══════════════════════════════════════════════════════════════════════
   COLOUR — rules 43, 44, 45, 46, 47, 48
   No pure #000 or #FFF anywhere: near-black and near-white read as softer
   and cause less eye strain at the long sittings this system gets used for.
   ═══════════════════════════════════════════════════════════════════════ */
:root{
  /* 45 — near-black / near-white instead of pure values */
  --ux-ink:        #10141c;   /* was #000 */
  --ux-ink-soft:   #3b4658;
  --ux-ink-muted:  #6b7789;
  --ux-paper:      #fbfcfd;   /* was #fff, for page backgrounds */
  --ux-surface:    #fdfefe;   /* was #fff, for cards */
  --ux-line:       #e3e9f1;

  /* 46, 47 — a deliberately small palette: two brand colours, one accent,
     plus semantic states. Everything else is a tint of these. */
  --ux-navy:       #12386f;
  --ux-navy-deep:  #0b2545;
  --ux-green:      #1a6e4e;
  --ux-gold:       #d9a800;

  /* 31 — destructive is always red, never the brand colour */
  --ux-danger:     #c0392b;
  --ux-warn:       #b45309;
  --ux-ok:         #15803d;
  --ux-info:       #1d4ed8;

  /* 64 — a spacing scale, not arbitrary pixels. Within a group: 8–12px.
     Between groups: 24–32px. Proximity does the grouping (85). */
  --ux-gap-xs: 6px;
  --ux-gap-sm: 12px;
  --ux-gap-md: 20px;
  --ux-gap-lg: 32px;
  --ux-gap-xl: 48px;

  /* 66 — nested radii step down so curves don't fight */
  --ux-radius-lg: 16px;   /* outer container */
  --ux-radius-md: 12px;   /* card inside container */
  --ux-radius-sm: 8px;    /* control inside card */

  /* 68 — soft, evenly diffused shadows, never hard one-sided drops */
  --ux-shadow-sm: 0 1px 3px rgba(16,20,28,.05), 0 1px 2px rgba(16,20,28,.04);
  --ux-shadow-md: 0 4px 16px rgba(16,20,28,.07), 0 2px 6px rgba(16,20,28,.04);
  --ux-shadow-lg: 0 12px 36px rgba(16,20,28,.10), 0 4px 12px rgba(16,20,28,.05);

  /* 87 — Fitts's law: nothing tappable below 44px */
  --ux-tap: 44px;
}

/* 45 — retire pure values wherever they were used as page or text colour. */
body{ background-color: var(--ux-paper); color: var(--ux-ink); }

/* 44, 48 — dark surfaces get desaturated accents; light surfaces get vivid
   ones. The same hex on both reads harsh on dark and washed out on light. */
@media (prefers-color-scheme: dark){
  :root{ --ux-paper:#0f1219; --ux-surface:#161b24; --ux-ink:#e8ecf2; --ux-line:#252c38; }
}

/* ═══════════════════════════════════════════════════════════════════════
   TYPOGRAPHY — rules 49, 50, 51
   ═══════════════════════════════════════════════════════════════════════ */

/* 49, 50 — one readable sans-serif family, used everywhere. Decorative and
   display faces are removed from UI text entirely. */
body, button, input, select, textarea, .btn{
  font-family: 'Public Sans', 'DM Sans', 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

/* 51 — anything four lines or longer is left-aligned; a centred paragraph
   gives the eye a different starting point on every line. Headings, single
   lines and deliberately centred blocks keep their alignment. */
p, li, dd, .ux-prose, .story-body, .prog-deepdive p, .dd-lead,
.news-excerpt, .ann-excerpt, .field-hint{
  text-align: left;
}
.text-center > p:not(.ux-keep-center),
p.text-center:not(.ux-keep-center){
  text-align: left;
}
.text-center > p.section-lead,
.text-center > .lead{ text-align: center; }   /* short leads stay centred */

/* 56 — numerals, not spelled-out numbers, are the house style. Enforced in
   copy; this just makes them scan properly. */
.ux-figure, .kpi-val, .stat-value, .mod-stat-val{ font-variant-numeric: tabular-nums; }

/* ═══════════════════════════════════════════════════════════════════════
   FORMS & INPUTS — rules 2, 3, 4, 7, 82
   ═══════════════════════════════════════════════════════════════════════ */

/* 2, 3 — a label sits directly above its input, and every label/input pair
   lines up on the same left edge down the whole column. */
.field, .form-group, .ux-field{ display:block; margin-bottom: var(--ux-gap-sm); }
.field > label, .form-group > label, .ux-field > label{
  display:block;
  margin: 0 0 6px;
  text-align: left;
  font-weight: 700;
  line-height: 1.4;
}

/* 7 — inputs are boxes, never bare underlines. An underline doesn't read as
   an interactive field. */
/* Base look for a bare input, wrapped in :where() so its specificity is zero.

   This matters: ten wrappers in this codebase place an icon over the left of
   an input and reserve room for it with a large padding-left — .input-grp on
   the login and registration forms, the sidebar searches, the service,
   directory, news and help searches. At normal specificity the flat
   `padding: 11px 14px` below tied with those rules and, loading later, won a
   cascade it had no business winning — so the glyph ended up sitting on top of
   the placeholder text. The same applied to `background`, which repainted the
   dark sidebar searches white.

   At zero specificity any component that styles its own fields simply wins,
   with no !important needed anywhere. */
:where(input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
input[type="number"], input[type="search"], input[type="date"], input[type="url"],
select, textarea){
  border: 1.5px solid var(--ux-line);
  border-radius: var(--ux-radius-sm);
  background: var(--ux-surface);
  padding: 11px 14px;
  width: 100%;
}

/* The tap target is an accessibility floor rather than a matter of taste, so
   it is enforced at normal specificity (rule 87). */
input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
input[type="number"], input[type="search"], input[type="date"], input[type="url"],
select, textarea{
  min-height: var(--ux-tap);
}

/* …with the exception of controls that are deliberately compact and sit in a
   dense panel, where a 44px field would tower over its neighbours. These are
   still comfortably clickable. */
.dswd-sb-search input, .cz-search input, .dswd-topsearch input,
.mod-search input, .dir-search input, .cs-search input{
  min-height: 38px;
}
input[style*="border-bottom"]:not([style*="border:"]),
.form-underline input{
  border: 1.5px solid var(--ux-line) !important;
  border-radius: var(--ux-radius-sm) !important;
}

/* Focus is unmistakable — 3px ring, never a removed outline. */
input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible, [tabindex]:focus-visible{
  outline: 3px solid var(--ux-navy);
  outline-offset: 2px;
}

/* 82 — controls sized for fingers, not a cursor. */
input[type="checkbox"], input[type="radio"]{
  width: 20px; height: 20px;
  accent-color: var(--ux-navy);
  cursor: pointer;
}
/* The label is part of the target, not just the box itself (25) — but only
   where the label is genuinely a text label. A toggle switch is a label
   wrapping a hidden checkbox and a styled track; forcing 44px onto it turned
   a 42x24 pill into a 42x44 blob with a 24px radius, which is why several of
   these were reading as circles rather than switches. */
label:has(> input[type="checkbox"]):not(.mod-toggle):not(.ux-switch):not(.switch),
label:has(> input[type="radio"]):not(.mod-toggle):not(.ux-switch):not(.switch){
  display: inline-flex; align-items: center; gap: 9px;
  min-height: var(--ux-tap); cursor: pointer;
}

/* Switch tracks keep their own geometry, and the input inside them stays
   hidden — the blanket checkbox sizing below was giving it a visible 20px
   box sitting on top of the track. */
.mod-toggle, .ux-switch, .switch{
  min-height: 0 !important;
  min-width: 0 !important;
}
.mod-toggle > input[type="checkbox"],
.ux-switch > input[type="checkbox"],
.switch > input[type="checkbox"]{
  width: 0 !important; height: 0 !important;
  min-width: 0 !important; min-height: 0 !important;
  opacity: 0 !important; position: absolute !important;
}

/* 5 — one box per character for codes. Already used for OTP and MPIN. */
.ux-code-row{ display:flex; gap:9px; }
.ux-code-row input{
  width: 48px; min-width:48px; height: 56px; text-align:center;
  font-size: 22px; font-weight: 800; padding: 0;
}

/* 74 — errors appear inline, directly under the field they belong to. */
.ux-field-error{
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--ux-danger); line-height: 1.5;
}
.ux-field-error::before{ content: "\f06a"; font-family: "Font Awesome 6 Free"; font-weight: 900; }
input.ux-invalid, select.ux-invalid, textarea.ux-invalid{
  border-color: var(--ux-danger);
  background: #fff5f4;
}
/* A live requirement checklist beats a single "invalid" message. */
.ux-req-list{ list-style:none; padding:0; margin:8px 0 0; font-size:12px; }
.ux-req-list li{ display:flex; align-items:center; gap:7px; padding:2px 0; color: var(--ux-ink-muted); }
.ux-req-list li::before{ content:"○"; font-size:11px; }
.ux-req-list li.met{ color: var(--ux-ok); }
.ux-req-list li.met::before{ content:"●"; }

/* ═══════════════════════════════════════════════════════════════════════
   BUTTONS — rules 24, 25, 26, 27, 29, 31, 32, 87
   ═══════════════════════════════════════════════════════════════════════ */

/* 24, 87 — comfortable side padding and a real tap target. */
button, .btn, [role="button"], input[type="submit"], input[type="button"]{
  min-height: var(--ux-tap);
  padding-left: 20px; padding-right: 20px;
  border-radius: var(--ux-radius-sm);
  font-weight: 700;
  cursor: pointer;
  transition: background .18s, box-shadow .18s, transform .18s;
}
/* Icon-only buttons stay square rather than inheriting the wide padding. */
button.ux-icon-only, .btn-icon, .tb-icon-btn, .topbar-btn{
  min-width: var(--ux-tap); padding-left: 0; padding-right: 0;
}

/* Controls that declare their own fixed geometry must keep it.
   The blanket rule above was stretching the 21px info symbol and the 9px
   slideshow dots into 44px-tall ovals.

   Only `min-height` needed releasing. Padding and border-radius were never
   the problem: those controls set both from a class selector, which already
   outranks the bare `button` above them — but none of them set `min-height`,
   so the 44px floor was the one declaration that got through. Releasing just
   that keeps the fix as small as the bug.

   Where a visible target is genuinely too small to hit, .ux-tap-target
   expands the *hit area* instead, which satisfies Fitts's law without
   distorting the control. */
.dswd-info-btn,
.dswd-hero-dots button,
.dswd-emg-fab, .dswd-cookie-fab, .ds-a11y-fab, .track-fab,
.ann-modal-close, .tp-back, .tp-close,
.ip-close, .emg-close, .dswd-modal-head button,
.album-thumb, .cap-btn, .dp-day, .dp-clear, .ux-readmore,
.dir-tab, .filter-btn, .cz-logout, .sidebar-toggle,
.otp-row input, .mpin-row input,
button.dq-btn, .dq-btn{
  min-height: 0;
  min-width: 0;
}

/* Anything genuinely round stays round. The geometry is restated here rather
   than left to specificity: this stylesheet loads last, so stating it outright
   is deterministic in every engine instead of depending on a class selector
   out-ranking the bare `button` above. */
.dswd-info-btn, .dswd-hero-dots button, .dswd-emg-fab,
.dswd-cookie-fab, .ds-a11y-fab, .track-fab{
  flex: 0 0 auto;
  aspect-ratio: 1 / 1;
  padding: 0;
  border-radius: 50%;
}

/* 25 — the whole surrounding element is tappable, not just the glyph. */
/* `position: relative` here is only meant to give ::after something to
   position against — but it is an unconditional declaration, so it also
   overwrote `position: fixed` on every small floating control the script
   tagged. The back-to-top button was the casualty: it dropped out of its
   fixed layer into normal flow as the last child of <body>, which put it
   BELOW the footer (adding 46px under what was supposed to be the end of the
   page) and made it scroll away instead of following the reader.

   :where() drops this to zero specificity so any real positioning the
   control declares wins, and the fixed/absolute/sticky cases are excluded
   outright — an element that is already positioned needs no help. */
:where(.ux-tap-target){ position: relative; }
.ux-tap-target:is([style*="position:fixed"], [style*="position: fixed"]){ position: fixed; }
.dswd-totop.ux-tap-target,
.ds-a11y-fab.ux-tap-target,
.dswd-emg-fab.ux-tap-target,
.dswd-cookie-fab.ux-tap-target,
.track-fab.ux-tap-target{ position: fixed; }

.ux-tap-target::after{
  content: ""; position: absolute;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: max(100%, var(--ux-tap)); height: max(100%, var(--ux-tap));
}

/* 26, 27 — exactly one primary per action. Secondary is visibly subordinate:
   lighter fill, no shadow, muted text. */
.ux-primary, .btn-primary-lg, .btn-primary{
  background: linear-gradient(135deg, var(--ux-navy), var(--ux-navy-deep));
  color: #fff; border: none;
  box-shadow: var(--ux-shadow-md);
}
.ux-primary:hover, .btn-primary-lg:hover:not(:disabled){
  transform: translateY(-2px); box-shadow: var(--ux-shadow-lg);
}
.ux-secondary, .btn-ghost, .btn-secondary, .btn-outline-primary{
  background: var(--ux-surface);
  color: var(--ux-navy);
  border: 1.5px solid var(--ux-line);
  box-shadow: none;
}
.ux-secondary:hover, .btn-ghost:hover{ background: #f1f5fb; }
/* Two solid, equally-weighted buttons side by side is the exact thing
   rule 26 warns about — demote the second one. */
.ux-actions > .ux-primary ~ .ux-primary{
  background: var(--ux-surface); color: var(--ux-navy);
  border: 1.5px solid var(--ux-line); box-shadow: none;
}

/* 31 — destructive actions are red before the label is even read. */
.btn-danger, .btn-outline-danger, .ux-danger,
button[data-destructive], a[data-destructive]{
  --ux-btn: var(--ux-danger);
}
.btn-danger, .ux-danger{ background: var(--ux-danger); color:#fff; border:none; }
.btn-outline-danger{ color: var(--ux-danger); border: 1.5px solid var(--ux-danger); background: transparent; }
.btn-outline-danger:hover, .btn-danger:hover{ background: #a5301f; color:#fff; }

/* 32 — a button in a card or form fills its container instead of floating
   with awkward space around it. */
.ux-actions{ display:flex; gap: var(--ux-gap-sm); flex-wrap: wrap; }
.ux-actions > *{ flex: 1 1 auto; }
form .btn-block, .ux-full{ width: 100%; display:block; }
@media (max-width: 575px){
  /* 22, 23 — on a phone the primary action belongs at the bottom, full
     width, inside the thumb arc. */
  form .ux-primary, form .btn-primary-lg{ width: 100%; }
  .ux-actions{ flex-direction: column-reverse; }   /* primary ends up lowest */
  .ux-actions > *{ width: 100%; }
}

/* 29 — anything clickable looks clickable. A "Read more" styled as body
   text is invisible as an affordance. */
a:not([class]), .ux-link{
  color: var(--ux-navy);
  text-decoration: underline;
  text-underline-offset: 2px;
}
a:not([class]):hover, .ux-link:hover{ color: var(--ux-navy-deep); }
.ux-readmore{
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ux-navy); font-weight: 700; font-size: 12.5px;
  text-decoration: underline; text-underline-offset: 2px;
  background: none; border: none; padding: 4px 0; min-height: auto; cursor: pointer;
}

/* 30, 91 — the recommended option is visibly elevated, not identical to
   its neighbours. */
.ux-recommended{
  border: 2px solid var(--ux-navy) !important;
  box-shadow: var(--ux-shadow-lg);
  position: relative;
}
.ux-recommended::before{
  content: attr(data-badge);
  position: absolute; top: -11px; left: 18px;
  background: var(--ux-navy); color: #fff;
  font-size: 10px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  padding: 3px 11px; border-radius: 20px;
}

/* ═══════════════════════════════════════════════════════════════════════
   LAYOUT & SPACING — rules 62, 64, 66, 67, 68, 69, 71, 85
   ═══════════════════════════════════════════════════════════════════════ */

/* 62 — breathing room. Content never runs edge to edge. */
.content, .citizen-content, .container{ padding-left: max(16px, env(safe-area-inset-left)); padding-right: max(16px, env(safe-area-inset-right)); }

/* 64, 85 — tight inside a group, loose between groups. Proximity is what
   communicates the grouping, so even spacing everywhere destroys it. */
.ux-group > * + *{ margin-top: var(--ux-gap-sm); }
.ux-group + .ux-group{ margin-top: var(--ux-gap-lg); }
fieldset + fieldset, .form-section + .form-section{ margin-top: var(--ux-gap-lg); }

/* 69 — one gap size, applied consistently to every row and column. */
.row.g-3{ --bs-gutter-x: var(--ux-gap-sm); --bs-gutter-y: var(--ux-gap-sm); }
.row.g-4{ --bs-gutter-x: var(--ux-gap-md); --bs-gutter-y: var(--ux-gap-md); }
.kpi-grid, .mod-stat-grid, .chart-grid, .dir-grid, .album-grid,
.story-gallery, .cap-grid{ gap: var(--ux-gap-md); }

/* 66 — inner radius steps down from its container so the curves nest. */
.panel, .mod-card, .card-citizen, .reg-card, .sec-card{ border-radius: var(--ux-radius-lg); }
.panel img, .mod-card img, .card-citizen img,
.panel .kpi, .panel .dd-figure img, .cap-stage{ border-radius: var(--ux-radius-md); }
.panel input, .panel select, .panel .q-pill, .mod-card input{ border-radius: var(--ux-radius-sm); }

/* 68 — soft, diffuse elevation, or a border. Never a hard one-sided drop. */
.panel, .mod-card, .card-citizen, .kpi, .sec-card, .dir-person, .album{
  box-shadow: var(--ux-shadow-sm);
  border: 1px solid var(--ux-line);
}
.panel:hover, .kpi:hover, .dir-person:hover{ box-shadow: var(--ux-shadow-md); }

/* 67 — text over a photograph always gets a gradient scrim behind it. */
.ux-image-text, .story-cover, .news-img[style*="background-image"]{ position: relative; }
.ux-image-text::after, .story-cover::after{
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(16,20,28,.78) 0%, rgba(16,20,28,.25) 45%, transparent 100%);
}
.ux-image-text > *{ position: relative; z-index: 1; }

/* 70 — gradients move between adjacent hues, never slam across the wheel. */
.ux-gradient{ background: linear-gradient(135deg, var(--ux-navy), var(--ux-navy-deep)); }

/* 65 — large background areas are muted so foreground content stays the
   focus. */
.ux-soft-bg{ background: #f4f7fb; }

/* ═══════════════════════════════════════════════════════════════════════
   FEEDBACK & STATES — rules 72, 73, 76, 77, 78, 79
   ═══════════════════════════════════════════════════════════════════════ */

/* 73 — skeletons in the shape of the incoming content, not a bare spinner
   on an empty screen. */
.ux-skeleton{
  background: linear-gradient(90deg, #eef1f6 25%, #e2e8f1 37%, #eef1f6 63%);
  background-size: 400% 100%;
  animation: uxShimmer 1.3s ease-in-out infinite;
  border-radius: var(--ux-radius-sm);
}
@keyframes uxShimmer{ 0%{ background-position:100% 50% } 100%{ background-position:0 50% } }
.ux-skeleton-line{ height: 12px; margin-bottom: 9px; }
.ux-skeleton-line.short{ width: 55%; }
.ux-skeleton-line.title{ height: 18px; width: 40%; margin-bottom: 14px; }
.ux-skeleton-card{ height: 120px; border-radius: var(--ux-radius-md); margin-bottom: var(--ux-gap-sm); }

/* 78 — the number carries the weight; the label is subordinate. The reverse
   (bold label, small value) buries the thing people came to read. */
.ux-metric .value, .kpi-val, .stat-value, .mod-stat-val{
  font-size: clamp(1.6rem, 3vw, 2rem); font-weight: 800; line-height: 1.1;
  color: var(--ux-ink);
}
.ux-metric .label, .kpi-label, .stat-label, .mod-stat-label{
  font-size: 12.5px; font-weight: 600; color: var(--ux-ink-muted);
}

/* 76, 90 — a bar alongside the numbers, so progress is felt not just read. */
.ux-progress{ height: 8px; border-radius: 20px; background: #e6ecf5; overflow: hidden; }
.ux-progress > span{
  display:block; height:100%; border-radius: 20px;
  background: linear-gradient(90deg, var(--ux-green), #2aa06f);
  transition: width .5s ease;
}
.ux-progress-row{ display:flex; align-items:center; gap: var(--ux-gap-sm); }
.ux-progress-row .ux-progress{ flex: 1; }
.ux-progress-label{ font-size: 12px; font-weight: 700; color: var(--ux-ink-soft); white-space: nowrap; }

/* 72 — minimal, but never so minimal it loses meaning: a bare bar with no
   percentage says nothing. */
.ux-progress[data-label]::after{ content: attr(data-label); }

/* 79 — say what is happening, rather than leaving a blank screen. */
.ux-status{
  display:flex; align-items:center; gap: 9px;
  font-size: 13px; color: var(--ux-ink-soft);
  padding: 11px 15px; border-radius: var(--ux-radius-sm);
  background: #eef3fb; border: 1px solid #d5e2f5;
}
.ux-status .spin{ animation: uxSpin 1s linear infinite; }
@keyframes uxSpin{ to{ transform: rotate(360deg) } }

/* 77, 89 — the end of a flow gets a real moment, not a flat line of text. */
.ux-celebrate{ text-align: center; padding: var(--ux-gap-lg) var(--ux-gap-md); }
.ux-celebrate .icon{
  width: 88px; height: 88px; border-radius: 50%; margin: 0 auto var(--ux-gap-md);
  display:flex; align-items:center; justify-content:center;
  font-size: 2.4rem; color: #fff;
  background: linear-gradient(135deg, var(--ux-ok), #0f6b33);
  box-shadow: 0 14px 34px rgba(21,128,61,.32);
  animation: uxPop .5s cubic-bezier(.2,.9,.3,1.4) both;
}
@keyframes uxPop{ from{ transform: scale(.5); opacity:0 } to{ transform: scale(1); opacity:1 } }
.ux-celebrate h2{ font-size: 1.5rem; font-weight: 800; color: var(--ux-ink); }
.ux-celebrate p{ text-align:center; color: var(--ux-ink-muted); margin-top: 8px; }

/* Empty states say what to do next rather than just being empty (59, 79). */
.ux-empty{ text-align:center; padding: var(--ux-gap-xl) var(--ux-gap-md); color: var(--ux-ink-muted); }
.ux-empty i{ display:block; font-size: 2rem; opacity:.3; margin-bottom: 12px; }

/* ═══════════════════════════════════════════════════════════════════════
   NAVIGATION & ICONS — rules 35, 37, 38, 39, 40, 41
   ═══════════════════════════════════════════════════════════════════════ */

/* 37, 38 — every nav item pairs an icon with a text label. Neither alone. */
.nav-link, .cz-link, .sb-link{ display:flex; align-items:center; gap: 11px; min-height: var(--ux-tap); }
.nav-link i, .cz-link i, .sb-link i{ width: 20px; text-align:center; flex-shrink:0; }

/* 39, 40, 84 — one shape, one weight, one treatment for equivalent items. */
.q-pill, .cms-badge, .news-cat, .ux-chip{
  border-radius: 20px; font-size: 10.5px; font-weight: 800;
  letter-spacing: .05em; text-transform: uppercase; padding: 4px 10px;
  display: inline-block;
}

/* 13 — long option lists render as tappable chips, not a vertical checkbox
   column. */
.ux-chips{ display:flex; flex-wrap:wrap; gap: var(--ux-gap-xs); }
.ux-chip-toggle{
  border: 1.5px solid var(--ux-line); background: var(--ux-surface);
  border-radius: 20px; padding: 9px 16px; font-size: 13px; font-weight: 600;
  color: var(--ux-ink-soft); cursor: pointer; min-height: var(--ux-tap);
}
.ux-chip-toggle[aria-pressed="true"]{
  background: var(--ux-navy); border-color: var(--ux-navy); color: #fff;
}

/* 9, 17 — a handful of options is shown, not hidden behind a dropdown. */
.ux-segmented{ display:flex; gap: var(--ux-gap-xs); flex-wrap: wrap; }
.ux-segmented label{
  flex: 1 1 auto; min-width: 110px;
  display:flex; align-items:center; justify-content:center; gap: 8px;
  border: 1.5px solid var(--ux-line); background: var(--ux-surface);
  border-radius: var(--ux-radius-sm); padding: 11px 14px;
  font-size: 13.5px; font-weight: 600; cursor: pointer;
  min-height: var(--ux-tap); transition: .16s;
}
.ux-segmented label:hover{ border-color: var(--ux-navy); background:#f4f8ff; }
.ux-segmented input{ position:absolute; opacity:0; width:0; height:0; }
.ux-segmented input:checked + span,
.ux-segmented label:has(input:checked){
  border-color: var(--ux-navy); background: var(--ux-navy); color:#fff;
}
.ux-segmented label:has(input:focus-visible){ outline:3px solid var(--ux-navy); outline-offset:2px; }

/* The "any / all" segment is the off position of a filter, not a value.
   Lighter than the real choices so the group reads as "nothing applied" at a
   glance, but still clearly selectable — it is how a filter gets cleared. */
.ux-segmented label.ux-seg-any{ min-width: 92px; color:#64748b; border-style: dashed; }
.ux-segmented label.ux-seg-any:has(input:checked){
  background:#eef3fb; color:#123a7e; border-color:#c3d4ee; border-style: solid;
}
/* The rule above paints the LABEL; the checked rule further up also paints the
   SPAN navy. On a normal segment both are the same colour so the doubling does
   not show. On this one the navy span sat inside a pale label and read as
   highlighted text — as though the officer had dragged a selection across it.
   The span keeps the label's own colours here. */
.ux-segmented label.ux-seg-any input:checked + span{
  background: transparent; color: inherit;
}

/* 16 — a binary choice is a switch, not a two-option select. */
.ux-switch{ display:inline-flex; align-items:center; gap: 11px; cursor:pointer; min-height: var(--ux-tap); }
.ux-switch input{ position:absolute; opacity:0; }
.ux-switch .track{
  width: 48px; height: 28px; border-radius: 20px; background:#cbd5e1;
  position:relative; transition: background .2s; flex-shrink:0;
}
.ux-switch .track::after{
  content:""; position:absolute; top:3px; left:3px;
  width:22px; height:22px; border-radius:50%; background:#fff;
  box-shadow: var(--ux-shadow-sm); transition: transform .2s;
}
.ux-switch input:checked + .track{ background: var(--ux-green); }
.ux-switch input:checked + .track::after{ transform: translateX(20px); }
.ux-switch input:focus-visible + .track{ outline:3px solid var(--ux-navy); outline-offset:2px; }

/* 21, 15 — a long list keeps its dropdown but gains type-to-filter. */
.ux-combo{ position: relative; }
.ux-combo-list{
  position:absolute; top: calc(100% + 4px); left:0; right:0; z-index: 60;
  max-height: 260px; overflow-y:auto;
  background: var(--ux-surface); border: 1.5px solid var(--ux-line);
  border-radius: var(--ux-radius-sm); box-shadow: var(--ux-shadow-lg);
  display:none;
}
.ux-combo-list.show{ display:block; }
.ux-combo-list button{
  display:block; width:100%; text-align:left; background:none; border:none;
  padding: 11px 15px; font-size: 13.5px; color: var(--ux-ink);
  min-height: auto; border-radius: 0;
}
/* `.on` is the class the keyboard handler sets on the highlighted option.
   Only `.active` was styled, and nothing ever added it — so once arrow-key
   navigation existed, the row it had moved to looked exactly like the rest.
   The outline, not just the tint, is what makes the position readable at a
   glance and in high-contrast mode, where a pale background can vanish. */
.ux-combo-list button:hover,
.ux-combo-list button.active,
.ux-combo-list button.on{ background:#eef3fb; }
.ux-combo-list button.on{ box-shadow: inset 3px 0 0 var(--ux-navy, #0038a8); font-weight: 700; }
.ux-combo-empty{ padding: 15px; font-size: 12.5px; color: var(--ux-ink-muted); text-align:center; }

/* 19, 20, 81 — numeric values and ranges get a slider, not a dropdown. */
.ux-range{ width:100%; height: var(--ux-tap); accent-color: var(--ux-navy); cursor: pointer; }

/* ═══════════════════════════════════════════════════════════════════════
   ACCESSIBILITY & MOTION
   ═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce){
  .ux-skeleton, .ux-celebrate .icon, .ux-status .spin{ animation: none !important; }
  *{ transition-duration: .01ms !important; }
}

.ux-sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* 92 — the polish itself is functional: people judge an interface as easier
   to use when it looks considered. Consistent radii, soft shadows and a
   restrained palette above are that investment. */
