diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..4005562
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,186 @@
+# Agent Guide: marstemedia
+
+This document is for AI agents working on this codebase. Read it before touching anything.
+
+---
+
+## What is this project?
+
+**marstemedia** is a cross-platform desktop (+ Android) news reader app built with:
+- **Tauri v2** as the native shell
+- **Yew** (Rust → WebAssembly) for the UI
+- **Rust** for all backend logic (networking, file I/O, cryptography)
+
+It has three features:
+1. **Nostr feed** — anonymous browsing of decentralized social posts, filterable by hashtag
+2. **RSS reader** — fetches and displays articles from a user-configurable list of RSS/Atom feeds
+3. **AI news** — one-click tech news summary from Groq (Llama 3) or OpenRouter
+
+---
+
+## Repository Layout
+
+```
+marstemedia/
+├── src/ # Frontend (Yew/Wasm)
+│ ├── main.rs # Wasm bootstrap
+│ ├── app.rs # Router + top-level layout
+│ ├── navbar/
+│ │ ├── mod.rs
+│ │ └── navbar.rs # Bottom navigation dock
+│ └── pages/
+│ ├── mod.rs
+│ ├── home.rs # Nostr feed page
+│ ├── news.rs # RSS + AI news page
+│ └── greet.rs # Placeholder/demo page
+│
+├── src-tauri/ # Backend (native Rust)
+│ ├── Cargo.toml
+│ ├── tauri.conf.json # App config (window size, build commands)
+│ ├── capabilities/
+│ │ └── default.json # Tauri capability permissions
+│ └── src/
+│ ├── main.rs # Desktop executable entry
+│ ├── lib.rs # Tauri setup, command registration
+│ ├── home.rs # Nostr: fetch_nostr_posts command
+│ └── news.rs # RSS + AI: all news commands + NewsState
+│ └── easy-nostr/ # Local path-dependency (nostr-sdk wrapper)
+│
+├── Cargo.toml # Frontend workspace root
+├── styles.css # All CSS for the app
+├── index.html # Trunk entry point
+├── Trunk.toml # Trunk (Wasm bundler) config
+├── justfile # Dev shortcuts
+└── .gitea/workflows/
+ └── android.yaml # CI: builds Android APK
+```
+
+---
+
+## How to Add a New Page
+
+1. Create `src/pages/yourpage.rs` as a Yew `#[function_component]`.
+2. Export it in `src/pages/mod.rs`: `pub mod yourpage;`
+3. Add a variant to the `Route` enum in `src/app.rs`:
+ ```rust
+ #[at("/yourpage")]
+ YourPage,
+ ```
+4. Add a match arm in the `switch` function in `src/app.rs`:
+ ```rust
+ Route::YourPage => html! { },
+ ```
+5. Add a `>` in `src/navbar/navbar.rs`.
+
+---
+
+## How to Add a New Backend Command
+
+1. Write your async function in the appropriate module (`src-tauri/src/home.rs`, `news.rs`, or a new file).
+2. Annotate it with `#[tauri::command]`.
+3. If you need shared state, add a field to `NewsState` in `src-tauri/src/news.rs`, or create a new state struct and call `.manage()` in `lib.rs`.
+4. Register the command in `src-tauri/src/lib.rs` inside `tauri::generate_handler![...]`.
+5. In the frontend, call it with `invoke("your_command_name", args)`.
+
+---
+
+## How to Call a Backend Command from the Frontend
+
+Every page that needs backend calls declares this extern at the top:
+
+```rust
+#[wasm_bindgen]
+extern "C" {
+ #[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
+ async fn invoke(cmd: &str, args: JsValue) -> Result;
+}
+```
+
+Serialize args with `serde_wasm_bindgen::to_value(...)`, deserialize results with `serde_wasm_bindgen::from_value(...)`. Use `spawn_local` to run async calls inside Yew callbacks.
+
+---
+
+## Key Patterns to Know
+
+### State management (frontend)
+Yew uses `use_state` hooks. There is no global store — state lives inside components. If two pages need to share data, consider lifting state to `app.rs` or using Tauri's managed state on the backend.
+
+### State management (backend)
+`NewsState` in `src-tauri/src/news.rs` is registered with `.manage()` and injected into commands via `State<'_, NewsState>`. All fields are `Mutex` — always lock, read/write, and drop the lock quickly. **API keys are in-memory only** (not saved to disk).
+
+### Config persistence
+RSS feeds are saved to `/rss.ron` using the `ron` crate. Access the config path via `app.path().app_config_dir()`. Pattern is: read on load, write on save, keep in-memory state in sync.
+
+### Markdown rendering
+The `News` page renders article content as Markdown using `pulldown-cmark`. The output is injected with `Html::from_html_unchecked`. Only use this for content you control or have sanitized.
+
+### Privacy pattern (Nostr)
+A new random keypair is generated on every `fetch_nostr_posts` call. This is intentional — do not change this to a persistent key without understanding the privacy implications.
+
+---
+
+## CSS
+
+`styles.css` in the project root is the single entry point — it only contains `@import` statements. All actual styles are split into files under `assets/`:
+
+| File | What's inside |
+|---|---|
+| `assets/variables.css` | CSS custom properties (colors, fonts), reset, `body`/`html` |
+| `assets/layout.css` | `.feed-container`, responsive media queries |
+| `assets/navbar.css` | `.navbar-dock`, `.nav-link` |
+| `assets/header.css` | `.feed-header`, `.mode-toggle`, `.toggle-btn`, `.action-btn`, `.provider-switch` |
+| `assets/cards.css` | `.post-card`, `.markdown-body`, `.post-category-badge` |
+| `assets/config.css` | `.config-panel`, RSS list, inputs, `.add-btn`, `.delete-btn`, `.save-master-btn` |
+| `assets/filters.css` | Hashtag chips (Home), `.category-bar`, `.cat-btn` (News) |
+| `assets/animations.css` | `@keyframes float`, `slideIn`, `pulse-subtle` |
+| `assets/utils.css` | `.reload-btn-large`, `.error-box`, `.loading-spinner`, `.status-msg` |
+| `assets/greet.css` | `.robot-container`, `.robot-icon`, `.bubble` |
+
+When adding styles for a new page, create a new file in `assets/` and add an `@import` line to `styles.css`. There is no CSS framework — vanilla CSS with custom classes.
+
+---
+
+## Build & Dev
+
+```bash
+# Start dev server (hot reload)
+cargo tauri dev
+
+# Build for desktop release
+cargo tauri build
+
+# Build for Android (requires Android SDK + NDK)
+cargo tauri android build
+
+# Frontend only (no Tauri shell)
+trunk serve
+```
+
+The `justfile` may contain additional shortcuts — check it first.
+
+---
+
+## Dependencies Worth Knowing
+
+| Crate | Used for |
+|---|---|
+| `yew` | UI components |
+| `yew-router` | Client-side routing |
+| `wasm-bindgen` / `wasm-bindgen-futures` | Wasm ↔ JS bridge |
+| `serde-wasm-bindgen` | Serialize/deserialize across the Tauri IPC |
+| `pulldown-cmark` | Markdown → HTML in the frontend |
+| `nostr-sdk` | Nostr protocol (via `easy-nostr` wrapper) |
+| `reqwest` | HTTP client (rustls, no OpenSSL) |
+| `feed-rs` | RSS/Atom feed parsing |
+| `ron` | Config file serialization |
+| `tauri-plugin-opener` | Open URLs in system browser |
+
+---
+
+## What to Watch Out For
+
+- **`easy-nostr`** is a local path dependency at `src-tauri/easy-nostr/`. It is also a private Git repo cloned by CI. If you need to update it, clone it manually into that path.
+- **No OpenSSL** — both `reqwest` usages (frontend and backend) use `rustls-tls` with `default-features = false`. Keep it that way to avoid cross-compilation pain on Android/CI.
+- **`wasm32-unknown-unknown`** target is required for the frontend. `aarch64-linux-android` and friends are required for Android CI.
+- The `Greet` page (`src/pages/greet.rs`) appears to be a leftover demo page from the Tauri template. It is still linked in the navbar — decide whether to keep or remove it.
+- API keys entered by the user are held in `Mutex` fields in `NewsState`. They are lost when the app restarts. There is no persistence for keys by design.
diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md
new file mode 100644
index 0000000..fccc2bc
--- /dev/null
+++ b/ARCHITECTURE.md
@@ -0,0 +1,174 @@
+# Architecture: marstemedia
+
+marstemedia is a desktop (and Android) application built with **Tauri v2**. The UI is written in Rust compiled to WebAssembly using **Yew**, and the backend logic runs as native Rust code inside the Tauri host process.
+
+---
+
+## Big Picture
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Tauri Desktop App │
+│ │
+│ ┌──────────────────────┐ IPC (invoke) ┌──────────┐ │
+│ │ Frontend (Wasm) │ ──────────────► │ Backend │ │
+│ │ Yew / Rust │ ◄────────────── │ Rust │ │
+│ │ src/ │ JSON results │ src-tauri│ │
+│ └──────────────────────┘ └──────────┘ │
+└─────────────────────────────────────────────────────────┘
+ ▲ renders inside a webview (WebKit/Edge)
+```
+
+The frontend never touches the network directly for heavy operations — it sends named commands via `window.__TAURI__.core.invoke()` to the backend, which does the actual networking, file I/O, and cryptography.
+
+---
+
+## Frontend (`src/`)
+
+Built with **Yew 0.21** (React-style component model for Rust/Wasm) and **yew-router** for client-side routing.
+
+### Entry point
+
+| File | Role |
+|---|---|
+| `src/main.rs` | Bootstraps the Wasm app, mounts the `App` component |
+| `src/app.rs` | Defines the `Route` enum and the top-level layout (router + navbar) |
+
+### Routes
+
+| Path | Component | File |
+|---|---|---|
+| `/` | `Home` | `src/pages/home.rs` |
+| `/news` | `News` | `src/pages/news.rs` |
+| `/greet` | `Greet` | `src/pages/greet.rs` |
+
+### Components
+
+- **`Navbar`** (`src/navbar/navbar.rs`) — Fixed bottom dock with links to all three routes. Reads the current route via `use_route` to highlight the active link.
+- **`Home`** (`src/pages/home.rs`) — Nostr feed. Manages hashtag filter state, invokes `fetch_nostr_posts` on load and on user interaction, renders posts as `PostCard` components.
+- **`News`** (`src/pages/news.rs`) — Dual-mode news page. Toggles between AI mode (Groq / OpenRouter) and RSS mode. Has a collapsible config panel for API keys and RSS feed management. Renders articles as `NewsCard` with Markdown support via `pulldown-cmark`.
+
+### How the frontend talks to the backend
+
+Every backend call uses the `invoke` wasm-bindgen extern:
+
+```rust
+#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
+async fn invoke(cmd: &str, args: JsValue) -> Result;
+```
+
+Arguments are serialized with `serde-wasm-bindgen` and results are deserialized back into typed Rust structs.
+
+---
+
+## Backend (`src-tauri/src/`)
+
+Native Rust code that runs in the Tauri host process. Registered commands are the only API surface the frontend can call.
+
+### Entry point
+
+| File | Role |
+|---|---|
+| `src-tauri/src/main.rs` | Desktop executable entry point, calls `run()` |
+| `src-tauri/src/lib.rs` | Configures Tauri: registers `NewsState`, registers all commands |
+
+### Modules
+
+#### `home.rs` — Nostr
+
+Handles the `fetch_nostr_posts` command.
+
+1. Generates a fresh, random throwaway keypair on every call (privacy by design — no persistent identity).
+2. Creates an `EasyNostr` client using the temp key (via the local `easy-nostr` library).
+3. Connects to three relays: `relay.damus.io`, `nos.lol`, `relay.snort.social`.
+4. Fetches posts by hashtag list, or random posts if no tags are given.
+5. Maps results to the serializable `LocalPost` struct and returns JSON.
+
+#### `news.rs` — RSS & AI News
+
+Manages `NewsState` (held in Tauri's managed state, shared across calls):
+
+```
+NewsState {
+ openrouter_key: Mutex
+ groq_key: Mutex
+ rss_config: Mutex
+}
+```
+
+**Commands:**
+
+| Command | What it does |
+|---|---|
+| `load_rss_config` | Reads `rss.ron` from the app config dir; writes 16 default feeds if the file is missing or has fewer than 3 entries |
+| `save_rss_urls` | Serializes the feed list to `rss.ron` and updates in-memory state |
+| `save_openrouter_key` | Stores the key in `NewsState.openrouter_key` (in-memory only, not persisted to disk) |
+| `save_groq_key` | Same for the Groq key |
+| `fetch_ai_news` | POSTs to Groq or OpenRouter chat completions API; asks for a short tech news summary in German Markdown |
+| `fetch_rss_news` | Iterates the in-memory feed list, fetches each feed with `reqwest`, parses with `feed-rs`, takes up to 3 articles per feed, returns `Vec` |
+
+### Local library: `easy-nostr`
+
+Located at `src-tauri/easy-nostr/` (path dependency). Wraps `nostr-sdk` to provide high-level helpers like `get_random_posts()` and `get_posts_by_hashtags()`. Written by the same author (Bytemalte).
+
+---
+
+## Configuration & Persistence
+
+| What | Where | Format |
+|---|---|---|
+| RSS feed list | `/rss.ron` | RON |
+| API keys | In-memory (`NewsState`) — lost on restart | — |
+| App window config | `src-tauri/tauri.conf.json` | JSON |
+
+API keys are intentionally not persisted to disk. The user re-enters them each session via the config panel.
+
+---
+
+## Build System
+
+| Tool | Role |
+|---|---|
+| `trunk` | Builds and bundles the Yew/Wasm frontend (`trunk build` / `trunk serve`) |
+| `tauri-cli` | Wraps the full build and produces the native app bundle |
+| `Trunk.toml` | Trunk configuration (output dir `dist/`, used by Tauri) |
+| `justfile` | Developer shortcuts |
+
+The `tauri.conf.json` wires them together:
+```
+beforeDevCommand: trunk serve (starts dev server on :1420)
+beforeBuildCommand: trunk build (builds Wasm)
+frontendDist: ../dist (Tauri embeds this)
+```
+
+### CI / Android
+
+`.gitea/workflows/android.yaml` runs on push to `main` or version tags. It:
+1. Clones `easy-nostr` as a private submodule.
+2. Installs Rust with four Android targets + `wasm32-unknown-unknown`.
+3. Installs Trunk (binary) and `tauri-cli` (cargo).
+4. Sets up Android SDK + NDK 25.
+5. Runs `cargo tauri android build`.
+
+---
+
+## Data Flow Summary
+
+```
+User action
+ │
+ ▼
+Yew component (state update)
+ │ invoke("command_name", args_json)
+ ▼
+Tauri IPC bridge
+ │
+ ▼
+Backend Rust function
+ │ network / file I/O
+ ▼
+External service (Nostr relay / RSS feed / AI API / disk)
+ │ serialized result
+ ▼
+Yew component re-renders
+```
diff --git a/assets/animations.css b/assets/animations.css
new file mode 100644
index 0000000..93fc707
--- /dev/null
+++ b/assets/animations.css
@@ -0,0 +1,37 @@
+/* --- Animations --- */
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px);
+ }
+
+ 50% {
+ transform: translateY(-15px);
+ }
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes pulse-subtle {
+ 0% {
+ box-shadow: 0 0 0 0 rgba(74, 144, 226, 0.4);
+ }
+
+ 70% {
+ box-shadow: 0 0 0 10px rgba(74, 144, 226, 0);
+ }
+
+ 100% {
+ box-shadow: 0 0 0 0 rgba(74, 144, 226, 0);
+ }
+}
diff --git a/assets/cards.css b/assets/cards.css
new file mode 100644
index 0000000..3a6e633
--- /dev/null
+++ b/assets/cards.css
@@ -0,0 +1,50 @@
+/* --- Post & News Cards --- */
+.post-card {
+ background: var(--card-bg);
+ border: 1px solid var(--card-border);
+ border-radius: 18px;
+ padding: 16px;
+ margin-bottom: 16px;
+ word-wrap: break-word;
+}
+
+.post-author {
+ color: var(--accent-color);
+ font-weight: 800;
+ font-size: 0.85rem;
+}
+
+.post-meta {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.post-category-badge {
+ font-size: 0.7rem;
+ font-weight: 700;
+ color: var(--accent-color);
+ background: rgba(74, 144, 226, 0.12);
+ border: 1px solid rgba(74, 144, 226, 0.25);
+ padding: 2px 8px;
+ border-radius: 10px;
+}
+
+/* --- Markdown Content --- */
+.markdown-body {
+ font-size: 0.95rem;
+ color: var(--text-primary);
+ line-height: 1.6;
+}
+
+.markdown-body h3 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ font-size: 1.15rem;
+ color: #ffffff;
+ font-weight: 700;
+}
+
+.markdown-body p {
+ margin: 0;
+}
diff --git a/assets/config.css b/assets/config.css
new file mode 100644
index 0000000..c161791
--- /dev/null
+++ b/assets/config.css
@@ -0,0 +1,196 @@
+/* --- Config Panel --- */
+.config-panel {
+ background: rgba(30, 30, 46, 0.6);
+ border: 1px solid rgba(74, 144, 226, 0.3);
+ border-radius: 20px;
+ padding: 20px;
+ margin-bottom: 30px;
+ box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
+ animation: slideIn 0.3s ease-out;
+}
+
+@supports (backdrop-filter: blur(20px)) {
+ .config-panel {
+ backdrop-filter: blur(20px);
+ }
+}
+
+.config-section {
+ margin-bottom: 20px;
+}
+
+.config-section label {
+ display: block;
+ font-size: 0.7rem;
+ color: var(--text-secondary);
+ margin-bottom: 6px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.config-input {
+ flex: 1;
+ padding: 14px 16px;
+ background: rgba(0, 0, 0, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: white;
+ border-radius: 12px;
+ font-size: 16px;
+ font-family: inherit;
+ transition: border-color 0.2s;
+}
+
+.config-input:focus {
+ outline: none;
+ border-color: var(--accent-color);
+}
+
+/* --- RSS Feed List --- */
+.rss-list-scroll {
+ max-height: 180px;
+ overflow-y: auto;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 12px;
+ padding: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ margin-bottom: 16px;
+}
+
+.rss-url-entry {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 14px;
+ background: rgba(255, 255, 255, 0.03);
+ border-radius: 10px;
+ margin-bottom: 6px;
+ font-size: 0.85rem;
+ border: 1px solid transparent;
+ transition: all 0.2s;
+}
+
+.rss-url-entry:hover {
+ background: rgba(255, 255, 255, 0.05);
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+.url-text {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-right: 12px;
+ color: var(--text-secondary);
+}
+
+.cat-badge {
+ font-size: 0.7rem;
+ font-weight: 700;
+ color: var(--accent-color);
+ background: rgba(74, 144, 226, 0.12);
+ border: 1px solid rgba(74, 144, 226, 0.25);
+ padding: 2px 8px;
+ border-radius: 10px;
+ white-space: nowrap;
+ margin: 0 8px;
+ flex-shrink: 0;
+}
+
+/* --- RSS Add Row --- */
+.rss-input-row {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 16px;
+}
+
+.rss-add-row {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.cat-select {
+ background: rgba(0, 0, 0, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: white;
+ border-radius: 12px;
+ padding: 14px 10px;
+ font-size: 0.85rem;
+ font-family: inherit;
+ cursor: pointer;
+ flex-shrink: 0;
+ width: 130px;
+ transition: border-color 0.2s;
+}
+
+.cat-select:focus {
+ outline: none;
+ border-color: var(--accent-color);
+}
+
+.cat-select option {
+ background: #1e1e2e;
+ color: white;
+}
+
+/* --- Buttons --- */
+.add-btn {
+ background: var(--accent-color);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ width: 50px;
+ min-width: 50px;
+ cursor: pointer;
+ font-weight: 700;
+ font-size: 1.4rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+}
+
+.add-btn:active {
+ transform: scale(0.9);
+}
+
+.delete-btn {
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.2);
+ color: var(--danger);
+ cursor: pointer;
+ padding: 8px 12px;
+ border-radius: 8px;
+ font-weight: 700;
+ font-size: 0.8rem;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 44px;
+ min-height: 36px;
+}
+
+.delete-btn:active {
+ transform: scale(0.95);
+}
+
+.delete-btn:hover {
+ background: var(--danger);
+ color: white;
+}
+
+.save-master-btn {
+ background: linear-gradient(135deg, #4a90e2, #357abd);
+ color: white;
+ border: none;
+ padding: 16px;
+ border-radius: 12px;
+ cursor: pointer;
+ width: 100%;
+ font-weight: 700;
+ font-size: 1rem;
+ user-select: none;
+ min-height: 48px;
+ margin-top: 10px;
+}
diff --git a/assets/filters.css b/assets/filters.css
new file mode 100644
index 0000000..93c357a
--- /dev/null
+++ b/assets/filters.css
@@ -0,0 +1,102 @@
+/* --- Hashtag Filter (Home page) --- */
+.hashtag-section {
+ margin-bottom: 24px;
+}
+
+.hashtag-input-row {
+ display: flex;
+ gap: 10px;
+}
+
+.hashtag-input {
+ flex: 1;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 12px 16px;
+ color: white;
+ font-size: 0.95rem;
+ outline: none;
+ transition: all 0.2s;
+}
+
+.hashtag-input:focus {
+ border-color: var(--accent-color);
+ background: rgba(255, 255, 255, 0.08);
+}
+
+.tag-chips {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-top: 12px;
+}
+
+.tag-chip {
+ background: rgba(74, 144, 226, 0.15);
+ color: var(--accent-color);
+ border: 1px solid rgba(74, 144, 226, 0.3);
+ padding: 4px 12px;
+ border-radius: 20px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ transition: all 0.2s;
+}
+
+.tag-chip:hover {
+ background: rgba(74, 144, 226, 0.25);
+ transform: scale(1.05);
+}
+
+.tag-remove {
+ font-size: 1.1rem;
+ line-height: 1;
+ opacity: 0.6;
+}
+
+.tag-chip:hover .tag-remove {
+ opacity: 1;
+}
+
+/* --- Category Filter Bar (News page) --- */
+.category-bar {
+ display: flex;
+ gap: 8px;
+ overflow-x: auto;
+ padding-bottom: 12px;
+ margin-bottom: 20px;
+ scrollbar-width: none;
+}
+
+.category-bar::-webkit-scrollbar {
+ display: none;
+}
+
+.cat-btn {
+ flex-shrink: 0;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: var(--text-secondary);
+ padding: 8px 16px;
+ border-radius: 20px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ font-weight: 600;
+ white-space: nowrap;
+ transition: all 0.2s ease;
+}
+
+.cat-btn.active {
+ background: var(--accent-color);
+ border-color: var(--accent-color);
+ color: white;
+}
+
+.cat-btn:hover:not(.active) {
+ background: rgba(255, 255, 255, 0.1);
+ color: var(--text-primary);
+}
diff --git a/assets/greet.css b/assets/greet.css
index e69de29..3edaf86 100644
--- a/assets/greet.css
+++ b/assets/greet.css
@@ -0,0 +1,36 @@
+/* --- Greet Page --- */
+.robot-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ margin: 40px auto;
+ width: 100%;
+ user-select: none;
+}
+
+.robot-icon {
+ font-size: 5rem;
+ filter: drop-shadow(0 0 20px rgba(74, 144, 226, 0.3));
+ animation: float 3s ease-in-out infinite;
+}
+
+.bubble {
+ margin-top: 20px;
+ padding: 16px 24px;
+ background: rgba(30, 30, 46, 0.8);
+ border-radius: 20px;
+ border: 1px solid rgba(255, 173, 210, 0.3);
+ color: #ffffff;
+ font-weight: 600;
+ font-size: 1rem;
+ text-align: center;
+ max-width: 90%;
+}
+
+@supports (backdrop-filter: blur(12px)) {
+ .bubble {
+ backdrop-filter: blur(12px);
+ }
+}
diff --git a/assets/header.css b/assets/header.css
new file mode 100644
index 0000000..15d8175
--- /dev/null
+++ b/assets/header.css
@@ -0,0 +1,97 @@
+/* --- Feed Header & Controls --- */
+.feed-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ gap: 16px;
+}
+
+.header-main {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+}
+
+.header-main h1 {
+ margin: 0;
+ font-size: 1.5rem;
+}
+
+.header-actions {
+ display: flex;
+ gap: 8px;
+ align-items: center;
+}
+
+/* --- Mode Toggle (AI / RSS) --- */
+.mode-toggle {
+ display: flex;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 12px;
+ padding: 3px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.toggle-btn {
+ border: none;
+ background: transparent;
+ color: #8e8e93;
+ padding: 8px 16px;
+ border-radius: 9px;
+ cursor: pointer;
+ font-size: 0.8rem;
+ font-weight: 700;
+ user-select: none;
+ min-height: 36px;
+}
+
+.toggle-btn.active {
+ background: var(--accent-color);
+ color: white;
+}
+
+/* --- Action Buttons (reload, settings) --- */
+.action-btn {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ padding: 12px;
+ cursor: pointer;
+ font-size: 1.1rem;
+ user-select: none;
+ min-width: 44px;
+ min-height: 44px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* --- AI Provider Switch --- */
+.provider-switch {
+ display: flex;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 12px;
+ padding: 3px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ margin: 0 10px;
+}
+
+.prov-btn {
+ border: none;
+ background: transparent;
+ color: var(--text-secondary);
+ padding: 6px 14px;
+ border-radius: 9px;
+ cursor: pointer;
+ font-size: 0.75rem;
+ font-weight: 700;
+ transition: all 0.2s ease;
+}
+
+.prov-btn.active {
+ background: var(--accent-color);
+ color: white;
+}
diff --git a/assets/layout.css b/assets/layout.css
new file mode 100644
index 0000000..57f64a6
--- /dev/null
+++ b/assets/layout.css
@@ -0,0 +1,13 @@
+/* --- Layout --- */
+.feed-container {
+ width: 100%;
+ max-width: 600px;
+ padding: calc(20px + env(safe-area-inset-top)) 16px
+ calc(120px + env(safe-area-inset-bottom)) 16px;
+}
+
+@media (min-width: 768px) {
+ .feed-container {
+ padding: 40px 20px 150px 20px;
+ }
+}
diff --git a/assets/navbar.css b/assets/navbar.css
index e69de29..c6c769a 100644
--- a/assets/navbar.css
+++ b/assets/navbar.css
@@ -0,0 +1,48 @@
+/* --- Navigation Dock --- */
+.navbar-dock {
+ position: fixed;
+ bottom: calc(20px + env(safe-area-inset-bottom));
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: var(--nav-bg);
+ padding: 12px 24px;
+ border-radius: 50px;
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ display: flex;
+ gap: 20px;
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
+ z-index: 1000;
+ width: max-content;
+ max-width: 90%;
+}
+
+@supports (backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px)) {
+ .navbar-dock {
+ background-color: rgba(30, 30, 46, 0.85);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ }
+}
+
+.nav-link {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-weight: 600;
+ font-size: 0.9rem;
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ padding: 10px 12px;
+ user-select: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 44px;
+}
+
+.nav-link:hover {
+ color: #ffffff;
+ transform: translateY(-3px);
+}
+
+.nav-link.active {
+ color: var(--accent-color);
+}
diff --git a/assets/utils.css b/assets/utils.css
new file mode 100644
index 0000000..0ea2aa4
--- /dev/null
+++ b/assets/utils.css
@@ -0,0 +1,65 @@
+/* --- Utility & State Classes --- */
+.reload-btn-large {
+ cursor: pointer;
+ background: linear-gradient(
+ 135deg,
+ rgba(74, 144, 226, 0.15),
+ rgba(74, 144, 226, 0.05)
+ );
+ color: var(--accent-color);
+ padding: 16px;
+ border-radius: 16px;
+ width: 100%;
+ font-weight: 700;
+ border: 1px solid rgba(74, 144, 226, 0.3);
+ user-select: none;
+ min-height: 52px;
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+}
+
+.reload-btn-large:hover {
+ background: rgba(74, 144, 226, 0.2);
+ border-color: var(--accent-color);
+ box-shadow: 0 6px 20px rgba(74, 144, 226, 0.2);
+ transform: translateY(-2px);
+}
+
+.reload-btn-large:active {
+ transform: scale(0.96) translateY(0);
+}
+
+.error-box {
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.3);
+ color: var(--danger);
+ padding: 15px;
+ border-radius: 12px;
+ text-align: center;
+}
+
+.loading-spinner {
+ text-align: center;
+ padding: 40px;
+ color: var(--text-secondary);
+ font-style: italic;
+ animation: pulse-subtle 2s infinite;
+}
+
+.empty-msg {
+ text-align: center;
+ color: var(--text-secondary);
+ margin-top: 40px;
+ font-size: 0.9rem;
+ opacity: 0.8;
+}
+
+.status-msg {
+ text-align: center;
+ color: var(--text-secondary);
+ padding: 40px 16px;
+}
diff --git a/assets/variables.css b/assets/variables.css
new file mode 100644
index 0000000..309b5f2
--- /dev/null
+++ b/assets/variables.css
@@ -0,0 +1,40 @@
+/* --- Root Variables & Reset --- */
+:root {
+ --bg-color: #0f0f13;
+ --card-bg: rgba(255, 255, 255, 0.03);
+ --card-border: rgba(255, 255, 255, 0.08);
+ --text-primary: #e2e2e7;
+ --text-secondary: #a0a0b0;
+ --accent-color: #4a90e2;
+ --nav-bg: rgba(30, 30, 46, 0.95);
+ --danger: #ef4444;
+}
+
+* {
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: transparent;
+}
+
+body {
+ background-color: var(--bg-color);
+ color: var(--text-primary);
+ font-family:
+ "Inter",
+ -apple-system,
+ BlinkMacSystemFont,
+ "Segoe UI",
+ Roboto,
+ sans-serif;
+ margin: 0;
+ line-height: 1.5;
+ display: flex;
+ justify-content: center;
+ min-height: 100vh;
+ overflow-x: hidden;
+ -webkit-tap-highlight-color: transparent;
+}
+
+html {
+ scroll-behavior: smooth;
+ overflow-x: hidden;
+}
diff --git a/justfile b/justfile
new file mode 100644
index 0000000..a5049fe
--- /dev/null
+++ b/justfile
@@ -0,0 +1,12 @@
+default:
+ just --list
+
+run:
+ cargo tauri dev
+
+build:
+ cargo tauri build --apk
+
+check:
+ cargo check
+ cargo clippy
diff --git a/styles.css b/styles.css
index 833b5f2..d3cd12c 100644
--- a/styles.css
+++ b/styles.css
@@ -1,690 +1,10 @@
-@import url("assets/home.css");
-@import url("assets/news.css");
+@import url("assets/variables.css");
+@import url("assets/animations.css");
+@import url("assets/layout.css");
+@import url("assets/navbar.css");
+@import url("assets/header.css");
+@import url("assets/cards.css");
+@import url("assets/config.css");
+@import url("assets/filters.css");
+@import url("assets/utils.css");
@import url("assets/greet.css");
-
-/* --- Root Variables & Reset --- */
-:root {
- --bg-color: #0f0f13;
- --card-bg: rgba(255, 255, 255, 0.03);
- --card-border: rgba(255, 255, 255, 0.08);
- --text-primary: #e2e2e7;
- --text-secondary: #a0a0b0;
- --accent-color: #4a90e2;
- --nav-bg: rgba(30, 30, 46, 0.95);
- /* Fallback for blur */
- --danger: #ef4444;
-}
-
-* {
- box-sizing: border-box;
- -webkit-tap-highlight-color: transparent;
- /* UX-Finishing: No blue flash */
-}
-
-/* --- Global & Layout --- */
-body {
- background-color: var(--bg-color);
- color: var(--text-primary);
- font-family:
- "Inter",
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Roboto,
- sans-serif;
- margin: 0;
- line-height: 1.5;
- display: flex;
- justify-content: center;
- min-height: 100vh;
- overflow-x: hidden;
- /* Performance: Prevent horizontal scroll */
- -webkit-tap-highlight-color: transparent;
-}
-
-html {
- scroll-behavior: smooth;
- overflow-x: hidden;
-}
-
-.feed-container {
- width: 100%;
- max-width: 600px;
- /* Safe Areas: Top inset and mobile-friendly padding */
- padding: calc(20px + env(safe-area-inset-top)) 16px
- calc(120px + env(safe-area-inset-bottom)) 16px;
-}
-
-@media (min-width: 768px) {
- .feed-container {
- padding: 40px 20px 150px 20px;
- }
-}
-
-/* --- Navigation Dock --- */
-.navbar-dock {
- position: fixed;
- /* Safe Areas: Bottom inset */
- bottom: calc(20px + env(safe-area-inset-bottom));
- left: 50%;
- transform: translateX(-50%);
- background-color: var(--nav-bg);
- padding: 12px 24px;
- border-radius: 50px;
- border: 1px solid rgba(255, 255, 255, 0.1);
- display: flex;
- gap: 20px;
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
- z-index: 1000;
- width: max-content;
- max-width: 90%;
-}
-
-@supports (backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px)) {
- .navbar-dock {
- background-color: rgba(30, 30, 46, 0.85);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- }
-}
-
-.nav-link {
- color: var(--text-secondary);
- text-decoration: none;
- font-weight: 600;
- font-size: 0.9rem;
- transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
- /* Touch-Ergonomie: Min 44px effective height */
- padding: 10px 12px;
- user-select: none;
- /* UX-Finishing */
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 44px;
-}
-
-.nav-link:hover {
- color: #ffffff;
- transform: translateY(-3px);
-}
-
-.nav-link.active {
- color: var(--accent-color);
-}
-
-/* --- Header & Navigation --- */
-.feed-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 24px;
- flex-wrap: wrap;
- gap: 16px;
-}
-
-.header-main h1 {
- margin: 0;
- font-size: 1.5rem;
-}
-
-.mode-toggle {
- display: flex;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 12px;
- padding: 3px;
- border: 1px solid rgba(255, 255, 255, 0.08);
-}
-
-.toggle-btn {
- border: none;
- background: transparent;
- color: #8e8e93;
- padding: 8px 16px;
- border-radius: 9px;
- cursor: pointer;
- font-size: 0.8rem;
- font-weight: 700;
- user-select: none;
- min-height: 36px;
-}
-
-.toggle-btn.active {
- background: var(--accent-color);
- color: white;
-}
-
-.action-btn {
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 12px;
- padding: 12px;
- cursor: pointer;
- font-size: 1.1rem;
- user-select: none;
- min-width: 44px;
- min-height: 44px;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-/* --- Config Panel --- */
-.config-panel {
- background: rgba(30, 30, 46, 0.6);
- border: 1px solid rgba(74, 144, 226, 0.3);
- border-radius: 20px;
- padding: 20px;
- margin-bottom: 30px;
- box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
- animation: slideIn 0.3s ease-out;
-}
-
-@supports (backdrop-filter: blur(20px)) {
- .config-panel {
- backdrop-filter: blur(20px);
- }
-}
-
-.config-input {
- flex: 1;
- padding: 14px 16px;
- background: rgba(0, 0, 0, 0.3);
- border: 1px solid rgba(255, 255, 255, 0.1);
- color: white;
- border-radius: 12px;
- /* Input-Fix: Prevent Android Auto-Zoom */
- font-size: 16px;
- font-family: inherit;
- transition: border-color 0.2s;
-}
-
-.config-input:focus {
- outline: none;
- border-color: var(--accent-color);
-}
-
-.rss-input-row {
- display: flex;
- gap: 10px;
- margin-bottom: 16px;
-}
-
-.add-btn {
- background: var(--accent-color);
- color: white;
- border: none;
- border-radius: 12px;
- width: 50px;
- min-width: 50px;
- cursor: pointer;
- font-weight: 700;
- font-size: 1.4rem;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
-}
-
-.add-btn:active {
- transform: scale(0.9);
-}
-
-.rss-list-scroll {
- max-height: 180px;
- overflow-y: auto;
- background: rgba(0, 0, 0, 0.2);
- border-radius: 12px;
- padding: 8px;
- border: 1px solid rgba(255, 255, 255, 0.05);
- margin-bottom: 16px;
-}
-
-.rss-url-entry {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 10px 14px;
- background: rgba(255, 255, 255, 0.03);
- border-radius: 10px;
- margin-bottom: 6px;
- font-size: 0.85rem;
- border: 1px solid transparent;
- transition: all 0.2s;
-}
-
-.rss-url-entry:hover {
- background: rgba(255, 255, 255, 0.05);
- border-color: rgba(255, 255, 255, 0.1);
-}
-
-.url-text {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin-right: 12px;
- color: var(--text-secondary);
-}
-
-.delete-btn {
- background: rgba(239, 68, 68, 0.1);
- border: 1px solid rgba(239, 68, 68, 0.2);
- color: var(--danger);
- cursor: pointer;
- padding: 8px 12px;
- border-radius: 8px;
- font-weight: 700;
- font-size: 0.8rem;
- transition: all 0.2s ease;
- display: flex;
- align-items: center;
- justify-content: center;
- min-width: 44px;
- min-height: 36px;
-}
-
-.delete-btn:active {
- transform: scale(0.95);
-}
-
-.delete-btn:hover {
- background: var(--danger);
- color: white;
-}
-
-.save-master-btn {
- background: linear-gradient(135deg, #4a90e2, #357abd);
- color: white;
- border: none;
- padding: 16px;
- border-radius: 12px;
- cursor: pointer;
- width: 100%;
- font-weight: 700;
- font-size: 1rem;
- user-select: none;
- min-height: 48px;
- margin-top: 10px;
-}
-
-/* --- Post Cards --- */
-.post-card {
- background: var(--card-bg);
- border: 1px solid var(--card-border);
- border-radius: 18px;
- padding: 16px;
- margin-bottom: 16px;
- word-wrap: break-word;
-}
-
-.post-author {
- color: var(--accent-color);
- font-weight: 800;
- font-size: 0.85rem;
-}
-
-/* --- Greet Page --- */
-.robot-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- margin: 40px auto;
- width: 100%;
- user-select: none;
-}
-
-.robot-icon {
- font-size: 5rem;
- filter: drop-shadow(0 0 20px rgba(74, 144, 226, 0.3));
- animation: float 3s ease-in-out infinite;
-}
-
-.bubble {
- margin-top: 20px;
- padding: 16px 24px;
- background: rgba(30, 30, 46, 0.8);
- border-radius: 20px;
- border: 1px solid rgba(255, 173, 210, 0.3);
- color: #ffffff;
- font-weight: 600;
- font-size: 1rem;
- text-align: center;
- max-width: 90%;
-}
-
-@supports (backdrop-filter: blur(12px)) {
- .bubble {
- backdrop-filter: blur(12px);
- }
-}
-
-/* --- Animations --- */
-@keyframes float {
- 0%,
- 100% {
- transform: translateY(0px);
- }
-
- 50% {
- transform: translateY(-15px);
- }
-}
-
-@keyframes slideIn {
- from {
- opacity: 0;
- transform: translateY(-10px);
- }
-
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* --- Utilities --- */
-.reload-btn-large {
- cursor: pointer;
- background: linear-gradient(
- 135deg,
- rgba(74, 144, 226, 0.15),
- rgba(74, 144, 226, 0.05)
- );
- color: var(--accent-color);
- padding: 16px;
- border-radius: 16px;
- width: 100%;
- font-weight: 700;
- border: 1px solid rgba(74, 144, 226, 0.3);
- user-select: none;
- min-height: 52px;
- transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
-}
-
-.reload-btn-large:hover {
- background: rgba(74, 144, 226, 0.2);
- border-color: var(--accent-color);
- box-shadow: 0 6px 20px rgba(74, 144, 226, 0.2);
- transform: translateY(-2px);
-}
-
-.reload-btn-large:active {
- transform: scale(0.96) translateY(0);
-}
-
-/* Optional: Add a subtle pulse animation when content is old */
-@keyframes pulse-subtle {
- 0% {
- box-shadow: 0 0 0 0 rgba(74, 144, 226, 0.4);
- }
-
- 70% {
- box-shadow: 0 0 0 10px rgba(74, 144, 226, 0);
- }
-
- 100% {
- box-shadow: 0 0 0 0 rgba(74, 144, 226, 0);
- }
-}
-
-.error-box {
- background: rgba(239, 68, 68, 0.1);
- border: 1px solid rgba(239, 68, 68, 0.3);
- color: var(--danger);
- padding: 15px;
- border-radius: 12px;
- text-align: center;
-}
-
-/* --- AI Provider Switch (Neu) --- */
-.provider-switch {
- display: flex;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 12px;
- padding: 3px;
- border: 1px solid rgba(255, 255, 255, 0.08);
- /* Zwischen h1 und den Icons positionieren */
- margin: 0 10px;
-}
-
-.prov-btn {
- border: none;
- background: transparent;
- color: var(--text-secondary);
- padding: 6px 14px;
- border-radius: 9px;
- cursor: pointer;
- font-size: 0.75rem;
- font-weight: 700;
- transition: all 0.2s ease;
-}
-
-.prov-btn.active {
- background: var(--accent-color);
- color: white;
-}
-
-/* --- Config Section Refinement --- */
-.config-section {
- margin-bottom: 20px;
-}
-
-.config-section label {
- display: block;
- font-size: 0.7rem;
- color: var(--text-secondary);
- margin-bottom: 6px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
-}
-
-/* --- Markdown Content Styling --- */
-.markdown-body {
- font-size: 0.95rem;
- color: var(--text-primary);
- line-height: 1.6;
-}
-
-.markdown-body h3 {
- margin-top: 0;
- margin-bottom: 10px;
- font-size: 1.15rem;
- color: #ffffff;
- font-weight: 700;
-}
-
-.markdown-body p {
- margin: 0;
-}
-
-/* --- Status Indicators --- */
-.loading-spinner {
- text-align: center;
- padding: 40px;
- color: var(--text-secondary);
- font-style: italic;
- animation: pulse-subtle 2s infinite;
-}
-
-.empty-msg {
- text-align: center;
- color: var(--text-secondary);
- margin-top: 40px;
- font-size: 0.9rem;
- opacity: 0.8;
-}
-
-/* --- Hashtag Section --- */
-.hashtag-section {
- margin-bottom: 24px;
-}
-
-.hashtag-input-row {
- display: flex;
- gap: 10px;
-}
-
-.hashtag-input {
- flex: 1;
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 12px;
- padding: 12px 16px;
- color: white;
- font-size: 0.95rem;
- outline: none;
- transition: all 0.2s;
-}
-
-.hashtag-input:focus {
- border-color: var(--accent-color);
- background: rgba(255, 255, 255, 0.08);
-}
-
-.tag-chips {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 12px;
-}
-
-.tag-chip {
- background: rgba(74, 144, 226, 0.15);
- color: var(--accent-color);
- border: 1px solid rgba(74, 144, 226, 0.3);
- padding: 4px 12px;
- border-radius: 20px;
- font-size: 0.85rem;
- font-weight: 600;
- cursor: pointer;
- display: flex;
- align-items: center;
- gap: 6px;
- transition: all 0.2s;
-}
-
-.tag-chip:hover {
- background: rgba(74, 144, 226, 0.25);
- transform: scale(1.05);
-}
-
-.tag-remove {
- font-size: 1.1rem;
- line-height: 1;
- opacity: 0.6;
-}
-
-.tag-chip:hover .tag-remove {
- opacity: 1;
-}
-
-/* --- Layout Adjustment für Provider Switch --- */
-.header-main {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
-}
-
-/* --- Category filter --- */
-/* ── Kategorie-Filterleiste ── */
-.category-bar {
- display: flex;
- gap: 8px;
- overflow-x: auto;
- padding-bottom: 12px;
- margin-bottom: 20px;
- scrollbar-width: none;
-}
-.category-bar::-webkit-scrollbar {
- display: none;
-}
-
-.cat-btn {
- flex-shrink: 0;
- background: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.1);
- color: var(--text-secondary);
- padding: 8px 16px;
- border-radius: 20px;
- cursor: pointer;
- font-size: 0.8rem;
- font-weight: 600;
- white-space: nowrap;
- transition: all 0.2s ease;
-}
-.cat-btn.active {
- background: var(--accent-color);
- border-color: var(--accent-color);
- color: white;
-}
-.cat-btn:hover:not(.active) {
- background: rgba(255, 255, 255, 0.1);
- color: var(--text-primary);
-}
-
-/* ── Kategorie-Badge im Feed-Eintrag ── */
-.cat-badge {
- font-size: 0.7rem;
- font-weight: 700;
- color: var(--accent-color);
- background: rgba(74, 144, 226, 0.12);
- border: 1px solid rgba(74, 144, 226, 0.25);
- padding: 2px 8px;
- border-radius: 10px;
- white-space: nowrap;
- margin: 0 8px;
- flex-shrink: 0;
-}
-
-/* ── Kategorie-Badge auf NewsCard ── */
-.post-meta {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.post-category-badge {
- font-size: 0.7rem;
- font-weight: 700;
- color: var(--accent-color);
- background: rgba(74, 144, 226, 0.12);
- border: 1px solid rgba(74, 144, 226, 0.25);
- padding: 2px 8px;
- border-radius: 10px;
-}
-
-/* ── Neue URL-Zeile mit Kategorie-Dropdown ── */
-.rss-add-row {
- display: flex;
- gap: 8px;
- align-items: center;
- margin-bottom: 16px;
-}
-.cat-select {
- background: rgba(0, 0, 0, 0.3);
- border: 1px solid rgba(255, 255, 255, 0.1);
- color: white;
- border-radius: 12px;
- padding: 14px 10px;
- font-size: 0.85rem;
- font-family: inherit;
- cursor: pointer;
- flex-shrink: 0;
- width: 130px;
- transition: border-color 0.2s;
-}
-.cat-select:focus {
- outline: none;
- border-color: var(--accent-color);
-}
-.cat-select option {
- background: #1e1e2e;
- color: white;
-}