/* Inter, served by ipx itself (/inter.woff2) rather than a font CDN, so the page asks nothing of anyone else. One variable file covers every weight used here; italics are synthesized. Classic keeps Lucida Grande, the 2004 app's face. */ @font-face{font-family:Inter;src:url(/inter.woff2) format("woff2");font-weight:100 900;font-display:swap} /* Palette taken from the 2004 iPodderX icon: the silver device body, the blue screen, and the amber EQ bars. Hex values in comments are sampled straight from it. */ :root { --bg:#0e131b; /* the screen's navy (#314B74), taken right down */ --panel:#151c27; --panel2:#1c2431; --raise:#25303f; --line:#2c3849; --fg:#f5f5f5; /* #F5F5F5 device highlight */ --dim:#95a0b1; /* #95A0B1 straight from the icon's blue-grey */ --faint:#7e8b9c; /* lifted from the icon ramp until it clears AA at small sizes */ --accent:#92b2e6; /* #92B2E6 the screen blue */ --accent2:#f49e2c; /* #F49E2C the EQ bars */ --ink:#0e131b; /* text on an accent fill */ --good:#6fbf8b; --warn:#f49e2c; /* the amber doubles as the pending colour */ --bad:#e2705f; --shadow:0 8px 28px rgba(6,10,16,.55); } /* Every other theme overrides the same variables, under data-theme and data-mode. The page's script sets data-mode to light or dark, working Auto out from the system, so each theme has one block per variant here and none needs repeating under a media query. */ :root[data-theme="modern"][data-mode="light"] { --bg:#f2f4f7; --panel:#ffffff; /* #FFFFFF device body */ --panel2:#e9edf3; --raise:#dde3ec; --line:#d6d6d6; /* #D6D6D6 device edge */ --fg:#1a1a1a; /* #1A1A1A icon outline */ --dim:#606060; /* #606060 */ --faint:#6b6b6b; /* between the icon's #929292 and #606060, to clear AA on the page ground */ --accent:#2d5391; /* #2D5391 the deep screen blue reads better on white */ --accent2:#985e0a; /* the EQ amber, taken down until white on it clears AA */ --ink:#ffffff; --good:#2f7d4f; --warn:#985e0a; /* the same amber; the lighter one failed AA as text */ --bad:#b3402f; --shadow:0 8px 28px rgba(45,83,145,.14); } /* Dracula, and Alucard, its light half, from draculatheme.com/spec. The spec's comment colour (#6272A4, #6C664B) is too faint for small text on its own background, so --faint is lifted. */ :root[data-theme="dracula"] { --bg:#282a36; --panel:#21222c; --panel2:#343746; --raise:#44475a; /* selection */ --line:#414558; --fg:#f8f8f2; --dim:#c9cbd6; --faint:#9ea8c7; --accent:#bd93f9; /* purple */ --accent2:#ff79c6; /* pink */ --ink:#282a36; --good:#50fa7b; --warn:#ffb86c; --bad:#ff8080; --shadow:0 8px 28px rgba(0,0,0,.45); } :root[data-theme="dracula"][data-mode="light"] { --bg:#fffbeb; --panel:#f7f2df; --panel2:#efe9d3; --raise:#cfcfde; /* selection */ --line:#ddd6bd; --fg:#1f1f1f; --dim:#454137; --faint:#635d44; --accent:#644ac9; --accent2:#a3144d; --ink:#ffffff; --good:#14710a; --warn:#a34d14; --bad:#b83322; --shadow:0 8px 28px rgba(108,102,75,.18); } /* Material 3's baseline scheme: surface, the surface containers, outline and primary. */ :root[data-theme="material"] { --bg:#141218; /* surface */ --panel:#1d1b20; /* surface-container-low */ --panel2:#211f26; /* surface-container */ --raise:#36343b; /* surface-container-highest */ --line:#49454f; /* outline-variant */ --fg:#e6e0e9; /* on-surface */ --dim:#cac4d0; /* on-surface-variant */ --faint:#9a95a0; /* outline, a shade up to clear AA on the containers */ --accent:#d0bcff; /* primary */ --accent2:#efb8c8; /* tertiary */ --ink:#381e72; /* on-primary */ --good:#8fd6a0; --warn:#f2c46b; --bad:#f2b8b5; /* error */ --shadow:0 8px 28px rgba(0,0,0,.5); } :root[data-theme="material"][data-mode="light"] { --bg:#fef7ff; --panel:#f7f2fa; --panel2:#f3edf7; --raise:#e6e0e9; --line:#cac4d0; --fg:#1d1b20; --dim:#49454f; --faint:#67626c; /* outline (#79747E), a shade down to clear AA on the containers */ --accent:#6750a4; --accent2:#7d5260; --ink:#ffffff; --good:#2b6c3c; --warn:#7c5800; --bad:#b3261e; --shadow:0 8px 28px rgba(103,80,164,.14); } /* Adwaita, from libadwaita's own CSS variables: view, window and sidebar backgrounds, the blue accent, and its destructive, success and warning colours. */ :root[data-theme="adwaita"] { --bg:#1d1d20; /* view */ --panel:#2e2e32; /* sidebar, headerbar */ --panel2:#222226; /* window */ --raise:#3a3a3f; --line:#3f3f45; --fg:#ffffff; --dim:#c4c4c8; --faint:#a0a0a7; --accent:#81d0ff; --accent2:#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); } *{box-sizing:border-box} /* A rule that sets display beats the UA's [hidden], and several below do. */ [hidden]{display:none!important} html,body{height:100%} body{ margin:0;background:var(--bg);color:var(--fg); font:14.5px/1.55 Inter,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; display:grid;grid-template-rows:auto 1fr auto auto;overflow:hidden; /* iOS Safari's address bar collapses and expands without firing a resize, so 100vh is measured against whichever state happened to be current -- sized too tall while the bar is showing, which puts the topbar (the hamburger included) under Safari's own chrome, where a tap never reaches the page. Only a hard refresh reset it, forcing 100vh to be recomputed. 100dvh tracks the real visible viewport as the bar moves; the 100vh above is the fallback for a browser that does not know dvh. */ height:100vh;height:100dvh; } button{font:inherit;color:inherit;background:none;border:0;cursor:pointer} a{color:var(--accent)} /* Inset wherever a parent clips its overflow, or the toolbar and the feed list cut the ring off. */ :focus-visible{outline:2px solid var(--accent);outline-offset:2px} .tgroup button:focus-visible,.tgroup a:focus-visible,.feed:focus-visible,.place:focus-visible,.chev:focus-visible{outline-offset:-2px} @media (prefers-reduced-motion:reduce){ *,*::before,*::after{animation:none!important;transition:none!important} } ::-webkit-scrollbar{width:10px;height:10px} ::-webkit-scrollbar-thumb{background:var(--raise);border-radius:6px} ::-webkit-scrollbar-track{background:transparent} /* ---------- shell ---------- */ #shell{display:grid;grid-template-columns:290px 1fr;min-height:0;min-width:0;overflow:hidden} /* [ hides the feed list. A phone slides it over the page instead, so this is for wider screens. */ @media (min-width:821px){ body.nosb #shell{grid-template-columns:minmax(0,1fr)} body.nosb #sidebar{display:none} } #sidebar{ background:var(--panel);border-right:1px solid var(--line); display:flex;flex-direction:column;min-height:0; } .brand{display:flex;align-items:center;gap:9px;padding:14px 14px 10px} .brand .logo{ width:30px;height:30px;flex:none;object-fit:contain; } .brand h1{font-size:16px;margin:0;font-weight:650;letter-spacing:-.01em;color:var(--fg);flex:1} .iconbtn{ width:30px;height:30px;border-radius:8px;display:grid;place-items:center; color:var(--dim);flex:none; } .iconbtn:hover{background:var(--raise);color:var(--fg)} /* One toolbar across the window, as the original had: grouped buttons, search on the right. */ #topbar{ display:flex;align-items:center;gap:10px;padding:7px 12px;min-width:0;overflow:hidden; background:var(--panel);border-bottom:1px solid var(--line); } #topbar .grow{flex:1} #topbar #epSearch{width:260px;flex:none} .tgroup{display:flex;flex:none;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--panel2)} .tgroup button,.tgroup a{padding:5px 11px;min-width:34px;color:var(--dim);font-size:14px;line-height:1.3} .tgroup a{display:inline-grid;place-items:center} .tgroup button+button,.tgroup button+a{border-left:1px solid var(--line)} .tgroup button:hover:not(:disabled),.tgroup a:hover{background:var(--raise);color:var(--fg)} .tgroup button:disabled{opacity:.35;cursor:default} .sidetools button,.sidefoot button{ flex:1;background:var(--panel2);border:1px solid var(--line);border-radius:8px; padding:6px 8px;font-size:12.5px;color:var(--dim); } .sidetools button:hover,.sidefoot button:hover{border-color:var(--accent);color:var(--fg)} /* Settings and the log are housekeeping: they sit under the feeds, out of the way. */ .sidefoot{ display:flex;flex-direction:column;gap:7px;padding:8px 12px; border-top:1px solid var(--line);flex:none; } .sidefoot .row{display:flex;gap:6px} .who{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--faint)} .who span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .who button{flex:none;background:none;border:0;padding:2px 4px;color:var(--faint);text-decoration:underline} .who button:hover{color:var(--fg)} .sidefoot button{display:inline-flex;align-items:center;justify-content:center;gap:6px} .sidefoot i{font-style:normal;opacity:.75} .searchwrap{padding:0 12px 8px} input[type=search],input[type=text],input[type=password],input[type=number],select{ width:100%;background:var(--bg);border:1px solid var(--line);color:var(--fg); border-radius:8px;padding:7px 10px;font:inherit;font-size:13.5px; } input:focus,select:focus{outline:0;border-color:var(--accent)} /* The wider left gutter is the folder triangle's; everything in the list shifts with it, so the feeds still line up with the places above. */ #feedlist{overflow-y:auto;padding:0 8px 12px 16px;flex:1;min-height:0} .feed{ display:flex;gap:10px;align-items:center;padding:7px 8px;border-radius:9px; cursor:pointer;margin-bottom:1px;position:relative; } .feed:hover{background:var(--panel2)} .feed.sel{background:var(--raise)} /* A show sits under its folder's title, a size down, so an open folder reads as one. */ .feed.child{margin-left:11px} /* Pinned feeds, at the top of the list: a small pin before the name, and a rule under the last. */ .feed .fpin .i{width:10px;height:10px;margin-right:5px;vertical-align:-1px;color:var(--accent)} .feed.lastpin{margin-bottom:9px} .feed.lastpin::after{content:"";position:absolute;left:8px;right:8px;bottom:-5px;border-bottom:1px solid var(--line)} .feed.child .art{width:28px;height:28px;font-size:11px} /* Only a folder has a triangle, hung in the margin so every feed's art lines up with the places above it. The button is the row's full height and 24 px wide: a near miss used to open the folder's page, and 16 px left the triangle cramped against the art. */ .chev{ position:absolute;left:-16px;top:0;bottom:0;width:24px;display:grid;place-items:center; border-radius:4px;color:var(--faint); } .chev:hover{color:var(--fg)} .chev.bad,.chev.bad:hover{color:var(--bad)} /* A feed 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; grid-template-columns:minmax(0,1fr) 270px; grid-template-rows:minmax(90px,var(--listh,60%)) 7px 1fr; grid-template-areas:"list files" "grab grab" "detail detail"; } #list{grid-area:list;overflow:auto;padding:0 14px 10px;overscroll-behavior-y:contain} /* Pull-to-refresh's note (gestures.ts). overscroll-behavior keeps the browser's own pull, which reloads the page, from starting on top of it. */ #pulltip{display:flex;align-items:flex-end;justify-content:center;padding-bottom:6px;overflow:hidden; font-size:12px;color:var(--faint)} /* The selected item's files, beside the list, as the original's Files pane was. */ #files{grid-area:files;overflow-y:auto;padding:8px 12px;border-left:1px solid var(--line);background:var(--panel)} /* Nothing selected, or an item with no files: the list takes the width. */ #split:has(>#files[hidden]){grid-template-columns:minmax(0,1fr) 0} #files .encbox{margin-top:8px} #files .empty{padding:24px 0} .fhd{font-size:12px;color:var(--faint)} #grab{grid-area:grab;cursor:row-resize;background:var(--line)} #grab:hover{background:var(--accent)} #detail{grid-area:detail;overflow-y:auto;padding:16px 20px 28px;background:var(--panel);border-top:1px solid var(--line)} .dt{font-size:18px;font-weight:650;margin:0 0 5px;line-height:1.3;overflow-wrap:anywhere} .dmeta{color:var(--faint);font-size:12.5px;display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px} .dmeta .btn{padding:3px 9px;font-size:12px} #detail .encbox{margin:0 0 10px} .dbody{font-size:14.5px;line-height:1.65;overflow-wrap:anywhere;color:var(--fg)} .dbody img{max-width:100%;height:auto;border-radius:6px} .dbody a{color:var(--accent)} .dbody pre{overflow-x:auto;background:var(--panel2);padding:10px;border-radius:8px} .encbox{ display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:14px; padding:10px 12px;border:1px solid var(--line);border-radius:9px;background:var(--panel2); } .fhead{display:flex;gap:18px;margin-bottom:18px} .fhead .art{width:118px;height:118px;font-size:34px;box-shadow:var(--shadow)} .fhead .meta{min-width:0;flex:1;display:flex;flex-direction:column} .fhead h2{margin:0 0 3px;font-size:23px;line-height:1.2;overflow-wrap:anywhere} .fhead .sub{color:var(--dim);font-size:13px;margin-bottom:8px} .fhead .sub a{color:var(--dim);text-decoration:none} .fhead .sub a:hover{color:var(--accent)} .acts{display:flex;gap:7px;flex-wrap:wrap;align-items:center;margin-top:auto} .acts .btn{display:inline-flex;align-items:center;gap:6px;padding:7px 13px;border-radius:999px} .acts .btn i{font-style:normal;font-size:13px;line-height:1;opacity:.7} .acts .btn.primary i{opacity:.9} .acts .btn:hover{background:var(--raise)} .acts .btn.primary:hover{background:var(--accent)} .btn{ background:var(--panel2);border:1px solid var(--line);border-radius:8px; padding:6px 12px;font-size:13px; } .btn:hover{border-color:var(--accent)} a.btn{text-decoration:none;color:inherit} .btn.primary{background:var(--accent);border-color:var(--accent);color:var(--ink);font-weight:600} .btn.primary:hover{filter:brightness(1.08)} .btn.danger:hover{border-color:var(--bad);color:var(--bad)} /* An icon in place of a word; the word is in its tooltip. */ .btn.ico{padding:4px 9px;min-width:32px;font-size:14px;line-height:1.25;text-align:center} /* Inline in a sentence, next to a plain-word error explanation. */ .btn.tiny{padding:2px 7px;font-size:11.5px;border-radius:6px;margin-left:2px} /* The keys ? lists. */ .keys{border-collapse:collapse;width:100%;font-size:13px} .keys th{text-align:left;font-weight:600;padding:12px 0 4px;color:var(--fg)} .keys td{padding:3px 0;color:var(--dim)} .keys td:first-child{width:10em;white-space:nowrap} kbd{font:inherit;font-size:12px;color:var(--fg);background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:0 5px} /* An icon (Font Awesome, embedded as SVG) in the button's own colour. */ .i{display:inline-block;width:16px;height:16px;vertical-align:-3px;flex:none;fill:currentColor} /* A file's type as an icon, in place of the old DOWNLOADED / PENDING / audio chips: green once it is here, red when the download failed. */ .kind{display:inline-grid;place-items:center;color:var(--dim)} .kind.here{color:var(--good)} .subbed{display:inline-flex;padding:0 9px;color:var(--good)} .subbed .i{width:18px;height:18px} .kind.bad{color:var(--bad)} .fhead .art .i{width:22px;height:22px} .toolbar{ display:flex;gap:10px;align-items:center;margin-bottom:12px;flex-wrap:wrap; position:sticky;top:0;background:var(--bg);padding:6px 0 8px;z-index:3; } .tabs{display:flex;gap:2px;background:var(--panel2);border-radius:9px;padding:3px} .tabs button{padding:5px 12px;border-radius:7px;font-size:13px;color:var(--dim)} .tabs button.on{background:var(--raise);color:var(--fg);font-weight:500} .tabs a{padding:5px 12px;border-radius:7px;font-size:13px;color:var(--dim);text-decoration:none} .tabs a.on{background:var(--raise);color:var(--fg);font-weight:500} /* ---------- the admin page (admin.html) ---------- */ /* One column that scrolls under the same header, rather than the app's fixed panes. */ body.adminpage{display:block;overflow:auto} body.adminpage #topbar{position:sticky;top:0;z-index:5} body.adminpage #topbar h1{font-size:16px;margin:0;font-weight:650} body.adminpage #topbar .logo{border-radius:5px} body.adminpage #topbar > a.btn{text-decoration:none} body.adminpage #admin{max-width:780px;margin:0 auto} body.adminpage #admin h2{font-size:18px;margin:0 0 12px} body.adminpage #log{max-width:none} .urow{padding:9px 0;border-bottom:1px solid var(--line)} .toolbar .grow{flex:1;min-width:150px;max-width:320px} /* ---------- items ---------- */ /* A table, as the original's was: unread, kept, title, feed, file, size, published. */ .ephead,.ep{ display:grid;gap:8px;align-items:center; grid-template-columns:22px 22px minmax(120px,1fr) minmax(0,160px) 56px minmax(0,72px) minmax(0,92px) 64px; } /* A pixel more side padding than a row's: a row has a 1px border the heading has not, and without it every heading sat a pixel left of its column. */ .ephead{ position:sticky;top:0;z-index:2;background:var(--bg);padding:7px 11px 5px; border-bottom:1px solid var(--line);margin-bottom:3px; font-size:12px;color:var(--faint); } /* Each heading sorts by its column; the caret says which way. */ .ephead .hs{display:flex;align-items:center;gap:4px;min-width:0;padding:0;border:0;background:none; font:inherit;color:inherit;text-transform:inherit;letter-spacing:inherit;text-align:left;cursor:pointer} .ephead .hs:hover,.ephead .hs.on{color:var(--fg)} .ephead .hs .arr{display:inline-flex} .ephead .hs .arr .i{width:8px;height:8px} .ephead .hs .arr.asc{transform:rotate(-90deg)} .ephead .hs .arr.desc{transform:rotate(90deg)} /* An icon heading is centred over its column, as the icons under it are, and its caret hangs outside so sorting by it does not push the icon off line. */ .ephead .hs.h-ic{justify-content:center;position:relative;width:100%} .ephead .hs.h-ic .arr{position:absolute;left:100%} .ep{padding:4px 10px;border-radius:7px;border:1px solid transparent;cursor:pointer;margin-bottom:1px} .ep>*{min-width:0} .ep.sel{background:var(--raise);border-color:var(--line)} .ep:hover{background:var(--panel)} /* Amber means new: the unread dot, the badges and a download under way. Blue is for the one primary action and links, so a count no longer looks like a button. */ /* No padding: a button keeps the browser's side padding, which left too little room for the 16px icon, so it spilled 3px right of centre and off line with its heading. */ .ep .st,.ep .fl{width:22px;height:22px;padding:0;border-radius:5px;display:grid;place-items:center;font-size:11px;color:var(--accent2)} .ep .fl{color:var(--faint);font-size:13px} .ep .fl.on{color:var(--fg)} /* The icon's EQ bars mark what is playing: standing still, and moving only while it plays. A paused animation was tried first; the frames it held were a pixel apart and read as dots. */ .eq{display:inline-flex;align-items:flex-end;gap:2px;width:13px;height:12px;flex:none} .eq i{flex:1;height:100%;background:currentColor;border-radius:1px;transform-origin:bottom} .eq i:nth-child(1){height:60%} .eq i:nth-child(3){height:40%} body.playing .eq i{animation:eq .9s ease-in-out infinite alternate} body.playing .eq i:nth-child(1){animation-delay:-.3s} body.playing .eq i:nth-child(3){animation-delay:-.6s} @keyframes eq{from{transform:scaleY(.35)}} .ep .st:hover,.ep .fl:hover{background:var(--raise)} .ep .t{font-weight:600;font-size:13.5px;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .ep.read .t{color:var(--dim);font-weight:500} .ep .line{display:flex;gap:9px;align-items:center;flex-wrap:wrap;color:var(--faint);font-size:11.5px} .ep .line:empty{display:none} .ep .fd,.ep .size,.ep .date{color:var(--dim);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .ep .file{display:flex;gap:6px;align-items:center;position:relative;color:var(--faint);font-size:11.5px} /* Under the icon, not beside it or on a line of its own: taking a line, it lifted the icon of every file not yet downloaded above the downloaded ones' (issue #31). */ .ep .file .dlbar{position:absolute;left:0;right:0;bottom:-5px;margin:0} .ep .rowacts{justify-content:flex-end} /* One feed's own table has no need of a Feed column; All Subscriptions does. */ #split.one .ephead,#split.one .ep{grid-template-columns:22px 22px minmax(120px,1fr) 56px minmax(0,72px) minmax(0,92px) 64px} #split.one .h-fd,#split.one .ep .fd{display:none} .dot{width:3px;height:3px;border-radius:50%;background:var(--faint);flex:none} .ep .rowacts{display:flex;gap:2px;align-items:flex-start;opacity:0;transition:opacity .12s} .ep:hover .rowacts{opacity:1} .dlbar{height:3px;background:transparent;border-radius:2px;overflow:hidden;margin-top:7px} .dlbar.live{background:var(--raise)} .dlbar i{display:block;height:100%;width:0;background:var(--accent2);transition:width .25s} .empty{color:var(--faint);text-align:center;padding:50px 0} #more{display:block;width:100%;margin-top:10px} /* ---------- player ---------- */ #player{ border-top:1px solid var(--line);background:var(--panel); display:none;grid-template-columns:auto 1fr auto;gap:14px;align-items:center; padding:9px 16px;box-shadow:0 -6px 24px rgba(6,10,16,.4); } #player.on{display:grid} /* #audio is a