/* music-library.css — empty-state cards, tiles, library
   grid + list rows + head, drop overlay, danger button
   variant, the right-click context menu primitive shared
   across views, the import toast. */

/* ----- Empty state -----
   Mono ASCII treatment matching the rest of the site personality. No big
   library icon — that read as generic AI design. The kicker is a // token
   in cyan; the body is muted mono prose; the button is the accent style. */
.ms-empty {
    flex: 1;                 /* grow to fill the entire view area */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    color: var(--fg-muted);
    padding: 3rem 1rem;
    gap: 0.9rem;
    min-height: 50vh;        /* baseline presence even if parent is short */
    font-family: var(--font-mono);
}
.ms-empty .ms-empty-heading {
    font-size: var(--text-xl);
    color: var(--fg);
    font-weight: 600;
    letter-spacing: 0.01em;
    margin: 0;
}
.ms-empty .ms-empty-body {
    font-size: var(--text-sm);
    color: var(--fg-muted);
    max-width: 44ch;
    line-height: 1.7;
}
/* Accent button — matches the cyan style on Clipped's #export-btn:
   cyan border + cyan text + tinted background, 4px radius, bold mono.
   Selector covers both <button class="ms-add-btn"> and the <label> variant
   used to trigger hidden file inputs natively. */
.ms-empty .ms-add-btn {
    display: inline-block;
    margin-top: 0.4rem;
    min-width: 110px;
    padding: 6px 14px;
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    border: 1px solid var(--accent);
    border-radius: 4px;
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-align: center;
    cursor: pointer;
    user-select: none;
    transition: background var(--dur-fast) var(--ease-smooth), color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-smooth);
}
.ms-empty .ms-add-btn:hover {
    background: color-mix(in srgb, var(--accent) 22%, transparent);
}
.ms-empty .ms-add-btn:focus { outline: none; }
.ms-empty .ms-add-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.ms-empty .ms-empty-link {
    display: inline-block;
    margin-top: -0.15rem;
    font-size: 0.78rem;
    color: var(--fg-muted);
    background: none;
    border: none;
    cursor: pointer;
    border-bottom: 1px dotted var(--fg-dim);
    padding: 0 0 1px;
    user-select: none;
    transition: color var(--dur-fast) var(--ease-smooth), border-color var(--dur-fast) var(--ease-smooth);
}
.ms-empty .ms-empty-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.ms-empty .ms-empty-link:focus { outline: none; }
.ms-empty .ms-empty-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 2px;
}

/* ----- Tiles (grid + home rows) ----- */
.ms-tile {
    position: relative;            /* anchor for the absolute .ms-tile-more button */
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    scroll-snap-align: start;
    cursor: pointer;
    transition: transform var(--dur-fast) var(--ease-smooth);
    background: none;
    border: none;
    padding: 0;
    text-align: left;
    color: inherit;
    min-width: 0;
    user-select: none;
}
/* Gated to real pointers: iOS Safari otherwise interprets the first tap on
   a tile as a hover-fulfillment, swallowing the click event. The picked →
   play double-tap gesture (see attachRowBehavior in music-library.js) then
   needs three taps on iOS, not two. See § 15. */
@media (hover: hover) and (pointer: fine) {
    .ms-tile:hover { transform: scale(1.03); }
}

/* "Jump to current track" flash: ring of accent that fades out so the
   user can see where the track lives in the grid/list after clicking
   the title or artist in the player bar. JS adds `.jump-flash` to the
   row / tile (whichever element carries [data-track-id]); the
   stylesheet decides where the visual ring actually lands.
     - List rows: flash the whole row. The row IS the result.
     - Grid tiles: flash ONLY .ms-tile-cover. Otherwise the ring wraps
       a tall thin tile rectangle (cover + title + artist column)
       which reads as "this song's biography" instead of "this album".
       Scoping to the cover keeps the visual square. */
.ms-list-row.jump-flash {
    animation: ms-jump-flash var(--dur-slow) var(--ease-quick);
    border-radius: 8px;
}
.ms-tile.jump-flash .ms-tile-cover {
    animation: ms-jump-flash var(--dur-slow) var(--ease-quick);
}
@keyframes ms-jump-flash {
    0%   { box-shadow: 0 0 0 3px var(--accent), 0 0 18px 4px color-mix(in srgb, var(--accent) 55%, transparent); }
    100% { box-shadow: 0 0 0 0 transparent; }
}
.ms-tile:focus { outline: none; }
.ms-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 8px; }

/* "More" overlay on the cover — invisible until hover/focus, fades in. */
/* "Audio pending" pill — a track whose metadata synced but whose bytes haven't
   arrived from the peer device yet (cloud-sync §18). */
.ms-tile-pending {
    position: absolute;
    top: 8px;
    left: 8px;
    z-index: 2;
    padding: 2px 7px;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.4;
    color: var(--fg);
    background: var(--scrim);
    border: 1px solid var(--accent);
    border-radius: 4px;
    pointer-events: none;
}
.ms-tile.audio-pending .ms-tile-cover { opacity: 0.6; }

.ms-tile-more {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Cover-art scrim button: white-on-dark regardless of theme since
       the button always sits on top of the album-art scrim. */
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    opacity: 0;
    z-index: 2;
    transition: opacity var(--dur-fast) var(--ease-smooth), background var(--dur-fast) var(--ease-smooth);
}
.ms-tile:focus-within .ms-tile-more,
.ms-tile-more:focus-visible { opacity: 1; }
@media (hover: hover) and (pointer: fine) {
    .ms-tile:hover .ms-tile-more { opacity: 1; }
}
.ms-tile-more:hover { background: var(--accent); color: var(--bg); }
.ms-tile-more:focus { outline: none; }
.ms-tile-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.ms-tile-cover {
    aspect-ratio: 1;
    width: 100%;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: var(--shadow-md);
    background: var(--panel-bg);
    transition: box-shadow var(--dur-fast) var(--ease-smooth);
}
.ms-tile-cover img,
.ms-tile-cover svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
/* Title vs artist need clear hierarchy at a glance — bigger/bolder/white title
   over a smaller/lighter/dimmer artist line. */
.ms-tile-title {
    font-size: 0.95rem;
    line-height: 1.25;
    font-weight: 700;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ms-tile-sub {
    font-size: var(--text-xs);
    line-height: 1.2;
    font-weight: 400;
    color: var(--fg-dim);
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ----- Library grid ----- */
/* .ms-library-head + .ms-library-actions removed — those controls now live
   in the sticky .ms-topbar so they stay visible while the grid scrolls. */
.ms-mode-toggle {
    display: inline-flex;
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    overflow: hidden;
    /* Equal-height-in-row rule: the wrapper's OUTER box (including its
       1px border) must match --control-h, otherwise the library topbar
       is 2 px taller than playlists / playlist:X / search — which the
       user notices as misaligned search bars + a vertical shift when
       toggling select mode (this element lives in .ms-topbar-actions,
       which gets hidden during select, so the topbar momentarily
       contracts by 2 px). border-box keeps the border inside the
       declared height; buttons inside stretch to fill the remaining
       content area. */
    height: var(--control-h);
    box-sizing: border-box;
}
.ms-mode-toggle button {
    width: 34px;
    height: 100%;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--fg-muted);
    transition: background var(--dur-fast) var(--ease-smooth), color var(--dur-fast) var(--ease-smooth);
}
.ms-mode-toggle button:hover {
    background: color-mix(in srgb, var(--accent) 25%, transparent);
    color: var(--accent);
}
.ms-mode-toggle button[aria-pressed="true"] {
    background: var(--accent);
    color: var(--bg);
}
.ms-mode-toggle button[aria-pressed="true"]:hover {
    background: color-mix(in srgb, var(--accent) 80%, var(--fg));
    color: var(--bg);
}

.ms-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    /* Equal-height-in-row rule: all topbar / select-bar / action-bar
       interactive controls (.ms-btn, .ms-search-wrap, .ms-mode-toggle
       button) share --control-h. Vertical padding is dropped — the
       fixed height + box-sizing absorb it. Horizontal padding stays. */
    height: var(--control-h);
    box-sizing: border-box;
    padding: 0 0.85rem;
    background: var(--btn-bg);
    border: 1px solid var(--btn-border);
    border-radius: 6px;
    color: var(--fg);
    /* Inherit the body's font (var(--font-mono)) — <button> defaults to the
       UA's Arial-ish system font, which is what made the Cancel/Delete
       buttons look out of theme while the <label class="ms-btn"> ones
       (Add music / folder) inherited correctly. */
    font-family: inherit;
    font-size: 0.82rem;
    line-height: 1.2;
    cursor: pointer;              /* required for <label class="ms-btn"> */
    user-select: none;
    transition: border-color var(--dur-fast) var(--ease-smooth), color var(--dur-fast) var(--ease-smooth), background var(--dur-fast) var(--ease-smooth);
}
.ms-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--btn-bg-hover); }
.ms-btn:focus { outline: none; }
.ms-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ms-btn-primary { color: var(--accent); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.ms-btn-primary:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); }
/* Danger variant — used by the bulk-delete button in select mode. */
.ms-btn-danger {
    color: var(--status-err);
    border-color: var(--status-err);
    background: color-mix(in srgb, var(--status-err) 8%, transparent);
}
.ms-btn-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--status-err) 20%, transparent);
}
.ms-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* ===================== Select mode ===================== */
/* While selecting:
   - topbar's normal actions are hidden, the .ms-select-bar takes their place
   - tiles + list rows get a hover/selected ring + a checkbox overlay
   - native drag (onto playlists) is suppressed via JS to avoid colliding
     with the drag-select gesture */
.ms-select-bar {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
}
.ms-select-bar[hidden] { display: none; }
.ms-select-count {
    color: var(--fg);
    margin-right: 0.4rem;
    font-variant-numeric: tabular-nums;
}
.music-app[data-selecting="true"] .ms-topbar-actions { display: none; }
.music-app[data-selecting="true"] {
    cursor: default;
    /* drag-select feedback: hint the user they can sweep across rows. */
}
.music-app[data-selecting="true"].dragging-select { user-select: none; }

/* No checkmark overlay — selection is communicated purely by the cyan
   highlight. The .ms-select-check element stays in DOM as a no-op so JS
   doesn't need to know whether the visual exists. */
.ms-select-check { display: none; }

/* Selected = cyan accent. Tile gets a strong outline ring; list row gets a
   tinted background + a left-edge accent stripe. Both unambiguous at a
   glance and identical regardless of theme. */
.ms-tile.selected {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 8px;
}
/* !important is intentional: `.ms-list-row:hover` is defined later in the
   stylesheet at equal specificity, so without !important the hover bg
   would replace the selected-row tint mid-hover and the row would
   visually "lose" its selected state while the mouse is over it. */
.ms-list-row.selected {
    background: color-mix(in srgb, var(--accent) 18%, var(--row-hover)) !important;
    box-shadow: inset 3px 0 0 var(--accent);
}

/* In select mode the tile's hover scale is distracting — pin it. */
@media (hover: hover) and (pointer: fine) {
    .music-app[data-selecting="true"] .ms-tile:hover { transform: none; }
}
/* Hide the hover-revealed ⋮ button — the only action in select mode is
   selecting; ⋮ menu would confuse the gesture. */
.music-app[data-selecting="true"] .ms-tile-more,
.music-app[data-selecting="true"] .ms-row-more { display: none; }

/* Library / playlist cover grid. MOBILE-FIRST on purpose: the phone column count lives
   HERE, in the always-loaded library stylesheet — NOT in music-mobile.css. Previously the
   only mobile column rule was in music-mobile.css, so a stale/uncached copy of that one file
   could drop touch devices to the implicit 1-column fallback (the bug the user hit). Base =
   ALWAYS 3-up (every phone size, robust to cache); desktop widens to auto-fill. */
.ms-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.75rem 0.5rem;
}
@media (min-width: 721px) {
    .ms-grid {
        grid-template-columns: repeat(auto-fill, minmax(var(--cover-tile), 1fr));
        gap: 1.25rem 1rem;
    }
}


/* ----- Library list / track list -----
   .ms-list is a vertical flex stack of rows. Each row owns its own internal
   grid (identical column template across rows so columns line up). The row is
   a real painted box, so :hover paints one continuous rectangle that the
   cover image and ⋮ button sit ON TOP OF — no more chopped highlight. */
.ms-list {
    --list-cols: calc(var(--cover-thumb) + 1.2rem) minmax(0, 3fr) minmax(0, 2fr) minmax(0, 2fr) 4.5rem 2.5rem;
    display: flex;
    flex-direction: column;
    gap: 2px;                        /* tiny breathing room between rows */
}
.ms-list-head,
.ms-list-row {
    display: grid;
    grid-template-columns: var(--list-cols);
    align-items: center;
}
.ms-list-head > * {
    padding: 0.55rem 0.85rem 0.45rem;
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--fg);
}
.ms-list-head-rule {
    height: 1px;
    background: var(--panel-border);
    margin: 0 0 0.35rem;
}
.ms-list-row {
    padding: 0.15rem 0;              /* extra vertical breathing room per song */
    border-radius: 6px;
    transition: background var(--dur-fast) var(--ease-smooth);
    cursor: pointer;
    user-select: none;
}
@media (hover: hover) and (pointer: fine) {
    .ms-list-row:hover { background: var(--row-hover); }
}
/* ============================================================
   Unified "chosen" indicator — applies identically across tile +
   list view and across desktop + mobile.

   A song is "chosen" when it's the user's current focus. That is
   either the currently-playing track OR (mobile only, post-1st-tap)
   a freshly-picked track waiting for confirmation. In CSS terms
   "chosen" = `.playing` OR `.picked`.

   Visuals on the chosen row/tile:
     • Title text → accent
     • Artist text → accent
     • Border → 2 px accent ring on .ms-tile-cover (tile view) or on
       the whole .ms-list-row (list view; the row's border-radius
       shapes it). NEVER both at once.

   "Never more than one border on screen" — when a `.picked` row
   exists, the `.playing` row's visuals are suppressed on every row
   that ISN'T also picked. This is mobile-only in practice (picked
   never sets on desktop per attachRowBehavior's isMobileWidth gate)
   but the CSS is unscoped because :has() handles the empty case
   trivially on desktop. ============================================================ */

/* Tile — chosen visuals */
.ms-tile.playing .ms-tile-cover,
.ms-tile.picked .ms-tile-cover {
    box-shadow: 0 0 0 2px var(--accent), 0 6px 16px rgba(0, 0, 0, 0.4);
}
.ms-tile.playing .ms-tile-title,
.ms-tile.playing .ms-tile-sub,
.ms-tile.picked .ms-tile-title,
.ms-tile.picked .ms-tile-sub {
    color: var(--accent);
}

/* List — chosen visuals */
.ms-list-row.playing,
.ms-list-row.picked {
    box-shadow: 0 0 0 2px var(--accent);
}
.ms-list-row.playing .cell-title,
.ms-list-row.playing .cell-artist,
.ms-list-row.playing .cell-album,
.ms-list-row.playing .cell-duration,
.ms-list-row.picked .cell-title,
.ms-list-row.picked .cell-artist,
.ms-list-row.picked .cell-album,
.ms-list-row.picked .cell-duration {
    color: var(--accent);
}

/* Suppress the playing-row visuals whenever a DIFFERENT row is
   picked. Only the user's last-clicked song carries the highlight
   at any moment — the previously-playing song reverts to neutral
   styling until the pick resolves (either auto-clears after 2 s or
   the user taps again to play). The :has() needs the .music-app
   wrapper as the scope so the suppression doesn't bleed into
   unrelated pages. */
.music-app:has(.picked) .ms-tile.playing:not(.picked) .ms-tile-cover {
    box-shadow: var(--shadow-md);
}
.music-app:has(.picked) .ms-tile.playing:not(.picked) .ms-tile-title {
    color: var(--fg);
}
.music-app:has(.picked) .ms-tile.playing:not(.picked) .ms-tile-sub {
    color: var(--fg-dim);
}
.music-app:has(.picked) .ms-list-row.playing:not(.picked) {
    box-shadow: none;
}
.music-app:has(.picked) .ms-list-row.playing:not(.picked) .cell-title,
.music-app:has(.picked) .ms-list-row.playing:not(.picked) .cell-artist,
.music-app:has(.picked) .ms-list-row.playing:not(.picked) .cell-album,
.music-app:has(.picked) .ms-list-row.playing:not(.picked) .cell-duration {
    color: var(--fg);
}
.ms-list-row > * {
    padding: 0.4rem 0.85rem;
    font-size: var(--text-sm);
    color: inherit;                  /* picks up .playing's accent color */
    min-width: 0;
}
.ms-list-row .cell-title,
.ms-list-row .cell-artist,
.ms-list-row .cell-album {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* All four columns in the same readable white — the title's weight is the
   hierarchy marker, not its color. Gray-on-dark was too dim to scan quickly. */
.ms-list-row .cell-title {
    font-weight: 700;
    color: var(--fg);
}
.ms-list-row .cell-artist,
.ms-list-row .cell-album,
.ms-list-row .cell-duration {
    color: var(--fg);
}
/* Cover + ⋮ cells: kill the inherited 0.85rem padding so the image and the
   button sit flush against the cell's full paint area. Otherwise the cell's
   visible bounds (where the hover bg paints) is smaller than the content,
   and you get gaps + apparent "highlight covering the cover". */
.ms-list-row .cell-cover,
.ms-list-row .cell-more,
.ms-list-head .cell-cover,
.ms-list-head .cell-more {
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ms-list-row .cell-cover img,
.ms-list-row .cell-cover svg {
    width: var(--cover-thumb);
    height: var(--cover-thumb);
    border-radius: 4px;
    object-fit: cover;
    display: block;
}
.ms-list-row .cell-duration {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-size: 0.82rem;
}
.ms-list-row .cell-more { text-align: right; padding: 0; }
.ms-list-row .cell-more .ms-icon-btn { visibility: hidden; }
@media (hover: hover) and (pointer: fine) {
    .ms-list-row:hover .cell-more .ms-icon-btn { visibility: visible; }
}

/* `.ms-row-more` hover state mirrors `.ms-tile-more` (defined further
   up in this file) — a cyan circle with an inverted (bg-color) icon
   so the ⋮ surface reads as one consistent primitive across grid
   tiles, list rows, and queue items (the queue's `.ms-queue-more`
   gets the same treatment in music-queue.css). Round shape comes
   from `border-radius: 50%`; the default fg-muted icon hue from
   `.ms-icon-btn` carries the un-hovered state. */
.ms-row-more {
    border-radius: 50%;
}
.ms-row-more:hover,
.ms-row-more:focus-visible {
    background: var(--accent);
    color: var(--bg);
}

/* ===================== Drop overlay ===================== */
.ms-drop-overlay {
    position: absolute;
    inset: 0;
    background: color-mix(in srgb, var(--accent) 10%, var(--scrim));
    /* backdrop-filter intentionally lives only in the active state
       below — see the long comment on .overlay in site.css for the
       browser quirk (Chrome leaves backdrop-filter active on
       opacity:0 + visibility:hidden elements, slightly desaturating
       the page behind them all the time). */
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    z-index: 30;
    transition: opacity var(--dur-fast) var(--ease-smooth), visibility 0s var(--dur-fast);
}
.ms-main.drag-over .ms-drop-overlay {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    transition: opacity var(--dur-fast) var(--ease-smooth), visibility 0s 0s;
}
.ms-drop-card {
    background: var(--panel-bg);
    border: 2px dashed var(--accent);
    color: var(--fg);
    padding: 2rem 2.5rem;
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.95rem;
}
.ms-drop-card .icon { color: var(--accent); }


/* ===================== Context menu (right-click + ⋮) ===================== */
/* One floating popover at a time. Mirrors the topbar's .tools-menu look so
   the site feels consistent. position: fixed + viewport-clamped by JS. */
.ms-context-menu {
    position: fixed;
    min-width: 180px;
    background: var(--bg);
    border: 1px solid var(--fg-dim);
    border-radius: 4px;
    padding: 0.2rem;
    box-shadow: var(--shadow-lg);
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: 1px;
    animation: ms-context-pop var(--dur-fast) var(--ease-smooth) both;
}
@keyframes ms-context-pop {
    from { opacity: 0; transform: translateY(-4px) scaleY(0.97); transform-origin: top left; }
    to   { opacity: 1; transform: translateY(0)    scaleY(1); }
}
.ms-context-item {
    display: block;
    width: 100%;
    padding: 6px 12px;
    background: transparent;
    border: none;
    color: var(--fg);
    text-align: left;
    cursor: pointer;
    border-radius: 3px;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    transition: background var(--dur-fast) var(--ease-smooth), color var(--dur-fast) var(--ease-smooth);
}
.ms-context-item:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
}
.ms-context-item:focus { outline: none; }
.ms-context-item:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.ms-context-item-danger:hover {
    background: color-mix(in srgb, var(--status-err) 14%, transparent);
    color: var(--status-err);
}


/* ===================== Toast ===================== */
.ms-toast {
    position: fixed;
    bottom: calc(var(--player-h) + 1rem);
    right: 1rem;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    padding: 0.65rem 0.85rem;
    min-width: 220px;
    box-shadow: var(--shadow-md);
    z-index: 40;
    opacity: 0;
    transform: translateY(8px);
    visibility: hidden;
    transition:
        opacity var(--dur-fast) var(--ease-smooth),
        transform var(--dur-fast) var(--ease-smooth),
        visibility 0s var(--dur-fast);
}
.ms-toast[data-open="true"] {
    opacity: 1;
    transform: translateY(0);
    visibility: visible;
    transition:
        opacity var(--dur-fast) var(--ease-smooth),
        transform var(--dur-fast) var(--ease-smooth),
        visibility 0s 0s;
}
.ms-toast-text {
    display: block;
    font-size: 0.82rem;
    color: var(--fg);
    margin-bottom: 0.5rem;
}
.ms-toast-bar {
    height: 3px;
    background: var(--panel-border);
    border-radius: 2px;
    overflow: hidden;
}
.ms-toast-bar-fill {
    height: 100%;
    background: var(--accent);
    width: 0%;
    transition: width var(--dur-fast) var(--ease-smooth);
}



/* ----- Drag-to-reorder (desktop) -----
   The dragged source row/tile dims; the insertion point shows a glowing
   accent line on the relevant edge — top/bottom for list rows, left/right
   for grid tiles. Markers are toggled by attachRowBehavior's dragover.
   Colors via tokens only. */
.ms-list-row.reordering,
.ms-tile.reordering { opacity: 0.4; }

.ms-list-row.reorder-before,
.ms-list-row.reorder-after,
.ms-tile.reorder-before,
.ms-tile.reorder-after { position: relative; }

.ms-list-row.reorder-before::after,
.ms-list-row.reorder-after::after,
.ms-tile.reorder-before::after,
.ms-tile.reorder-after::after {
    content: '';
    position: absolute;
    background: var(--accent);
    box-shadow: 0 0 6px var(--accent);
    z-index: 3;
    pointer-events: none;
}
/* List rows: full-width line on the top (before) or bottom (after) edge. */
.ms-list-row.reorder-before::after,
.ms-list-row.reorder-after::after { left: 0; right: 0; height: 2px; }
.ms-list-row.reorder-before::after { top: -1px; }
.ms-list-row.reorder-after::after  { bottom: -1px; }
/* Grid tiles: full-height line on the left (before) or right (after) edge. */
.ms-tile.reorder-before::after,
.ms-tile.reorder-after::after { top: 0; bottom: 0; width: 2px; }
.ms-tile.reorder-before::after { left: -2px; }
.ms-tile.reorder-after::after  { right: -2px; }

/* Touch long-press reorder: the floating "picked-up" clone that tracks
   the finger, and callout suppression so iOS doesn't fire its text
   selection / magnifier on the long press. */
.reorder-ghost {
    position: fixed;
    z-index: 300;
    pointer-events: none;
    margin: 0;
    opacity: 0.92;
    border-radius: 8px;
    background: var(--panel-bg);
    box-shadow: var(--shadow-lg);
    box-sizing: border-box;
    overflow: hidden;
}
.ms-list-row,
.ms-tile {
    -webkit-touch-callout: none;
}
