/* Custom widget tile — sandboxed third-party SDK widget host. Rendered into
   .custom-widget-mount by custom-widget.js. Accent-tinted (no brand colour of
   its own) so it follows the dashboard theme; the guest iframe fills the body. */

.custom-panel { display: flex; }
.custom-widget-mount { width: 100%; height: 100%; display: flex; min-height: 0; border-radius: inherit; }

.cw-wrap {
  position: relative; box-sizing: border-box;
  width: 100%; height: 100%; min-width: 0; min-height: 0;
  padding: 13px 15px;
  display: flex; flex-direction: column; gap: 9px;
  overflow: hidden; border-radius: inherit;
}

/* On a SHAPED tile the guest fills the silhouette. The stock padding above is
   there to hold a guest off a rectangular card's edges; on a shaped tile the
   card is clipped to the silhouette and the guest is inset INSIDE it, so what
   the padding actually produces is a band of the card itself between the cut
   edge and the guest — a ring, concentric and unmistakable, and pure white on a
   light palette. It is the reason a round widget could never sit flush in its
   own tile, and it was reported as "why is there a white circle behind it".
   Keeping content clear of a cut edge is what the shape's own safe rectangle is
   for (`inset`, applied by DashboardGrid.css as a transparent border on the
   panel), so a package that wants breathing room asks for it there and one that
   wants full bleed declares inset 0 and gets it. The head keeps an inset of its
   own: a title jammed against a curve is the thing this padding DOES solve. */
.grid-stack-item[data-tile-shape] .cw-wrap { padding: 0; }
.grid-stack-item[data-tile-shape] .cw-wrap > .cw-head { padding: 11px 15px 0; }

/* ── Head: title + swap control ─────────────────────────────────── */
.cw-head { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.cw-brand { display: flex; align-items: center; gap: 7px; min-width: 0; }
.cw-logo { display: inline-flex; color: rgba(var(--accent-rgb), 0.95); }
.cw-logo svg { width: 16px; height: 16px; }
.cw-title {
  font-size: 0.82rem; font-weight: 800; letter-spacing: 0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cw-ctl { display: flex; align-items: center; gap: 4px; }
.cw-hbtn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 28px; height: 26px; padding: 0 6px; border-radius: 8px; cursor: pointer;
  color: var(--text-dim, #8b91a1); background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.08);
  transition: background 0.15s ease, color 0.15s ease;
}
.cw-hbtn:hover { color: var(--text, #e7e9ee); background: rgba(255,255,255,0.09); }
.cw-hbtn[hidden] { display: none; }
.cw-hbtn svg { width: 14px; height: 14px; }

/* A mounted widget reads exactly like a native tile: no host brand/title/swap
   bar and no host padding — the guest iframe IS the whole tile (it draws its own
   look). The setup chrome only returns while editing the layout, and then as a
   small overlay so it doesn't reflow/shift the widget. */
.cw-wrap.cw-mounted { padding: 0; gap: 0; }
.cw-wrap.cw-mounted .cw-head { display: none; }
body.layout-editing .cw-wrap.cw-mounted .cw-head {
  display: flex; position: absolute; top: 6px; left: 8px; right: 8px; z-index: 5;
  padding: 4px 8px; border-radius: 8px;
  background: rgba(10, 12, 20, 0.55); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}

/* ── Expanded (the `expand` capability) ──────────────────────────────
   The tile paints over the whole dashboard. Done with a class ON THE WRAP,
   never by re-parenting it: moving an iframe reloads it, which would restart
   the widget both entering and leaving. `position: fixed` escapes the
   GridStack ancestors (nothing in that chain establishes a containing block),
   so this is all it takes. The tile keeps its slot in the grid underneath. */
/* Lifting the tile alone is NOT enough, and this is the whole trick: `.shell`
   is `position: relative; z-index: 1`, so it is a stacking context and every
   z-index inside it — including the one below — is only ever compared against
   its siblings IN the shell. Verified live: an expanded tile at z-index 12050
   still painted under the update overlay (4000), which sits on <body>. So the
   shell itself is lifted while a widget is expanded.

   70 is chosen, not arbitrary. It clears the edge rails (58) so the fullscreen
   widget is not fringed by them, and stays below every layer the user
   deliberately opens: day modal 90, picker 100, game companion 130, tab
   switcher 180, app switcher 190, settings 220, weather 230, lockscreen and
   ambient 260, AI 1200, updates 4000. An expanded widget is dashboard content,
   so anything the user summons must still come over it. */
body.cw-has-expanded .shell { z-index: 70; }

.cw-wrap.cw-expanded {
  position: fixed; inset: 0; z-index: 12050;   /* within .shell — see above */
  width: auto; height: auto; border-radius: 0;
  padding: 0; gap: 0;
  background: var(--bg, #070808);
  animation: cw-expand-in 0.18s ease both;
}
@keyframes cw-expand-in { from { opacity: 0; } to { opacity: 1; } }
.cw-wrap.cw-expanded .cw-frame { border-radius: 0; }
/* The head would sit over the guest's own top-left while fullscreen. */
.cw-wrap.cw-expanded .cw-head { display: none !important; }

/* The way out, drawn by the host and not removable by the guest. Matches the
   Ambient scene's exit: top-right, ~20px inset, comfortable touch target. */
.cw-expand-close {
  position: absolute; top: 20px; right: 20px; z-index: 2;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0;
  font-size: 1rem; line-height: 1; cursor: pointer;
  color: var(--text, #e7e9ee);
  background: rgba(10, 12, 20, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 50%;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: background 0.15s ease, transform 0.15s ease;
}
.cw-expand-close:hover { background: rgba(10, 12, 20, 0.78); }
.cw-expand-close:active { transform: scale(0.94); }
.cw-wrap:not(.cw-expanded) .cw-expand-close { display: none; }

/* ── Guest iframe ────────────────────────────────────────────────── */
.cw-body { flex: 1 1 auto; min-height: 0; display: flex; }
.cw-frame {
  flex: 1 1 auto; width: 100%; height: 100%; min-height: 0;
  /* Follows the tile's radius rather than a literal: a widget whose tile was
     given square or heavily rounded corners kept a 10px frame inside it. */
  border: 0; border-radius: var(--radius-tile, 16px); background: transparent;
}

/* While arranging the layout the guest iframe must be inert: a sandboxed frame
   fills the whole tile and would otherwise swallow the pointer, leaving only the
   tiny header handle grabbable and making the tile awkward to drag, resize or
   remove. Neutralising it hands the whole tile back to GridStack's drag/resize
   and the host's hide/move controls (which sit above at their own z-index). */
body.layout-editing .cw-wrap.cw-mounted .cw-frame { pointer-events: none; }

/* ── Deliberate states: off / loading / none / missing ──────────── */
.cw-state {
  flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; text-align: center; padding: 12px;
}
.cw-state-ico { color: var(--text-dim, #8b91a1); opacity: 0.7; }
.cw-state-ico svg { width: 26px; height: 26px; }
.cw-state-txt { font-size: 0.8rem; font-weight: 700; opacity: 0.85; max-width: 300px; }
.cw-state-hint { font-size: 0.71rem; font-weight: 600; opacity: 0.55; line-height: 1.4; max-width: 320px; }
.cw-state-btns { display: flex; align-items: center; gap: 8px; margin-top: 2px; flex-wrap: wrap; justify-content: center; }

.cw-btn {
  font: inherit; font-size: 0.74rem; font-weight: 800; cursor: pointer;
  padding: 7px 16px; border-radius: 999px; color: var(--text, #e7e9ee);
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12);
  transition: background 0.15s ease;
}
.cw-btn:hover { background: rgba(255,255,255,0.11); }
.cw-btn--primary {
  color: rgba(var(--accent-rgb), 1);
  background: rgba(var(--accent-rgb), 0.14); border-color: rgba(var(--accent-rgb), 0.45);
}
.cw-btn--primary:hover { background: rgba(var(--accent-rgb), 0.26); }
.cw-btn--ghost { padding: 7px 12px; opacity: 0.85; }
/* "Create a widget" affordance at the foot of the picker list. */
.cw-pick-create { flex: 0 0 auto; display: flex; justify-content: center; padding: 8px 2px 2px; margin-top: 2px; border-top: 1px dashed rgba(255, 255, 255, 0.12); }

/* ── Package picker ──────────────────────────────────────────────── */
.cw-pick-title { flex: 0 0 auto; font-size: 0.76rem; font-weight: 800; opacity: 0.85; padding: 2px 2px 6px; }
.cw-body:has(.cw-pick-title) { flex-direction: column; min-height: 0; }
/* Pack header inside the picker list — groups widgets by manifest author. */
.cw-pick-group {
  flex: 0 0 auto;
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: rgba(var(--accent-rgb), 0.9);
  padding: 8px 2px 3px; margin-top: 2px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.cw-pick-list > .cw-pick-group:first-child { padding-top: 0; margin-top: 0; }
/* Search + category chips. Only rendered once the list is long enough to be
   worth filtering, so short lists keep every pixel for the rows themselves. */
.cw-pick-filter { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; padding: 0 2px 8px; }
.cw-pick-search { position: relative; display: flex; }
.cw-pick-input {
  flex: 1 1 auto; min-width: 0;
  /* Comfortable on the touchscreen without eating the list. */
  padding: 8px 10px; border-radius: 9px;
  font: inherit; font-size: 0.74rem; font-weight: 600; color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
.cw-pick-input::placeholder { opacity: 0.5; }
.cw-pick-input:focus {
  outline: none;
  border-color: rgba(var(--accent-rgb), 0.6);
  background: rgba(var(--accent-rgb), 0.08);
}
.cw-pick-input::-webkit-search-cancel-button { filter: invert(1); opacity: 0.5; cursor: pointer; }
/* One scrollable line — wrapping chips would push the list off a short tile. */
.cw-pick-chips {
  display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px;
  scrollbar-width: none;
}
.cw-pick-chips::-webkit-scrollbar { display: none; }
.cw-pick-chip {
  flex: 0 0 auto; cursor: pointer;
  padding: 5px 10px; border-radius: 999px;
  font: inherit; font-size: 0.66rem; font-weight: 700; color: inherit;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.12);
}
.cw-pick-chip:hover { background: rgba(255, 255, 255, 0.1); }
.cw-pick-chip.is-on {
  color: rgba(var(--accent-rgb), 1);
  background: rgba(var(--accent-rgb), 0.16);
  border-color: rgba(var(--accent-rgb), 0.5);
}
.cw-pick-empty {
  flex: 0 0 auto; padding: 14px 2px; text-align: center;
  font-size: 0.72rem; font-weight: 600; opacity: 0.6;
}
.cw-pick-list {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 6px; padding: 1px 4px 0 0;
  scrollbar-width: thin; scrollbar-color: rgba(var(--accent-rgb), 0.55) transparent;
}
.cw-pick-list::-webkit-scrollbar { width: 6px; }
.cw-pick-list::-webkit-scrollbar-thumb { background: rgba(var(--accent-rgb), 0.5); border-radius: 999px; }
.cw-pick-list::-webkit-scrollbar-track { background: transparent; }
.cw-pick-row {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 9px;
  border: 1px solid rgba(255,255,255,0.09); background: rgba(255,255,255,0.04);
}
.cw-pick-txt { flex: 1 1 auto; min-width: 0; }
.cw-pick-name {
  font-size: 0.76rem; font-weight: 800;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cw-pick-meta { font-size: 0.66rem; font-weight: 700; opacity: 0.55; margin-top: 1px; }
.cw-pick-desc {
  font-size: 0.7rem; font-weight: 600; opacity: 0.75; line-height: 1.35; margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  overflow-wrap: anywhere;
}

/* ── Permission dialog (solid background — must stay readable at any
      glass transparency, like the preset share/import dialogs) ───── */
/* Above .preset-modal-overlay (2000). A grant prompt is raised BY those dialogs —
   an import installing a package asks right after — so anything lower renders
   behind the modal that triggered it and reads as "the permission dialog didn't
   appear". Every host-drawn decision below shares this floor for the same reason. */
.cw-perm-backdrop {
  position: fixed; inset: 0; z-index: 2100;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.55); padding: 18px;
}
.cw-perm {
  width: min(440px, 94vw); max-height: 86vh; overflow-y: auto;
  border-radius: 16px; padding: 18px 18px 16px;
  background: rgb(var(--panel-rgb, 17, 19, 20));
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 18px 60px rgba(0,0,0,0.5);
  display: flex; flex-direction: column; gap: 8px;
}
:root[data-appearance="light"] .cw-perm { background: #f4f6f8; border-color: rgba(0,0,0,0.1); }
.cw-perm-title { font-size: 0.95rem; font-weight: 800; }
.cw-perm-pkg { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.cw-perm-name { font-size: 0.85rem; font-weight: 800; color: rgba(var(--accent-rgb), 1); }
.cw-perm-meta { font-size: 0.7rem; font-weight: 700; opacity: 0.6; }
.cw-perm-desc { font-size: 0.75rem; font-weight: 600; opacity: 0.8; line-height: 1.4; overflow-wrap: anywhere; }
.cw-perm-sec { margin-top: 4px; font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.6; }
.cw-perm-nothing { text-transform: none; letter-spacing: 0; font-weight: 700; opacity: 0.75; }
.cw-perm-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.cw-perm-chip {
  font-size: 0.7rem; font-weight: 700; padding: 4px 10px; border-radius: 999px;
  color: rgba(var(--accent-rgb), 1); background: rgba(var(--accent-rgb), 0.12);
  border: 1px solid rgba(var(--accent-rgb), 0.35);
}
.cw-perm-note {
  margin-top: 6px; font-size: 0.71rem; font-weight: 600; line-height: 1.45; opacity: 0.65;
  padding: 8px 10px; border-radius: 9px;
  background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.09);
}
:root[data-appearance="light"] .cw-perm-note { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.08); }
/* User-filled host slots: the address fields a package's `userHosts` declares.
   Sized for the Xeneon Edge touchscreen (44px rows), and the error line is
   reserved space rather than inserted on demand so a message never reflows the
   dialog under the finger that's typing. */
.cw-perm-fields { display: flex; flex-direction: column; gap: 10px; }
.cw-perm-field { display: flex; flex-direction: column; gap: 4px; }
.cw-perm-field-label { font-size: 0.72rem; font-weight: 700; opacity: 0.85; }
.cw-perm-input {
  min-height: 44px; padding: 8px 12px; border-radius: 9px;
  font: inherit; font-size: 0.78rem; font-weight: 600;
  color: inherit; background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.14);
}
.cw-perm-input::placeholder { opacity: 0.4; }
.cw-perm-input:focus-visible { outline: 2px solid rgba(var(--accent-rgb), 0.7); outline-offset: 1px; }
.cw-perm-input--bad { border-color: rgba(var(--danger-rgb),0.7); }
.cw-perm-field-err {
  min-height: 0.95rem; font-size: 0.68rem; font-weight: 600; line-height: 1.2;
  color: rgba(var(--danger-rgb),1);
}
:root[data-appearance="light"] .cw-perm-input { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.14); }
.cw-perm-actions-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
/* Allow stays disabled until every declared address field holds a usable value
   — it must not read as pressable while it does nothing. */
.cw-btn:disabled { opacity: 0.4; pointer-events: none; }

/* ── Clipboard copy confirmation — Widget SDK `clipboard` capability ──
   The host-drawn gate a widget's copy request must pass through. Same modal
   language as the permission dialog (shared .cw-btn), so it reads as a Xenon
   decision the user is making, not the widget's own UI. */
.cw-clip-backdrop {
  position: fixed; inset: 0; z-index: 2110;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.55); padding: 18px;
}
.cw-clip {
  width: min(400px, 94vw); border-radius: 16px; padding: 18px 18px 16px;
  background: rgb(var(--panel-rgb, 17, 19, 20));
  border: 1px solid rgba(255,255,255,0.12);
  box-shadow: 0 18px 60px rgba(0,0,0,0.5);
  display: flex; flex-direction: column; gap: 10px;
}
:root[data-appearance="light"] .cw-clip { background: #f4f6f8; border-color: rgba(0,0,0,0.1); }
.cw-clip-title { font-size: 0.95rem; font-weight: 800; }
.cw-clip-what { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.cw-clip-who { font-size: 0.82rem; font-weight: 800; color: rgba(var(--accent-rgb), 1); }
.cw-clip-label { font-size: 0.78rem; font-weight: 600; opacity: 0.85; overflow-wrap: anywhere; }
.cw-clip-preview {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-radius: 10px;
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.08);
}
:root[data-appearance="light"] .cw-clip-preview { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.08); }
.cw-clip-val {
  flex: 1 1 auto; min-width: 0;
  font-family: ui-monospace, "Cascadia Code", "Consolas", monospace;
  font-size: 0.95rem; font-weight: 700; letter-spacing: 0.04em;
  overflow-wrap: anywhere;
}
.cw-clip-eye {
  flex: 0 0 auto; padding: 4px 9px; border-radius: 7px;
  border: 1px solid rgba(255,255,255,0.14); background: transparent;
  color: inherit; font-size: 0.68rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: 0.05em; cursor: pointer; opacity: 0.8;
}
.cw-clip-eye:hover { opacity: 1; }
.cw-clip-note {
  font-size: 0.72rem; font-weight: 600; line-height: 1.4; opacity: 0.7;
}
.cw-clip-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 2px; }

.cw-ext-backdrop {
  position: fixed; inset: 0; z-index: 2110;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.55); padding: 18px;
}

/* External-open confirmation — reuses the clipboard modal shell. The domain is
   the decision the user is making, so it is the one thing set at full size;
   the path sits under it, dimmed and truncated, and can never push it out of
   view. */
.cw-ext-preview { align-items: flex-start; }
.cw-ext-urlbox { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.cw-ext-host {
  font-family: ui-monospace, "Cascadia Code", "Consolas", monospace;
  font-size: 0.98rem; font-weight: 800; letter-spacing: 0.01em; overflow-wrap: anywhere;
}
.cw-ext-path {
  font-family: ui-monospace, "Cascadia Code", "Consolas", monospace;
  font-size: 0.72rem; font-weight: 600; opacity: 0.62; overflow-wrap: anywhere;
}

/* ── SDK badge chips — Widget SDK `badge` capability (js/sdk-badges.js) ──
   Small always-on readouts next to the clock, one per granted package (up to a
   few at once). #clock-sdkbadges is a plain span inside .clock in the base
   markup; in the minimal chrome topbar-minimal.js reparents it into the island
   pill as the 'badges' segment, where the segment system draws its own hairline
   divider and padding (.island-seg::before) — so NO divider/padding belongs
   here, or it double-draws. Spacing in the full bar comes from .clock-meta's
   own gap, exactly like the vitals chips next door. The chips themselves are
   FLAT text — no fill, no outline — for the same reason the weather chip is:
   next to the plain clock and date, a raised pill reads as a button you can
   press, and this one does nothing when tapped. Two chips stay legible apart on
   the row gap alone, exactly like the vitals chips. */
.clock-sdkbadges { display: inline-flex; align-items: center; gap: 10px; }
/* An author `display` overrides the UA stylesheet's [hidden] { display: none },
   so the empty row would keep its box — and, as an island segment, keep drawing
   the segment hairline: a stray trailing divider with nothing after it. */
.clock-sdkbadges[hidden] { display: none; }
.sdk-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 13px; font-weight: 700; line-height: 1;
  color: var(--text, #e7e9ee);
  white-space: nowrap;
}
/* ── SDK mini slot — Widget SDK `mini` capability (js/sdk-mini.js) ──
   A row of host-drawn blocks living among the topbar BUTTONS rather than in the
   clock, wherever the user placed it (Settings → Dynamic Island → Bottoni della
   barra). It borrows the island's block styles verbatim — the same renderer
   draws them — and adds only the container: a plate that reads as one object
   sitting between buttons, at the buttons' own height so the bar never grows.
   In Minimal the whole thing rides its rail, which is a vertical flex, so the
   plate must not assume a horizontal neighbourhood beyond its own contents. */
.topbar-mini-slot { display: inline-flex; align-items: center; gap: 8px; }
.topbar-mini-slot[hidden] { display: none; }
.topbar-mini-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  max-width: 190px;
  padding: 5px 10px;
  border-radius: 11px;
  background: rgba(255,255,255,0.05);
  overflow: hidden;
  --island-accent: var(--accent);
}
/* Text inside the slot is the one thing that can run long, and the bar has no
   room to give: cut it rather than let one package push the buttons off. */
.topbar-mini-item .sdk-island-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A chip that CAN be tapped (`badge: { action: true }`) is a real <button>, and
   it has to look like one — the comment above explains that a raised pill next
   to the clock reads as pressable, which is exactly what this one is. It keeps
   the flat chip's type and spacing and adds only the affordance: a faint plate,
   a pressed state, and a comfortable target on the Edge touchscreen. */
.sdk-badge-btn {
  border: 0;
  border-radius: 9px;
  padding: 4px 8px;
  margin: -4px 0;   /* the plate must not raise the clock row */
  background: rgba(255,255,255,0.06);
  font: inherit;
  font-size: 13px; font-weight: 700; line-height: 1;
  color: var(--text, #e7e9ee);
  cursor: pointer;
  transition: background 0.15s ease, transform 0.12s ease;
}
.sdk-badge-btn:hover { background: rgba(255,255,255,0.12); }
.sdk-badge-btn:active { transform: scale(0.94); }
@media (prefers-reduced-motion: reduce) {
  .sdk-badge-btn { transition: none; }
  .sdk-badge-btn:active { transform: none; }
}
/* The widget's own glyph. Its colour is set inline from the (hex-validated)
   badge payload — a star is gold, a battery is green — and falls back to the
   chip's text colour when the widget names none. */
.sdk-badge-ico { display: inline-flex; line-height: 1; }
.sdk-badge-ico[hidden] { display: none; }
