@charset "UTF-8";
/* ==========================================================================
   components.css - Liquid Glass materials and the v2 component set
   (plan T4.4/T4.5; docs/design/ia-wizard-design-system.md 4.1, 4.3, 4.5,
   4.6, 4.8). Loaded after base.css and admin-bridge.css.

   Contents
     1. Materials: .glass, .glass--thick, .glass--clear
     2. Accessibility fallbacks (no backdrop-filter, reduced transparency,
        increased contrast, reduced motion) and the :focus-visible ring
     3. Page header          .page-header
     4. Segmented control    .segmented
     5. Status pill          .status-pill        (includes/ui.php status_pill())
     6. Stat tile            .stat-tile
     7. Inset grouped list   .inset-group / .inset-list / .inset-table
     8. Empty state          .empty-state        (includes/ui.php empty_state())
     9. Skeleton             .skeleton
    10. Progress ring        .progress-ring
    11. Close button         .ui-close
    12. Floating bulk bar    .bulk-bar.glass
    13. Toast                .ui-toast-region > .ui-toast.glass.glass--thick
    14. Sheet chrome         .sheet-layer > .sheet (+ .sheet-backdrop)
    15. Forced colours

   Layering rules (4.1): glass only on the navigation/control layer (sidebar,
   top bar, bulk bar, toasts, menus, sheet header/footer chrome). Content
   (cards, tables, stat tiles, banners, sheet bodies) stays solid. No glass
   on glass; at most three blurred layers on screen.

   Fixed UI must live directly under <body>: an element with backdrop-filter
   becomes the containing block of its position:fixed descendants.

   No colour literals here: colours are tokens from tokens.css (system colour
   keywords appear only in the forced-colours block).
   ========================================================================== */

/* ── 1. Materials (design 4.3) ───────────────────────────────────────────── */
.glass {
  position: relative;
  isolation: isolate;
  background: var(--glass-regular);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
          backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  box-shadow: var(--glass-rim), var(--glass-edge), var(--shadow-float);
}
.glass::before {                   /* specular sheen */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: var(--glass-sheen);
  z-index: -1;
}
.glass--thick { background: var(--glass-thick); }
.glass--clear { background: var(--glass-clear); }     /* only over imagery, never over text */
.glass:focus-visible {             /* keep the rim when the ring is drawn */
  box-shadow: var(--focus-ring), var(--glass-rim), var(--glass-edge), var(--shadow-float);
}

/* ── 2. Accessibility fallbacks (design 4.3, 4.8) ────────────────────────────
   The :root overrides use ":root, :root[data-appearance]" so they also beat
   the dark set in tokens.css (specificity 0,2,0, loaded earlier). */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass, .glass--thick, .glass--clear { background: var(--surface); }
  .sheet__header, .sheet__footer { background: var(--surface); }
}

@media (prefers-reduced-transparency: reduce) {
  :root, :root[data-appearance] {
    --glass-regular: var(--surface); --glass-thick: var(--surface); --glass-clear: var(--surface);
    --glass-blur: 0px; --glass-sheen: none;
  }
  .glass, .sheet__header, .sheet__footer {
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
  }
}

@media (prefers-contrast: more) {
  :root, :root[data-appearance] {
    --separator: var(--separator-hc); --label-2: var(--label-2-hc); --label-3: var(--label-3-hc);
    --glass-regular: var(--glass-regular-hc); --glass-thick: var(--glass-thick-hc);
    --glass-edge: var(--glass-edge-hc);
  }
}

@media (prefers-reduced-motion: reduce) {
  :root, :root[data-appearance] { --dur-press: 0ms; --dur-1: 0ms; --dur-2: 0ms; --dur-sheet: 0ms; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Focus: never remove the indicator without a replacement. The transparent
   outline is invisible normally and becomes a system-coloured ring under
   forced colours, where box-shadow is dropped. "html " lifts specificity
   above Bootstrap reboot's button:focus. */
html :focus-visible {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: var(--focus-ring);
}
html :focus:not(:focus-visible) { outline: none; }

/* ── 3. Page header (design 4.4) ──────────────────────────────────────────────
   <header class="page-header">
     <nav class="page-header__crumbs">…</nav>                      (optional)
     <div class="page-header__row">
       <div class="page-header__text">
         <h1 class="page-header__title">Brand &amp; Toko</h1>
         <p class="page-header__subtitle">…</p>
       </div>
       <div class="page-header__actions">
         <button class="page-header__help" type="button">Apa ini?</button>
         <a class="btn btn-primary" href="…">Tambah brand</a>        (one filled action)
       </div>
     </div>
     <nav class="page-header__tabs segmented" aria-label="…">…</nav> (registry tabs)
   </header> */
.page-header { margin: 0 0 var(--sp-6); }
.page-header__crumbs {
  margin-bottom: var(--sp-1);
  font-size: var(--fs-footnote);
  color: var(--label-2);
}
.page-header__crumbs a { color: var(--label-2); }
.page-header__crumbs a:hover { color: var(--accent-text); }
.page-header__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-3) var(--sp-4);
}
.page-header__text { flex: 1 1 20rem; min-width: 0; }
.page-header__title {
  margin: 0;
  font-size: var(--fs-title1);          /* 28px phone/tablet, 34px desktop */
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-title);
  color: var(--label);
  overflow-wrap: anywhere;
}
.page-header__subtitle {
  max-width: 68ch;
  margin: var(--sp-1) 0 0;
  font-size: var(--fs-subhead);
  line-height: 1.4;
  color: var(--label-2);
}
.page-header__actions {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.page-header__help {                    /* plain button: no fill, accent text */
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  min-height: var(--hit);
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-capsule);
  background: transparent;
  color: var(--accent-text);
  font: inherit;
  font-weight: var(--fw-medium);
  transition: background-color var(--dur-1) var(--ease-out);
}
.page-header__help:hover { background: var(--fill-3); color: var(--accent-text); }
.page-header__tabs { margin-top: var(--sp-4); }
@media (min-width: 992px) {
  .page-header__title { font-size: var(--fs-large); }
}

/* ── 4. Segmented control (design 4.5) ────────────────────────────────────────
   Links:   <nav class="segmented"><a class="segmented__item" aria-current="page">…</a>…</nav>
   Tabs:    <div class="segmented" role="tablist"><button class="segmented__item" role="tab" aria-selected="true">…
   Radios:  <div class="segmented" role="radiogroup"><input class="segmented__input" type="radio" id="a" checked>
            <label class="segmented__item" for="a">…</label>…
   At most 5 segments; the track scrolls sideways instead of wrapping. */
.segmented {
  position: relative;
  display: inline-flex;
  max-width: 100%;
  padding: 2px;
  gap: 2px;
  border-radius: var(--r-control);
  background: var(--fill-2);
  overflow-x: auto;
  scrollbar-width: none;
  vertical-align: middle;
}
.segmented::-webkit-scrollbar { display: none; }
.segmented--block { display: flex; width: 100%; }
.segmented--block > .segmented__item { flex: 1 1 0; }
.segmented__item {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: calc(var(--hit) - 4px);
  margin: 0;
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: calc(var(--r-control) - 2px);
  background: transparent;
  color: var(--label);
  font: inherit;
  font-size: var(--fs-subhead);
  font-weight: var(--fw-medium);
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
.segmented__item:hover { background: var(--fill-3); color: var(--label); text-decoration: none; }
.segmented__item[aria-selected="true"],
.segmented__item[aria-current="page"],
.segmented__item[aria-pressed="true"],
.segmented__item.is-active,
.segmented__input:checked + .segmented__item {
  background: var(--control-selected);
  box-shadow: var(--shadow-1), var(--shadow-hairline);
  color: var(--label);
  font-weight: var(--fw-semibold);
}
.segmented__item:disabled, .segmented__item[aria-disabled="true"] { color: var(--label-3); cursor: default; }
.segmented__input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.segmented__input:focus-visible + .segmented__item { box-shadow: var(--focus-ring); }
.segmented__count { color: var(--label-2); font-weight: var(--fw-regular); font-variant-numeric: tabular-nums; }
/* The track scrolls, so the ring is drawn inside the segment (not clipped). */
.segmented__item:focus-visible,
.segmented__input:focus-visible + .segmented__item { box-shadow: inset 0 0 0 2px var(--accent-ui); }

/* ── 5. Status pill (design 4.5) ──────────────────────────────────────────────
   status_pill() prints <span class="badge badge-x status-pill status-pill--tone">
   <i class="fas … mr-1" aria-hidden="true"></i>Label</span>. Capsule, 22px,
   12px/600, tint + coloured text + leading icon (never colour alone). The
   tone classes override Bootstrap's white-on-saturated .badge-* colours. */
.status-pill {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 2px var(--sp-2) 2px 7px;
  border-radius: var(--r-capsule);
  background: var(--neutral-tint);
  color: var(--neutral);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  line-height: 1.1;
  white-space: nowrap;
  vertical-align: middle;
}
.status-pill > i, .status-pill > svg { flex: none; margin-right: var(--sp-1); font-size: .95em; }
.status-pill--success { background: var(--success-tint); color: var(--success); }
.status-pill--warning { background: var(--warning-tint); color: var(--warning); }
.status-pill--danger  { background: var(--danger-tint);  color: var(--danger); }
.status-pill--info    { background: var(--info-tint);    color: var(--info); }
.status-pill--neutral { background: var(--neutral-tint); color: var(--neutral); }
.status-pill--accent  { background: var(--accent-tint);  color: var(--accent-text); }
a.status-pill:hover { filter: brightness(.96); text-decoration: none; }

/* ── 6. Stat tile (design 4.5; partials/stat-tile.php) ───────────────────────
   <div class="stat-tile">
     <div class="stat-tile__head">
       <span class="stat-tile__icon stat-tile__icon--success"><i class="fas fa-coins" aria-hidden="true"></i></span>
       <span class="stat-tile__label">Poin terbit</span>
     </div>
     <div class="stat-tile__value">12.340</div>
     <div class="stat-tile__meta"><span class="stat-tile__delta stat-tile__delta--up">…+4%</span> vs minggu lalu</div>
     <a class="stat-tile__link" href="…">Lihat <span aria-hidden="true">›</span></a>
   </div>
   Solid content surface: no glass, no gradient text; the value is --label. */
.stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-width: 0;
  height: 100%;
  padding: var(--sp-4);
  border-radius: var(--r-card);
  background: var(--surface);
  color: var(--label);
  box-shadow: var(--shadow-hairline), var(--shadow-1);
}
.stat-tile__head { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.stat-tile__icon {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-text);
  font-size: .9375rem;
}
.stat-tile__icon--success { background: var(--success-tint); color: var(--success); }
.stat-tile__icon--warning { background: var(--warning-tint); color: var(--warning); }
.stat-tile__icon--danger  { background: var(--danger-tint);  color: var(--danger); }
.stat-tile__icon--info    { background: var(--info-tint);    color: var(--info); }
.stat-tile__icon--neutral { background: var(--neutral-tint); color: var(--neutral); }
.stat-tile__label {
  min-width: 0;
  overflow: hidden;
  font-size: var(--fs-footnote);
  font-weight: var(--fw-medium);
  color: var(--label-2);
  text-overflow: ellipsis;
  white-space: nowrap;
}
.stat-tile__value {
  font-size: var(--fs-title1);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-title);
  color: var(--label);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.stat-tile__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-2);
  font-size: var(--fs-footnote);
  color: var(--label-2);
}
.stat-tile__delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 20px;
  padding: 1px 7px;
  border-radius: var(--r-capsule);
  background: var(--neutral-tint);
  color: var(--neutral);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}
.stat-tile__delta--up   { background: var(--success-tint); color: var(--success); }
.stat-tile__delta--down { background: var(--danger-tint);  color: var(--danger); }
.stat-tile__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--hit);
  margin: auto calc(-1 * var(--sp-4)) calc(-1 * var(--sp-4));
  padding: 0 var(--sp-4);
  border-top: .5px solid var(--separator);
  border-radius: 0 0 var(--r-card) var(--r-card);
  color: var(--accent-text);
  font-size: var(--fs-subhead);
  font-weight: var(--fw-medium);
}
.stat-tile__link:hover { background: var(--fill-3); color: var(--accent-text); text-decoration: none; }

/* ── 7. Inset grouped list / table (design 4.5) ──────────────────────────────
   <section class="inset-group">
     <h2 class="inset-group__header">Koneksi</h2>
     <ul class="inset-list inset-list--icons">
       <li><a class="inset-list__row" href="…">
         <span class="inset-list__icon"><i class="fas fa-store" aria-hidden="true"></i></span>
         <span class="inset-list__content"><span class="inset-list__title">Shopee</span>
           <span class="inset-list__detail">Terhubung 2 jam lalu</span></span>
         <span class="inset-list__trailing">…</span>
         <span class="inset-list__chevron" aria-hidden="true"></span></a></li>
     </ul>
     <p class="inset-group__footer">…</p>
   </section>
   Tables: <div class="inset-table-wrap"><table class="inset-table">…</table></div>
   (the wrapper keeps overflow-x:auto; numbers use th/td.num). No zebra. */
.inset-group { margin: 0 0 var(--sp-6); }
.inset-group__header {
  margin: 0 0 var(--sp-2);
  padding: 0 var(--sp-4);
  font-size: var(--fs-footnote);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
  color: var(--label-2);
}
.inset-group__footer {
  margin: var(--sp-2) 0 0;
  padding: 0 var(--sp-4);
  font-size: var(--fs-footnote);
  color: var(--label-2);
}
.inset-list {
  --inset-sep-start: var(--sp-4);
  margin: 0;
  padding: 0;
  overflow: hidden;
  list-style: none;
  border-radius: var(--r-card);
  background: var(--surface);
  box-shadow: var(--shadow-hairline);
}
.inset-list--icons { --inset-sep-start: calc(var(--sp-4) + 30px + var(--sp-3)); }
.inset-list > li { position: relative; margin: 0; }
.inset-list > li + li::before {             /* inset separator */
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  left: var(--inset-sep-start);
  border-top: .5px solid var(--separator);
  pointer-events: none;
}
.inset-list__row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: var(--hit-lg);
  padding: var(--sp-2) var(--sp-4);
  border: 0;
  background: transparent;
  color: var(--label);
  font: inherit;
  font-size: var(--fs-subhead);
  text-align: left;
  text-decoration: none;
}
a.inset-list__row, button.inset-list__row, label.inset-list__row {
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out);
}
a.inset-list__row:hover, button.inset-list__row:hover, label.inset-list__row:hover {
  background: var(--fill-3);
  color: var(--label);
  text-decoration: none;
}
.inset-list__row:focus-visible { box-shadow: inset 0 0 0 2px var(--accent-ui); }
.inset-list__row--step { min-height: var(--hit-row); }     /* wizard step rows */
.inset-list__icon {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-xs);
  background: var(--accent-tint);
  color: var(--accent-text);
  font-size: .875rem;
}
.inset-list__content { display: flex; flex: 1 1 auto; flex-direction: column; min-width: 0; }
.inset-list__title { color: var(--label); overflow-wrap: anywhere; }
.inset-list__detail { font-size: var(--fs-footnote); color: var(--label-2); overflow-wrap: anywhere; }
.inset-list__trailing {
  flex: none;
  max-width: 50%;
  color: var(--label-2);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.inset-list__chevron {
  flex: none;
  width: 8px;
  height: 8px;
  margin-left: var(--sp-1);
  border-top: 2px solid var(--label-4);
  border-right: 2px solid var(--label-4);
  transform: rotate(45deg);
}
.inset-list__row--destructive, .inset-list__row--destructive:hover { color: var(--danger); }

.inset-table-wrap {
  max-width: 100%;
  overflow-x: auto;                        /* wide tables scroll, never clip (UI-03) */
  -webkit-overflow-scrolling: touch;
  border-radius: var(--r-card);
  background: var(--surface);
  box-shadow: var(--shadow-hairline);
}
.inset-table { width: 100%; margin: 0; border-collapse: separate; border-spacing: 0; color: var(--label); }
.inset-table th, .inset-table td {
  height: var(--hit-lg);
  padding: var(--sp-2) var(--sp-4);
  border: 0;
  font-size: var(--fs-subhead);
  vertical-align: middle;
}
.inset-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  height: auto;
  padding-top: var(--sp-3);
  padding-bottom: var(--sp-2);
  background: var(--surface);
  border-bottom: .5px solid var(--separator);
  font-size: var(--fs-footnote);
  font-weight: var(--fw-semibold);
  color: var(--label-2);
  text-align: left;
  white-space: nowrap;
}
.inset-table tbody tr + tr > td { border-top: .5px solid var(--separator); }
.inset-table tbody tr + tr > td:first-child { position: relative; border-top-color: transparent; }
.inset-table tbody tr + tr > td:first-child::before {   /* inset: the line starts at the text */
  content: "";
  position: absolute;
  top: -.5px;
  right: 0;
  left: var(--sp-4);
  border-top: .5px solid var(--separator);
  pointer-events: none;
}
.inset-table tbody tr:hover > td { background: var(--fill-3); }
.inset-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.inset-table tr.is-selected > td { background: var(--accent-tint); }
/* A wrapper with overflow-x:auto is also the vertical scroll container, so the
   sticky header only sticks when the wrapper has its own height. */
.inset-table-wrap--scroll { max-height: min(70vh, 720px); overflow: auto; overscroll-behavior: contain; }

/* ── 8. Empty state (design 4.5; includes/ui.php empty_state()) ──────────────
   48px icon in a tint circle, 17px/600 title, 1–2 sentences, one filled
   primary + an optional plain "Pelajari". The helper also prints Bootstrap
   utilities (text-muted, fa-3x, mb-*); the icon colour needs !important to
   beat .text-muted. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 34rem;
  margin: 0 auto;
  padding: var(--sp-12) var(--sp-4);
  color: var(--label);
  text-align: center;
}
.empty-state .empty-state__icon {
  display: grid;
  place-items: center;
  width: 96px;
  height: 96px;
  margin: 0 auto var(--sp-4);
  border-radius: 50%;
  background: var(--accent-tint);
  color: var(--accent-text) !important;
}
.empty-state .empty-state__icon > i,
.empty-state .empty-state__icon > svg { font-size: 48px; width: 48px; line-height: 1; text-align: center; }
.empty-state .empty-state__title {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
  color: var(--label);
}
.empty-state .empty-state__body {
  max-width: 28rem;
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-subhead);
  color: var(--label-2) !important;
}
.empty-state__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
}

/* ── 9. Skeleton (design 4.5, 4.7) ───────────────────────────────────────────
   <span class="skeleton skeleton--text" aria-hidden="true"></span>; the
   loading container carries aria-busy="true". One sheen sweep per 1.2 s,
   static under reduced motion. */
.skeleton {
  position: relative;
  display: block;
  min-height: 1em;
  overflow: hidden;
  border-radius: var(--r-xs);
  background: var(--fill-2);
  color: transparent;
  pointer-events: none;
  user-select: none;
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--skeleton-sheen);
  transform: translateX(-100%);
  animation: ui-skeleton-sweep 1.2s linear infinite;
}
.skeleton--text   { width: 100%; height: .8em; min-height: 0; margin: .35em 0; }
.skeleton--title  { width: 55%; height: 1.3em; margin-bottom: .5em; }
.skeleton--value  { width: 40%; height: var(--fs-title1); }
.skeleton--circle { border-radius: 50%; aspect-ratio: 1; }
.skeleton--block  { width: 100%; height: 100%; min-height: 120px; border-radius: var(--r-row); }
@keyframes ui-skeleton-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  .skeleton::after { animation: none; display: none; }
}

/* ── 10. Progress ring (design 4.5; wizard / Beranda) ────────────────────────
   <div class="progress-ring" role="img" aria-label="7 dari 13 langkah" style="--ring-value: 54">
     <svg viewBox="0 0 64 64" aria-hidden="true">
       <circle class="progress-ring__track" cx="32" cy="32" r="29" pathLength="100"/>
       <circle class="progress-ring__arc"   cx="32" cy="32" r="29" pathLength="100"/>
     </svg>
     <span class="progress-ring__label">7<small>/13</small></span>
   </div>
   --ring-value is a plain number 0–100 (percent); 6px stroke at 64px. */
.progress-ring {
  --ring-size: 64px;
  position: relative;
  display: inline-grid;
  flex: none;
  place-items: center;
  width: var(--ring-size);
  height: var(--ring-size);
}
.progress-ring--lg { --ring-size: 96px; }
.progress-ring > svg {
  grid-area: 1 / 1;
  width: 100%;
  height: 100%;
  overflow: visible;
  transform: rotate(-90deg);
}
.progress-ring__track, .progress-ring__arc { fill: none; stroke-width: 6; }
.progress-ring__track { stroke: var(--fill-2); }
.progress-ring__arc {
  stroke: var(--accent-fill);
  stroke-linecap: round;
  stroke-dasharray: var(--ring-value, 0) 100;
  transition: stroke-dasharray var(--dur-sheet) var(--ease-out);
}
.progress-ring--done .progress-ring__arc { stroke: var(--success); }
.progress-ring--empty .progress-ring__arc { stroke-linecap: butt; }
.progress-ring__label {
  grid-area: 1 / 1;
  font-size: var(--fs-subhead);
  font-weight: var(--fw-semibold);
  line-height: 1;
  color: var(--label);
  font-variant-numeric: tabular-nums;
}
.progress-ring__label small { font-size: .75em; font-weight: var(--fw-medium); color: var(--label-2); }
.progress-ring--lg .progress-ring__label { font-size: var(--fs-title3); }

/* ── 11. Close button (sheets, toasts, banners) ──────────────────────────────
   <button class="ui-close" type="button" aria-label="Tutup"><span aria-hidden="true">&times;</span></button>
   A 30px grey circle inside a --hit-lg hit area. */
.ui-close {
  position: relative;
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--fill-2);
  color: var(--label-2);
  font-size: 1.125rem;
  font-weight: var(--fw-semibold);
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease-out);
}
.ui-close::after {                           /* enlarge the hit area to --hit-lg */
  content: "";
  position: absolute;
  inset: calc((30px - var(--hit-lg)) / 2);
}
.ui-close:hover { background: var(--fill); color: var(--label); }

/* ── 12. Floating bulk bar (design 4.4; Stok QR, Pesanan, Penukaran) ─────────
   Directly under <body>:
   <div class="bulk-bar glass" role="region" aria-label="Aksi massal" hidden>
     <span class="bulk-bar__count" aria-live="polite"><strong>3</strong> dipilih</span>
     <span class="bulk-bar__sep" aria-hidden="true"></span>
     <div class="bulk-bar__actions"><button class="btn btn-primary">Cetak</button>
       <button class="btn btn-link">Batal</button></div>
   </div>
   Centred 16px above the bottom edge (safe area aware). The shell can set
   --bulk-bar-offset to centre it on the content column instead of the page. */
.bulk-bar {
  position: fixed;
  bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
  left: calc(50% + var(--bulk-bar-offset, 0px));
  z-index: var(--z-float);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: max-content;
  max-width: calc(100vw - 2 * var(--sp-4));
  min-height: calc(var(--hit) + 2 * var(--sp-2));
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-5);
  border-radius: var(--r-capsule);
  color: var(--label);
  transform: translateX(-50%);
  animation: ui-bulk-bar-in var(--dur-2) var(--ease-spring) both;
}
.bulk-bar__count { font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; white-space: nowrap; }
.bulk-bar__sep { align-self: stretch; width: 1px; margin: var(--sp-1) 0; background: var(--separator); }
.bulk-bar__actions { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--sp-2); }
@keyframes ui-bulk-bar-in {
  from { opacity: 0; transform: translate(-50%, 16px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ── 13. Toast (design 4.5, 4.6; ui.toast()) ─────────────────────────────────
   The shell renders one region directly under <body>:
     <div class="ui-toast-region" role="status" aria-live="polite"></div>
   ui.toast() appends
     <div class="ui-toast glass glass--thick ui-toast--success">
       <i class="ui-toast__icon fas fa-check-circle" aria-hidden="true"></i>
       <span class="ui-toast__text">Brand tersimpan.</span>
       <button class="ui-toast__action" type="button">Urungkan</button>
       <button class="ui-close ui-toast__close" type="button" aria-label="Tutup">…</button>
     </div>
   One visible at a time; enter 260ms spring from −8px; .is-leaving fades out. */
.ui-toast-region {
  position: fixed;
  top: calc(var(--topbar-h) + var(--sp-2) + env(safe-area-inset-top, 0px));
  left: 50%;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  width: max-content;
  max-width: calc(100vw - 2 * var(--sp-4));
  transform: translateX(-50%);
  pointer-events: none;
}
.ui-toast {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: 36rem;
  min-height: var(--hit-lg);
  padding: var(--sp-1) var(--sp-2) var(--sp-1) var(--sp-4);
  border-radius: calc(var(--hit-lg) / 2);       /* capsule on one line, rounded when wrapped */
  color: var(--label);
  font-size: var(--fs-subhead);
  font-weight: var(--fw-medium);
  line-height: 1.3;
  pointer-events: auto;
  animation: ui-toast-in var(--dur-2) var(--ease-spring) both;
}
@media (max-width: 359.98px) { .ui-toast { flex-wrap: wrap; } }
.ui-toast.is-leaving { animation: ui-toast-out var(--dur-2) var(--ease-out) both; }
.ui-toast__icon { flex: none; font-size: 1.05em; color: var(--accent-text); }
.ui-toast--success .ui-toast__icon { color: var(--success); }
.ui-toast--warning .ui-toast__icon { color: var(--warning); }
.ui-toast--danger  .ui-toast__icon { color: var(--danger); }
.ui-toast--info    .ui-toast__icon { color: var(--info); }
.ui-toast__text { flex: 1 1 10rem; min-width: 0; padding: var(--sp-2) 0; overflow-wrap: break-word; }
.ui-toast__action {
  flex: none;
  min-height: var(--hit);
  padding: 0 var(--sp-3);
  border: 0;
  border-radius: var(--r-capsule);
  background: transparent;
  color: var(--accent-text);
  font: inherit;
  font-weight: var(--fw-semibold);
  cursor: pointer;
}
.ui-toast__action:hover { background: var(--fill-3); }
.ui-toast__close { margin-left: var(--sp-1); }
@keyframes ui-toast-in {
  from { opacity: 0; transform: translateY(-8px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes ui-toast-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-8px) scale(.98); }
}

/* ── 14. Sheet chrome (design 4.5; ui.js sheets + confirm) ───────────────────
   Directly under <body>:
   <div class="sheet-backdrop"></div>
   <div class="sheet-layer">
     <div class="sheet sheet--sm" role="dialog" aria-modal="true" aria-labelledby="sheetTitle">
       <header class="sheet__header">
         <span class="sheet__grabber" aria-hidden="true"></span>
         <h2 class="sheet__title" id="sheetTitle">Hapus brand?</h2>
         <button class="ui-close sheet__close" type="button" aria-label="Tutup">…</button>
       </header>
       <div class="sheet__body">…</div>
       <footer class="sheet__footer">
         <button class="btn btn-link" type="button">Batal</button>       (plain, leading)
         <button class="btn btn-primary" type="button">Simpan</button>   (filled, trailing)
       </footer>
     </div>
   </div>
   The body is SOLID; only the sticky header/footer chrome is glass, so content
   scrolls under it. Widths 480/640/880; <=576px becomes a bottom sheet. */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-backdrop);
  background: var(--scrim);
  animation: ui-fade-in var(--dur-2) var(--ease-out) both;
}
.sheet-layer {
  position: fixed;
  inset: 0;
  z-index: var(--z-sheet);
  display: flex;
  padding: var(--sp-6) var(--sp-4);
  overflow: hidden;
}
.sheet {
  --sheet-w: 640px;
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(100%, var(--sheet-w));
  max-height: 100%;
  margin: auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-radius: var(--r-sheet);
  background: var(--surface);
  color: var(--label);
  box-shadow: var(--shadow-sheet);
  animation: ui-sheet-in var(--dur-sheet) var(--ease-sheet) both;
}
.sheet--sm { --sheet-w: 480px; }
.sheet--md { --sheet-w: 640px; }
.sheet--lg { --sheet-w: 880px; }
.sheet__header, .sheet__footer {
  position: sticky;
  z-index: 1;
  flex: none;
  background: var(--glass-thick);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
          backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
}
.sheet__header {
  top: 0;
  display: grid;
  grid-template-columns: minmax(var(--hit-lg), 1fr) auto minmax(var(--hit-lg), 1fr);
  align-items: center;
  min-height: var(--topbar-h);
  padding: 0 var(--sp-3);
  border-bottom: .5px solid var(--separator);
}
.sheet__header > .sheet__leading { grid-column: 1; justify-self: start; }
.sheet__title {
  grid-column: 2;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  line-height: 1.3;
  text-align: center;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.sheet__header > .sheet__close, .sheet__header > .sheet__trailing { grid-column: 3; justify-self: end; }
.sheet__grabber { display: none; }
.sheet__body { flex: 1 0 auto; padding: var(--sp-5); }
.sheet__message { margin: 0; color: var(--label-2); }        /* confirm: one-sentence consequence */
.sheet__footer {
  bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: auto;
  padding: var(--sp-3) var(--sp-5);
  border-top: .5px solid var(--separator);
}
.sheet__footer > :last-child { margin-left: auto; }
@media (max-width: 576px) {
  .sheet-layer { padding: var(--sp-10) 0 0; }
  .sheet {
    width: 100%;
    margin: auto 0 0;
    border-radius: var(--r-sheet) var(--r-sheet) 0 0;
    animation-name: ui-sheet-up;
  }
  .sheet__grabber {
    position: absolute;
    top: 5px;
    left: 50%;
    display: block;
    width: 36px;
    height: 5px;
    border-radius: var(--r-capsule);
    background: var(--label-4);
    transform: translateX(-50%);
  }
  .sheet__footer { padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0px)); }
}
@keyframes ui-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ui-sheet-in {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}
@keyframes ui-sheet-up {
  from { transform: translateY(100%); }
  to   { transform: none; }
}

/* ── 15. Forced colours (Windows High Contrast) ──────────────────────────── */
@media (forced-colors: active) {
  .glass, .sheet, .stat-tile, .inset-list, .inset-table-wrap {
    border: 1px solid CanvasText;
  }
  .glass::before, .skeleton::after { display: none; }
  .sheet__header, .sheet__footer { background: Canvas; }
  .status-pill, .stat-tile__delta, .segmented { border: 1px solid CanvasText; }
  .segmented__item[aria-selected="true"], .segmented__item[aria-current="page"],
  .segmented__item[aria-pressed="true"], .segmented__item.is-active,
  .segmented__input:checked + .segmented__item {
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
  }
  .skeleton { background: GrayText; }
  .progress-ring__track { stroke: GrayText; }
  .progress-ring__arc { stroke: Highlight; }
  .bulk-bar__sep { background: CanvasText; }
}
