/* phone.css — lifted verbatim from cloudtape/shelf.html (the design source of truth).
   Only the phone frame and the Shelf screen styles; mat furniture is left behind. */
/* Source rows: which shelf a folder is on, said with a mark rather than a word.
   The path shows its last two segments because the head of a Dropbox path is
   the same on every row and the tail is the part that identifies it; tapping
   opens the full one. One overflow target replaces the "remove" link, which sat
   against the status and read as part of it. */
.srow{display:flex;align-items:center;gap:10px;min-height:44px}
.srow .sico{width:17px;height:17px;flex:none;color:var(--muted);display:block}
.srow .sico svg{width:100%;height:100%;display:block}
.srow .sbody{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center}
.srow .sleaf{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
  font-size:16.5px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The parent path, ellipsised from the HEAD: its end is what tells two folders
   apart, its start is the same on every row. direction:rtl moves the ellipsis
   to the left; the <bdi> keeps the slashes in LTR order. Status rides right. */
.srow .sdir{display:flex;gap:10px;align-items:baseline;margin-top:2px;
  font-size:12.5px;color:var(--muted);min-width:0}
.srow .sdir .sp{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;direction:rtl;text-align:left}
.srow .sdir b{font-weight:400;flex:none;margin-left:auto;font-variant-numeric:tabular-nums}
.srow{min-height:52px}
.srow .sfull{font-size:12.5px;color:var(--muted);margin-top:2px}
.srow .dots{width:44px;height:44px;margin-right:-12px;flex:none;border:0;background:none;padding:0;
  color:var(--muted);display:flex;align-items:center;justify-content:center;cursor:pointer}
.srow .dots svg{width:19px;height:19px;display:block}

/* Choose folders: two verbs, two targets.
   The row navigates — that is what tapping a folder does in every file browser
   on the phone, and it is also the thing you do most while hunting for the
   right directory. Selection is rare and deliberate, so it gets its own
   explicit control: a full-height 44px column, set off by a hairline so it
   reads as a separate button rather than as decoration on the row. The old row
   had a 24px box with 8px of slop — 40px, under the minimum — sitting flush
   against a full-width navigation target, so a near-miss drilled in instead. */
.drow{display:flex;align-items:stretch;min-height:56px}
.drow .pick{width:44px;flex:none;display:flex;align-items:center;justify-content:center;
  border:0;background:none;padding:0;cursor:pointer;border-right:1px solid var(--rule)}
.drow .nav{flex:1;min-width:0;display:flex;align-items:center;gap:12px;padding-left:12px;
  border:0;background:none;cursor:pointer;text-align:left}
.drow .nav .fp{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.drow .chev{color:var(--muted);font-size:18px;flex:none}
.drow.inc .nav .fp,.drow.inc .fsz{opacity:.45}
.crumb{display:flex;align-items:center;gap:6px;min-height:44px}
  /* .slot reserves the SCALED footprint; .phone is always a true 390x844 and is
     scaled into it, so every direction is designed at real device pixels. */
  .slot{width:calc(390px * var(--ps));flex:0 0 auto}
  .stage{width:calc(390px * var(--ps));height:calc(844px * var(--ps));position:relative}
  .phone{
    position:absolute;top:0;left:0;width:390px;height:844px;
    transform:scale(var(--ps));transform-origin:top left;
    border-radius:54px;background:var(--bezel);padding:11px;
    box-shadow:0 1px 2px rgba(20,18,14,.18), 0 26px 60px rgba(20,18,14,.28),
               inset 0 0 0 1.5px rgba(255,255,255,.1);
  }
  .screen{
    position:relative;width:100%;height:100%;overflow:hidden;
    border-radius:44px;background:var(--paper);color:var(--ink);
    display:flex;flex-direction:column;
    font-family:var(--body);line-height:1.45;
    font-variant-numeric:tabular-nums;        /* runtimes must align. always. */
  }
  /* display type takes the case control; directions may also use var(--case) */
  .screen h1,.screen h2,.screen h3{text-transform:var(--case)}

  /* iOS furniture ---------------------------------------------------------- */
  .island{position:absolute;top:9px;left:50%;transform:translateX(-50%);
    width:118px;height:33px;border-radius:20px;background:var(--bezel);z-index:30;pointer-events:none}
  .statusbar{flex:0 0 auto;height:52px;padding:0 30px;display:flex;align-items:center;
    justify-content:space-between;font-family:var(--display);font-weight:600;
    font-size:14px;letter-spacing:0;color:var(--ink);z-index:20}
  .statusbar .glyphs{display:flex;gap:5px;align-items:center}
  .statusbar svg{display:block}
  .homebar{flex:0 0 auto;height:26px;display:flex;align-items:center;justify-content:center;z-index:20}
  .homebar span{width:140px;height:5px;border-radius:3px;background:var(--ink);opacity:.32}
  /* the scrollable body of a screen */
  .canvas{flex:1 1 auto;min-height:0;overflow:hidden;position:relative;display:flex;flex-direction:column}
  /* ---- generated-sleeve ink set. Everything derives from tokens, so the
       whole sleeve series re-tints with the ground and never fights it. ---- */
  :root{
    --s-bg: color-mix(in srgb, var(--ink)  7%, var(--paper));
    --s-a:  color-mix(in srgb, var(--accent) 18%, var(--paper));
    --s-b:  color-mix(in srgb, var(--accent) 46%, var(--paper));
    --s-c:  color-mix(in srgb, var(--accent) 76%, var(--ink));
    --s-d:  color-mix(in srgb, var(--ink)   88%, var(--accent));
  }
  .art{flex:0 0 auto;border-radius:4px;overflow:hidden;position:relative;
    box-shadow:0 0 0 1px color-mix(in srgb, var(--ink) 10%, transparent)}
  .art svg{display:block;width:100%;height:100%;isolation:isolate}
  .a44{width:44px;height:44px}.a48{width:48px;height:48px}
  .a52{width:52px;height:52px}.a56{width:56px;height:56px}
  .a60{width:60px;height:60px}.a308{width:308px;height:308px;border-radius:6px}

  /* ---- shared type ---- */
  .screen{--pad:30px}
  .pad{padding-inline:var(--pad)}
  .dsp{font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case)}
  .h1{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:30px;line-height:1.06}
  .sec{font-family:var(--display);font-weight:var(--dw);text-transform:var(--case);
    font-size:12px;letter-spacing:.13em;color:var(--muted);
    padding:16px 0 6px;line-height:1.2}
  .sec:first-child{padding-top:2px}
  .fig{font-variant-numeric:tabular-nums;letter-spacing:0}
  /* prose is authored lowercase; --case drives the DISPLAY faces. Unit
     abbreviations opt out of the transform so "GB" never becomes "gb". */
  .qt{color:var(--muted);font-size:13px;line-height:1.5;text-transform:var(--case)}
  .unit{text-transform:none}
  .nb{white-space:nowrap}

  /* ---- album row: sleeve, title, artist. Nothing else. No rule, no chevron. ---- */
  .screen button{appearance:none;background:none;border:0;font:inherit;color:inherit;
    text-align:left;cursor:pointer}
  .row{display:flex;align-items:center;gap:14px;width:100%;padding:10px 0}
  .row .meta{min-width:0;flex:1 1 auto}
  .al{display:block;font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:18px;line-height:1.16}
  .ar{display:block;font-size:13px;line-height:1.35;margin-top:3px;
    color:var(--muted);text-transform:var(--case)}
  .rt{flex:0 0 auto;font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums}
  .list{flex:1 1 auto;min-height:0;overflow:hidden}

  /* ---- segmented control + search field ---- */
  .segbar{display:flex;background:var(--field);border-radius:13px;padding:3px;height:50px}
  .seg{flex:1 1 0;border-radius:9px;font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:14.5px;
    display:flex;align-items:center;justify-content:center;color:var(--muted)}
  .seg[aria-selected="true"]{background:var(--paper);color:var(--ink);
    box-shadow:0 1px 2px color-mix(in srgb, var(--ink) 14%, transparent)}
  .field{height:44px;border-radius:12px;background:var(--field);display:flex;
    align-items:center;gap:9px;padding:0 13px;color:var(--muted)}
  .field svg{width:17px;height:17px;flex:0 0 auto}
  .field .ph{font-size:15px;text-transform:var(--case)}
  .field .q{font-size:15px;color:var(--ink);text-transform:var(--case);
    display:inline-flex;align-items:center}
  .caret{width:2px;height:19px;background:var(--accent);margin-left:2px;display:inline-block}

  /* ---- dock: mini-player + tab bar ---- */
  .dock{flex:0 0 auto;background:var(--tint)}
  .mini{height:58px;display:flex;align-items:center;gap:12px;padding:0 var(--pad);
    position:relative}
  .mini .prog{position:absolute;top:0;left:0;height:2px;background:var(--accent);opacity:.55}
  .mini .t{flex:1 1 auto;min-width:0;font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:15px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mini .pp,.mini .nx{width:44px;height:44px;flex:none;display:flex;align-items:center;
    justify-content:center;color:var(--ink)}
  /* The two transports sit as one cluster: the 12px row gap would read as two
     unrelated buttons, and the last one hangs into the padding like the play
     button used to on its own. */
  .mini .pp{margin-right:-8px}
  .mini .nx{margin-right:-10px}
  .mini .pp svg,.mini .nx svg{width:22px;height:22px}

  /* ---- playing waveform -------------------------------------------------
     A synthetic waveform, not an analyser. expo-audio exposes no metering, so
     pretending to draw the signal would be a lie the animation cannot keep;
     what this says is only "sound is coming out of this", and it says it as
     background texture rather than as a widget.
     Full bleed to both edges — it ignores --pad, because it is ground, not
     content. Accent at 13%, mirrored around the band's centre line, and it
     settles to a flat rule when paused instead of freezing mid-swing. */
  .wave{position:absolute;inset:0;z-index:0;display:flex;align-items:center;
    gap:3px;overflow:hidden;pointer-events:none}
  .wave b{flex:1 1 0;min-width:0;height:100%;border-radius:2px;
    background:var(--accent);opacity:.13;transform:scaleY(.07);
    transform-origin:center;animation:wv 1.5s ease-in-out infinite alternate}
  /* Paused is a flat rule, not a frozen mid-swing — and the rule is ~3px in
     both bands, so the taller one does not settle into a row of stubs. */
  .wave.still b{animation:none;transform:scaleY(.05)}
  @keyframes wv{from{transform:scaleY(.05)}to{transform:scaleY(.74)}}
  /* content rides above the ground; .prog stays absolute, so it is named
     rather than swept up by a :not() — turning it into a flex item squeezes
     the transport buttons under 44px and the verifier catches it. */
  .mini .art,.mini .t,.mini .pp,.mini .nx{position:relative;z-index:1}
  .mini .prog{z-index:2}

  /* In the drawer the same band is anchored to the bottom edge, under the
     transport, so the sleeve keeps a clean white field around it. */
  .wave.np{inset:auto 0 0 0;height:150px}
  .wave.np b{opacity:.10;border-radius:3px;animation-name:wvnp}
  .wave.np.still b{transform:scaleY(.02)}
  /* Shorter swing than the mini's: the band has to stay clear of the
     transport above it and inside the sheet below it, or the bars clip on the
     bottom edge and read as a cropped block rather than as ground. */
  @keyframes wvnp{from{transform:scaleY(.02)}to{transform:scaleY(.78)}}
  .tabs{height:60px;display:flex;align-items:stretch}
  .tab{flex:1 1 0;display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:3px;color:var(--muted)}
  .tab[aria-current="page"]{color:var(--accent)}
  .tab svg{width:23px;height:23px}
  .tab span{font-family:var(--display);font-weight:var(--dw);font-size:10px;
    letter-spacing:.02em;text-transform:var(--case);line-height:1}

  /* ---- buttons ---- */
  /* min-height, not flex:0 0 auto: a column must never squash a button below
     the 44px tap minimum, but Album lays two of these out in a ROW, where
     refusing to shrink overflows the frame instead. */
  .btn{display:flex;align-items:center;justify-content:center;width:100%;
    height:54px;min-height:54px;
    border-radius:13px;font-family:var(--display);font-weight:var(--dw);font-size:17px;
    letter-spacing:var(--track);text-transform:var(--case);border:0;cursor:pointer}
  .btn.pri{background:var(--accent);color:var(--paper)}
  .btn.sec{background:none;color:var(--ink);
    box-shadow:inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 22%, transparent)}
  .btn.txt{background:none;color:var(--muted);font-size:16px;height:48px}

  /* ---- folder picker ---- */
  .frow{display:flex;align-items:center;gap:14px;width:100%;height:60px}
  .ck{width:24px;height:24px;border-radius:7px;flex:0 0 auto;display:flex;
    align-items:center;justify-content:center;
    box-shadow:inset 0 0 0 1.5px color-mix(in srgb, var(--ink) 26%, transparent)}
  .ck svg{width:16px;height:16px;opacity:0}
  .ck.on{background:var(--accent);box-shadow:none;color:var(--paper)}
  .ck.on svg{opacity:1}
  .fp{flex:1 1 auto;min-width:0;font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:17.5px}
  .fp i{font-style:normal;color:var(--muted)}
  .fsz{flex:0 0 auto;font-size:13.5px;color:var(--muted);font-variant-numeric:tabular-nums}
  .frow[aria-checked="false"] .fp{color:var(--muted)}

  /* ---- progress ---- */
  .track{height:6px;border-radius:4px;background:var(--field);overflow:hidden}
  .track span{display:block;height:100%;background:var(--accent);border-radius:4px}
  .track.thin{height:3px}
  .big{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    font-size:44px;line-height:1.04;font-variant-numeric:tabular-nums}
  .big i{font-style:normal;color:var(--muted)}
  .split{display:flex;justify-content:space-between;align-items:baseline;gap:12px}

  /* ---- now playing ---- */
  .grab{width:38px;height:5px;border-radius:3px;background:var(--ink);opacity:.2;margin:0 auto}

  /* Now Playing is a DRAWER, not a tab. It rises over whatever screen you were
     on and you drag it back down. The band of dimmed app above its rounded
     corners is the whole point: it says there is something behind this, and
     the grab handle is then a promise the screen can actually keep. */
  .npscrim{position:absolute;inset:0;background:var(--ink);opacity:.2;z-index:14}

  /* ---- the scrubber IS the waveform --------------------------------------
     2026-09-18: the band used to sit behind the mini-player's title and under
     the drawer's transport, where it fought the text laid over it. It moved
     to the one place it means something: the position control. Same real
     envelope, drawn as the scrubber — played bars in the accent, the rest a
     quiet field tone — with a 2px playhead you drag. The mini-player keeps
     only its 2px progress line. */
  .swave{flex:1 1 auto;display:flex;align-items:center;gap:2px;height:40px;position:relative}
  .swave b{flex:1 1 0;min-width:0;border-radius:2px;background:var(--accent);opacity:.2}
  .swave b.on{opacity:.8}
  .swave i{position:absolute;top:0;bottom:0;width:2px;border-radius:1px;
    background:var(--ink);transform:translateX(-50%)}
  /* ---- colour picker: a real in-app setting, not the kit switcher ---- */
  .swatches{display:flex;gap:13px;padding:6px 0 2px}
  .swatch{width:44px;height:44px;border-radius:50%;flex:0 0 auto;border:0;padding:0;
    cursor:pointer;box-shadow:inset 0 0 0 1px rgba(23,21,28,.10)}
  /* The selected one is ringed in paper then ink, so the mark reads on every
     one of the six without tinting the swatch it sits on. */
  .swatch[aria-pressed="true"]{box-shadow:0 0 0 3px var(--paper), 0 0 0 4.5px var(--ink)}
  .swatch[data-c="a"]{background:#6F5D9E}
  .swatch[data-c="b"]{background:#B23A1E}
  .swatch[data-c="c"]{background:#2F5D3F}
  .swatch[data-c="d"]{background:#1B4FA0}
  .swatch[data-c="e"]{background:#8A6A1F}
  .swatch[data-c="f"]{background:#3A3A3A}

  /* ---- destructive ---- */
  .btn.dgr{background:none;color:var(--danger);
    box-shadow:inset 0 0 0 1.5px color-mix(in srgb, var(--danger) 34%, transparent)}
  .sec.dgr{color:var(--danger)}
  /* A confirm rises from the bottom edge like Now Playing, but only as tall as
     its content — it is a question, not a screen. */
  .cfsheet{position:absolute;left:0;right:0;bottom:0;z-index:15;background:var(--paper);
    border-radius:20px 20px 0 0;box-shadow:0 -18px 40px rgba(23,21,28,.13)}
  .cf-t{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:24px;line-height:1.12}
  .npsheet{position:absolute;left:0;right:0;bottom:0;top:52px;z-index:15;
    background:var(--paper);border-radius:20px 20px 0 0;overflow:hidden;
    display:flex;flex-direction:column;
    box-shadow:0 -18px 40px rgba(23,21,28,.13)}
  .np-t{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:30px;line-height:1.1}
  .np-a{font-size:17px;color:var(--muted);text-transform:var(--case);margin-top:4px}
  .strack{height:44px;display:flex;align-items:center;cursor:pointer}
  .sbar{flex:1 1 auto;display:block;height:5px;border-radius:3px;
    background:var(--field);position:relative}
  .sbar span{display:block;height:100%;border-radius:3px;background:var(--accent);opacity:.75}
  .sbar i{position:absolute;top:50%;width:13px;height:13px;border-radius:50%;
    background:var(--accent);transform:translate(-50%,-50%)}
  .stimes{display:flex;justify-content:space-between;margin-top:1px;
    font-size:12.5px;color:var(--muted);font-variant-numeric:tabular-nums}
  .transport{display:flex;align-items:center;justify-content:space-between}
  .ctl{display:flex;align-items:center;justify-content:center;color:var(--ink)}
  .ctl.sm{width:64px;height:64px}.ctl.sm svg{width:32px;height:32px}
  .ctl.lg{width:76px;height:76px}.ctl.lg svg{width:46px;height:46px}
  .ctl.tiny{width:52px;height:52px;color:var(--muted)}
  .ctl.tiny svg{width:22px;height:22px}
  .ctl.tiny[aria-pressed="true"]{color:var(--accent)}

  /* ---- long-form transport, lyrics, sleep timer (2026-09-18 round) ---- */
  .ctl.xs{width:44px;height:64px;color:var(--muted)}.ctl.xs svg{width:22px;height:22px}
  .ctl.skip{position:relative}.ctl.skip svg{width:36px;height:36px}
  .ctl.skip .skn{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
    padding-top:2px;font-family:var(--display);font-weight:var(--dw);font-size:10.5px;
    letter-spacing:-.01em;font-variant-numeric:tabular-nums;pointer-events:none}
  .ctl.tiny.tm{width:auto;min-width:52px;gap:6px;padding-inline:4px;
    font-size:13px;font-variant-numeric:tabular-nums}
  .ctl.tiny.tm[aria-pressed="true"]{color:var(--accent)}
  /* ---- the Last.fm love heart (shown only while signed in to Last.fm) ---- */
  .np-head{display:flex;align-items:flex-start;gap:12px}
  .np-head>div{flex:1 1 auto;min-width:0}
  .heart{flex:none;width:44px;height:44px;margin:-4px -10px 0 0;display:flex;align-items:center;
    justify-content:center;color:var(--muted);background:none;border:0;padding:0}
  .heart svg{width:24px;height:24px}
  .heart[aria-pressed="true"]{color:var(--accent)}
  .heart[aria-pressed="true"] svg{fill:currentColor}
  .np-row .heart{margin:0 -10px 0 auto}
  .np-row{display:flex;align-items:center;gap:14px}
  .np-row .t{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:18px;line-height:1.15}
  .np-row .a{font-size:13px;color:var(--muted);text-transform:var(--case);margin-top:2px}
  .lyr{flex:1 1 auto;min-height:0;overflow:hidden;position:relative;margin-top:22px;
    font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    font-size:23px;line-height:1.38;color:var(--ink)}
  .lyr p+p{margin-top:18px}
  .lyr::after{content:"";position:absolute;left:0;right:0;bottom:0;height:56px;
    background:linear-gradient(to bottom,transparent,var(--paper))}
  .tmrow{display:flex;align-items:center;justify-content:space-between;width:100%;
    height:48px;font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:17px;color:var(--ink)}
  .tmrow .fsz{font-family:var(--ui, inherit);font-weight:400;letter-spacing:0}
  .tmrow svg{width:18px;height:18px;color:var(--accent)}
  .tmrow[aria-checked="true"]{color:var(--accent)}


  /* ---- the toast lip -----------------------------------------------------
     The toast is not a card any more. It is a strip that slides up out of the
     dock and sits flush on its top edge — full bleed, the dock's own --field
     ground, no radius, no shadow — so it reads as the mini-player saying one
     more line, not as a notification dropped over the list. It never covers
     text mid-row because it only ever occupies the band directly above the
     dock.
     It is also where sync reports itself. There used to be a floating card
     for that — the only thing in the app that floated — and it was a second
     notification system over the list. Now the lip has one line and two
     sources: a toast says its line and leaves; with no toast up, a running
     scan or download holds the lip until it finishes. Never on the Sync tab,
     which says all of it at length already. Tap a toast to dismiss it, tap
     sync status to open Sync (or retry, when something failed). */
  .dock{position:relative}
  .lip{position:absolute;left:0;right:0;bottom:100%;height:34px;z-index:3;
    background:var(--field);display:flex;align-items:center;justify-content:center;
    padding:0 var(--pad);font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:13.5px;
    color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .lip.acc{color:var(--accent)}
  /* `button.lip`: `.screen button{background:none}` outranks a bare class. */
  button.lip{border:0;width:auto;cursor:pointer;border-radius:0}
  /* 34px to see, 44px to hit: the extra 10px is a transparent band on top
     (hitSlop in RN), so the strip still sits flush on the dock. */
  button.lip{height:44px;padding-top:10px;
    background:linear-gradient(to bottom,transparent 10px,var(--field) 10px)}
  .lip .fig{color:var(--muted);font-family:var(--body,inherit);font-weight:400;
    text-transform:none;font-variant-numeric:tabular-nums;margin-left:.45em}

  /* ---- now playing, empty ---- */
  .npempty{flex:1;display:flex;flex-direction:column;justify-content:center;padding-bottom:40px}
  .npempty .fan{position:relative;width:256px;height:110px;margin:0 auto 30px}
  .npempty .fan .art:not([style*="z-index"]){top:5px}
  .npempty .fan .art{position:absolute;top:0;box-shadow:0 8px 22px rgba(23,21,28,.10)}
  .npe-t{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:26px;line-height:1.1}
  .npe-q{margin-top:8px}

  /* ---- album & artist detail ------------------------------------------
       The native music app's two missing levels. Same row grammar as the
       library; the only new pieces are a header block and a numbered track
       row. Track numbers are muted and tabular so the titles stay the thing
       you read, and a playing row takes the accent rather than an icon.
       -------------------------------------------------------------------- */
  .back{display:inline-flex;align-items:center;gap:5px;min-height:44px;
    font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:15px;color:var(--accent);background:none;border:0;
    cursor:pointer;padding:0}
  .dhead{display:flex;gap:16px;align-items:flex-start;padding-top:6px}
  .dhead .meta{min-width:0;flex:1 1 auto;padding-top:2px;
    display:flex;flex-direction:column}
  .dtitle{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:22px;line-height:1.14}
  .dsub{font-size:14px;color:var(--muted);text-transform:var(--case);margin-top:4px}
  .dsub b{font-weight:400;color:var(--accent)}
  .dfacts{font-size:12.5px;color:var(--muted);margin-top:8px;font-variant-numeric:tabular-nums}
  .trow{display:flex;align-items:center;gap:14px;width:100%;padding:11px 0;
    background:none;border:0;color:inherit;text-align:left;cursor:pointer;font:inherit}
  .tno{flex:0 0 22px;font-size:13.5px;color:var(--muted);font-variant-numeric:tabular-nums;
    text-align:right}
  .ttitle{flex:1 1 auto;min-width:0;font-size:16px;overflow:hidden;text-overflow:ellipsis;
    white-space:nowrap;text-transform:var(--case)}
  .trow[aria-current="true"] .ttitle,.trow[aria-current="true"] .tno{color:var(--accent)}
  /* Not landed yet: the app plays from disk, so there is nothing to play.
     Muted and not tappable. A DEVICE track is never in this state — it is
     already on the phone — which is why the rule is "playable", not
     "downloaded". Getting that wrong greys out the whole device library. */
  .trow[disabled] .ttitle,.trow[disabled] .tno,.trow[disabled] .tdur{color:var(--muted)}
  .trow[disabled]{cursor:default}
  .tdur{flex:0 0 auto;font-size:13px;color:var(--muted);font-variant-numeric:tabular-nums}
  .playall{display:flex;gap:10px;margin-top:16px}
  .playall .btn{height:46px;font-size:15.5px}

  /* ---- sync tab ---- */
  .sw{width:50px;height:30px;border-radius:15px;background:var(--field);flex:0 0 auto;
    position:relative;transition:background .2s var(--ease)}
  .swrow[aria-checked="true"] .sw{background:var(--accent)}
  .sw b{position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;
    background:var(--paper);transition:left .2s var(--ease);
    box-shadow:0 1px 2px color-mix(in srgb, var(--ink) 22%, transparent)}
  .swrow[aria-checked="true"] .sw b{left:23px}
  .screen .lfm-send{background:none;border:0;padding:0 2px;min-height:44px;color:var(--accent);font:inherit;font-family:var(--display);font-weight:var(--dw);font-size:15px;text-transform:var(--case);cursor:pointer}
  .scrow{display:flex;align-items:center;gap:12px;min-height:58px;background:var(--paper)}
  .scrow .scm{flex:1 1 auto;min-width:0}
  .scrow .al{font-size:16.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .scrow .lv{flex:0 0 auto;width:16px;height:16px;color:var(--accent);fill:var(--accent)}
  .swipe{position:relative;margin-inline:calc(var(--pad) * -1);padding-inline:var(--pad);overflow:hidden}
  .swipe .sw-on{margin-right:66px}
  .swipe .sw-on .rt{display:none}
  .screen .rm{position:absolute;top:0;bottom:0;right:0;width:96px;background:var(--danger);color:var(--paper);
    font-family:var(--display);font-weight:var(--dw);font-size:15px;text-transform:var(--case);border:0;
    display:flex;align-items:center;justify-content:center}
  .screen .navrow{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:44px;color:var(--ink)}
  .navrow svg{width:16px;height:16px;color:var(--muted);transform:rotate(-90deg)}
  .swrow{display:flex;align-items:center;justify-content:space-between;gap:14px;
    width:100%;height:56px;background:none;border:0;font:inherit;color:inherit;cursor:pointer}
  .swrow .lbl{font-family:var(--display);font-weight:var(--dw);letter-spacing:var(--track);
    text-transform:var(--case);font-size:17px}

  /* ---- the unlock (2026-09-19 round) --------------------------------------
     Money is mentioned in exactly three places: this notice on the Sync tab,
     one row in Settings, and the Unlock screen itself. Nowhere near the
     Library, nowhere near Now Playing, and never over the top of something
     that is playing — what is on the phone is paid for by being on the phone.
     The notice is a --field card rather than a banner with an accent ground:
     an accent block reads as an ad, and this is a status. */
  .notice{background:var(--field);border-radius:14px;padding:15px 15px 13px}
  .notice .nt{display:block;font-family:var(--display);font-weight:var(--dw);
    letter-spacing:var(--track);text-transform:var(--case);font-size:17px;line-height:1.2}
  .notice .btn{margin-top:13px;height:48px;min-height:48px;font-size:16px}
  /* The price sits INSIDE the button's label, not above it in large display
     type. A number set large is a pitch; a number on the button is a fact
     about what pressing it does. It comes from StoreKit at runtime and is
     never a literal in the app — Apple localises it, and App Review checks. */
  .btn .px{opacity:.72;font-size:15px;margin-left:8px}
  /* "what stops / what doesn't" — the whole ethic of the app in two lists. */
  .tally{display:flex;flex-direction:column}
  .tally .ti{display:flex;gap:11px;align-items:flex-start;padding:5px 0;
    font-size:14.5px;line-height:1.4;text-transform:var(--case)}
  .tally .ti span{flex:1 1 auto;min-width:0}
  .tally .ti em{font-style:normal;color:var(--muted)}
  .tally .mk{flex:0 0 auto;width:15px;height:15px;margin-top:2px;color:var(--accent)}
  .tally.off .mk{color:var(--muted);opacity:.55}
