Letting go of a pull removed its note at once and showed nothing else; the sidebar's scanning spinner is hidden on a phone. With no sign the check had started, people pulled again, and again. A "Checking for new items" pill with a spinner now sits under the top bar until the request is sent and two seconds have passed, and a pull meanwhile does nothing. It lives outside #list, which a feed's render rebuilds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1284 lines
61 KiB
CSS
1284 lines
61 KiB
CSS
/* 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 logo (web/logo.svg, logo-dark.svg): its navy ground, the tuning scale's
|
|
blue and the orange needle. The dark half follows logo-dark.svg. */
|
|
:root {
|
|
--bg:#0a1726; /* the dark logo's navy (#0c2238), taken down */
|
|
--panel:#0f1f31;
|
|
--panel2:#15283d;
|
|
--raise:#1d3450;
|
|
--line:#264060;
|
|
--fg:#f3f7fb;
|
|
--dim:#a3b6ca;
|
|
--faint:#8a9fb5;
|
|
--accent:#8fc2ea; /* #8FC2EA the dark logo's scale */
|
|
--accent2:#ff6a1a; /* #FF6A1A the dark logo's needle */
|
|
--ink:#0a1726; /* text on an accent fill */
|
|
--good:#6fbf8b;
|
|
--warn:#f5a524; /* amber, apart from the needle's orange, for pending */
|
|
--bad:#ff6b7a; /* pushed towards pink, apart from the needle */
|
|
--shadow:0 8px 28px rgba(4,10,20,.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:#eef5fb; /* the light logo's sky (#DFF0FB), paler */
|
|
--panel:#ffffff;
|
|
--panel2:#e4eff9;
|
|
--raise:#d5e6f5;
|
|
--line:#c6d9ea;
|
|
--fg:#10233a;
|
|
--dim:#46596e;
|
|
--faint:#56697e;
|
|
--accent:#2f6aa0; /* #2F6AA0 the logo's scale */
|
|
--accent2:#c43e00; /* the needle (#FF5500), taken down until white on it clears AA */
|
|
--ink:#ffffff;
|
|
--good:#2f7d4f;
|
|
--warn:#985e0a; /* amber, apart from the needle's orange; lighter failed AA as text */
|
|
--bad:#b3263a; /* pushed towards crimson, apart from the needle */
|
|
--shadow:0 8px 28px rgba(47,106,160,.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:#3685e4; /* a hair lighter so the badge's dark count clears AA */
|
|
--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:#1c71d8; /* blue 4, a step down from the accent blue, so white on it clears AA */
|
|
--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:#bb5c00; /* the warning orange taken down until white on it clears AA */
|
|
--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:#957100; /* taken down until white on it clears AA */
|
|
--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:#4c566a; /* nord3: nord2 is --panel2, and borders on it vanished */
|
|
--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:#ab644c;
|
|
--ink:#ffffff;
|
|
--good:#446632;
|
|
--warn:#7e590e;
|
|
--bad:#9c3f49;
|
|
--shadow:0 8px 28px rgba(46,52,64,.15);
|
|
}
|
|
/* Catppuccin: Mocha for the dark half, Latte for the light, from catppuccin.com/palette. Base,
|
|
mantle and the surfaces for the grounds, text and subtext for type, mauve (its default accent)
|
|
and peach for the rest. */
|
|
:root[data-theme="catppuccin"] {
|
|
--bg:#1e1e2e; /* base */
|
|
--panel:#181825; /* mantle */
|
|
--panel2:#313244; /* surface0 */
|
|
--raise:#45475a; /* surface1 */
|
|
--line:#45475a;
|
|
--fg:#cdd6f4; /* text */
|
|
--dim:#bac2de; /* subtext1 */
|
|
--faint:#a6adc8; /* subtext0 */
|
|
--accent:#cba6f7; /* mauve */
|
|
--accent2:#fab387; /* peach */
|
|
--ink:#11111b; /* crust */
|
|
--good:#a6e3a1;
|
|
--warn:#f9e2af;
|
|
--bad:#f38ba8;
|
|
--shadow:0 8px 28px rgba(0,0,0,.45);
|
|
}
|
|
:root[data-theme="catppuccin"][data-mode="light"] {
|
|
--bg:#eff1f5; /* base */
|
|
--panel:#e6e9ef; /* mantle */
|
|
--panel2:#dce0e8; /* crust */
|
|
--raise:#ccd0da; /* surface0 */
|
|
--line:#bcc0cc; /* surface1 */
|
|
--fg:#4c4f69; /* text */
|
|
--dim:#5c5f77; /* subtext1 */
|
|
--faint:#5f6276; /* subtext0 */
|
|
--accent:#8638ec; /* mauve */
|
|
--accent2:#c74e09; /* peach */
|
|
--ink:#ffffff;
|
|
--good:#3a9127;
|
|
--warn:#925d13;
|
|
--bad:#d00f39;
|
|
--shadow:0 8px 28px rgba(76,79,105,.15);
|
|
}
|
|
/* Gruvbox, from its README's palette: the bg and fg ramps, with its faded colours for the light
|
|
half as gruvbox itself does. Blue for the accent, orange for what is new. */
|
|
:root[data-theme="gruvbox"] {
|
|
--bg:#282828; /* bg0 */
|
|
--panel:#1d2021; /* bg0_h */
|
|
--panel2:#32302f; /* bg0_s */
|
|
--raise:#3c3836; /* bg1 */
|
|
--line:#504945; /* bg2 */
|
|
--fg:#ebdbb2; /* fg */
|
|
--dim:#d5c4a1; /* fg2 */
|
|
--faint:#a89984; /* fg4 */
|
|
--accent:#83a598; /* blue */
|
|
--accent2:#fe8019; /* orange */
|
|
--ink:#1d2021;
|
|
--good:#b8bb26;
|
|
--warn:#fabd2f;
|
|
--bad:#fb533f;
|
|
--shadow:0 8px 28px rgba(0,0,0,.45);
|
|
}
|
|
:root[data-theme="gruvbox"][data-mode="light"] {
|
|
--bg:#fbf1c7; /* bg0 */
|
|
--panel:#f2e5bc; /* bg0_s */
|
|
--panel2:#ebdbb2; /* bg1 */
|
|
--raise:#d5c4a1; /* bg2 */
|
|
--line:#d5c4a1;
|
|
--fg:#3c3836; /* fg */
|
|
--dim:#504945; /* fg2 */
|
|
--faint:#665c54; /* fg3 */
|
|
--accent:#076678; /* faded blue */
|
|
--accent2:#af3a03; /* faded orange */
|
|
--ink:#fbf1c7;
|
|
--good:#79740e;
|
|
--warn:#8d5c10;
|
|
--bad:#9d0006;
|
|
--shadow:0 8px 28px rgba(60,56,54,.15);
|
|
}
|
|
/* Solarized, from ethanschoonover.com/solarized: base03 and base3 for the grounds, the base tones
|
|
for type. It has two grounds per half, so panel2 and raise are steps between its own. */
|
|
:root[data-theme="solarized"] {
|
|
--bg:#002b36; /* base03 */
|
|
--panel:#073642; /* base02 */
|
|
--panel2:#0b3f4c;
|
|
--raise:#124a58;
|
|
--line:#1d5563;
|
|
--fg:#eee8d5; /* base2 */
|
|
--dim:#c3c7be; /* between base2 and base1: base1 had to lift to read on base02 */
|
|
--faint:#97a5a7; /* base0, lifted to base1 until it reads on base02 */
|
|
--accent:#4b9fda; /* blue */
|
|
--accent2:#b58900; /* yellow */
|
|
--ink:#002b36;
|
|
--good:#859900;
|
|
--warn:#bb9315;
|
|
--bad:#e87674;
|
|
--shadow:0 8px 28px rgba(0,0,0,.45);
|
|
}
|
|
:root[data-theme="solarized"][data-mode="light"] {
|
|
--bg:#fdf6e3; /* base3 */
|
|
--panel:#eee8d5; /* base2 */
|
|
--panel2:#e6dfca;
|
|
--raise:#ddd5bd;
|
|
--line:#d3cab0;
|
|
--fg:#073642; /* base02 */
|
|
--dim:#52666d; /* base01 */
|
|
--faint:#54666d; /* base00 */
|
|
--accent:#1e6da5; /* blue */
|
|
--accent2:#cb4b16; /* orange */
|
|
--ink:#ffffff;
|
|
--good:#768700;
|
|
--warn:#846400;
|
|
--bad:#c52d2a;
|
|
--shadow:0 8px 28px rgba(0,43,54,.14);
|
|
}
|
|
/* High contrast: black and white with no mid-greys, every pair at 7:1 (AAA) or more, and borders
|
|
that show. For reading, not for looks. */
|
|
:root[data-theme="contrast"] {
|
|
--bg:#000000;
|
|
--panel:#000000;
|
|
--panel2:#121212;
|
|
--raise:#333333;
|
|
--line:#9a9a9a;
|
|
--fg:#ffffff;
|
|
--dim:#ffffff;
|
|
--faint:#d6d6d6;
|
|
--accent:#8cc8ff;
|
|
--accent2:#ffd400;
|
|
--ink:#000000;
|
|
--good:#7ee787;
|
|
--warn:#ffd400;
|
|
--bad:#ff9a8f;
|
|
--shadow:0 0 0 1px #9a9a9a;
|
|
}
|
|
:root[data-theme="contrast"][data-mode="light"] {
|
|
--bg:#ffffff;
|
|
--panel:#ffffff;
|
|
--panel2:#f0f0f0;
|
|
--raise:#d6d6d6;
|
|
--line:#555555;
|
|
--fg:#000000;
|
|
--dim:#000000;
|
|
--faint:#303030;
|
|
--accent:#0033a0;
|
|
--accent2:#7a3d00;
|
|
--ink:#ffffff;
|
|
--good:#0a5c1c;
|
|
--warn:#6b4500;
|
|
--bad:#a30000;
|
|
--shadow:0 0 0 1px #555555;
|
|
}
|
|
/* 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:#3268c0; /* Aqua selection blue, a shade down so links clear AA on the source list */
|
|
--accent2:#2a5db0;
|
|
--ink:#ffffff;
|
|
--good:#237a23;
|
|
--warn:#9b5b00;
|
|
--bad:#c42b1c;
|
|
--shadow:0 4px 16px rgba(0,0,0,.28);
|
|
}
|
|
/* Glass, after Apple's Liquid Glass: Apple's system colours, on panels that let a soft coloured
|
|
wash show through. The hex values are what a panel reads as once blended, so the contrast check
|
|
still means something; the translucency and the wash are in the chrome at the end of the sheet. */
|
|
:root[data-theme="glass"] {
|
|
--bg:#0b0d14;
|
|
--panel:#161a24;
|
|
--panel2:#1f2430;
|
|
--raise:#2a3040;
|
|
--line:#343b4c;
|
|
--fg:#f5f5f7; /* Apple's label */
|
|
--dim:#aeaeb2; /* systemGray2 */
|
|
--faint:#98989f; /* systemGray, a shade up to clear AA over the wash */
|
|
--accent:#409cff; /* systemBlue's accessible dark variant; #0a84ff falls short as link text */
|
|
--accent2:#ff9f0a; /* systemOrange */
|
|
--ink:#000000;
|
|
--good:#30d158;
|
|
--warn:#ff9f0a;
|
|
--bad:#ff6961;
|
|
--shadow:0 10px 36px rgba(0,0,0,.45);
|
|
--glass-edge:rgba(255,255,255,.10);
|
|
--glass-hi:rgba(255,255,255,.26);
|
|
--glass-lo:rgba(255,255,255,.10);
|
|
--glass-sheen:rgba(255,255,255,.07);
|
|
--wash1:rgba(64,120,255,.30);
|
|
--wash2:rgba(175,82,222,.24);
|
|
--wash3:rgba(48,176,199,.20);
|
|
}
|
|
:root[data-theme="glass"][data-mode="light"] {
|
|
--bg:#eef1f7;
|
|
--panel:#ffffff;
|
|
--panel2:#f2f4f8;
|
|
--raise:#e4e8f0;
|
|
--line:#d1d5de;
|
|
--fg:#1d1d1f;
|
|
--dim:#515154;
|
|
--faint:#5a5a5f; /* systemGray, taken down to clear AA over the wash */
|
|
--accent:#0055aa; /* a shade under Apple's #0066cc, which falls short over the wash */
|
|
--accent2:#b25000; /* systemOrange taken down until white on it clears AA */
|
|
--ink:#ffffff;
|
|
--good:#248a3d;
|
|
--warn:#824200;
|
|
--bad:#b8000f;
|
|
--shadow:0 10px 36px rgba(30,50,90,.16);
|
|
--glass-edge:rgba(255,255,255,.65);
|
|
--glass-hi:rgba(255,255,255,.9);
|
|
--glass-lo:rgba(255,255,255,.45);
|
|
--glass-sheen:rgba(255,255,255,.5);
|
|
--wash1:rgba(64,120,255,.22);
|
|
--wash2:rgba(175,82,222,.16);
|
|
--wash3:rgba(48,176,199,.18);
|
|
}
|
|
*{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;
|
|
}
|
|
#applogo{display:flex;flex:none}
|
|
#applogo img{width:28px;height:28px;border-radius:7px}
|
|
/* The logo in the mode's own variant. Dark unless the page says light, as the palette is: with
|
|
Auto, data-mode arrives with the script, and until then the page is drawn dark. */
|
|
:root[data-mode="light"] .logo.ondark,:root:not([data-mode="light"]) .logo.onlight{display:none}
|
|
.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. */
|
|
/* The display's own intrusions -- the Dynamic Island and status bar along the top (#54), the
|
|
home indicator along the bottom, the notch at one side in landscape. viewport-fit=cover hands the page the whole screen, which is what a standalone
|
|
shell and an iOS home-screen app both give it, so the bars along the edges pay for them in
|
|
padding. A browser with its own chrome reports nought and nothing moves. */
|
|
:root{
|
|
--safe-t:env(safe-area-inset-top,0px);
|
|
--safe-b:env(safe-area-inset-bottom,0px);
|
|
--safe-l:env(safe-area-inset-left,0px);
|
|
--safe-r:env(safe-area-inset-right,0px);
|
|
}
|
|
#topbar{
|
|
display:flex;align-items:center;gap:10px;min-width:0;overflow:hidden;
|
|
/* Longhand, and it has to stay longhand: the minifier runs a calc() in a padding shorthand
|
|
into the value after it -- `calc(12px + var(--safe-r))7px` -- and the browser then throws
|
|
the whole declaration away, leaving the bar with no padding at all. */
|
|
padding-top:calc(7px + var(--safe-t));padding-bottom:7px;
|
|
padding-left:calc(12px + var(--safe-l));padding-right:calc(12px + var(--safe-r));
|
|
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:12px 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 being checked: a small spinner in place of its unread count (issue #37). */
|
|
.feed.scanning .badge{display:none}
|
|
.feed.scanning::after{
|
|
content:"";flex:none;width:12px;height:12px;margin:0 6px;border-radius:50%;
|
|
border:2px solid var(--line);border-top-color:var(--accent);animation:ipxspin .8s linear infinite;
|
|
}
|
|
@keyframes ipxspin{to{transform:rotate(360deg)}}
|
|
/* 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)}
|
|
/* Outlined, not filled: on --raise the warning and error colours fell short of AA in most light
|
|
themes, where on the row's own ground they clear it. */
|
|
.tag{
|
|
font-size:11px;font-weight:600;
|
|
padding:0 4px;border-radius:4px;border:1px solid;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)}
|
|
/* A selected row is --raise too, and the zero count's pill vanished into it. */
|
|
.sel .badge.zero{background:var(--bg)}
|
|
/* 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;overflow:hidden; /* the reader sliding on a swipe */
|
|
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)}
|
|
/* What a pull started, for a couple of seconds after letting go, under the top bar. */
|
|
#pullspin{position:fixed;left:50%;top:calc(58px + var(--safe-t));transform:translateX(-50%);z-index:55;
|
|
display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;font-size:12px;
|
|
color:var(--dim);background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow)}
|
|
#pullspin::before{content:"";width:12px;height:12px;border-radius:50%;
|
|
border:2px solid var(--line);border-top-color:var(--accent);animation:ipxspin .8s linear infinite}
|
|
/* 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:6px}
|
|
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-top:9px;padding-bottom:calc(9px + var(--safe-b));
|
|
padding-left:calc(16px + var(--safe-l));padding-right:calc(16px + var(--safe-r));
|
|
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}
|
|
/* A dialog with nothing to confirm closes from the corner, beside its title, not from a lone button
|
|
at the foot of a long card. */
|
|
.cardx{float:right;margin:-5px -6px 0 8px}
|
|
.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;
|
|
}
|
|
/* On --panel, not a toast's --raise: the error colours are tuned to read on the page's grounds. */
|
|
.toast.bad{border-color:var(--bad);color:var(--bad);background:var(--panel)}
|
|
@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-top:3px;padding-bottom:calc(3px + var(--safe-b));
|
|
padding-left:calc(14px + var(--safe-l));padding-right:calc(14px + var(--safe-r));
|
|
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;
|
|
}
|
|
/* Only the last bar along the bottom owes the indicator anything, and with a player open that
|
|
is the player, not this. Without :has() the worst of it is a gap above the player bar. */
|
|
body:has(#player.on) #status{padding-bottom:3px}
|
|
/* 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;padding-top:var(--safe-t);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;gap:8px;
|
|
padding-top:7px;padding-bottom:calc(7px + var(--safe-b));
|
|
padding-left:calc(10px + var(--safe-l));padding-right:calc(10px + var(--safe-r));
|
|
}
|
|
|
|
/* The feed list is reachable whether or not anything is playing. */
|
|
#burger{display:grid}
|
|
/* No logo on a phone: the bar has no room to spare for it, and no hover to show its version. */
|
|
#applogo{display:none}
|
|
#topbar{
|
|
gap:6px;padding-top:calc(6px + var(--safe-t));padding-bottom:6px;
|
|
padding-left:calc(8px + var(--safe-l));padding-right:calc(8px + var(--safe-r));
|
|
}
|
|
#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;padding-top:calc(12px + var(--safe-t))}
|
|
body.reading #detail{display:block}
|
|
#dback{display:inline-block;margin-bottom:10px}
|
|
/* Beside the reader while it is swiped (gestures.ts): the item next to it, with a strip of the
|
|
page's own background between them, so the list under the reader never shows (#52). */
|
|
#dpeek{position:fixed;top:0;bottom:0;left:0;width:calc(100% + 16px);z-index:38;background:var(--bg);pointer-events:none}
|
|
#dpeek>div{width:calc(100% - 16px);height:100%;overflow:hidden;padding:12px 16px 90px;padding-top:calc(12px + var(--safe-t));background:var(--panel)}
|
|
#dpeek.next>div{margin-left:16px}
|
|
#dpeek .encbox{margin:0 0 10px}
|
|
#dpeek.end{display:flex;align-items:center;padding-left:28px;color:var(--faint);font-size:13px}
|
|
#dpeek.end p{max-width:4.5em;margin:0}
|
|
/* Going back to the list from the first item: the list waits under a dimmer that lifts as the
|
|
reader, shadowed along its edge, is drawn off it. */
|
|
#dpeek.dim{width:100%;z-index:37;background:#000}
|
|
#detail.popping{box-shadow:-12px 0 32px rgba(0,0,0,.28)}
|
|
|
|
#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}
|
|
}
|
|
|
|
/* ---------- A phone, drawn the way iOS draws its own apps (#53) ---------- */
|
|
/* Filled shapes rather than outlines, corners that grow rounder as the shape grows and nest inside
|
|
each other, 44px to tap and 17px to read. The outlines stay where they are what makes a control
|
|
visible at all: the high-contrast theme, and anyone who has asked the system for more contrast.
|
|
--edge is the one switch for that. */
|
|
@media (max-width:820px){
|
|
:root{--edge:transparent}
|
|
:root[data-theme="contrast"]{--edge:var(--line)}
|
|
/* Glass rules its panels off with an edge of its own; the light along them (--glass-rim) stays. */
|
|
:root[data-theme="glass"],:root[data-theme="glass"][data-mode="light"]{--glass-edge:transparent}
|
|
}
|
|
@media (max-width:820px) and (prefers-contrast:more){
|
|
:root{--edge:var(--line)}
|
|
}
|
|
@media (max-width:820px){
|
|
/* The bars run into the page instead of being ruled off from it. */
|
|
#topbar{background:var(--bg);border-bottom-color:var(--edge);gap:8px}
|
|
#status{background:var(--bg);border-top-color:var(--edge)}
|
|
#player{border-top-color:var(--edge)}
|
|
.sidefoot{border-top-color:var(--edge)}
|
|
|
|
/* Buttons: filled and round. Hover is reset because Safari keeps it on whatever was last
|
|
tapped, which left an accent outline on it. */
|
|
.btn,.sidetools button,.sidefoot button,.tgroup,.toast,kbd,#logbox{border-color:var(--edge)}
|
|
.btn:hover,.sidetools button:hover,.sidefoot button:hover{border-color:var(--edge)}
|
|
.btn,.sidetools button,.sidefoot button{border-radius:999px;min-height:40px;padding-left:14px;padding-right:14px}
|
|
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px}
|
|
.btn.ico,#dback{min-width:40px;height:40px;padding:0 10px;display:inline-grid;place-items:center}
|
|
.btn.tiny{min-height:0;padding:2px 9px}
|
|
.iconbtn{width:40px;height:40px;border-radius:999px;background:var(--panel2)}
|
|
.tgroup{border-radius:999px}
|
|
.tgroup button+button,.tgroup button+a{border-left-color:var(--edge)}
|
|
.tgroup button,.tgroup a{min-width:36px;height:40px;padding:0 8px}
|
|
|
|
/* Text boxes filled, and at 16px or more: Safari zooms the page in on one any smaller. */
|
|
input[type=search],input[type=text],input[type=password],input[type=number],select{
|
|
font-size:17px;background:var(--panel2);border-color:var(--edge);border-radius:10px;padding:9px 12px;
|
|
}
|
|
#topbar #epSearch{font-size:16px;border-radius:999px;padding:9px 14px}
|
|
input:focus,select:focus{border-color:var(--accent)}
|
|
|
|
/* The feed's name as the page's large title, the way Podcasts heads a show. */
|
|
.fhead h2,.fhead.slim h2{font-size:26px;font-weight:700;letter-spacing:-.01em;line-height:1.2}
|
|
.fhead .sub{font-size:15px}
|
|
.acts{gap:8px}
|
|
.acts .btn{min-width:44px;height:44px;padding:0 13px;justify-content:center;font-size:15px}
|
|
|
|
/* Tabs as a segmented control: equal segments on one track, the chosen one lifted. */
|
|
.tabs{flex:1 1 100%;border-radius:10px;padding:2px}
|
|
.tabs button,.tabs a{flex:1;text-align:center;padding:7px 4px;border-radius:8px;font-size:14px}
|
|
.tabs button.on,.tabs a.on{box-shadow:0 1px 3px rgba(0,0,0,.22)}
|
|
|
|
/* Rows: taller, larger type, and a hairline between them that starts where the text does. */
|
|
.ep{padding:10px 8px;border-radius:10px;min-height:44px}
|
|
.ep.sel{border-color:var(--edge)}
|
|
.ep:hover:not(.sel){background-color:transparent}
|
|
.ep .t{font-size:17px}
|
|
.ep .line,.ep .date{font-size:15px}
|
|
.ep+.ep{background-image:linear-gradient(var(--line),var(--line));background-size:calc(100% - 64px) 1px;background-position:64px 0;background-repeat:no-repeat}
|
|
.ep.sel,.ep.sel+.ep{background-image:none}
|
|
.feed{padding:9px 10px;min-height:44px;border-radius:10px}
|
|
.places{border-bottom-color:var(--edge);padding-bottom:10px;margin-bottom:10px}
|
|
.feed .txt b{font-size:17px}
|
|
.feed .txt small{font-size:13px}
|
|
|
|
/* The reader. */
|
|
.dt{font-size:26px;font-weight:700;line-height:1.2;letter-spacing:-.01em;margin-bottom:8px}
|
|
.dmeta{font-size:15px;gap:10px}
|
|
.encbox{border-color:var(--edge);border-radius:14px;padding:8px 8px 8px 14px}
|
|
.dbody{font-size:17px;line-height:1.55}
|
|
|
|
/* Dialogs are sheets, up from the bottom. */
|
|
#modal{place-items:end stretch;padding:0}
|
|
.card,.card.wide{
|
|
width:100%;max-height:calc(100dvh - var(--safe-t) - 12px);border-color:var(--edge);border-radius:20px 20px 0 0;
|
|
padding:20px 16px calc(20px + var(--safe-b));animation:sheet .3s cubic-bezier(.2,.8,.2,1);
|
|
}
|
|
.card h3{font-size:22px}
|
|
.field label,.field .hint{font-size:13px}
|
|
.check{font-size:17px}
|
|
.toast{border-radius:14px}
|
|
}
|
|
@keyframes sheet{from{transform:translateY(100%)}}
|
|
|
|
/* ---------- 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,
|
|
:root[data-theme="classic"] .ep.sel .iconbtn:not(:hover){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}
|
|
|
|
/* ---------- Glass: after Apple's Liquid Glass ---------- */
|
|
/* Translucent panels over a coloured wash, blurred and saturated the way macOS and iOS do it.
|
|
Apple's glass also bends light at its edges; that needs an SVG displacement filter that only
|
|
Chromium applies to a backdrop, and only on a shape of fixed size, so it is left out (#43).
|
|
What is kept is the light: the rim catches it at the top-left edges and, fainter, the far ones,
|
|
as it passes through (#51). */
|
|
:root[data-theme="glass"]{--glass-rim:inset 1px 1px 0 var(--glass-hi),inset -1px -1px 0 var(--glass-lo)}
|
|
:root[data-theme="glass"] body{
|
|
font-family:-apple-system,BlinkMacSystemFont,system-ui,Inter,"Segoe UI",Roboto,sans-serif;
|
|
background:
|
|
radial-gradient(60% 55% at 8% 0%,var(--wash1),transparent),
|
|
radial-gradient(50% 50% at 92% 18%,var(--wash2),transparent),
|
|
radial-gradient(60% 60% at 60% 100%,var(--wash3),transparent),
|
|
var(--bg);
|
|
}
|
|
:root[data-theme="glass"] #sidebar,
|
|
:root[data-theme="glass"] #topbar,
|
|
:root[data-theme="glass"] #player,
|
|
:root[data-theme="glass"] #status,
|
|
:root[data-theme="glass"] #files,
|
|
:root[data-theme="glass"] #detail,
|
|
:root[data-theme="glass"] #dpeek>div,
|
|
:root[data-theme="glass"] .card,
|
|
:root[data-theme="glass"] .toast{
|
|
/* A sheen from the top-left corner, where the light comes from. It is a background layer rather
|
|
than a pseudo-element because the detail pane, the file list and the dialogs scroll, and an
|
|
absolutely placed layer inside a scroller scrolls away with the content. */
|
|
background:linear-gradient(135deg,var(--glass-sheen),transparent 45%),color-mix(in srgb,var(--panel) 70%,transparent);
|
|
-webkit-backdrop-filter:blur(24px) saturate(180%);
|
|
backdrop-filter:blur(24px) saturate(180%);
|
|
border-color:var(--glass-edge);
|
|
box-shadow:var(--glass-rim);
|
|
}
|
|
:root[data-theme="glass"] .toolbar,
|
|
:root[data-theme="glass"] .ephead{
|
|
/* Sticky, so the list scrolls under them: the one place the frosting has something to blur. */
|
|
background:color-mix(in srgb,var(--bg) 65%,transparent);
|
|
-webkit-backdrop-filter:blur(20px) saturate(180%);
|
|
backdrop-filter:blur(20px) saturate(180%);
|
|
}
|
|
:root[data-theme="glass"] .card,
|
|
:root[data-theme="glass"] .toast{box-shadow:var(--glass-rim),var(--shadow)}
|
|
:root[data-theme="glass"] .toast{border-radius:14px}
|
|
:root[data-theme="glass"] .toast.bad{border-color:var(--bad)}
|
|
/* A narrow screen slides the feed list over the page, so it keeps its shadow. */
|
|
@media (max-width:820px){
|
|
:root[data-theme="glass"] #sidebar{box-shadow:inset -1px 0 0 var(--glass-hi),var(--shadow)}
|
|
}
|
|
/* The card is the glass; a lighter scrim leaves something behind it to see through. */
|
|
:root[data-theme="glass"] #modal{background:rgba(0,0,0,.3)}
|
|
:root[data-theme="glass"] .tgroup,
|
|
:root[data-theme="glass"] .tabs{
|
|
background:color-mix(in srgb,var(--panel2) 60%,transparent);
|
|
border-color:var(--glass-edge);box-shadow:var(--glass-rim);
|
|
}
|
|
/* Rounder than the other themes; a phone has shapes of its own (#53), which these would outrank. */
|
|
@media (min-width:821px){
|
|
:root[data-theme="glass"] .card{border-radius:20px}
|
|
:root[data-theme="glass"] .tgroup,
|
|
:root[data-theme="glass"] .tabs{border-radius:12px}
|
|
:root[data-theme="glass"] .btn,
|
|
:root[data-theme="glass"] .sidetools button,
|
|
:root[data-theme="glass"] .sidefoot button{border-radius:10px}
|
|
}
|
|
:root[data-theme="glass"] .btn.primary{box-shadow:inset 0 1px 0 rgba(255,255,255,.35)}
|
|
/* On a phone the rim all round a small capsule or bar read as the outline #53 took away, so the
|
|
light only catches the top edge there, and the bars at the top and bottom have none. */
|
|
@media (max-width:820px){
|
|
:root[data-theme="glass"]{--glass-rim:inset 0 1px 0 var(--glass-lo)}
|
|
:root[data-theme="glass"] #topbar,
|
|
:root[data-theme="glass"] #status{box-shadow:none}
|
|
}
|
|
/* Someone who has asked the system for less transparency, or more contrast, gets solid panels. */
|
|
@media (prefers-reduced-transparency:reduce),(prefers-contrast:more){
|
|
:root[data-theme="glass"] body{background:var(--bg)}
|
|
:root[data-theme="glass"] #sidebar,
|
|
:root[data-theme="glass"] #topbar,
|
|
:root[data-theme="glass"] #player,
|
|
:root[data-theme="glass"] #status,
|
|
:root[data-theme="glass"] #files,
|
|
:root[data-theme="glass"] #detail,
|
|
:root[data-theme="glass"] #dpeek>div,
|
|
:root[data-theme="glass"] .card,
|
|
:root[data-theme="glass"] .toast,
|
|
:root[data-theme="glass"] .tgroup,
|
|
:root[data-theme="glass"] .tabs{background:var(--panel);-webkit-backdrop-filter:none;backdrop-filter:none;border-color:var(--line)}
|
|
:root[data-theme="glass"] .toolbar,
|
|
:root[data-theme="glass"] .ephead{background:var(--bg);-webkit-backdrop-filter:none;backdrop-filter:none}
|
|
:root[data-theme="glass"] #modal{background:rgba(0,0,0,.6)}
|
|
}
|