4ed2d59 drew a pressed toggle in each theme's accent colour; the themes'
colours were not to change. Back as they were, pinned rows included. The
read button carries its state in its shape instead, as the pin does with
outline and solid: a tick when read, the envelope when not, where before
it showed the action (the envelope on a read item).
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)}
|
|
}
|