/* ── Topbar minimal mode ──────────────────────────────────────
   body.topbar-minimal swaps the full glass bar for:
   - one compact "island" pill at top centre — small clock, date,
     weather chip and the dashboard page dots in a single capsule
   - two collapsible vertical rails hugging the screen edges with
     the reparented topbar actions (see js/topbar-minimal.js)
   Full-dashboard only; embed modes (?panel=…) keep their own bar. */

body.topbar-minimal:not([data-panel]) .topbar { display: none; }

/* The pill and rails float (position: fixed), so the shell drops the topbar row
   entirely and the dashboard grid reclaims the FULL viewport height. Crucially the
   grid keeps that full height WHILE EDITING too (not pushed down for a dock row):
   the layout is then truly WYSIWYG — a tile that will tuck under the floating clock
   island shows that overlap live (reflowIsland previews the content inset), so you
   can tell before pressing Done whether a shortened tile clears the clock/weather.
   The editor's dock floats over the grid instead of displacing it — as a compact
   capsule (NOT edge-to-edge: a full-width bar buried the top row of tiles and
   their controls). It starts collapsed to its slim header bar, centred below the
   clock island, and can be dragged anywhere by that bar (free position persisted
   per device by dashboard-layout.js; inline top/left then override the centring). */
body.topbar-minimal:not([data-panel]) .shell { grid-template-rows: minmax(0, 1fr); }
body.topbar-minimal.layout-editing:not([data-panel]) .layout-dock {
  position: fixed;
  top: 58px;              /* clears the floating clock island (pill: top 9 + height) */
  left: 0;
  right: 0;
  width: fit-content;
  max-width: min(880px, calc(100vw - 24px));
  margin: 0 auto;
  z-index: 56;            /* over tiles, under the pill (57) / rails (58) / modals */
}

/* The grid keeps the FULL viewport height: tiles may sit level with the clock
   island, using the whole top band at the pill's sides. Only a tile that
   actually lies UNDER the pill insets its content down (--island-clear, set per
   tile in js/topbar-minimal.js on every fit pass) so its header clears the
   clock while the tile box — and its neighbours — stay full height. The old
   full-width `.pager { padding-top: 54px }` reserve kept the whole band
   off-limits on every page, which wasted the top strip on the fixed-height
   Xeneon Edge; the known cost of the per-tile inset is that on a screen where a
   page scrolls vertically the inset scrolls with the tile (cosmetic, and the
   kiosk never scrolls). No transition on the inset: it's recomputed on every
   layout fit, so animating it would make the tile bob on each interaction. */
body.topbar-minimal .grid-stack-item.island-clear > .grid-stack-item-content {
  top: var(--island-clear, 0px);
}

/* ── Island pill — floating capsule, top centre ───────────────
   Segments (time · date · weather · page dots) read left→right, each set off
   by a hairline so nothing feels crammed together. Floats over the centre
   tile's top edge, mirroring how the rails hover over the side tiles. */
.topbar-mini { display: none; }

/* Style "none": rails yes, capsule no. The pill still HOLDS the reparented clock
   segments (so switching back to Minimal restores the user's exact island order
   and visibility) — it simply never paints. !important because every pill rule
   below is written against body.topbar-minimal, which "none" also carries. */
body.topbar-noisland .topbar-mini { display: none !important; }

body.topbar-minimal:not([data-panel]) .topbar-mini {
  position: fixed;
  top: 9px;
  /* Horizontal anchor follows Settings → Aspetto → Barra superiore → clock
     position. --mini-tx carries the base translate so the notification-island
     morph below composes cleanly in every alignment. Centre is the default. */
  --mini-tx: -50%;
  left: 50%;
  transform: translateX(var(--mini-tx));
  z-index: 57; /* above tiles, below every modal/overlay (same band as rails) */
  display: inline-flex;
  align-items: center;
  gap: 14px;
  padding: 7px 13px 7px 18px;
  border-radius: 999px;
  border: 1px solid var(--floating-ui-border);
  background: var(--floating-ui-bg);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,var(--panel-highlight-alpha)), var(--shadow-lg);
}

/* Left / right anchor the island to that screen edge (12px inset) instead of
   centring it — so it can sit clear of the tile beneath the centre. Only the
   horizontal anchor changes; the notification-island morph reuses --mini-tx. */
body.topbar-minimal:not([data-panel])[data-topbar-align="left"] .topbar-mini {
  left: 12px;
  --mini-tx: 0%;
}
body.topbar-minimal:not([data-panel])[data-topbar-align="right"] .topbar-mini {
  left: auto;
  right: 12px;
  --mini-tx: 0%;
}

/* Clock flattens to a single row: time · date · weather. Segment gaps + the
   hairlines below carry the spacing, so the pieces read as an organised set. */
body.topbar-minimal .topbar-mini .clock { flex-direction: row; align-items: center; gap: 14px; }
body.topbar-minimal .topbar-mini .clock-face { align-items: baseline; }
body.topbar-minimal .topbar-mini .clock-h,
body.topbar-minimal .topbar-mini .clock-m {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.5px;
  min-width: auto;
}
body.topbar-minimal .topbar-mini .clock-sep {
  font-size: 17px;
  font-weight: 400;
  margin-bottom: 0;
  padding: 0 1px;
}
/* am/pm keeps the clean box-less suffix from Topbar.css, just smaller */
body.topbar-minimal .topbar-mini .clock-ampm {
  font-size: 10px;
  letter-spacing: 1px;
  margin: 0 0 0 4px;
  align-self: center;
}

/* Meta row: hairline before it (splits time | date), no leading status dot. */
body.topbar-minimal .topbar-mini .clock-meta {
  gap: 12px;
  min-width: 0;
  padding-left: 14px;
  border-left: 1px solid var(--floating-ui-border);
}
body.topbar-minimal .topbar-mini .status-dot-inline { display: none; }
body.topbar-minimal .topbar-mini .clock-date {
  font-size: 12.5px;
  font-weight: 650;
  color: var(--muted-text);
  white-space: nowrap;
}
/* The mid-dot becomes a slim vertical hairline (date | weather) */
body.topbar-minimal .topbar-mini .clock-meta-sep {
  width: 1px;
  height: 15px;
  background: var(--floating-ui-border);
  color: transparent;
  font-size: 0;
  margin: 0;
  padding: 0;
  align-self: center;
}
/* Weather chip strips its box down to icon + temperature; the city lives in
   the modal (a tap still opens it). Base sizing comes from WeatherModal.css —
   flatten it to sit inline in the capsule row. */
body.topbar-minimal .topbar-mini .weather-place { display: none; }
body.topbar-minimal .topbar-mini .clock-weather {
  height: auto;
  padding: 0;
  gap: 5px;
  background: none;
  border: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
body.topbar-minimal .topbar-mini .clock-weather .weather-mini-icon { width: 23px; height: 20px; }
body.topbar-minimal .topbar-mini .weather-temp { font-size: 15px; }

/* SDK badge chips (js/sdk-badges.js) — size them to the pill's density. The row
   itself is an island segment, so its divider/padding come from .island-seg
   below; only the chips need resizing. Base look (flat, no fill) is in
   CustomWidget.css — it needs no padding to unset here. */
body.topbar-minimal .topbar-mini .sdk-badge { font-size: 12px; }

body.topbar-minimal .topbar-mini .pager-dots:empty { display: none; }

/* ── Flattened island segments — order · dividers · visibility ──
   Each direct pill child is one reorderable segment; js/topbar-minimal.js sets
   its CSS `order` and these classes from the user's list. A slim hairline sits
   before every segment except the first VISIBLE one (island-seg-lead — computed
   in JS because flex `order` is invisible to DOM :first-child). island-seg-hidden
   removes a segment the user turned off. */
body.topbar-minimal:not([data-panel]) .topbar-mini > .island-seg {
  position: relative;
  padding-left: 15px;
}
body.topbar-minimal:not([data-panel]) .topbar-mini > .island-seg::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 16px;
  transform: translateY(-50%);
  background: var(--floating-ui-border);
}
body.topbar-minimal:not([data-panel]) .topbar-mini > .island-seg-lead { padding-left: 0; }
body.topbar-minimal:not([data-panel]) .topbar-mini > .island-seg-lead::before { display: none; }
body.topbar-minimal .topbar-mini > .island-seg-hidden { display: none !important; }

/* ── Notification island ──────────────────────────────────────
   A heads-up notification takes the clock island's place (Apple Dynamic-Island):
   the toast stack drops onto the pill's exact spot, and while one is showing
   (body.xtoast-island, set by js/toast.js) the pill recedes so the card appears to
   expand straight out of it. The capsule→card morph itself lives in Toast.css. */
body.topbar-minimal:not([data-panel]) .xtoast-stack { top: 9px; }

.topbar-mini {
  transition:
    opacity 0.3s ease,
    transform 0.34s cubic-bezier(0.22, 1.15, 0.36, 1);
}

/* ── Island morph (js/topbar-minimal.js morphIsland) ──────────────────────────
   Structural swaps — today the music takeover starting or ending on play/pause —
   fade the capsule's contents out, exchange them while they are invisible, then
   glide the box between its old and new size as they fade back in. Widths are
   pinned inline for the duration only; nothing here sizes the pill on its own.
   The durations must stay in step with MORPH_DIM_MS / MORPH_BOX_MS there. */
body.topbar-minimal:not([data-panel]) .topbar-mini > * { transition: opacity 0.16s ease; }
body.topbar-minimal:not([data-panel]) .topbar-mini.mini-morph-dim > * { opacity: 0; }
body.topbar-minimal:not([data-panel]) .topbar-mini.mini-morphing {
  /* The pinned box is briefly smaller than what it holds; clip rather than let
     the old contents spill past the capsule's rounded edge. */
  overflow: hidden;
  transition:
    width 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    height 0.34s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.3s ease,
    transform 0.34s cubic-bezier(0.22, 1.15, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  body.topbar-minimal:not([data-panel]) .topbar-mini > * { transition: none; }
}
body.topbar-minimal.xtoast-island:not([data-panel]) .topbar-mini,
body.topbar-minimal.xisland-live:not([data-panel]) .topbar-mini {
  opacity: 0;
  transform: translateX(var(--mini-tx)) translateY(-6px) scale(0.82);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .topbar-mini { transition: opacity 0.2s ease; }
  body.topbar-minimal.xtoast-island:not([data-panel]) .topbar-mini,
  body.topbar-minimal.xisland-live:not([data-panel]) .topbar-mini { transform: translateX(var(--mini-tx)); }
}

/* ── SDK island — widget text projection ──────────────────────
   A granted SDK widget's text (js/sdk-island.js) takes the pill's spot exactly
   like a notification does: body.xisland-live recedes the pill (rule above)
   and this capsule morphs in at the same anchor, expanding into a small card
   when the line wraps — current line bright, optional follow-up line dimmed
   (NotchPrompter-style). Text is host-rendered (textContent) — this file only
   ever styles it. A SYSTEM TOAST ALWAYS WINS: while body.xtoast-island is set
   the capsule recedes with the pill (rule below the show rule, so it takes
   precedence at equal specificity). */
.sdk-island { display: none; }

/* Full and Minimal share one Dynamic Island surface. In Full the centre clock
   recedes inside the glass bar; in Minimal the floating capsule recedes through
   the existing rule above. Style None carries topbar-noisland and is excluded. */
.clock { transition: opacity 0.25s ease, transform 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
body.xisland-live:not(.topbar-minimal):not(.topbar-hidden):not(.topbar-noisland):not([data-panel]) .clock {
  opacity: 0;
  transform: translateY(-5px) scale(0.86);
  pointer-events: none;
}

body.xisland-live:not(.topbar-hidden):not(.topbar-noisland):not([data-panel]) .sdk-island {
  position: fixed;
  top: 9px;
  --mini-tx: -50%;
  left: 50%;
  transform: translateX(var(--mini-tx));
  z-index: 57; /* same band as the pill: above tiles, below modals/toasts */
  display: flex;
  align-items: stretch;
  gap: 12px;
  width: max-content; /* hug a short line; the cap below bounds a long one */
  max-width: min(520px, 66vw); /* fixed cap: never widens under a tile → no reflow bobbing */
  padding: 10px 16px;
  border-radius: 18px;
  border: 1px solid var(--floating-ui-border);
  background: var(--floating-ui-bg);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,var(--panel-highlight-alpha)), var(--shadow-lg);
  overflow: hidden;
  will-change: transform, opacity;
  --island-accent: var(--accent);
}
/* Same edge anchors as the pill, so the capsule lands where the pill was. */
body.topbar-minimal.xisland-live:not([data-panel])[data-topbar-align="left"] .sdk-island {
  left: 12px;
  --mini-tx: 0%;
}
body.topbar-minimal.xisland-live:not([data-panel])[data-topbar-align="right"] .sdk-island {
  left: auto;
  right: 12px;
  --mini-tx: 0%;
}
/* Capsule-squash entrance, mirroring the toast morph (transform/opacity only). */
@keyframes sdk-island-in {
  from { opacity: 0; transform: translateX(var(--mini-tx)) translateY(-4px) scaleX(0.6) scaleY(0.4); }
  to   { opacity: 1; transform: translateX(var(--mini-tx)); }
}
.sdk-island.enter-morph { animation: sdk-island-in 0.34s cubic-bezier(0.22, 1.15, 0.36, 1); }
@keyframes sdk-island-slide-in {
  from { opacity: 0; transform: translateX(var(--mini-tx)) translateY(-14px); }
  to { opacity: 1; transform: translateX(var(--mini-tx)); }
}
.sdk-island.enter-slide { animation: sdk-island-slide-in 0.32s cubic-bezier(0.22, 1, 0.36, 1); }
@keyframes sdk-island-pop-in {
  from { opacity: 0; transform: translateX(var(--mini-tx)) scale(0.72); }
  to { opacity: 1; transform: translateX(var(--mini-tx)); }
}
.sdk-island.enter-pop { animation: sdk-island-pop-in 0.28s cubic-bezier(0.2, 1.3, 0.3, 1); }
@keyframes sdk-island-fade-in { from { opacity: 0; } to { opacity: 1; } }
.sdk-island.enter-fade { animation: sdk-island-fade-in 0.24s ease; }

.sdk-island.is-leaving { animation: none; transition: opacity 0.2s ease, transform 0.22s ease; }
.sdk-island.is-leaving.exit-morph { opacity: 0; transform: translateX(var(--mini-tx)) translateY(-5px) scale(0.78); }
.sdk-island.is-leaving.exit-slide { opacity: 0; transform: translateX(var(--mini-tx)) translateY(-14px); }
.sdk-island.is-leaving.exit-fade { opacity: 0; }
/* System toast wins: recede exactly like the pill while a toast is up. */
body.xtoast-island:not([data-panel]) .sdk-island {
  opacity: 0;
  transform: translateX(var(--mini-tx)) translateY(-6px) scale(0.82);
  pointer-events: none;
  transition:
    opacity 0.3s ease,
    transform 0.34s cubic-bezier(0.22, 1.15, 0.36, 1);
}

/* Text flows as stacked sentence rows (js/sdk-island.js): dimmed past row,
   bright current (never clamped — it wraps in full), dimmed upcoming. On a
   prompter advance the block slides up one row (transform, its own layer)
   while the card height tweens — a karaoke glide, not a snap. */
.sdk-island-flow {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  will-change: transform;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.sdk-island-row {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: 0.1px;
  line-height: 1.4;
  color: var(--text);
  opacity: 0.5;
  transition: opacity 0.45s ease;
  animation: xe-isl-row-in 0.4s ease;
}
.sdk-island-row.is-cur { opacity: 1; }
/* The line just spoken: one dim ellipsized row, so history never stacks up. */
.sdk-island-row.is-past,
.sdk-island-row:not(.is-cur) { font-size: 12px; font-weight: 500; }
.sdk-island-row.is-past {
  opacity: 0.32;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@keyframes xe-isl-row-in { from { opacity: 0; } }
/* Multi-row flow pins the card width so per-sentence reflow can't wobble. */
body.xisland-live:not(.topbar-hidden):not(.topbar-noisland):not([data-panel]) .sdk-island.is-flow {
  width: min(520px, 66vw);
}
/* Live meta column (speed on top, time left below) — right-aligned, split off
   by a hairline like an iOS Live Activity, so numbers never touch the prose. */
.sdk-island-meta {
  display: none;
  flex: 0 0 auto;
  flex-direction: column;
  justify-content: center;
  align-items: flex-end;
  gap: 2px;
  padding-left: 12px;
  border-left: 1px solid var(--floating-ui-border);
}
.sdk-island.has-badge .sdk-island-meta { display: flex; }
.sdk-island-m1 {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.2px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--accent);
}
.sdk-island-m2 {
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--text);
  opacity: 0.55;
}
.sdk-island-m2:empty { display: none; }

/* ── Structured Live Activities (SDK islandDynamic) ────────────────
   Every node is built by Xenon from sdk-island-schema.js. Widget strings are
   textContent; the CSS is fixed host chrome and cannot be supplied by a guest. */
.sdk-island.is-structured { align-items: center; padding: 9px 12px; }
.sdk-island.is-structured.is-expanded { max-width: min(680px, 82vw); padding: 12px 16px; }

/* Full-bar activity (`"island": { "full": true }`, layout: 'full') — the widest
   step, for an activity that genuinely needs the room. It stays a CAPSULE: still
   `width: max-content`, so it hugs its content and grows only as far as it must,
   and it keeps the island's height, radius and padding. It is deliberately NOT
   pinned edge to edge — a capsule stretched across the screen stops reading as
   an island, leaves a wide empty middle, and pushes the content to the corners.
   Only the cap moves. */
.sdk-island.is-structured.is-full { max-width: min(1040px, 94vw); padding: 12px 18px; gap: 14px; }
.sdk-island.is-structured.is-full .sdk-island-blocks { gap: 14px; }
.sdk-island.is-structured .sdk-island-flow,
.sdk-island.is-structured .sdk-island-meta { display: none; }
.sdk-island-blocks {
  display: none;
  align-items: center;
  min-width: 0;
  gap: 9px;
}
.sdk-island.is-structured .sdk-island-blocks { display: flex; }
.sdk-island-icon {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  min-width: 24px;
  font-size: 18px;
  line-height: 1;
  color: var(--island-accent);
}
.sdk-island-text {
  min-width: 0;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
}
.sdk-island-text.two-lines {
  white-space: normal;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.sdk-island-text.is-strong { font-weight: 850; }
.sdk-island-text.tone-muted { color: var(--muted-text); }
.sdk-island-text.tone-accent { color: var(--island-accent); }
.sdk-island-text.tone-success { color: var(--color-success); }
.sdk-island-text.tone-warning { color: var(--color-warn); }
.sdk-island-text.tone-danger { color: var(--color-danger); }
.sdk-island-progress {
  width: 72px;
  height: 5px;
  flex: 0 0 auto;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255,255,255,0.12);
}
.sdk-island-progress > span {
  display: block;
  width: 100%;
  height: 100%;
  transform-origin: left center;
  border-radius: inherit;
  background: var(--island-accent);
  transition: transform 0.24s ease;
}
.sdk-island-bars {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 42px;
  height: 22px;
  flex: 0 0 auto;
}
.sdk-island-bars > i {
  width: 2.5px;
  height: 100%;
  border-radius: 999px;
  background: var(--island-accent);
  transform: scaleY(var(--bar-level));
  will-change: transform;
}
@keyframes sdk-island-bar-pulse {
  0%, 100% { transform: scaleY(var(--bar-level)); }
  50% { transform: scaleY(1); }
}
.sdk-island-bars.is-animated > i {
  animation: sdk-island-bar-pulse calc(0.72s + var(--bar-i) * 0.035s) ease-in-out infinite alternate;
  animation-delay: calc(var(--bar-i) * -0.08s);
}
body.overlay-frozen .sdk-island-bars.is-animated > i,
body.ambient-idle .sdk-island-bars.is-animated > i,
body.game-mode .sdk-island-bars.is-animated > i,
body.low-power-gpu .sdk-island-bars.is-animated > i { animation: none; }
.sdk-island-builtin {
  flex: 0 0 auto;
  font-size: 12px;
  font-weight: 750;
  color: var(--muted-text);
  white-space: nowrap;
}
.sdk-island-builtin-time { font-size: 15px; color: var(--text); font-variant-numeric: tabular-nums; }
.sdk-island-action {
  min-height: 30px;
  max-width: 120px;
  padding: 0 11px;
  border-radius: 10px;
  border: 1px solid var(--floating-ui-border);
  background: rgba(255,255,255,0.06);
  color: var(--text);
  font: inherit;
  font-size: 11.5px;
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.sdk-island-action.is-emphasis { color: var(--on-accent); background: var(--island-accent); border-color: transparent; }
.sdk-island-spacer { width: 5px; flex: 0 0 auto; }
.sdk-island-spacer.is-large { width: 14px; }
@media (prefers-reduced-motion: reduce) {
  body.xisland-live:not(.topbar-hidden):not(.topbar-noisland):not([data-panel]) .sdk-island { animation: none; }
  body.xtoast-island:not([data-panel]) .sdk-island {
    transition: opacity 0.2s ease;
    transform: translateX(var(--mini-tx));
  }
  .sdk-island-flow { transition: none; }
  .sdk-island-row { transition: none; animation: none; }
  .sdk-island-bars.is-animated > i { animation: none; }
}

/* ── Edge rails ───────────────────────────────────────────────
   Fixed capsules, vertically centred. Structure: [body][handle]
   (mirrored on the right). Collapsing slides the capsule off-screen
   leaving only the drawer-pull handle flush with the edge. */
.edge-rail { display: none; }

body.topbar-minimal:not([data-panel]) .edge-rail {
  position: fixed;
  top: 50%;
  z-index: 58; /* above tiles, below every modal/overlay */
  display: flex;
  align-items: center;
  gap: 4px;
  transform: translateY(-50%);
  transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
  will-change: transform;
}
.edge-rail-left  { left: 0;  padding-left: 8px; }
.edge-rail-right { right: 0; padding-right: 8px; flex-direction: row-reverse; }

/* While editing the layout, the fixed edge rails sit in the SAME z-band, and on
   the same screen edges, as the tiles' left-edge move grips and bottom-right
   resize corners — so an edge-column tile can't be grabbed or resized (the rail
   swallows the pointer). The rails aren't needed mid-edit (you exit through the
   layout dock's Done, and add widgets from the palette pull), so hide them while
   editing: the tile handles along both edges become reachable again. */
body.topbar-minimal.layout-editing:not([data-panel]) .edge-rail { display: none; }

/* A rail with nothing in it goes away, handle and all. Since v4.11.4 every button
   can be hidden or sent to the other side, so a rail can end up holding nothing
   — and what was left on screen was a lone arrow that opened an empty capsule:
   a control that promises something and delivers a blank.
   Deliberately CSS rather than a JS pass: the two things that can fill a rail
   without any settings change are the favourites strip (starring an app) and the
   SDK mini slot (a package drawing into it), so a class computed on the settings
   apply would be stale exactly when it mattered. `:has()` re-evaluates itself.
   Note this can never hide BOTH rails: Settings and Layout cannot be hidden, so
   whichever side they are on always has content. */
body.topbar-minimal:not([data-panel]) .edge-rail:not(:has(
  .edge-rail-body .topbtn:not(.topbar-item-hidden),
  .edge-rail-body .app-favorites:not(:empty):not(.topbar-item-hidden),
  .edge-rail-body .topbar-mini-slot:not([hidden]):not(.topbar-item-hidden)
)) { display: none; }

body.topbar-minimal .edge-rail-left.is-collapsed  { transform: translate(calc(-100% + 22px), -50%); }
body.topbar-minimal .edge-rail-right.is-collapsed { transform: translate(calc(100% - 22px), -50%); }

/* Auto-hidden after ~10s idle (js/topbar-minimal.js): tuck the rail body off the
   edge but leave the 22px handle peeking — same as .is-collapsed — so there's a
   visible arrow to bring it back (tapping the handle, or anywhere in the edge
   strip, reveals it). Fully hiding the handle left no discoverable way to reopen.
   Listed after .is-collapsed so it wins when a rail is both (equal specificity →
   source order); both land on the same peek, so the transition stays smooth. */
body.topbar-minimal .edge-rail-left.is-auto-hidden  { transform: translate(calc(-100% + 22px), -50%); }
body.topbar-minimal .edge-rail-right.is-auto-hidden { transform: translate(calc(100% - 22px), -50%); }

.edge-rail-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px 7px;
  border-radius: 19px;
  border: 1px solid var(--floating-ui-border);
  /* Rails float OVER tile content (unlike the in-flow topbar/pill), so they use
     the frosted floating-UI surface for readability on busy backgrounds. */
  background: var(--floating-ui-bg);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,var(--panel-highlight-alpha)), var(--shadow-lg);
  max-height: 76vh;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
}
.edge-rail-body::-webkit-scrollbar { display: none; }

/* Drawer pull — slim tab, chevron flips to always point "the way out" */
.edge-rail-handle {
  width: 22px;
  height: 62px;
  padding: 0;
  border-radius: 9px;
  border: 1px solid var(--floating-ui-border);
  background: var(--floating-ui-bg);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  color: var(--dim-text);
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: color 0.16s ease, background 0.16s ease;
}
.edge-rail-handle:hover { color: var(--text); }
.edge-rail-handle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.edge-rail-handle svg { width: 14px; height: 14px; transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1); }
.edge-rail-left  .edge-rail-handle svg { transform: rotate(180deg); }
/* Collapsed OR auto-hidden: the rail is tucked away, so the chevron points "out"
   (tap to reveal). */
.edge-rail-left.is-collapsed  .edge-rail-handle svg,
.edge-rail-left.is-auto-hidden  .edge-rail-handle svg { transform: rotate(0deg); }
.edge-rail-right.is-collapsed .edge-rail-handle svg,
.edge-rail-right.is-auto-hidden .edge-rail-handle svg { transform: rotate(180deg); }

/* ── Reparented topbar actions: stack vertically, icon-only ── */
body.topbar-minimal .edge-rail .quickbar,
body.topbar-minimal .edge-rail .top-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
}
body.topbar-minimal .edge-rail .topbtn {
  width: 42px;
  height: 42px;
  padding: 0;
  justify-content: center;
  border-radius: 14px;
  /* The rail capsule is the glass surface — buttons sit on it as quiet tiles */
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
body.topbar-minimal .edge-rail .topbtn:hover { background: rgba(255,255,255,0.07); }
body.topbar-minimal .edge-rail .topbtn svg { width: 19px; height: 19px; }
/* Hide labels (keep the settings-update dot, which is also a span) */
body.topbar-minimal .edge-rail .topbtn span:not(.topbtn-update-dot):not(.cgal-store-dot) { display: none; }
/* Xenon keeps its solid accent identity even as a compact tile */
body.topbar-minimal .edge-rail .topbtn.topbtn-xenon {
  background: var(--accent);
  box-shadow: 0 2px 12px rgba(var(--accent-rgb), 0.42);
}
body.topbar-minimal .edge-rail .topbtn.qbtn-layout.active {
  background: var(--accent);
  box-shadow: 0 2px 12px rgba(var(--accent-rgb), 0.4);
}
/* Favorites stack under the main actions past a hairline divider */
body.topbar-minimal .edge-rail .app-favorites {
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-width: none;
  overflow: visible;
}
body.topbar-minimal .edge-rail .app-favorites:not(:empty) {
  border-top: 1px solid var(--panel-border);
  padding-top: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .edge-rail,
  .edge-rail-handle svg { transition: none; }
}
