diff --git a/cmd/kafka/main.go b/cmd/kafka/main.go index 29ab620..68fa1dd 100644 --- a/cmd/kafka/main.go +++ b/cmd/kafka/main.go @@ -19,6 +19,7 @@ package main import ( "flag" "fmt" + "io/fs" "log" "log/slog" "net/http" @@ -30,7 +31,7 @@ import ( "github.com/metamorphosis-dev/kafka/internal/httpapi" "github.com/metamorphosis-dev/kafka/internal/middleware" "github.com/metamorphosis-dev/kafka/internal/search" - "github.com/metamorphosis-dev/kafka/internal/spa" + "github.com/metamorphosis-dev/kafka/internal/views" ) func main() { @@ -80,15 +81,23 @@ func main() { mux := http.NewServeMux() - // API routes - handled by Go - mux.HandleFunc("/healthz", h.Healthz) + // HTML template routes + mux.HandleFunc("/", h.Index) mux.HandleFunc("/search", h.Search) + mux.HandleFunc("/preferences", h.Preferences) + + // API routes + mux.HandleFunc("/healthz", h.Healthz) mux.HandleFunc("/autocompleter", h.Autocompleter) mux.HandleFunc("/opensearch.xml", h.OpenSearch(cfg.Server.BaseURL)) - // SPA handler - serves React app for all other routes - spaHandler := spa.NewHandler() - mux.Handle("/", spaHandler) + // Serve embedded static files (CSS, JS, images). + staticFS, err := views.StaticFS() + if err != nil { + log.Fatalf("failed to load static files: %v", err) + } + var subFS fs.FS = staticFS + mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(subFS)))) // Apply middleware: global rate limit → burst rate limit → per-IP rate limit → CORS → security headers → handler. var handler http.Handler = mux diff --git a/internal/views/static/css/kafka.css b/internal/views/static/css/kafka.css index ac740d8..3b501bd 100644 --- a/internal/views/static/css/kafka.css +++ b/internal/views/static/css/kafka.css @@ -1,9 +1,8 @@ /* ============================================================ - kafka — modern, clean search UI + kafka — clean, minimal search UI ============================================================ */ :root { - /* Light theme */ --bg: #ffffff; --bg-secondary: #f8f9fa; --bg-tertiary: #f1f3f5; @@ -12,38 +11,17 @@ --text-primary: #1a1a1a; --text-secondary: #5c6370; --text-muted: #8b929e; - --accent: #2563eb; - --accent-hover: #1d4ed8; - --accent-soft: #eff6ff; - --accent-glow: rgba(37, 99, 235, 0.15); - --shadow-sm: 0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04); - --shadow-md: 0 4px 12px rgba(0,0,0,0.08), 0 2px 4px rgba(0,0,0,0.04); - --shadow-lg: 0 12px 32px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.06); + --accent: #0d9488; + --accent-hover: #0f766e; + --accent-soft: #f0fdfa; + --shadow-sm: 0 1px 3px rgba(0,0,0,0.06); + --shadow-md: 0 4px 12px rgba(0,0,0,0.08); --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 9999px; - --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif; - --max-width: 880px; - --header-height: 56px; -} - -[data-theme="dark"] { - --bg: #0f0f0f; - --bg-secondary: #1a1a1a; - --bg-tertiary: #242424; - --border: #2e2e2e; - --border-focus: #404040; - --text-primary: #e8eaed; - --text-secondary: #9aa0a6; - --text-muted: #6b7280; - --accent: #60a5fa; - --accent-hover: #93c5fd; - --accent-soft: #1e3a5f; - --accent-glow: rgba(96, 165, 250, 0.2); - --shadow-sm: 0 1px 3px rgba(0,0,0,0.3); - --shadow-md: 0 4px 12px rgba(0,0,0,0.4); - --shadow-lg: 0 12px 32px rgba(0,0,0,0.5); + --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-mono: "IBM Plex Mono", ui-monospace, monospace; } *, *::before, *::after { @@ -64,7 +42,6 @@ body { line-height: 1.5; min-height: 100vh; -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } /* ============================================================ @@ -75,15 +52,13 @@ body { position: sticky; top: 0; z-index: 100; - height: var(--header-height); + height: 56px; display: flex; align-items: center; justify-content: space-between; padding: 0 1.5rem; background: var(--bg); border-bottom: 1px solid var(--border); - backdrop-filter: blur(12px); - background: color-mix(in srgb, var(--bg) 90%, transparent); } .site-logo { @@ -95,33 +70,16 @@ body { } .site-logo-mark { - width: 28px; - height: 28px; + width: 24px; + height: 24px; color: var(--accent); } .site-name { + font-family: var(--font-mono); font-size: 1.1rem; - font-weight: 600; - letter-spacing: -0.01em; -} - -.settings-trigger { - background: none; - border: none; - width: 36px; - height: 36px; - border-radius: var(--radius-sm); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - color: var(--text-secondary); - transition: background 0.15s, color 0.15s; -} -.settings-trigger:hover { - background: var(--bg-tertiary); - color: var(--text-primary); + font-weight: 700; + letter-spacing: -0.02em; } /* ============================================================ @@ -129,45 +87,64 @@ body { ============================================================ */ main { - max-width: var(--max-width); + max-width: 800px; margin: 0 auto; padding: 0 1.5rem; } /* ============================================================ - Homepage — Hero Search + Homepage ============================================================ */ .page-home { - min-height: calc(100vh - var(--header-height)); + min-height: calc(100vh - 56px); display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 4rem 1.5rem; +} + +.home-container { + width: 100%; + max-width: 640px; text-align: center; } -.hero-logo { - margin-bottom: 2rem; +.home-logo { + display: inline-flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; + color: var(--text-primary); + margin-bottom: 1rem; } -.hero-logo svg { - width: 64px; - height: 64px; +.home-logo svg { + width: 48px; + height: 48px; color: var(--accent); } -.hero-tagline { - font-size: 1rem; - color: var(--text-muted); - margin-bottom: 2.5rem; - max-width: 400px; +.home-logo-text { + font-family: var(--font-mono); + font-size: 2rem; + font-weight: 700; + letter-spacing: -0.02em; } -.search-hero { +.home-tagline { + color: var(--text-muted); + margin-bottom: 2rem; + font-size: 0.95rem; +} + +/* ============================================================ + Search Form + ============================================================ */ + +.search-form { width: 100%; - max-width: 640px; } .search-box { @@ -175,11 +152,10 @@ main { width: 100%; } -.search-box input[type="text"], -#q { +.search-box input[type="text"] { width: 100%; padding: 1rem 3.5rem 1rem 1.25rem; - font-size: 1.1rem; + font-size: 1.05rem; font-family: inherit; border: 2px solid var(--border); border-radius: var(--radius-full); @@ -189,24 +165,22 @@ main { transition: border-color 0.2s, box-shadow 0.2s; } -.search-box input[type="text"]:focus, -#q:focus { +.search-box input[type="text"]:focus { border-color: var(--accent); - box-shadow: var(--shadow-lg), 0 0 0 4px var(--accent-glow); + box-shadow: var(--shadow-md), 0 0 0 3px rgba(13, 148, 136, 0.1); } -.search-box input[type="text"]::placeholder, -#q::placeholder { +.search-box input[type="text"]::placeholder { color: var(--text-muted); } -.search-box-submit { +.search-btn { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); - width: 40px; - height: 40px; + width: 42px; + height: 42px; border: none; border-radius: var(--radius-full); background: var(--accent); @@ -215,16 +189,11 @@ main { display: flex; align-items: center; justify-content: center; - transition: background 0.15s, transform 0.15s; + transition: background 0.15s; } -.search-box-submit:hover { +.search-btn:hover { background: var(--accent-hover); - transform: translateY(-50%) scale(1.05); -} - -.search-box-submit:active { - transform: translateY(-50%) scale(0.97); } /* ============================================================ @@ -235,97 +204,122 @@ main { padding-top: 1.5rem; } -/* 3-column layout: empty left buffer | center results | right column - max-width 1400px, centered on page */ -.results-layout { - display: grid; - grid-template-columns: 1fr min(768px, 100%) 300px; - gap: 2rem; - align-items: start; - max-width: 1400px; +.results-container { + max-width: 768px; margin: 0 auto; } -@media (max-width: 1000px) { - .results-layout { - grid-template-columns: 1fr 260px; - gap: 1.5rem; - } +.results-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; } -@media (max-width: 700px) { - .results-layout { - grid-template-columns: 1fr; - gap: 1rem; - } +.results-logo { + display: flex; + align-items: center; + gap: 0.4rem; + text-decoration: none; + color: var(--text-primary); + flex-shrink: 0; } -/* Compact search bar spans all columns */ -.search-compact { - grid-column: 1 / -1; +.results-logo svg { + width: 20px; + height: 20px; + color: var(--accent); } -.search-compact .search-box { - max-width: 100%; -} - -.search-compact input { +.results-logo span { + font-family: var(--font-mono); font-size: 1rem; - padding: 0.75rem 2.75rem 0.75rem 1rem; + font-weight: 700; } -.search-compact .search-box-submit { +.header-search { + flex: 1; +} + +.header-search .search-box input { + padding: 0.65rem 2.5rem 0.65rem 1rem; + font-size: 0.95rem; +} + +.header-search .search-btn { width: 34px; height: 34px; } /* ============================================================ - Results Column + Category Tabs ============================================================ */ -.results-column { - min-width: 0; +.category-tabs { + display: flex; + gap: 0.25rem; + margin-bottom: 1.5rem; + border-bottom: 1px solid var(--border); + padding-bottom: 0.5rem; } -/* Result count + meta */ +.category-tab { + padding: 0.5rem 0.85rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--text-secondary); + text-decoration: none; + border-radius: var(--radius-sm); + transition: background 0.15s, color 0.15s; +} + +.category-tab:hover { + background: var(--bg-secondary); + color: var(--text-primary); +} + +.category-tab.active { + background: var(--accent-soft); + color: var(--accent); +} + +/* ============================================================ + Results Meta + ============================================================ */ + .results-meta { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 1rem; font-size: 0.85rem; color: var(--text-muted); + margin-bottom: 1rem; } -/* Individual result card */ +/* ============================================================ + Result Cards + ============================================================ */ + .result { - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 1rem 1.1rem; - margin-bottom: 0.75rem; - transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s; + padding: 0.85rem 0; + border-bottom: 1px solid var(--border); } -.result:hover { - border-color: var(--border-focus); - box-shadow: var(--shadow-sm); - transform: translateY(-1px); +.result:last-child { + border-bottom: none; } .result_header { - margin-bottom: 0.25rem; + margin-bottom: 0.2rem; } -.result-favicon { - display: inline-block; - width: 16px; - height: 16px; - border-radius: 3px; - vertical-align: middle; - margin-right: 0.4rem; - background: var(--bg-tertiary); - flex-shrink: 0; +.result_header a { + font-size: 1rem; + font-weight: 500; + color: #2563eb; + text-decoration: none; + line-height: 1.4; +} + +.result_header a:hover { + text-decoration: underline; } .result_url { @@ -334,179 +328,42 @@ main { gap: 0.4rem; font-size: 0.8rem; color: var(--text-muted); - margin-bottom: 0.35rem; - overflow: hidden; + margin-bottom: 0.3rem; +} + +.result-favicon { + width: 14px; + height: 14px; + border-radius: 2px; + background: var(--bg-tertiary); + flex-shrink: 0; } .result_url a { color: var(--text-muted); text-decoration: none; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; } .result_url a:hover { color: var(--accent); } -.result_url .engine-badge { +.engine-badge { flex-shrink: 0; - display: inline-flex; - align-items: center; - gap: 0.25rem; - padding: 0.1rem 0.4rem; - background: var(--bg-tertiary); - border-radius: var(--radius-sm); + padding: 0.1rem 0.35rem; + background: var(--bg-secondary); + border-radius: 4px; font-size: 0.7rem; color: var(--text-muted); margin-left: auto; } -.result_header a { - font-size: 1rem; - font-weight: 500; - color: var(--text-primary); - text-decoration: none; - line-height: 1.4; - display: block; -} - -.result_header a:hover { - color: var(--accent); -} - -.result_header a:visited { - color: var(--text-secondary); -} - .result_content { - font-size: 0.9rem; - color: var(--text-secondary); - line-height: 1.55; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -/* ============================================================ - Right Column (formerly sidebar) - ============================================================ */ - -.right-column { - position: sticky; - top: calc(var(--header-height) + 1.5rem); - display: flex; - flex-direction: column; - gap: 1rem; -} - -.sidebar-card { - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 1rem; -} - -.sidebar-card-title { - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-muted); - margin-bottom: 0.75rem; -} - -/* Knowledge Panel card */ -.knowledge-panel { - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 1rem; -} - -.knowledge-panel-title { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 0.5rem; -} - -.knowledge-panel-content { font-size: 0.875rem; color: var(--text-secondary); line-height: 1.55; } -.knowledge-panel-thumb { - width: 100%; - border-radius: var(--radius-sm); - margin-bottom: 0.75rem; -} - -/* Related searches */ -.related-searches { - display: flex; - flex-direction: column; - gap: 0.35rem; -} - -.related-search-link { - font-size: 0.85rem; - color: var(--accent); - text-decoration: none; - padding: 0.25rem 0; - border-radius: var(--radius-sm); - transition: color 0.15s; -} - -.related-search-link:hover { - color: var(--accent-hover); - text-decoration: underline; -} - -/* Suggestions in sidebar */ -.suggestion-list { - display: flex; - flex-wrap: wrap; - gap: 0.4rem; -} - -.suggestion a { - display: inline-block; - padding: 0.3rem 0.65rem; - font-size: 0.8rem; - border: 1px solid var(--border); - border-radius: var(--radius-full); - color: var(--accent); - text-decoration: none; - background: var(--accent-soft); - transition: background 0.15s, border-color 0.15s; -} - -.suggestion a:hover { - background: color-mix(in srgb, var(--accent-soft) 70%, var(--bg-tertiary)); - border-color: var(--accent); -} - -/* Unresponsive engines warning */ -.unresponsive-engines { - font-size: 0.8rem; - color: var(--text-muted); -} - -.unresponsive-engines li { - margin: 0.3rem 0; -} - -/* Corrections */ -.correction { - font-size: 0.85rem; - color: var(--text-muted); - margin-bottom: 0.5rem; -} - /* ============================================================ No Results ============================================================ */ @@ -541,13 +398,17 @@ main { display: flex; align-items: center; justify-content: center; - gap: 0.4rem; + gap: 0.5rem; padding: 2rem 0; flex-wrap: wrap; } .pagination button, -.pagination .page-link { +.pagination form { + display: inline-flex; +} + +.pagination button { min-width: 40px; height: 40px; padding: 0 0.75rem; @@ -558,32 +419,18 @@ main { font-size: 0.9rem; font-family: inherit; cursor: pointer; - text-decoration: none; - display: inline-flex; - align-items: center; - justify-content: center; - transition: background 0.15s, border-color 0.15s, color 0.15s; + transition: background 0.15s, border-color 0.15s; } -.pagination button:hover, -.pagination .page-link:hover { - background: var(--bg-tertiary); +.pagination button:hover { + background: var(--bg-secondary); border-color: var(--border-focus); } -.pagination .page-current { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} - -.pagination .page-current:hover { - background: var(--accent-hover); - border-color: var(--accent-hover); -} - -.pagination .prev-next { - font-weight: 500; +.page-current { + background: var(--accent) !important; + border-color: var(--accent) !important; + color: #fff !important; } /* ============================================================ @@ -595,7 +442,6 @@ footer { padding: 3rem 1.5rem 2rem; color: var(--text-muted); font-size: 0.85rem; - margin-top: auto; } footer a { @@ -608,289 +454,90 @@ footer a:hover { } /* ============================================================ - Autocomplete Dropdown + Corrections ============================================================ */ -#autocomplete-dropdown { - position: absolute; - top: calc(100% + 6px); - left: 0; - right: 0; - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-md); - box-shadow: var(--shadow-lg); - z-index: 200; - max-height: 360px; - overflow-y: auto; - display: none; -} - -#autocomplete-dropdown.open { - display: block; -} - -.autocomplete-suggestion { - padding: 0.7rem 1rem; - cursor: pointer; - font-size: 0.95rem; - color: var(--text-primary); - border-bottom: 1px solid var(--border); - transition: background 0.1s; -} - -.autocomplete-suggestion:last-child { - border-bottom: none; -} - -.autocomplete-suggestion:hover, -.autocomplete-suggestion.active { - background: var(--bg-tertiary); -} - -.autocomplete-suggestion mark { - background: none; - color: var(--accent); - font-weight: 600; -} - -.autocomplete-footer { - padding: 0.5rem 1rem; - font-size: 0.75rem; - color: var(--text-muted); +.correction { + font-size: 0.9rem; + color: var(--text-secondary); + margin-bottom: 1rem; + padding: 0.5rem 0.75rem; background: var(--bg-secondary); - border-radius: 0 0 var(--radius-md) var(--radius-md); - border-top: 1px solid var(--border); + border-radius: var(--radius-sm); } /* ============================================================ - Settings Panel + Image Grid ============================================================ */ -.settings-popover { - position: fixed; - top: calc(var(--header-height) + 8px); - right: 1rem; - width: 300px; - max-height: calc(100vh - var(--header-height) - 2rem); - overflow-y: auto; - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-lg); - z-index: 300; - display: none; -} - -.settings-popover[data-open="true"] { - display: block; - animation: pop-in 0.2s ease; -} - -@keyframes pop-in { - from { opacity: 0; transform: translateY(-8px) scale(0.97); } - to { opacity: 1; transform: translateY(0) scale(1); } -} - -.settings-popover-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 1.25rem; - border-bottom: 1px solid var(--border); - font-weight: 600; - font-size: 0.95rem; - position: sticky; - top: 0; - background: var(--bg); - z-index: 1; -} - -.settings-popover-close { - background: none; - border: none; - width: 28px; - height: 28px; - border-radius: var(--radius-sm); - cursor: pointer; - color: var(--text-secondary); - display: flex; - align-items: center; - justify-content: center; - font-size: 1.2rem; - transition: background 0.15s, color 0.15s; -} - -.settings-popover-close:hover { - background: var(--bg-tertiary); - color: var(--text-primary); -} - -.settings-popover-body { - padding: 1rem 1.25rem; - display: flex; - flex-direction: column; - gap: 1.25rem; -} - -.settings-section-title { - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-muted); - margin-bottom: 0.6rem; -} - -/* Theme buttons */ -.theme-buttons { - display: flex; - gap: 0.4rem; -} - -.theme-btn { - flex: 1; - padding: 0.45rem; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg); - color: var(--text-secondary); - cursor: pointer; - font-size: 0.8rem; - font-family: inherit; - transition: background 0.15s, border-color 0.15s, color 0.15s; -} - -.theme-btn:hover { - background: var(--bg-tertiary); -} - -.theme-btn.active { - background: var(--accent-soft); - border-color: var(--accent); - color: var(--accent); - font-weight: 500; -} - -/* Engine toggles */ -.engine-grid { +.image-grid { display: grid; - grid-template-columns: 1fr 1fr; - gap: 0.4rem; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 1rem; } -.engine-toggle { - display: flex; - align-items: center; - gap: 0.4rem; - padding: 0.4rem 0.5rem; +.image-result { + display: block; border-radius: var(--radius-sm); + overflow: hidden; + background: var(--bg-secondary); + border: 1px solid var(--border); + text-decoration: none; + color: inherit; +} + +.image-thumb { + aspect-ratio: 1; + overflow: hidden; background: var(--bg-tertiary); +} + +.image-thumb img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.image-meta { + padding: 0.5rem; +} + +.image-title { font-size: 0.8rem; - cursor: pointer; - transition: background 0.1s; -} - -.engine-toggle:hover { - background: var(--border); -} - -.engine-toggle input[type="checkbox"] { - width: 15px; - height: 15px; - margin: 0; - cursor: pointer; - accent-color: var(--accent); - flex-shrink: 0; -} - -.engine-toggle span { - color: var(--text-secondary); - white-space: nowrap; + font-weight: 500; + color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; } -/* Select dropdowns */ -.setting-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.75rem; -} - -.setting-row label { - font-size: 0.85rem; - color: var(--text-secondary); -} - -.setting-row select { - padding: 0.35rem 0.6rem; - font-size: 0.8rem; - font-family: inherit; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg); - color: var(--text-primary); - cursor: pointer; - min-width: 100px; -} - -.setting-row select:focus { - outline: none; - border-color: var(--accent); -} - -.settings-notice { - font-size: 0.75rem; +.image-source { + font-size: 0.7rem; color: var(--text-muted); - font-style: italic; - margin-top: 0.25rem; -} - -/* Mobile settings: bottom sheet */ -@media (max-width: 480px) { - .settings-popover { - position: fixed; - top: auto; - bottom: 0; - left: 0; - right: 0; - width: 100%; - max-height: 70vh; - border-radius: var(--radius-lg) var(--radius-lg) 0 0; - border-bottom: none; - } - - .settings-trigger-mobile { - display: flex; - } -} - -@media (min-width: 481px) { - .settings-trigger-mobile { - display: none; - } } /* ============================================================ - HTMX Loading State + Video Results ============================================================ */ -.htmx-indicator { - display: none; - text-align: center; - padding: 2rem; - color: var(--text-muted); +.video-result { + display: flex; + gap: 1rem; + padding: 0.85rem 0; + border-bottom: 1px solid var(--border); } -.htmx-request .htmx-indicator { - display: block; +.video-result .result_thumbnail { + flex-shrink: 0; + width: 160px; + border-radius: var(--radius-sm); + overflow: hidden; + background: var(--bg-tertiary); } -.htmx-request .results-column { - opacity: 0.5; - transition: opacity 0.2s; +.video-result .result_content_wrapper { + flex: 1; + min-width: 0; } /* ============================================================ @@ -913,617 +560,30 @@ footer a:hover { } /* ============================================================ - Video Results + Responsive ============================================================ */ -.video-result { - display: flex; - gap: 1rem; - align-items: flex-start; -} - -.video-result .result_thumbnail { - flex-shrink: 0; - width: 180px; - border-radius: var(--radius-sm); - overflow: hidden; - background: var(--bg-tertiary); -} - -.video-result .result_thumbnail img { - width: 100%; - height: auto; - display: block; - object-fit: cover; -} - -.video-result .result_content_wrapper { - flex: 1; - min-width: 0; -} - -@media (max-width: 480px) { - .video-result { +@media (max-width: 600px) { + .results-header { flex-direction: column; + gap: 0.75rem; } - .video-result .result_thumbnail { - width: 100%; + .results-logo { + display: none; } -} -/* ============================================================ - Image Results - ============================================================ */ + .category-tabs { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } -.image-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 1rem; -} + .category-tabs::-webkit-scrollbar { + display: none; + } -.image-result { - display: block; - border-radius: var(--radius-sm); - overflow: hidden; - background: var(--bg-secondary); - border: 1px solid var(--border); - transition: transform 0.15s ease, box-shadow 0.15s ease; - text-decoration: none; - color: inherit; -} - -.image-result:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px var(--shadow); -} - -.image-result:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.image-thumb { - aspect-ratio: 1; - overflow: hidden; - background: var(--bg-tertiary); - display: flex; - align-items: center; - justify-content: center; -} - -.image-thumb img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; - transition: transform 0.2s ease; -} - -.image-result:hover .image-thumb img { - transform: scale(1.05); -} - -.image-thumb.image-error img, -.image-thumb.image-error { - display: none; -} - -.image-placeholder { - font-size: 2rem; - opacity: 0.3; -} - -.image-meta { - padding: 0.5rem; - min-height: 2.5rem; - display: flex; - flex-direction: column; - gap: 0.15rem; -} - -.image-title { - font-size: 0.8rem; - font-weight: 500; - color: var(--text-primary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.image-source { - font-size: 0.7rem; - color: var(--text-secondary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -@media (max-width: 480px) { .image-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.5rem; } } - -/* ============================================================ - Infoboxes - ============================================================ */ - -.infobox { - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius-md); - padding: 1rem; - margin-bottom: 0.75rem; -} - -.infobox .title { - font-weight: 600; - margin-bottom: 0.5rem; - font-size: 0.95rem; -} - -.infobox img { - max-width: 100%; - border-radius: var(--radius-sm); - margin-top: 0.5rem; -} - -/* ============================================================ - Errors - ============================================================ */ - -.dialog-error { - background: color-mix(in srgb, var(--accent) 8%, var(--bg)); - border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); - color: var(--accent); - border-radius: var(--radius-md); - padding: 0.8rem 1rem; - margin-bottom: 1rem; - font-size: 0.9rem; -} - -/* ============================================================ - Focus visible — keyboard navigation - ============================================================ */ - -:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -button:focus-visible, -a:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -/* ============================================================ - Selection color - ============================================================ */ - -::selection { - background: var(--accent-soft); - color: var(--accent); -} - -/* ============================================================ - Scrollbar - ============================================================ */ - -::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -::-webkit-scrollbar-track { - background: var(--bg-secondary); -} - -::-webkit-scrollbar-thumb { - background: var(--border); - border-radius: 4px; -} - -::-webkit-scrollbar-thumb:hover { - background: var(--border-focus); -} - -/* ============================================================ - Three-Column Results Layout - ============================================================ */ - -.results-layout { - display: grid; - grid-template-columns: 200px 1fr 240px; - gap: 2rem; - align-items: start; -} - -.results-layout .left-sidebar, -.results-layout .right-sidebar { - position: sticky; - top: calc(var(--header-height) + 1.5rem); - max-height: calc(100vh - var(--header-height) - 3rem); - overflow-y: auto; -} - -.results-layout .results-column { - min-width: 0; -} - -/* Tablet: hide left sidebar, two columns */ -@media (min-width: 769px) and (max-width: 1024px) { - .results-layout { - grid-template-columns: 1fr 220px; - } - .results-layout .left-sidebar { - display: none; - } -} - -/* Mobile: single column, no sidebars */ -@media (max-width: 768px) { - .results-layout { - grid-template-columns: 1fr; - } - .results-layout .left-sidebar, - .results-layout .right-sidebar { - display: none; - } -} - -/* ============================================================ - Preferences Page Layout - ============================================================ */ - -.preferences-layout { - display: grid; - grid-template-columns: 200px 1fr; - gap: 2rem; - align-items: start; - padding: 2rem 0; -} - -.preferences-nav { - position: sticky; - top: calc(var(--header-height) + 1.5rem); -} - -.preferences-nav-item { - display: flex; - align-items: center; - gap: 0.6rem; - padding: 0.6rem 0.75rem; - border-radius: var(--radius-sm); - color: var(--text-secondary); - text-decoration: none; - font-size: 0.9rem; - transition: background 0.15s, color 0.15s; - cursor: pointer; -} - -.preferences-nav-item:hover { - background: var(--bg-tertiary); - color: var(--text-primary); -} - -.preferences-nav-item.active { - background: var(--accent-soft); - color: var(--accent); - font-weight: 500; -} - -.preferences-content { - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - padding: 1.5rem; -} - -@media (max-width: 768px) { - .preferences-layout { - grid-template-columns: 1fr; - } - .preferences-nav { - position: static; - display: flex; - overflow-x: auto; - gap: 0.5rem; - padding-bottom: 0.5rem; - } - .preferences-nav-item { - white-space: nowrap; - } -} - -/* ============================================================ - Category Tiles - ============================================================ */ - -.category-tiles { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); - gap: 1rem; - margin-top: 2rem; -} - -.category-tile { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; - padding: 1rem 0.5rem; - border-radius: var(--radius-md); - text-decoration: none; - color: var(--text-secondary); - font-size: 0.85rem; - transition: background 0.15s, color 0.15s, transform 0.15s, box-shadow 0.15s; -} - -.category-tile:hover { - background: var(--bg-tertiary); - color: var(--text-primary); - transform: translateY(-2px); - box-shadow: var(--shadow-sm); -} - -.category-tile-icon { - font-size: 1.5rem; - line-height: 1; -} - -.category-tile.disabled { - opacity: 0.5; - cursor: not-allowed; - pointer-events: none; -} - -@media (max-width: 768px) { - .category-tiles { - grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); - gap: 0.75rem; - } - .category-tile { - padding: 0.75rem 0.25rem; - font-size: 0.75rem; - } - .category-tile-icon { - font-size: 1.25rem; - } -} - -/* ============================================================ - Left Sidebar (Results Page) - ============================================================ */ - -.left-sidebar { - padding: 0; -} - -.sidebar-nav { - display: flex; - flex-direction: column; - gap: 0.25rem; -} - -.sidebar-nav-title { - font-size: 0.7rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-muted); - padding: 0.5rem 0.75rem; - margin-top: 0.5rem; -} - -.sidebar-nav-item { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.5rem 0.75rem; - border-radius: var(--radius-sm); - color: var(--text-secondary); - text-decoration: none; - font-size: 0.875rem; - transition: background 0.15s, color 0.15s; -} - -.sidebar-nav-item:hover { - background: var(--bg-tertiary); - color: var(--text-primary); -} - -.sidebar-nav-item.active { - background: var(--accent-soft); - color: var(--accent); - font-weight: 500; -} - -.sidebar-nav-item-icon { - font-size: 1rem; - width: 20px; - text-align: center; -} - -.sidebar-filters { - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid var(--border); -} - -.sidebar-filter-group { - margin-bottom: 0.75rem; -} - -.sidebar-filter-label { - font-size: 0.75rem; - font-weight: 500; - color: var(--text-muted); - padding: 0 0.75rem; - margin-bottom: 0.25rem; -} - -.sidebar-filter-option { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.35rem 0.75rem; - font-size: 0.8rem; - color: var(--text-secondary); - cursor: pointer; - border-radius: var(--radius-sm); - transition: background 0.15s; -} - -.sidebar-filter-option:hover { - background: var(--bg-tertiary); -} - -.sidebar-filter-option input[type="radio"] { - accent-color: var(--accent); -} - -/* Mobile filter chips */ -.mobile-filter-chips { - display: none; - overflow-x: auto; - gap: 0.5rem; - padding: 0.75rem 0; - -webkit-overflow-scrolling: touch; -} - -.mobile-filter-chips::-webkit-scrollbar { - display: none; -} - -.mobile-filter-chip { - display: inline-flex; - align-items: center; - padding: 0.4rem 0.75rem; - border: 1px solid var(--border); - border-radius: var(--radius-full); - font-size: 0.8rem; - color: var(--text-secondary); - white-space: nowrap; - text-decoration: none; - transition: background 0.15s, border-color 0.15s; -} - -.mobile-filter-chip:hover, -.mobile-filter-chip.active { - background: var(--accent-soft); - border-color: var(--accent); - color: var(--accent); -} - -@media (max-width: 768px) { - .mobile-filter-chips { - display: flex; - } -} - -/* ============================================================ - Preferences Page Styles - ============================================================ */ - -.pref-section { - margin-bottom: 2rem; -} - -.pref-section:last-child { - margin-bottom: 0; -} - -.pref-section-title { - font-size: 1rem; - font-weight: 600; - color: var(--text-primary); - margin-bottom: 1rem; - padding-bottom: 0.5rem; - border-bottom: 1px solid var(--border); -} - -.pref-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 1rem; - padding: 0.75rem 0; - border-bottom: 1px solid var(--border); -} - -.pref-row:last-child { - border-bottom: none; -} - -.pref-row label { - font-size: 0.9rem; - color: var(--text-primary); -} - -.pref-row-info { - flex: 1; -} - -.pref-row-info label { - font-weight: 500; -} - -.pref-desc { - font-size: 0.8rem; - color: var(--text-muted); - margin-top: 0.25rem; -} - -.pref-row select { - padding: 0.5rem 0.75rem; - font-size: 0.85rem; - font-family: inherit; - border: 1px solid var(--border); - border-radius: var(--radius-sm); - background: var(--bg); - color: var(--text-primary); - cursor: pointer; - min-width: 150px; -} - -.pref-row select:focus { - outline: none; - border-color: var(--accent); -} - -.pref-row input[type="checkbox"] { - width: 18px; - height: 18px; - accent-color: var(--accent); - cursor: pointer; - flex-shrink: 0; -} - -.pref-row input[type="checkbox"]:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -/* ============================================================ - Print - ============================================================ */ - -@media print { - .site-header, footer, .settings-popover, #backToTop, .htmx-indicator, .settings-trigger { - display: none !important; - } - - body { - background: #fff; - color: #000; - } - - .result { - break-inside: avoid; - border: 1px solid #ddd; - margin-bottom: 1rem; - } -} diff --git a/internal/views/templates/base.html b/internal/views/templates/base.html index d2fec79..103385f 100644 --- a/internal/views/templates/base.html +++ b/internal/views/templates/base.html @@ -1,15 +1,14 @@ {{define "base"}} - + - {{template "title" .}}kafka + {{if .Query}}{{.Query}} — {{end}}kafka - @@ -22,12 +21,6 @@ kafka -
@@ -37,133 +30,6 @@ - - - - - -
- - {{end}} diff --git a/internal/views/templates/index.html b/internal/views/templates/index.html index 7a241f1..71b274c 100644 --- a/internal/views/templates/index.html +++ b/internal/views/templates/index.html @@ -1,60 +1,25 @@ {{define "title"}}{{end}} {{define "content"}} -
- +
-
-{{end}} \ No newline at end of file +{{end}} diff --git a/internal/views/templates/results.html b/internal/views/templates/results.html index 74d5f93..b98e61b 100644 --- a/internal/views/templates/results.html +++ b/internal/views/templates/results.html @@ -1,58 +1,38 @@ {{define "title"}}{{if .Query}}{{.Query}} — {{end}}{{end}} {{define "content"}} -
- -
- +
+
- -
+
+ All + General + IT + News + Images +
+ +
{{template "results_inner" .}}
- - -
{{end}}