A separate admin page: server settings, accounts and the log
/admin, with Server, Accounts and Log sections chosen by the URL's hash. The server sends the page and /admin.js to admins only (anyone else asking for the page goes back to the app, and the script is 403), and removes the header's link to it from everyone else's page rather than hiding it. The API keeps refusing all of it to non-admins as before. Settings becomes personal: theme, OPML import and export, and the schedule and download folder to read. The server fields, the Users dialog and the Log dialog move out of dialogs.ts into admin.ts. The CSS moves out of index.html into web/app.css, which both pages load as /app.css?v=<hash>, served immutable like the scripts. The smoke test checks both pages. Closes #19. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
847
web/app.css
Normal file
847
web/app.css
Normal file
@@ -0,0 +1,847 @@
|
||||
/* Inter, served by ipx itself (/inter.woff2) rather than a font CDN, so the page asks nothing of
|
||||
anyone else. One variable file covers every weight used here; italics are synthesized. Classic
|
||||
keeps Lucida Grande, the 2004 app's face. */
|
||||
@font-face{font-family:Inter;src:url(/inter.woff2) format("woff2");font-weight:100 900;font-display:swap}
|
||||
/* Palette taken from the 2004 iPodderX icon: the silver device body, the blue
|
||||
screen, and the amber EQ bars. Hex values in comments are sampled straight from it. */
|
||||
:root {
|
||||
--bg:#0e131b; /* the screen's navy (#314B74), taken right down */
|
||||
--panel:#151c27;
|
||||
--panel2:#1c2431;
|
||||
--raise:#25303f;
|
||||
--line:#2c3849;
|
||||
--fg:#f5f5f5; /* #F5F5F5 device highlight */
|
||||
--dim:#95a0b1; /* #95A0B1 straight from the icon's blue-grey */
|
||||
--faint:#7a8799; /* lifted from the icon ramp until it clears AA at small sizes */
|
||||
--accent:#92b2e6; /* #92B2E6 the screen blue */
|
||||
--accent2:#f49e2c; /* #F49E2C the EQ bars */
|
||||
--ink:#0e131b; /* text on an accent fill */
|
||||
--good:#6fbf8b;
|
||||
--warn:#f49e2c; /* the amber doubles as the pending colour */
|
||||
--bad:#e2705f;
|
||||
--shadow:0 8px 28px rgba(6,10,16,.55);
|
||||
}
|
||||
/* Every other theme overrides the same variables, under data-theme and data-mode. The page's
|
||||
script sets data-mode to light or dark, working Auto out from the system, so each theme has
|
||||
one block per variant here and none needs repeating under a media query. */
|
||||
:root[data-theme="modern"][data-mode="light"] {
|
||||
--bg:#f2f4f7;
|
||||
--panel:#ffffff; /* #FFFFFF device body */
|
||||
--panel2:#e9edf3;
|
||||
--raise:#dde3ec;
|
||||
--line:#d6d6d6; /* #D6D6D6 device edge */
|
||||
--fg:#1a1a1a; /* #1A1A1A icon outline */
|
||||
--dim:#606060; /* #606060 */
|
||||
--faint:#767676; /* between the icon's #929292 and #606060, to clear AA */
|
||||
--accent:#2d5391; /* #2D5391 the deep screen blue reads better on white */
|
||||
--accent2:#9a5f0a; /* the EQ amber, taken down until white on it clears AA */
|
||||
--ink:#ffffff;
|
||||
--good:#2f7d4f;
|
||||
--warn:#b06f10;
|
||||
--bad:#b3402f;
|
||||
--shadow:0 8px 28px rgba(45,83,145,.14);
|
||||
}
|
||||
/* Dracula, and Alucard, its light half, from draculatheme.com/spec. The spec's comment colour
|
||||
(#6272A4, #6C664B) is too faint for small text on its own background, so --faint is lifted. */
|
||||
:root[data-theme="dracula"] {
|
||||
--bg:#282a36;
|
||||
--panel:#21222c;
|
||||
--panel2:#343746;
|
||||
--raise:#44475a; /* selection */
|
||||
--line:#414558;
|
||||
--fg:#f8f8f2;
|
||||
--dim:#c9cbd6;
|
||||
--faint:#9ea8c7;
|
||||
--accent:#bd93f9; /* purple */
|
||||
--accent2:#ff79c6; /* pink */
|
||||
--ink:#282a36;
|
||||
--good:#50fa7b;
|
||||
--warn:#ffb86c;
|
||||
--bad:#ff8080;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.45);
|
||||
}
|
||||
:root[data-theme="dracula"][data-mode="light"] {
|
||||
--bg:#fffbeb;
|
||||
--panel:#f7f2df;
|
||||
--panel2:#efe9d3;
|
||||
--raise:#cfcfde; /* selection */
|
||||
--line:#ddd6bd;
|
||||
--fg:#1f1f1f;
|
||||
--dim:#454137;
|
||||
--faint:#635d44;
|
||||
--accent:#644ac9;
|
||||
--accent2:#a3144d;
|
||||
--ink:#ffffff;
|
||||
--good:#14710a;
|
||||
--warn:#a34d14;
|
||||
--bad:#b83322;
|
||||
--shadow:0 8px 28px rgba(108,102,75,.18);
|
||||
}
|
||||
/* Material 3's baseline scheme: surface, the surface containers, outline and primary. */
|
||||
:root[data-theme="material"] {
|
||||
--bg:#141218; /* surface */
|
||||
--panel:#1d1b20; /* surface-container-low */
|
||||
--panel2:#211f26; /* surface-container */
|
||||
--raise:#36343b; /* surface-container-highest */
|
||||
--line:#49454f; /* outline-variant */
|
||||
--fg:#e6e0e9; /* on-surface */
|
||||
--dim:#cac4d0; /* on-surface-variant */
|
||||
--faint:#9a95a0; /* outline, a shade up to clear AA on the containers */
|
||||
--accent:#d0bcff; /* primary */
|
||||
--accent2:#efb8c8; /* tertiary */
|
||||
--ink:#381e72; /* on-primary */
|
||||
--good:#8fd6a0;
|
||||
--warn:#f2c46b;
|
||||
--bad:#f2b8b5; /* error */
|
||||
--shadow:0 8px 28px rgba(0,0,0,.5);
|
||||
}
|
||||
:root[data-theme="material"][data-mode="light"] {
|
||||
--bg:#fef7ff;
|
||||
--panel:#f7f2fa;
|
||||
--panel2:#f3edf7;
|
||||
--raise:#e6e0e9;
|
||||
--line:#cac4d0;
|
||||
--fg:#1d1b20;
|
||||
--dim:#49454f;
|
||||
--faint:#67626c; /* outline (#79747E), a shade down to clear AA on the containers */
|
||||
--accent:#6750a4;
|
||||
--accent2:#7d5260;
|
||||
--ink:#ffffff;
|
||||
--good:#2b6c3c;
|
||||
--warn:#7c5800;
|
||||
--bad:#b3261e;
|
||||
--shadow:0 8px 28px rgba(103,80,164,.14);
|
||||
}
|
||||
/* Adwaita, from libadwaita's own CSS variables: view, window and sidebar backgrounds, the blue
|
||||
accent, and its destructive, success and warning colours. */
|
||||
:root[data-theme="adwaita"] {
|
||||
--bg:#1d1d20; /* view */
|
||||
--panel:#2e2e32; /* sidebar, headerbar */
|
||||
--panel2:#222226; /* window */
|
||||
--raise:#3a3a3f;
|
||||
--line:#3f3f45;
|
||||
--fg:#ffffff;
|
||||
--dim:#c4c4c8;
|
||||
--faint:#a0a0a7;
|
||||
--accent:#81d0ff;
|
||||
--accent2:#3584e4;
|
||||
--ink:#1d1d20;
|
||||
--good:#78e9ab;
|
||||
--warn:#ffc252;
|
||||
--bad:#ff938c;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.5);
|
||||
}
|
||||
:root[data-theme="adwaita"][data-mode="light"] {
|
||||
--bg:#ffffff;
|
||||
--panel:#ebebed;
|
||||
--panel2:#fafafb;
|
||||
--raise:#dcdce0;
|
||||
--line:#d9d9dd;
|
||||
--fg:#333338; /* rgb(0 0 6 / 80%) on white */
|
||||
--dim:#57575d;
|
||||
--faint:#66666c;
|
||||
--accent:#0461be;
|
||||
--accent2:#3584e4;
|
||||
--ink:#ffffff;
|
||||
--good:#00753a;
|
||||
--warn:#905400;
|
||||
--bad:#c30000;
|
||||
--shadow:0 8px 28px rgba(0,0,6,.12);
|
||||
}
|
||||
/* Flat Remix, from its GTK theme's _colors.scss: base and bg, fg, the selection blue, and its
|
||||
link colours, which are the selection blue taken down (light) or up (dark) until readable. */
|
||||
:root[data-theme="flatremix"] {
|
||||
--bg:#272a34; /* base */
|
||||
--panel:#23262f; /* bg: base darkened 2% */
|
||||
--panel2:#2e323d;
|
||||
--raise:#363b48;
|
||||
--line:#3a3f4c;
|
||||
--fg:#eeeeec;
|
||||
--dim:#babec8;
|
||||
--faint:#959baa;
|
||||
--accent:#8fb6ff; /* link */
|
||||
--accent2:#fd7d00; /* warning orange */
|
||||
--ink:#1a1c23;
|
||||
--good:#4cc28c;
|
||||
--warn:#ff9a3c;
|
||||
--bad:#ff6b6b;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.3);
|
||||
}
|
||||
:root[data-theme="flatremix"][data-mode="light"] {
|
||||
--bg:#fafafa; /* base */
|
||||
--panel:#ffffff; /* bg */
|
||||
--panel2:#f0f0f1;
|
||||
--raise:#e4e5e7;
|
||||
--line:#d9d9d9; /* borders: bg darkened 15% */
|
||||
--fg:#22252b;
|
||||
--dim:#5c616c; /* fg */
|
||||
--faint:#686d78;
|
||||
--accent:#0060f0; /* link */
|
||||
--accent2:#fd7d00;
|
||||
--ink:#ffffff;
|
||||
--good:#23794f;
|
||||
--warn:#a85400;
|
||||
--bad:#d41919;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.1);
|
||||
}
|
||||
/* Paper: a single light palette, black on the colour of paper, colour kept for what matters.
|
||||
Its blue and red are taken down a shade to clear AA on that background. */
|
||||
:root[data-theme="paper"] {
|
||||
--bg:#f2eede;
|
||||
--panel:#ebe7d7;
|
||||
--panel2:#e4e0d0;
|
||||
--raise:#d8d5c7; /* highlight */
|
||||
--line:#cdc9b9;
|
||||
--fg:#000000;
|
||||
--dim:#3d3a33;
|
||||
--faint:#5a564c;
|
||||
--accent:#1a5fae;
|
||||
--accent2:#b58900;
|
||||
--ink:#ffffff;
|
||||
--good:#216609;
|
||||
--warn:#795a00;
|
||||
--bad:#b1331f;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.12);
|
||||
}
|
||||
/* Nordic: the Nord palette. Polar Night for the dark half, Snow Storm for the light, Frost for
|
||||
the accent and Aurora for the rest. Aurora red and Frost blue are shifted until they read. */
|
||||
:root[data-theme="nordic"] {
|
||||
--bg:#2e3440; /* nord0 */
|
||||
--panel:#3b4252; /* nord1 */
|
||||
--panel2:#434c5e; /* nord2 */
|
||||
--raise:#4c566a; /* nord3 */
|
||||
--line:#434c5e;
|
||||
--fg:#eceff4; /* nord6 */
|
||||
--dim:#d8dee9; /* nord4 */
|
||||
--faint:#b4bccb;
|
||||
--accent:#93c9d8; /* nord8, a shade up */
|
||||
--accent2:#ebcb8b; /* nord13 */
|
||||
--ink:#2e3440;
|
||||
--good:#b0c99b; /* nord14, a shade up */
|
||||
--warn:#ebcb8b;
|
||||
--bad:#f2aeb4;
|
||||
--shadow:0 8px 28px rgba(0,0,0,.35);
|
||||
}
|
||||
:root[data-theme="nordic"][data-mode="light"] {
|
||||
--bg:#eceff4; /* nord6 */
|
||||
--panel:#e5e9f0; /* nord5 */
|
||||
--panel2:#d8dee9; /* nord4 */
|
||||
--raise:#cdd4e0;
|
||||
--line:#c5cedb;
|
||||
--fg:#2e3440; /* nord0 */
|
||||
--dim:#3b4252; /* nord1 */
|
||||
--faint:#4c566a; /* nord3 */
|
||||
--accent:#3a5e8a;
|
||||
--accent2:#b0674e;
|
||||
--ink:#ffffff;
|
||||
--good:#446632;
|
||||
--warn:#7e590e;
|
||||
--bad:#9c3f49;
|
||||
--shadow:0 8px 28px rgba(46,52,64,.15);
|
||||
}
|
||||
/* Classic: the 2004 Mac app. Colours here; the chrome it needs is at the end of the sheet. */
|
||||
:root[data-theme="classic"] {
|
||||
color-scheme:light;
|
||||
--bg:#ffffff;
|
||||
--panel:#e7ebf1; /* the source list's pale blue-grey */
|
||||
--panel2:#f2f2f2;
|
||||
--raise:#d5dce7;
|
||||
--line:#a9a9a9;
|
||||
--fg:#000000;
|
||||
--dim:#444444;
|
||||
--faint:#666666;
|
||||
--accent:#3875d7; /* Aqua selection blue */
|
||||
--accent2:#2a5db0;
|
||||
--ink:#ffffff;
|
||||
--good:#237a23;
|
||||
--warn:#a15f00;
|
||||
--bad:#c42b1c;
|
||||
--shadow:0 4px 16px rgba(0,0,0,.28);
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
/* A rule that sets display beats the UA's [hidden], and several below do. */
|
||||
[hidden]{display:none!important}
|
||||
html,body{height:100%}
|
||||
body{
|
||||
margin:0;background:var(--bg);color:var(--fg);
|
||||
font:14.5px/1.55 Inter,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
|
||||
display:grid;grid-template-rows:auto 1fr auto auto;overflow:hidden;
|
||||
/* iOS Safari's address bar collapses and expands without firing a resize, so 100vh is
|
||||
measured against whichever state happened to be current -- sized too tall while the bar
|
||||
is showing, which puts the topbar (the hamburger included) under Safari's own chrome,
|
||||
where a tap never reaches the page. Only a hard refresh reset it, forcing 100vh to be
|
||||
recomputed. 100dvh tracks the real visible viewport as the bar moves; the 100vh above is
|
||||
the fallback for a browser that does not know dvh. */
|
||||
height:100vh;height:100dvh;
|
||||
}
|
||||
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
|
||||
a{color:var(--accent)}
|
||||
/* Inset wherever a parent clips its overflow, or the toolbar and the feed list cut the ring off. */
|
||||
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
||||
.tgroup button:focus-visible,.tgroup a:focus-visible,.feed:focus-visible,.place:focus-visible,.chev:focus-visible{outline-offset:-2px}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
*,*::before,*::after{animation:none!important;transition:none!important}
|
||||
}
|
||||
::-webkit-scrollbar{width:10px;height:10px}
|
||||
::-webkit-scrollbar-thumb{background:var(--raise);border-radius:6px}
|
||||
::-webkit-scrollbar-track{background:transparent}
|
||||
|
||||
/* ---------- shell ---------- */
|
||||
#shell{display:grid;grid-template-columns:290px 1fr;min-height:0;min-width:0;overflow:hidden}
|
||||
/* [ hides the feed list. A phone slides it over the page instead, so this is for wider screens. */
|
||||
@media (min-width:821px){
|
||||
body.nosb #shell{grid-template-columns:minmax(0,1fr)}
|
||||
body.nosb #sidebar{display:none}
|
||||
}
|
||||
#sidebar{
|
||||
background:var(--panel);border-right:1px solid var(--line);
|
||||
display:flex;flex-direction:column;min-height:0;
|
||||
}
|
||||
.brand{display:flex;align-items:center;gap:9px;padding:14px 14px 10px}
|
||||
.brand .logo{
|
||||
width:30px;height:30px;flex:none;object-fit:contain;
|
||||
}
|
||||
.brand h1{font-size:16px;margin:0;font-weight:650;letter-spacing:-.01em;color:var(--fg);flex:1}
|
||||
.iconbtn{
|
||||
width:30px;height:30px;border-radius:8px;display:grid;place-items:center;
|
||||
color:var(--dim);flex:none;
|
||||
}
|
||||
.iconbtn:hover{background:var(--raise);color:var(--fg)}
|
||||
/* One toolbar across the window, as the original had: grouped buttons, search on the right. */
|
||||
#topbar{
|
||||
display:flex;align-items:center;gap:10px;padding:7px 12px;min-width:0;overflow:hidden;
|
||||
background:var(--panel);border-bottom:1px solid var(--line);
|
||||
}
|
||||
#topbar .grow{flex:1}
|
||||
#topbar #epSearch{width:260px;flex:none}
|
||||
.tgroup{display:flex;flex:none;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--panel2)}
|
||||
.tgroup button,.tgroup a{padding:5px 11px;min-width:34px;color:var(--dim);font-size:14px;line-height:1.3}
|
||||
.tgroup a{display:inline-grid;place-items:center}
|
||||
.tgroup button+button,.tgroup button+a{border-left:1px solid var(--line)}
|
||||
.tgroup button:hover:not(:disabled),.tgroup a:hover{background:var(--raise);color:var(--fg)}
|
||||
.tgroup button:disabled{opacity:.35;cursor:default}
|
||||
.sidetools button,.sidefoot button{
|
||||
flex:1;background:var(--panel2);border:1px solid var(--line);border-radius:8px;
|
||||
padding:6px 8px;font-size:12.5px;color:var(--dim);
|
||||
}
|
||||
.sidetools button:hover,.sidefoot button:hover{border-color:var(--accent);color:var(--fg)}
|
||||
/* Settings and the log are housekeeping: they sit under the feeds, out of the way. */
|
||||
.sidefoot{
|
||||
display:flex;flex-direction:column;gap:7px;padding:8px 12px;
|
||||
border-top:1px solid var(--line);flex:none;
|
||||
}
|
||||
.sidefoot .row{display:flex;gap:6px}
|
||||
.who{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--faint)}
|
||||
.who span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.who button{flex:none;background:none;border:0;padding:2px 4px;color:var(--faint);text-decoration:underline}
|
||||
.who button:hover{color:var(--fg)}
|
||||
.sidefoot button{display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
||||
.sidefoot i{font-style:normal;opacity:.75}
|
||||
.searchwrap{padding:0 12px 8px}
|
||||
input[type=search],input[type=text],input[type=password],input[type=number],select{
|
||||
width:100%;background:var(--bg);border:1px solid var(--line);color:var(--fg);
|
||||
border-radius:8px;padding:7px 10px;font:inherit;font-size:13.5px;
|
||||
}
|
||||
input:focus,select:focus{outline:0;border-color:var(--accent)}
|
||||
/* The wider left gutter is the folder triangle's; everything in the list shifts with it, so the
|
||||
feeds still line up with the places above. */
|
||||
#feedlist{overflow-y:auto;padding:0 8px 12px 16px;flex:1;min-height:0}
|
||||
.feed{
|
||||
display:flex;gap:10px;align-items:center;padding:7px 8px;border-radius:9px;
|
||||
cursor:pointer;margin-bottom:1px;position:relative;
|
||||
}
|
||||
.feed:hover{background:var(--panel2)}
|
||||
.feed.sel{background:var(--raise)}
|
||||
/* A show sits under its folder's title, a size down, so an open folder reads as one. */
|
||||
.feed.child{margin-left:11px}
|
||||
/* Pinned feeds, at the top of the list: a small pin before the name, and a rule under the last. */
|
||||
.feed .fpin .i{width:10px;height:10px;margin-right:5px;vertical-align:-1px;color:var(--accent)}
|
||||
.feed.lastpin{margin-bottom:9px}
|
||||
.feed.lastpin::after{content:"";position:absolute;left:8px;right:8px;bottom:-5px;border-bottom:1px solid var(--line)}
|
||||
.feed.child .art{width:28px;height:28px;font-size:11px}
|
||||
/* Only a folder has a triangle, hung in the margin so every feed's art lines up with the places
|
||||
above it. The button is the row's full height and 24 px wide: a near miss used to open the
|
||||
folder's page, and 16 px left the triangle cramped against the art. */
|
||||
.chev{
|
||||
position:absolute;left:-16px;top:0;bottom:0;width:24px;display:grid;place-items:center;
|
||||
border-radius:4px;color:var(--faint);
|
||||
}
|
||||
.chev:hover{color:var(--fg)}
|
||||
.chev.bad,.chev.bad:hover{color:var(--bad)}
|
||||
/* A feed's error mark, in the triangle's place: the same column as every folder's triangle,
|
||||
a child's included, which is why it moves left by the child's indent. */
|
||||
.ferr{position:absolute;left:-16px;top:0;bottom:0;width:24px;display:grid;place-items:center;color:var(--bad)}
|
||||
.ferr .i{width:12px;height:12px}
|
||||
.feed.child .ferr{left:-27px}
|
||||
.chev .i{width:12px;height:12px;transition:transform .12s}
|
||||
.chev[aria-expanded="true"] .i{transform:rotate(90deg)}
|
||||
.childlist{display:grid;grid-template-columns:minmax(0,1fr);gap:4px;margin-top:10px}
|
||||
.childrow{
|
||||
display:flex;gap:10px;align-items:center;padding:7px 9px;border:1px solid var(--line);
|
||||
border-radius:9px;cursor:pointer;background:var(--panel);
|
||||
}
|
||||
.childrow:hover{border-color:var(--accent)}
|
||||
.childrow .art{width:32px;height:32px;font-size:12px}
|
||||
.childrow .txt{flex:1;min-width:0}
|
||||
.childrow .txt b{display:block;font-weight:500;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
/* Currently Listening: how far into each episode you are, as a rail along the foot of its row.
|
||||
The one in the player is marked as it is everywhere else, by the amber EQ bars, and its rail
|
||||
and time left move as it plays. The rest stay neutral, so that one is what stands out. */
|
||||
#listening .childrow{position:relative;overflow:hidden;gap:12px;padding:10px 10px 13px}
|
||||
#listening .childrow .art{width:44px;height:44px;font-size:13px}
|
||||
#listening .txt b{display:flex;align-items:center;gap:7px}
|
||||
#listening .txt b span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#listening .txt small{display:flex;gap:12px;margin-top:3px;color:var(--faint);font-size:11.5px}
|
||||
#listening .txt small .fd{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#listening .txt small .left{flex:none;color:var(--dim)}
|
||||
#listening .childrow:not(.now) .eq{display:none}
|
||||
#listening .rail{position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--raise)}
|
||||
#listening .rail i{display:block;height:100%;width:0;background:var(--faint);transition:width .25s linear}
|
||||
#listening .now{border-color:color-mix(in srgb,var(--accent2) 60%,var(--line))}
|
||||
#listening .now .eq,#listening .now .left{color:var(--accent2)}
|
||||
#listening .now .rail i{background:var(--accent2)}
|
||||
/* Quiet buttons: a filled one on every row outshouted the row that is playing. */
|
||||
#listening [data-a=play]{color:var(--accent)}
|
||||
#listening [data-a=remove]{color:var(--faint)}
|
||||
/* Directory's filters: .tabs for what a feed is, as the item filters are, and chips for what it
|
||||
is about. A picked chip is underlined in --accent2, as the download bar and the now-playing EQ
|
||||
are; a .badge's fill already means unread in the sidebar. */
|
||||
.dirbar{display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin-top:4px}
|
||||
.dirbar .tabs{flex:none}
|
||||
.chips{display:flex;flex-wrap:wrap;gap:2px 6px;flex:1 1 0;min-width:0}
|
||||
.chips button{flex:none;padding:4px 6px;font-size:13px;color:var(--dim);white-space:nowrap;border-bottom:2px solid transparent}
|
||||
.chips button:hover{color:var(--fg)}
|
||||
.chips button[aria-pressed="true"]{color:var(--fg);border-bottom-color:var(--accent2)}
|
||||
/* Square cover art, title underneath: podcast art is made to be known at a glance. The subscribe
|
||||
button stays visible, since a button that only shows on hover cannot be reached on a phone. */
|
||||
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:18px 14px;margin-top:14px}
|
||||
.tiles>.hint{grid-column:1/-1}
|
||||
.tile{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 2px;align-items:start;cursor:pointer}
|
||||
.tile .art{grid-column:1/-1;width:100%;height:auto;aspect-ratio:1;font-size:34px}
|
||||
.tile .txt b{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-weight:500;font-size:13px;line-height:1.3}
|
||||
.tile .txt small{display:block;color:var(--faint);font-size:11.5px}
|
||||
.tile:hover .txt b{color:var(--accent)}
|
||||
.tag{
|
||||
font-size:11px;font-weight:600;
|
||||
padding:1px 5px;border-radius:4px;background:var(--raise);color:var(--warn);flex:none;
|
||||
}
|
||||
.art{
|
||||
border-radius:7px;object-fit:cover;background:var(--raise);flex:none;
|
||||
display:grid;place-items:center;color:var(--faint);font-weight:600;overflow:hidden;
|
||||
}
|
||||
/* Initials, tinted by a hash of the name so two "TS" in a row can be told apart. Mixed into the
|
||||
page ground, not --raise: that is also the selected row, and a selected tile vanished into it. */
|
||||
.art.ini{background:color-mix(in srgb,var(--tint) 24%,var(--bg));color:var(--tint)}
|
||||
/* A folder: its first four shows' art, as iTunes built a playlist's from its albums. */
|
||||
.art.mosaic{grid-template:1fr 1fr/1fr 1fr;place-items:stretch}
|
||||
.art.mosaic img{width:100%;height:100%;min-height:0;object-fit:cover;display:block}
|
||||
.feed .art{width:34px;height:34px;font-size:13px}
|
||||
.feed .txt{min-width:0;flex:1}
|
||||
.feed .txt b{display:block;font-weight:500;font-size:13.5px;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.feed .txt small{display:block;color:var(--faint);font-size:11.5px;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.badge{
|
||||
background:var(--accent2);color:var(--ink);border-radius:20px;padding:1px 7px;
|
||||
font-size:11px;font-weight:600;flex:none;min-width:26px;text-align:center;
|
||||
}
|
||||
.badge.zero{background:var(--raise);color:var(--faint)}
|
||||
/* Counts, sizes and times that change in place should not jiggle the text around them. */
|
||||
.badge,.feed small,.childrow small,.tile small,.ep,.fhead .sub,.dmeta,#status,#seekrow{font-variant-numeric:tabular-nums}
|
||||
|
||||
/* ---------- main ---------- */
|
||||
#main{overflow:hidden;min-height:0;min-width:0;display:flex;flex-direction:column}
|
||||
.wrap{flex:1;min-height:0;min-width:0;display:flex;flex-direction:column}
|
||||
.wrap.plain{overflow-y:auto;padding:18px 22px 40px;display:block}
|
||||
#content{display:flex;flex-direction:column;min-height:0;min-width:0;flex:1}
|
||||
#content>.fhead,#content>.toolbar{padding-left:20px;padding-right:20px;flex:none}
|
||||
#content>.fhead{padding-top:16px}
|
||||
|
||||
/* Three panes, as the original had: feeds beside, items above, the item below. */
|
||||
#split{
|
||||
display:grid;flex:1;min-height:0;
|
||||
grid-template-columns:minmax(0,1fr) 270px;
|
||||
grid-template-rows:minmax(90px,var(--listh,60%)) 7px 1fr;
|
||||
grid-template-areas:"list files" "grab grab" "detail detail";
|
||||
}
|
||||
#list{grid-area:list;overflow:auto;padding:0 14px 10px;overscroll-behavior-y:contain}
|
||||
/* Pull-to-refresh's note (gestures.ts). overscroll-behavior keeps the browser's own pull, which
|
||||
reloads the page, from starting on top of it. */
|
||||
#pulltip{display:flex;align-items:flex-end;justify-content:center;padding-bottom:6px;overflow:hidden;
|
||||
font-size:12px;color:var(--faint)}
|
||||
/* The selected item's files, beside the list, as the original's Files pane was. */
|
||||
#files{grid-area:files;overflow-y:auto;padding:8px 12px;border-left:1px solid var(--line);background:var(--panel)}
|
||||
/* Nothing selected, or an item with no files: the list takes the width. */
|
||||
#split:has(>#files[hidden]){grid-template-columns:minmax(0,1fr) 0}
|
||||
#files .encbox{margin-top:8px}
|
||||
#files .empty{padding:24px 0}
|
||||
.fhd{font-size:12px;color:var(--faint)}
|
||||
#grab{grid-area:grab;cursor:row-resize;background:var(--line)}
|
||||
#grab:hover{background:var(--accent)}
|
||||
#detail{grid-area:detail;overflow-y:auto;padding:16px 20px 28px;background:var(--panel);border-top:1px solid var(--line)}
|
||||
.dt{font-size:18px;font-weight:650;margin:0 0 5px;line-height:1.3;overflow-wrap:anywhere}
|
||||
.dmeta{color:var(--faint);font-size:12.5px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px}
|
||||
.dmeta .btn{padding:3px 9px;font-size:12px}
|
||||
#detail .encbox{margin:0 0 10px}
|
||||
.dbody{font-size:14.5px;line-height:1.65;overflow-wrap:anywhere;color:var(--fg)}
|
||||
.dbody img{max-width:100%;height:auto;border-radius:6px}
|
||||
.dbody a{color:var(--accent)}
|
||||
.dbody pre{overflow-x:auto;background:var(--panel2);padding:10px;border-radius:8px}
|
||||
.encbox{
|
||||
display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:14px;
|
||||
padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--panel2);
|
||||
}
|
||||
.fhead{display:flex;gap:18px;margin-bottom:18px}
|
||||
.fhead .art{width:118px;height:118px;font-size:34px;box-shadow:var(--shadow)}
|
||||
.fhead .meta{min-width:0;flex:1;display:flex;flex-direction:column}
|
||||
.fhead h2{margin:0 0 3px;font-size:23px;line-height:1.2;overflow-wrap:anywhere}
|
||||
.fhead .sub{color:var(--dim);font-size:13px;margin-bottom:8px}
|
||||
.fhead .sub a{color:var(--dim);text-decoration:none}
|
||||
.fhead .sub a:hover{color:var(--accent)}
|
||||
.acts{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:auto}
|
||||
.acts .btn{display:inline-flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px}
|
||||
.acts .btn i{font-style:normal;font-size:13px;line-height:1;opacity:.7}
|
||||
.acts .btn.primary i{opacity:.9}
|
||||
.acts .btn:hover{background:var(--raise)}
|
||||
.acts .btn.primary:hover{background:var(--accent)}
|
||||
.btn{
|
||||
background:var(--panel2);border:1px solid var(--line);border-radius:8px;
|
||||
padding:6px 12px;font-size:13px;
|
||||
}
|
||||
.btn:hover{border-color:var(--accent)}
|
||||
a.btn{text-decoration:none;color:inherit}
|
||||
.btn.primary{background:var(--accent);border-color:var(--accent);color:var(--ink);font-weight:600}
|
||||
.btn.primary:hover{filter:brightness(1.08)}
|
||||
.btn.danger:hover{border-color:var(--bad);color:var(--bad)}
|
||||
/* An icon in place of a word; the word is in its tooltip. */
|
||||
.btn.ico{padding:4px 9px;min-width:32px;font-size:14px;line-height:1.25;text-align:center}
|
||||
/* Inline in a sentence, next to a plain-word error explanation. */
|
||||
.btn.tiny{padding:2px 7px;font-size:11.5px;border-radius:6px;margin-left:2px}
|
||||
/* The keys ? lists. */
|
||||
.keys{border-collapse:collapse;width:100%;font-size:13px}
|
||||
.keys th{text-align:left;font-weight:600;padding:12px 0 4px;color:var(--fg)}
|
||||
.keys td{padding:3px 0;color:var(--dim)}
|
||||
.keys td:first-child{width:10em;white-space:nowrap}
|
||||
kbd{font:inherit;font-size:12px;color:var(--fg);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:0 5px}
|
||||
/* An icon (Font Awesome, embedded as SVG) in the button's own colour. */
|
||||
.i{display:inline-block;width:16px;height:16px;vertical-align:-3px;flex:none;fill:currentColor}
|
||||
/* A file's type as an icon, in place of the old DOWNLOADED / PENDING / audio chips: green once
|
||||
it is here, red when the download failed. */
|
||||
.kind{display:inline-grid;place-items:center;color:var(--dim)}
|
||||
.kind.here{color:var(--good)}
|
||||
.subbed{display:inline-flex;padding:0 9px;color:var(--good)}
|
||||
.subbed .i{width:18px;height:18px}
|
||||
.kind.bad{color:var(--bad)}
|
||||
.fhead .art .i{width:22px;height:22px}
|
||||
.toolbar{
|
||||
display:flex;gap:10px;align-items:center;margin-bottom:12px;flex-wrap:wrap;
|
||||
position:sticky;top:0;background:var(--bg);padding:6px 0 8px;z-index:3;
|
||||
}
|
||||
.tabs{display:flex;gap:2px;background:var(--panel2);border-radius:9px;padding:3px}
|
||||
.tabs button{padding:5px 12px;border-radius:7px;font-size:13px;color:var(--dim)}
|
||||
.tabs button.on{background:var(--raise);color:var(--fg);font-weight:500}
|
||||
.tabs a{padding:5px 12px;border-radius:7px;font-size:13px;color:var(--dim);text-decoration:none}
|
||||
.tabs a.on{background:var(--raise);color:var(--fg);font-weight:500}
|
||||
|
||||
/* ---------- the admin page (admin.html) ---------- */
|
||||
/* One column that scrolls under the same header, rather than the app's fixed panes. */
|
||||
body.adminpage{display:block;overflow:auto}
|
||||
body.adminpage #topbar{position:sticky;top:0;z-index:5}
|
||||
body.adminpage #topbar h1{font-size:16px;margin:0;font-weight:650}
|
||||
body.adminpage #topbar .logo{border-radius:5px}
|
||||
body.adminpage #topbar > a.btn{text-decoration:none}
|
||||
body.adminpage #admin{max-width:780px;margin:0 auto}
|
||||
body.adminpage #admin h2{font-size:18px;margin:0 0 12px}
|
||||
body.adminpage #log{max-width:none}
|
||||
.urow{padding:9px 0;border-bottom:1px solid var(--line)}
|
||||
.toolbar .grow{flex:1;min-width:150px;max-width:320px}
|
||||
|
||||
/* ---------- items ---------- */
|
||||
/* A table, as the original's was: unread, kept, title, feed, file, size, published. */
|
||||
.ephead,.ep{
|
||||
display:grid;gap:8px;align-items:center;
|
||||
grid-template-columns:22px 22px minmax(120px,1fr) minmax(0,160px) 56px minmax(0,72px) minmax(0,92px) 64px;
|
||||
}
|
||||
/* A pixel more side padding than a row's: a row has a 1px border the heading has not, and without
|
||||
it every heading sat a pixel left of its column. */
|
||||
.ephead{
|
||||
position:sticky;top:0;z-index:2;background:var(--bg);padding:7px 11px 5px;
|
||||
border-bottom:1px solid var(--line);margin-bottom:3px;
|
||||
font-size:12px;color:var(--faint);
|
||||
}
|
||||
/* Each heading sorts by its column; the caret says which way. */
|
||||
.ephead .hs{display:flex;align-items:center;gap:4px;min-width:0;padding:0;border:0;background:none;
|
||||
font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;text-align:left;cursor:pointer}
|
||||
.ephead .hs:hover,.ephead .hs.on{color:var(--fg)}
|
||||
.ephead .hs .arr{display:inline-flex}
|
||||
.ephead .hs .arr .i{width:8px;height:8px}
|
||||
.ephead .hs .arr.asc{transform:rotate(-90deg)}
|
||||
.ephead .hs .arr.desc{transform:rotate(90deg)}
|
||||
/* An icon heading is centred over its column, as the icons under it are, and its caret hangs
|
||||
outside so sorting by it does not push the icon off line. */
|
||||
.ephead .hs.h-ic{justify-content:center;position:relative;width:100%}
|
||||
.ephead .hs.h-ic .arr{position:absolute;left:100%}
|
||||
.ep{padding:4px 10px;border-radius:7px;border:1px solid transparent;cursor:pointer;margin-bottom:1px}
|
||||
.ep>*{min-width:0}
|
||||
.ep.sel{background:var(--raise);border-color:var(--line)}
|
||||
.ep:hover{background:var(--panel)}
|
||||
/* Amber means new: the unread dot, the badges and a download under way. Blue is for the one
|
||||
primary action and links, so a count no longer looks like a button. */
|
||||
/* No padding: a button keeps the browser's side padding, which left too little room for the 16px
|
||||
icon, so it spilled 3px right of centre and off line with its heading. */
|
||||
.ep .st,.ep .fl{width:22px;height:22px;padding:0;border-radius:5px;display:grid;place-items:center;font-size:11px;color:var(--accent2)}
|
||||
.ep .fl{color:var(--faint);font-size:13px}
|
||||
.ep .fl.on{color:var(--fg)}
|
||||
/* The icon's EQ bars mark what is playing: standing still, and moving only while it plays. A
|
||||
paused animation was tried first; the frames it held were a pixel apart and read as dots. */
|
||||
.eq{display:inline-flex;align-items:flex-end;gap:2px;width:13px;height:12px;flex:none}
|
||||
.eq i{flex:1;height:100%;background:currentColor;border-radius:1px;transform-origin:bottom}
|
||||
.eq i:nth-child(1){height:60%}
|
||||
.eq i:nth-child(3){height:40%}
|
||||
body.playing .eq i{animation:eq .9s ease-in-out infinite alternate}
|
||||
body.playing .eq i:nth-child(1){animation-delay:-.3s}
|
||||
body.playing .eq i:nth-child(3){animation-delay:-.6s}
|
||||
@keyframes eq{from{transform:scaleY(.35)}}
|
||||
.ep .st:hover,.ep .fl:hover{background:var(--raise)}
|
||||
.ep .t{font-weight:600;font-size:13.5px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.ep.read .t{color:var(--dim);font-weight:500}
|
||||
.ep .line{display:flex;gap:9px;align-items:center;flex-wrap:wrap;color:var(--faint);font-size:11.5px}
|
||||
.ep .line:empty{display:none}
|
||||
.ep .fd,.ep .size,.ep .date{color:var(--dim);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.ep .file{display:flex;gap:6px;align-items:center;position:relative;color:var(--faint);font-size:11.5px}
|
||||
/* Under the icon, not beside it or on a line of its own: taking a line, it lifted the icon of
|
||||
every file not yet downloaded above the downloaded ones' (issue #31). */
|
||||
.ep .file .dlbar{position:absolute;left:0;right:0;bottom:-5px;margin:0}
|
||||
.ep .rowacts{justify-content:flex-end}
|
||||
/* One feed's own table has no need of a Feed column; All Subscriptions does. */
|
||||
#split.one .ephead,#split.one .ep{grid-template-columns:22px 22px minmax(120px,1fr) 56px minmax(0,72px) minmax(0,92px) 64px}
|
||||
#split.one .h-fd,#split.one .ep .fd{display:none}
|
||||
.dot{width:3px;height:3px;border-radius:50%;background:var(--faint);flex:none}
|
||||
.ep .rowacts{display:flex;gap:2px;align-items:flex-start;opacity:0;transition:opacity .12s}
|
||||
.ep:hover .rowacts{opacity:1}
|
||||
.dlbar{height:3px;background:transparent;border-radius:2px;overflow:hidden;margin-top:7px}
|
||||
.dlbar.live{background:var(--raise)}
|
||||
.dlbar i{display:block;height:100%;width:0;background:var(--accent2);transition:width .25s}
|
||||
.empty{color:var(--faint);text-align:center;padding:50px 0}
|
||||
#more{display:block;width:100%;margin-top:10px}
|
||||
|
||||
/* ---------- player ---------- */
|
||||
#player{
|
||||
border-top:1px solid var(--line);background:var(--panel);
|
||||
display:none;grid-template-columns:auto 1fr auto;gap:14px;align-items:center;
|
||||
padding:9px 16px;box-shadow:0 -6px 24px rgba(6,10,16,.4);
|
||||
}
|
||||
#player.on{display:grid}
|
||||
/* #audio is a <video> playing double duty as the audio element (see its tag). Only a video
|
||||
file needs to be seen, floated above the bar rather than laid into it, so an audio episode's
|
||||
layout is unchanged. */
|
||||
#audio{display:none}
|
||||
body.has-video #audio{
|
||||
display:block;position:fixed;z-index:45;right:16px;bottom:120px;
|
||||
width:360px;max-width:calc(100vw - 32px);aspect-ratio:16/9;background:#000;
|
||||
border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,.5);
|
||||
}
|
||||
#pnow{display:flex;gap:11px;align-items:center;min-width:0;width:250px}
|
||||
#pnow .art{width:44px;height:44px;font-size:13px}
|
||||
#pnow .txt{min-width:0}
|
||||
#pnow b{display:block;font-size:13px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
#pnow .eq{color:var(--accent2);margin-right:6px}
|
||||
#pnow small{color:var(--faint);font-size:11.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
|
||||
#pmid{display:flex;flex-direction:column;gap:3px;min-width:0}
|
||||
#pbtns{display:flex;gap:5px;align-items:center;justify-content:center}
|
||||
#pbtns .iconbtn{width:34px;height:34px}
|
||||
#pplay{
|
||||
width:40px!important;height:40px!important;background:var(--fg);color:var(--bg);
|
||||
border-radius:50%;
|
||||
}
|
||||
#pplay:hover{background:var(--accent);color:var(--ink)}
|
||||
#seekrow{display:flex;gap:9px;align-items:center;font-variant-numeric:tabular-nums;font-size:11.5px;color:var(--faint)}
|
||||
input[type=range]{
|
||||
-webkit-appearance:none;appearance:none;height:4px;border-radius:3px;flex:1;
|
||||
background:var(--raise);cursor:pointer;
|
||||
}
|
||||
input[type=range]::-webkit-slider-thumb{
|
||||
-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:var(--accent);
|
||||
}
|
||||
input[type=range]::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:var(--accent)}
|
||||
#pright{display:flex;gap:8px;align-items:center}
|
||||
#pright select{width:auto;padding:4px 6px;font-size:12px}
|
||||
#vol{width:78px;flex:none}
|
||||
|
||||
/* ---------- overlays ---------- */
|
||||
#modal{
|
||||
position:fixed;inset:0;background:rgba(0,0,0,.6);display:none;
|
||||
place-items:center;z-index:50;padding:20px;
|
||||
}
|
||||
#modal.on{display:grid}
|
||||
.card{
|
||||
background:var(--panel);border:1px solid var(--line);border-radius:14px;
|
||||
padding:20px;width:min(460px,100%);box-shadow:var(--shadow);max-height:88vh;overflow:auto;
|
||||
}
|
||||
.card.wide{width:min(1000px,100%)}
|
||||
#logbox{
|
||||
background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:10px 12px;
|
||||
height:min(60vh,520px);overflow:auto;font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace;
|
||||
}
|
||||
#logbox .l{display:flex;gap:9px;white-space:pre-wrap;overflow-wrap:anywhere}
|
||||
#logbox time{color:var(--faint);flex:none}
|
||||
#logbox .lv{flex:none;width:42px;font-weight:700}
|
||||
#logbox .lv.ERROR{color:var(--bad)} #logbox .lv.WARN{color:var(--warn)}
|
||||
#logbox .lv.INFO{color:var(--accent)} #logbox .lv.DEBUG,#logbox .lv.TRACE{color:var(--faint)}
|
||||
#logbox .tg{color:var(--faint);flex:none}
|
||||
.logbar{display:flex;gap:8px;align-items:center;margin-bottom:9px;flex-wrap:wrap}
|
||||
.logbar .grow{flex:1;min-width:120px}
|
||||
.card h3{margin:0 0 14px;font-size:17px}
|
||||
.field{display:grid;gap:4px;margin-bottom:12px}
|
||||
.field label{font-size:12px;color:var(--dim)}
|
||||
.field .hint{font-size:11.5px;color:var(--faint)}
|
||||
.check{display:flex;gap:8px;align-items:center;font-size:13.5px;margin-bottom:9px}
|
||||
.inline{display:flex;gap:6px;align-items:stretch}
|
||||
.inline input{flex:1;min-width:0}
|
||||
.inline .btn{white-space:nowrap;flex:none}
|
||||
.inline select{flex:none;width:auto}
|
||||
.inline input[type=number]{flex:none;width:90px}
|
||||
.check input{width:16px;height:16px;accent-color:var(--accent)}
|
||||
.cardacts{display:flex;gap:8px;justify-content:flex-end;margin-top:16px}
|
||||
#toasts{position:fixed;bottom:88px;right:18px;display:flex;flex-direction:column;gap:8px;z-index:60}
|
||||
.toast{
|
||||
background:var(--raise);border:1px solid var(--line);border-radius:9px;
|
||||
padding:9px 13px;font-size:13px;box-shadow:var(--shadow);animation:in .18s;max-width:340px;
|
||||
}
|
||||
.toast.bad{border-color:var(--bad);color:var(--bad)}
|
||||
@keyframes in{from{opacity:0;transform:translateY(6px)}}
|
||||
#burger,#dback{display:none}
|
||||
/* Totals for what is showing, along the bottom, as the original's status bar. */
|
||||
#status{
|
||||
padding:3px 14px;min-height:22px;font-size:12px;color:var(--faint);background:var(--panel);
|
||||
border-top:1px solid var(--line);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
|
||||
}
|
||||
/* The feed's own header, kept to one line so the table starts high, as it did. */
|
||||
.fhead.slim{align-items:center;gap:8px 12px;margin-bottom:10px;flex-wrap:wrap}
|
||||
.fhead.slim .art{width:44px;height:44px;font-size:15px;box-shadow:none}
|
||||
.fhead.slim .meta{flex:1 1 260px}
|
||||
/* One line each, or a long title wraps to three and pushes the table down. */
|
||||
.fhead.slim h2,.fhead.slim .sub.stat{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.fhead.slim h2{font-size:17px}
|
||||
.fhead.slim .sub{margin-bottom:0;font-size:12.5px}
|
||||
.fhead.slim .acts{margin-top:0;flex:none}
|
||||
.fhead.slim .acts .btn{padding:5px 11px;font-size:12.5px}
|
||||
/* Places above the feeds: not subscriptions, but where to look. */
|
||||
.places{border-bottom:1px solid var(--line);margin:0 0 6px;padding-bottom:6px}
|
||||
.place{display:flex;gap:10px;align-items:center;padding:6px 8px;border-radius:9px;cursor:pointer}
|
||||
.place:hover{background:var(--panel2)}
|
||||
.place.sel{background:var(--raise)}
|
||||
.place .ico{width:18px;text-align:center;color:var(--accent);flex:none}
|
||||
.place b{flex:1;font-weight:500;font-size:13.5px}
|
||||
|
||||
@media (max-width:820px){
|
||||
#shell{grid-template-columns:1fr}
|
||||
#sidebar{position:fixed;inset:0 auto 0 0;width:min(300px,86vw);z-index:42;transform:translateX(-100%);transition:transform .2s;box-shadow:var(--shadow)}
|
||||
#sidebar.open{transform:none}
|
||||
#scrim{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:41}
|
||||
#player{position:relative;z-index:39;padding:7px 10px;gap:8px}
|
||||
|
||||
/* The feed list is reachable whether or not anything is playing. */
|
||||
#burger{display:grid}
|
||||
#topbar{gap:6px;padding:6px 8px}
|
||||
#topbar .grow,.tgroup.item{display:none}
|
||||
#topbar #epSearch{width:auto;flex:1;min-width:0}
|
||||
.tgroup button,.tgroup a{padding:4px 8px;min-width:32px}
|
||||
.fhead.slim .acts{flex:1 1 100%}
|
||||
.iconbtn{width:38px;height:38px}
|
||||
|
||||
/* One pane at a time: the list, then the item over it. */
|
||||
#split{grid-template-columns:1fr;grid-template-rows:1fr;grid-template-areas:"list"}
|
||||
/* Title and date only; the files sit above the item's text in the reader instead. */
|
||||
#files,.ephead,.ep .fd,.ep .file,.ep .size,.ep .rowacts{display:none}
|
||||
.ep,#split.one .ep{grid-template-columns:20px 20px minmax(0,1fr) auto}
|
||||
#grab{display:none}
|
||||
#detail{position:fixed;inset:0;z-index:38;display:none;border-top:0;padding:12px 16px 90px}
|
||||
body.reading #detail{display:block}
|
||||
#dback{display:inline-block;margin-bottom:10px}
|
||||
|
||||
#content>.fhead,#content>.toolbar{padding-left:14px;padding-right:14px}
|
||||
#content>.fhead{padding-top:12px}
|
||||
.fhead{gap:12px;margin-bottom:12px}
|
||||
.fhead .art{width:64px;height:64px;font-size:20px}
|
||||
.fhead h2{font-size:18px}
|
||||
.fhead .sub{font-size:12px;margin-bottom:6px}
|
||||
.acts{gap:6px}
|
||||
.acts .btn{padding:7px 10px;font-size:12.5px}
|
||||
.toolbar{gap:8px}
|
||||
#list{padding:0 12px 10px}
|
||||
#player{grid-template-columns:1fr auto;grid-template-areas:"now right" "mid mid";gap:2px 8px}
|
||||
#pnow{grid-area:now;width:auto}
|
||||
#pright{grid-area:right}
|
||||
#pmid{grid-area:mid}
|
||||
#pnow .art{width:38px;height:38px}
|
||||
#pnow .txt small,#pright #vol{display:none}
|
||||
.wrap.plain{padding:14px}
|
||||
.card{padding:16px}
|
||||
/* The chips get a line of their own and scroll sideways rather than wrap, so they never push
|
||||
the grid down. */
|
||||
.chips{flex:1 1 100%;flex-wrap:nowrap;overflow-x:auto}
|
||||
.tiles{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:14px 10px}
|
||||
|
||||
/* A log line has no room for four columns: keep time and message, wrap as prose. */
|
||||
#logbox{font-size:11.5px;padding:8px}
|
||||
#logbox .l{flex-wrap:wrap;gap:6px}
|
||||
#logbox .lv{width:auto}
|
||||
#logbox .tg{display:none}
|
||||
}
|
||||
|
||||
/* ---------- Classic: the 2004 Mac app ---------- */
|
||||
/* After the iPodderX screenshots: brushed-metal chrome, a pale blue-grey source list, Aqua blue
|
||||
for whatever is selected, red unread badges, a striped table and Lucida Grande. */
|
||||
:root[data-theme="classic"] body{
|
||||
font-family:"Lucida Grande","Lucida Sans Unicode","Lucida Sans",Geneva,Verdana,sans-serif;font-size:13px;
|
||||
}
|
||||
:root[data-theme="classic"] #topbar,
|
||||
:root[data-theme="classic"] #status,
|
||||
:root[data-theme="classic"] #player{background:linear-gradient(#ececec,#c9c9c9);border-color:#8e8e8e;color:#1a1a1a}
|
||||
:root[data-theme="classic"] #status{text-align:center}
|
||||
:root[data-theme="classic"] #sidebar{background:#e7ebf1;border-right-color:#a3a3a3}
|
||||
:root[data-theme="classic"] #files{background:#f4f4f4}
|
||||
:root[data-theme="classic"] .tgroup{background:none;border-color:#8e8e8e;border-radius:6px}
|
||||
:root[data-theme="classic"] .tgroup button+button,:root[data-theme="classic"] .tgroup button+a{border-left-color:#8e8e8e}
|
||||
:root[data-theme="classic"] .tgroup button,
|
||||
:root[data-theme="classic"] .tgroup a,
|
||||
:root[data-theme="classic"] .btn,
|
||||
:root[data-theme="classic"] .tabs{background:linear-gradient(#ffffff,#d9d9d9);border-color:#8e8e8e;color:#1a1a1a}
|
||||
:root[data-theme="classic"] .tabs{border:1px solid #8e8e8e;border-radius:6px}
|
||||
:root[data-theme="classic"] .tgroup button:hover:not(:disabled),
|
||||
:root[data-theme="classic"] .tgroup a:hover,
|
||||
:root[data-theme="classic"] .btn:hover{background:linear-gradient(#f5f9ff,#cbdbf4)}
|
||||
:root[data-theme="classic"] .btn.primary,
|
||||
:root[data-theme="classic"] .tabs button.on,
|
||||
:root[data-theme="classic"] .feed.sel,
|
||||
:root[data-theme="classic"] .place.sel{background:linear-gradient(#86b0ec,#3a73cf);border-color:#2d5eae;color:#fff}
|
||||
:root[data-theme="classic"] .feed.sel .txt small,
|
||||
:root[data-theme="classic"] .place.sel .ico{color:#e6eeff}
|
||||
:root[data-theme="classic"] #epSearch{border-radius:14px}
|
||||
:root[data-theme="classic"] .badge{background:#c9302c;color:#fff}
|
||||
:root[data-theme="classic"] .badge.zero{background:#b4bdc9;color:#fff}
|
||||
:root[data-theme="classic"] .ephead,
|
||||
:root[data-theme="classic"] .fhd{
|
||||
background:linear-gradient(#fbfbfb,#dcdcdc);color:#1a1a1a;font-size:11px;
|
||||
}
|
||||
:root[data-theme="classic"] .fhd{padding:3px 6px;border:1px solid #c2c2c2}
|
||||
:root[data-theme="classic"] .ephead{border-bottom-color:#a3a3a3}
|
||||
:root[data-theme="classic"] .ep{border-radius:0;margin:0}
|
||||
:root[data-theme="classic"] #eps .ep:nth-child(even){background:#edf3fe}
|
||||
:root[data-theme="classic"] .ep .t{color:#1c4fa8}
|
||||
:root[data-theme="classic"] .ep.read .t{color:#333}
|
||||
:root[data-theme="classic"] #eps .ep.sel{background:#3875d7;border-color:#3875d7}
|
||||
:root[data-theme="classic"] .ep.sel .t,
|
||||
:root[data-theme="classic"] .ep.sel .fd,
|
||||
:root[data-theme="classic"] .ep.sel .size,
|
||||
:root[data-theme="classic"] .ep.sel .date,
|
||||
:root[data-theme="classic"] .ep.sel .line,
|
||||
:root[data-theme="classic"] .ep.sel .st,
|
||||
:root[data-theme="classic"] .ep.sel .fl,
|
||||
:root[data-theme="classic"] .ep.sel .file,
|
||||
:root[data-theme="classic"] .ep.sel .kind{color:#fff}
|
||||
/* Green and red stay green and red on the blue, just lighter so they read. */
|
||||
:root[data-theme="classic"] .ep.sel .kind.here{color:#a6f3a6}
|
||||
:root[data-theme="classic"] .ep.sel .kind.bad{color:#ffb8ad}
|
||||
/* Lists were white in the original; the pale blue-grey belongs to the source list alone. */
|
||||
:root[data-theme="classic"] .childrow{background:#fff}
|
||||
:root[data-theme="classic"] .dt{background:linear-gradient(#80aae6,#3f78cf);color:#fff;padding:6px 12px;border-radius:4px}
|
||||
Reference in New Issue
Block a user