# Download and offline icon inventory
All production references after conversion, including SVG definitions, consumers and CSS. Generated from the final frontend; tests excluded.
| File:line | Reference |
| --- | --- |
| `frontend/admin.html:315` | `.media-shape { display:none; } .media-shape-classic { display:inline; } ` |
| `frontend/admin.html:316` | `.media-icon { width:18px; height:18px; vertical-align:middle; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; } ` |
| `frontend/admin.html:412` | ` ` |
| `frontend/admin.html:513` | ` Export all (JSON) ` |
| `frontend/app.js:3562` | `${isUploadId(st.videoId) ? '' : } ` |
| `frontend/app.js:3804` | ` ` |
| `frontend/app.js:6994` | `exportBtn.innerHTML = MediaIcons.svg('download') + ' Save to gallery'; ` |
| `frontend/app.js:7008` | `saveBtn.innerHTML = MediaIcons.svg(isSaved ? 'offline' : 'download') + (isSaved ? ' Saved offline — remove' : ' Save for offline'); ` |
| `frontend/app.js:7235` | `if (iconOnly) { setListIcon(btn, 'offlineDone'); btn.classList.add('done'); } else btn.innerHTML = MediaIcons.svg('offline') + ' Available offline'; ` |
| `frontend/app.js:7240` | `if (iconOnly) { btn.innerHTML = MediaIcons.svg('progress') + ${stats.cached}/${stats.total}; btn.classList.add('busy'); } else btn.innerHTML = MediaIcons.svg('progress') + Saving ${stats.cached}/${stats.total}…; ` |
| `frontend/app.js:7245` | `if (iconOnly) setListIcon(btn, 'offline'); else btn.innerHTML = MediaIcons.svg('download') + ' Make available offline'; ` |
| `frontend/app.js:7262` | `btn.innerHTML = MediaIcons.svg('progress') + ${stats.cached}/${stats.total}; ` |
| `frontend/download-actions.js:29` | `const svg = (kind, extra = '') => ; ` |
| `frontend/download-actions.js:35` | `defs.innerHTML = '
The server hasn't finished caching this video yet, and other players need that single-file copy. Keep it playing for a minute (or tap ${MediaIcons.svg('download')} Save), then try again.
; ` | | `frontend/share-external.js:196` | ` ` | | `frontend/share-external.js:197` | `${inList ? : ''} ` | | `frontend/shell-core.js:94` | `offlineBadge = ${MediaIcons.svg('offline')}; ` | | `frontend/shell-core.js:144` | `offlineHtml = ${MediaIcons.svg('offline')}; ` | | `frontend/styles.css:1113` | `.saved-badge .media-icon { width:16px; height:16px; filter:drop-shadow(0 1px 2px #000); } ` | | `frontend/styles.css:4337` | `.media-icon { display:inline-block; width:20px; height:20px; flex:none; vertical-align:middle; fill:none; stroke:currentColor; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; pointer-events:none; } ` | | `frontend/styles.css:4339` | `.empty-icon .media-icon { width:48px; height:48px; } ` | | `frontend/styles.css:4340` | `:is(.pl-offline-badge,.home-pl-offline) .media-icon { width:14px; height:14px; } ` | | `frontend/styles.css:4341` | `html[data-layout="classic"] .media-icon { stroke-width:1.8; } ` | | `frontend/styles.css:4342` | `html[data-layout="f7-swipe"] .media-icon { stroke-width:2; } ` | | `frontend/styles.css:4343` | `html[data-layout="glass-stage"] .media-icon { stroke-width:1.9; } ` | | `frontend/styles.css:4344` | `html[data-layout="bento-hub"] .media-icon { stroke-width:1.8; } ` | | `frontend/styles.css:4348` | `:is([data-media-icon="progress"], .np-btn.saving .media-icon) { animation:mediaSpin 1s linear infinite; transform-origin:center; } ` | | `frontend/styles.css:4350` | `@media(prefers-reduced-motion:reduce) { :is([data-media-icon="progress"], .np-btn.saving .media-icon) { animation:none; } } ` | | `frontend/styles.css:4351` | `.media-shape { display:none; } ` | | `frontend/styles.css:4352` | `.media-shape-classic { display:inline; } ` | | `frontend/styles.css:4353` | `html[data-layout="f7-swipe"] .media-shape-classic, ` | | `frontend/styles.css:4354` | `html[data-layout="glass-stage"] .media-shape-classic, ` | | `frontend/styles.css:4355` | `html[data-layout="bento-hub"] .media-shape-classic { display:none; } ` | | `frontend/styles.css:4356` | `html[data-layout="f7-swipe"] .media-shape-f7-swipe, ` | | `frontend/styles.css:4357` | `html[data-layout="glass-stage"] .media-shape-glass-stage, ` | | `frontend/styles.css:4358` | `html[data-layout="bento-hub"] .media-shape-bento-hub { display:inline; } ` | | `frontend/views-core.js:47` | `els.listActions.innerHTML = groups.length ? : ''; ` | | `frontend/views-core.js:133` | `offline: '', ` | | `frontend/views-core.js:134` | `offlineDone: '', ` | | `frontend/views-core.js:419` | `row.innerHTML = 📡; ` | | `frontend/views-core.js:520` | ` ` | | `frontend/views-core.js:553` | ` ` | | `frontend/views-core.js:569` | ` ` | | `frontend/views-core.js:571` | `Use ${MediaIcons.svg('download')} Save while playing, or add videos to a playlist to keep them offline.
; ` | | `frontend/views-core.js:906` | ` ` | | `frontend/views-core.js:978` | ` ` | | `frontend/views-core.js:1339` | `${MediaIcons.svg('offline', 'badge-offline')}${MediaIcons.svg('progress', 'badge-progress')} ` | The only remaining requested arrow glyphs are keyboard shortcut prose in index.html and admin.html, explicitly allowlisted by media-icons.test.js. Webmanifest/shortcut/icon images contain no download/save action glyph; generic app logos stay unchanged. Modal titles that previously prefixed a glyph now retain their accessible plain-text name.