diff --git a/docs/plans/current_spec.md b/docs/plans/current_spec.md
index 203e735c..5e8d763f 100644
--- a/docs/plans/current_spec.md
+++ b/docs/plans/current_spec.md
@@ -1,523 +1,60 @@
-# E2E Test Architecture Fix: Simulate Production Middleware Stack
+# Reddit Feedback Implementation Plan: Logs UI, Caddy Import, Settings 400 Errors
**Version:** 1.0
**Status:** Research Complete - Ready for Implementation
-**Priority:** CRITICAL
+**Priority:** HIGH
**Created:** 2026-01-29
-**Author:** Planning Agent
+**Source:** Reddit user feedback
+
+> **Note:** Previous active plan (E2E Test Architecture Fix) archived to [e2e_architecture_port80_spec.md](./e2e_architecture_port80_spec.md)
+
+---
+
+## Active Plan
+
+See **[reddit_feedback_spec.md](./reddit_feedback_spec.md)** for the complete specification.
+
+---
+
+## Quick Reference
+
+### Three Issues Addressed
+
+1. **Logs UI on widescreen** - Fixed `h-96` height, multi-span entries
+2. **Caddy import not working** - Silent skipping, cryptic errors
+3. **Settings 400 errors** - CIDR/URL validation, unfriendly messages
+
+### Key Files
+
+| Issue | Primary File | Line |
+|-------|-------------|------|
+| Logs UI | `frontend/src/components/LiveLogViewer.tsx` | 435 |
+| Import | `backend/internal/api/handlers/import_handler.go` | 297 |
+| Settings | `backend/internal/api/handlers/settings_handler.go` | 84 |
+
+### Implementation Timeline
+
+- **Day 1:** Quick wins (responsive height, error messages, normalization)
+- **Day 2:** Core features (compact mode, skipped hosts, validation)
+- **Day 3:** Polish (density control, import directive UI, inline validation)
---
## Executive Summary
-**Problem:** E2E tests bypass Caddy middleware by hitting the Go backend directly (port 8080), creating a critical gap between test and production environments. Middleware (ACL, WAF, Rate Limiting, CrowdSec) never executes during E2E tests.
+Three user-reported issues from Reddit:
+1. **Logs UI** - Fixed height wastes screen space, entries wrap across multiple lines
+2. **Caddy Import** - Silent failures, cryptic errors, missing feedback on skipped sites
+3. **Settings 400** - Validation errors not user-friendly, missing auto-correction
-**Root Cause [VERIFIED]:** Charon uses a **dual-serving architecture**:
-- **Port 8080:** Backend serves frontend DIRECTLY via Gin (bypasses middleware)
-- **Port 80:** Caddy serves frontend via `file_server` AND proxies API through middleware
+**Root Causes Identified:**
+- LiveLogViewer uses `h-96` fixed height, multi-span entries
+- Import handler silently skips hosts without `reverse_proxy`
+- Settings handler returns raw Go validation errors
-**Solution:** Modify E2E test environment to route Playwright requests through Caddy (port 80) instead of directly to backend (port 8080), matching production architecture.
-
-**Verification Complete:** Code analysis confirms:
-1. ✅ Caddy DOES serve frontend files via catch-all `file_server` handler
-2. ✅ Caddy proxies API requests through full middleware stack
-3. ✅ Port 80 tests the COMPLETE production flow (frontend + middleware + backend)
-4. ✅ Port 8080 bypasses ALL middleware (development/fallback only)
-
-**Impact:** Enables true E2E testing of security middleware enforcement, removes all `test.skip()` statements, ensures production parity.
+**Solution:** Responsive UI, enhanced error messages, input normalization
---
-## 1. Architecture Analysis: Frontend Serving (VERIFIED)
-
-**CRITICAL FINDING:** Charon uses a **dual-serving architecture** where BOTH backend and Caddy serve the frontend.
-
-### Port 8080 (Backend Direct) - Development/Fallback
-
-```
-Browser → Backend:8080 → Gin Router
- ├─ Frontend static files (via router.Static/StaticFile)
- └─ API endpoints (/api/*)
-
-⚠️ NO MIDDLEWARE - Security features bypassed
-```
-
-**Source:** `backend/internal/server/server.go` lines 21-25
-```go
-router.Static("/assets", frontendDir+"/assets")
-router.StaticFile("/", frontendDir+"/index.html")
-router.StaticFile("/banner.png", frontendDir+"/banner.png")
-router.StaticFile("/logo.png", frontendDir+"/logo.png")
-router.StaticFile("/favicon.png", frontendDir+"/favicon.png")
-```
-
-### Port 80 (Caddy Proxy) - Production Flow
-
-```
-Browser → Caddy:80
- ├─ Frontend UI (/*.html, /assets/*, images)
- │ └─ Served by catch-all file_server handler
- │ Source: backend/internal/caddy/config.go line 1136
- │
- └─ API Requests (/api/*)
- └─ Caddy Middleware Pipeline:
- ├─ CrowdSec Bouncer (IP blocking)
- ├─ Coraza WAF (OWASP rules)
- ├─ Rate Limiting (caddy-ratelimit)
- └─ ACL (whitelist/blacklist)
- └─ Reverse Proxy → Backend:8080
-```
-
-**Source:** `backend/internal/caddy/config.go` lines 1136-1147
-```go
-// Add catch-all 404 handler
-// This matches any request that wasn't handled by previous routes
-if frontendDir != "" {
- catchAllRoute := &Route{
- Handle: []Handler{
- RewriteHandler("/unknown.html"),
- FileServerHandler(frontendDir), // ← Serves frontend!
- },
- Terminal: true,
- }
- routes = append(routes, catchAllRoute)
-}
-```
-
-**Source:** `backend/internal/caddy/types.go` lines 230-235
-```go
-func FileServerHandler(root string) Handler {
- return Handler{
- "handler": "file_server",
- "root": root,
- }
-}
-```
-
-### Why Port 80 is MANDATORY for E2E Tests
-
-| Aspect | Port 8080 | Port 80 |
-|--------|-----------|---------|
-| **Frontend Serving** | ✅ Gin static handlers | ✅ Caddy file_server |
-| **API Requests** | ✅ Direct to backend | ✅ Through Caddy proxy |
-| **CrowdSec** | ❌ Bypassed | ✅ Tested |
-| **WAF (Coraza)** | ❌ Bypassed | ✅ Tested |
-| **Rate Limiting** | ❌ Bypassed | ✅ Tested |
-| **ACL** | ❌ Bypassed | ✅ Tested |
-| **Production Flow** | ❌ Dev only | ✅ Real-world |
-
-**Decision:** Tests MUST run against port 80. Port 8080 bypasses the entire Caddy middleware stack, making E2E tests of Cerberus security features impossible.
-
----
-
-## 2. Problem Statement
-
-### Current E2E Flow (WRONG)
-```
-Playwright Tests → Backend:8080 [BYPASSES CADDY & ALL MIDDLEWARE]
-```
-
-### Production Flow (CORRECT)
-```
-User Request → Caddy:443/80 → [ACL, WAF, Rate Limit, CrowdSec] → Backend:8080
-```
-
-### Requirements (EARS Notation)
-
-**R1 - Middleware Execution**
-WHEN Playwright sends an HTTP request to the test environment,
-THE SYSTEM SHALL route the request through Caddy on port 80.
-
-**R2 - Security Enforcement**
-WHEN Caddy processes the request,
-THE SYSTEM SHALL execute all configured middleware in the correct order.
-
-**R3 - Backend Isolation**
-WHEN running E2E tests,
-THE SYSTEM SHALL NOT allow direct access to backend port 8080 from Playwright.
-
----
-
-## 3. Root Cause Analysis
-
-### Current Docker Compose (`.docker/compose/docker-compose.playwright-local.yml`)
-
-```yaml
-ports:
- - "8080:8080" # ❌ Backend exposed directly
- - "127.0.0.1:2019:2019" # Caddy admin API
- - "2020:2020" # Emergency API
- # ❌ MISSING: Port 80/443 for Caddy proxy
-```
-
-### Current Playwright Config (`playwright.config.js:90-110`)
-
-```javascript
-use: {
- baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8080',
- // ^^^^^^^^^^^^^ WRONG
-}
-```
-
-### Container Architecture (Verified)
-
-**Services Running Inside `charon-e2e`:**
-
-1. **Caddy Proxy** (Confirmed in `docker-entrypoint.sh:274`)
- - Listens: `0.0.0.0:80`, `0.0.0.0:443`
- - Admin API: `0.0.0.0:2019`
- - Middleware: ACL, WAF, Rate Limiting, CrowdSec
-
-2. **Go Backend** (Confirmed in `backend/cmd/api/main.go:275`)
- - Listens: `0.0.0.0:8080`
- - Provides: REST API, serves frontend
-
-**Key Findings:**
-- ✅ Caddy IS running in E2E container
-- ✅ Caddy listens on ports 80/443 internally
-- ❌ Ports 80/443 NOT mapped in Docker Compose
-- ❌ Tests hit port 8080 directly, bypassing Caddy
-
----
-
-## 4. Solution Design
-
-### Port Mapping Update
-
-**File:** `.docker/compose/docker-compose.playwright-local.yml`
-
-```yaml
-ports:
- - "80:80" # ✅ ADD: Caddy HTTP proxy
- - "8080:8080" # KEEP: Management UI
- - "127.0.0.1:2019:2019" # KEEP: Caddy admin API
- - "2020:2020" # KEEP: Emergency API
-```
-
-### Playwright Config Update
-
-**File:** `playwright.config.js`
-
-```javascript
-use: {
- // OLD: baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8080',
- // NEW: Default to Caddy port
- baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:80',
-}
-```
-
-### Request Flow Post-Fix
-
-```
-Playwright Test
- ↓
-http://localhost:80 (Caddy)
- ↓
-Rate Limiter (if enabled)
- ↓
-CrowdSec Bouncer (if enabled)
- ↓
-Access Control Lists (if enabled)
- ↓
-Coraza WAF (if enabled)
- ↓
-Backend :8080 (proxied)
- ↓
-Response
-```
-
----
-
-## 5. Implementation Plan
-
-### Phase 1: Docker Compose Update (5 min)
-
-**File:** `.docker/compose/docker-compose.playwright-local.yml`
-
-```yaml
-# Add after line 13:
-ports:
- - "80:80" # ✅ ADD THIS LINE
- - "8080:8080"
- - "127.0.0.1:2019:2019"
- - "2020:2020"
-```
-
-**Testing:**
-```bash
-.github/skills/scripts/skill-runner.sh docker-rebuild-e2e --clean
-docker port charon-e2e | grep "80->"
-# Expected: 0.0.0.0:80->80/tcp
-curl -v http://localhost:80/api/v1/health
-# Expected: HTTP/1.1 200 OK
-```
-
-### Phase 2: Playwright Config Update (2 min)
-
-**File:** `playwright.config.js`
-
-```javascript
-// Line ~107:
-use: {
- baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:80',
- // Change from :8080 to :80 ^^^
-}
-```
-
-### Phase 3: Environment Variable Setup (3 min)
-
-**File:** `.github/skills/test-e2e-playwright-scripts/run.sh`
-
-```bash
-# Add after line ~30:
-export PLAYWRIGHT_BASE_URL="${PLAYWRIGHT_BASE_URL:-http://localhost:80}"
-
-# Verify Caddy is accessible
-if ! curl -sf "$PLAYWRIGHT_BASE_URL/api/v1/health" >/dev/null; then
- log_error "Caddy proxy not responding at $PLAYWRIGHT_BASE_URL"
- exit 1
-fi
-```
-
-### Phase 4: Health Check Enhancement (5 min)
-
-**File:** `.github/skills/docker-rebuild-e2e-scripts/run.sh`
-
-```bash
-# Add in verify_environment() function:
-log_info "Testing Caddy proxy path..."
-if curl -sf http://localhost:80/api/v1/health &>/dev/null; then
- log_success "Caddy proxy responding (port 80 → backend 8080)"
-else
- log_error "Caddy proxy not responding on port 80"
- error_exit "Proxy path verification failed"
-fi
-```
-
-### Phase 5: Remove test.skip() Statements (10 min)
-
-**Files:** `tests/security-enforcement/*.spec.ts`
-
-**Before:**
-```typescript
-test.skip('should block request from denied IP', async ({ page }) => {
-```
-
-**After:**
-```typescript
-test('should block request from denied IP', async ({ page }) => {
-```
-
-**Find all:**
-```bash
-grep -r "test.skip" tests/security-enforcement/ --include="*.spec.ts"
-# Remove .skip from all security tests
-```
-
----
-
-## 6. Verification Strategy
-
-### Pre-Fix Baseline
-
-```bash
-# Count skipped tests
-grep -r "test.skip" tests/ --include="*.spec.ts" | wc -l
-
-# Check which port tests hit
-tcpdump -i lo port 8080 or port 80 -c 10 &
-npx playwright test tests/security-enforcement/acl-enforcement.spec.ts --project=chromium
-# Expected: All traffic to port 8080
-```
-
-### Post-Fix Validation
-
-```bash
-# Rebuild
-.github/skills/scripts/skill-runner.sh docker-rebuild-e2e --clean
-
-# Verify ports
-docker port charon-e2e | grep "80->"
-
-# Test Caddy
-curl -v http://localhost:80/api/v1/health
-
-# Run security tests
-npx playwright test tests/security-enforcement/ --project=chromium
-
-# Check which port now
-tcpdump -i lo port 8080 or port 80 -c 10 &
-npx playwright test tests/security-enforcement/acl-enforcement.spec.ts --project=chromium
-# Expected: All traffic to port 80
-
-# Verify middleware executed
-docker exec charon-e2e grep "rate_limit\|crowdsec\|waf\|acl" /var/log/caddy/access.log
-```
-
-### Middleware-Specific Tests
-
-**ACL:**
-```bash
-# Enable ACL, deny test IP
-curl -X POST http://localhost:8080/api/v1/proxy-hosts/1/acl \
- -d '{"deny": ["127.0.0.1"]}'
-
-# Request through Caddy (should be blocked)
-curl -v http://localhost:80/
-# Expected: HTTP/1.1 403 Forbidden
-```
-
-**WAF:**
-```bash
-# Enable WAF
-curl -X POST http://localhost:8080/api/v1/security/waf -d '{"enabled": true}'
-
-# Send SQLi attack
-curl -v http://localhost:80/?id=1%27%20OR%20%271%27=%271
-# Expected: HTTP/1.1 403 Forbidden
-```
-
-**Rate Limiting:**
-```bash
-# Enable rate limit
-curl -X POST http://localhost:8080/api/v1/security/rate-limit -d '{"enabled": true, "limit": 10}'
-
-# Flood endpoint
-for i in {1..15}; do curl http://localhost:80/ & done; wait
-
-# Check for 429
-curl -v http://localhost:80/
-# Expected: HTTP/1.1 429 Too Many Requests
-```
-
----
-
-## 7. Success Criteria
-
-| Metric | Current | Target |
-|--------|---------|---------|
-| Skipped security tests | ~15-20 | 0 |
-| E2E test coverage | ~70% | 85%+ |
-| Middleware test pass rate | 0% (skipped) | 100% |
-| Port 80 traffic % | 0% | 100% |
-
-**Verification Script:**
-
-```bash
-#!/bin/bash
-# verify-e2e-architecture.sh
-
-# 1. Port mappings
-if ! docker port charon-e2e | grep -q "80->80"; then
- echo "❌ Port 80 not mapped"; exit 1
-fi
-
-# 2. Caddy accessibility
-if ! curl -sf http://localhost:80/api/v1/health; then
- echo "❌ Caddy not responding"; exit 1
-fi
-
-# 3. Security tests passing
-if ! npx playwright test tests/security-enforcement/ --project=chromium 2>&1 | grep -q "passed"; then
- echo "❌ Security tests not passing"; exit 1
-fi
-
-# 4. No skipped tests
-if grep -r "test.skip" tests/security-enforcement/ --include="*.spec.ts"; then
- echo "⚠️ WARNING: Tests still skipped"
-fi
-
-echo "✅ E2E architecture correctly routes through Caddy"
-```
-
----
-
-## 8. Risk Assessment
-
-| Risk | Likelihood | Impact | Mitigation |
-|------|-----------|--------|------------|
-| Port 80 in use | Medium | High | Use alternate port (8081:80) |
-| Breaking tests | Low | High | Run full suite before merge |
-| Flaky tests | Medium | Medium | Add retry logic |
-
-**Port Conflict Resolution:**
-```yaml
-# Alternative: Use high port for Caddy
-ports:
- - "8081:80" # Caddy on alternate port
-```
-```bash
-export PLAYWRIGHT_BASE_URL="http://localhost:8081"
-```
-
----
-
-## 9. Rollout Plan
-
-**Week 1: Development Environment**
-- Update compose file
-- Test locally
-- Validate middleware
-
-**Week 2: CI/CD Integration**
-- Update workflows
-- Test in CI
-- Monitor stability
-
-**Week 3: Documentation**
-- Update ARCHITECTURE.md
-- Add troubleshooting guide
-- Update testing.instructions.md
-
-**Week 4: Test Cleanup**
-- Remove test.skip()
-- Add new tests
-- Verify 100% pass rate
-
----
-
-## Implementation Checklist
-
-- [ ] Phase 1: Update docker-compose.playwright-local.yml (add port 80:80)
-- [ ] Phase 2: Update playwright.config.js (change baseURL to :80)
-- [ ] Phase 3: Update test-e2e-playwright-scripts/run.sh (export PLAYWRIGHT_BASE_URL)
-- [ ] Phase 4: Update docker-rebuild-e2e-scripts/run.sh (add proxy health check)
-- [ ] Phase 5: Run full E2E test suite (verify all pass)
-- [ ] Phase 6: Remove test.skip() from security enforcement tests
-- [ ] Verification: Run verify-e2e-architecture.sh script
-- [ ] Documentation: Update ARCHITECTURE.md
-- [ ] Documentation: Update testing.instructions.md
-- [ ] CI/CD: Update GitHub Actions workflows
-
----
-
-**Plan Status:** ✅ ARCHITECTURE VERIFIED - Port 80 is CORRECT and MANDATORY
-**Confidence:** 100% - Full codebase analysis confirms Caddy serves frontend AND proxies API
-**Next Step:** Backend_Dev to implement Phase 1-4
-**QA Step:** QA_Security to implement Phase 5-6 and verify
-
----
-
-## Related Files
-
-**Docker:**
-- `.docker/compose/docker-compose.playwright-local.yml`
-- `.docker/docker-entrypoint.sh`
-- `Dockerfile`
-
-**Playwright:**
-- `playwright.config.js`
-- `tests/security-enforcement/*.spec.ts`
-
-**Skills:**
-- `.github/skills/docker-rebuild-e2e-scripts/run.sh`
-- `.github/skills/test-e2e-playwright-scripts/run.sh`
-
-**Backend:**
-- `backend/internal/caddy/manager.go`
-- `backend/internal/caddy/config.go`
-- `backend/cmd/api/main.go`
-
----
-
-*End of Specification*
+*For full specification, see [reddit_feedback_spec.md](./reddit_feedback_spec.md)*
+*Previous E2E plan archived to [e2e_architecture_port80_spec.md](./e2e_architecture_port80_spec.md)*
diff --git a/docs/plans/reddit_feedback_spec.md b/docs/plans/reddit_feedback_spec.md
new file mode 100644
index 00000000..83750987
--- /dev/null
+++ b/docs/plans/reddit_feedback_spec.md
@@ -0,0 +1,1101 @@
+# Reddit Feedback Implementation Plan: Logs UI, Caddy Import, Settings 400 Errors
+
+**Version:** 1.1
+**Status:** Supervisor Review Complete - Ready for Implementation
+**Priority:** HIGH
+**Created:** 2026-01-29
+**Updated:** 2026-01-30
+**Source:** Reddit user feedback
+
+---
+
+## Executive Summary
+
+Three user-reported issues from Reddit requiring investigation and fixes:
+
+1. **Logs UI on widescreen** - "logs on widescreen could be stretched and one line for better view (and more lines)"
+2. **Caddy import not working** - "import from my caddy is not working"
+3. **Settings 400 errors** - "i'm getting several i think error 400 on saving different settings"
+
+---
+
+## Issue 1: Logs UI Widescreen Enhancement
+
+### Root Cause Analysis
+
+**Affected File:** [frontend/src/components/LiveLogViewer.tsx](../../frontend/src/components/LiveLogViewer.tsx)
+
+**Current Implementation (Lines 435-440):**
+```tsx
+
+```
+
+**Problems Identified:**
+
+1. **Fixed height (`h-96` = 384px)** - Does not adapt to viewport, wastes vertical space on large monitors
+2. **Multi-span log entries (Lines 448-497)** - Each log has multiple `` elements wrapped to new lines:
+ - Timestamp
+ - Source badge (security mode)
+ - Level badge (application mode)
+ - Client IP
+ - Message
+ - Block reason
+ - Status code
+ - Duration
+ - Details object
+3. **No horizontal layout optimization** - Missing `whitespace-nowrap` for single-line display
+4. **Font size fixed at `text-xs`** - No density options for users who prefer more/fewer lines
+
+### Requirements (EARS Notation)
+
+**R1.1 - Responsive Height**
+WHEN the LiveLogViewer is rendered on a viewport taller than 768px,
+THE SYSTEM SHALL expand the log container to use available vertical space (minimum 50vh).
+
+**R1.2 - Single-Line Log Format**
+WHEN the user enables "compact mode",
+THE SYSTEM SHALL display each log entry on a single line with horizontal scrolling.
+
+**R1.3 - Display Density Control**
+WHERE the logs panel settings are available,
+THE SYSTEM SHALL provide font size options: compact (text-xs), normal (text-sm), comfortable (text-base).
+
+**R1.4 - Preserve Existing Features**
+WHEN displaying logs in any mode,
+THE SYSTEM SHALL maintain all existing functionality: filtering, auto-scroll, pause, source badges, level colors.
+
+### Implementation Approach
+
+**Phase 1A: Responsive Height (Priority: Critical)**
+
+**File:** `frontend/src/components/LiveLogViewer.tsx`
+
+Replace fixed height with flex layout (avoids brittle pixel calculations):
+
+```tsx
+// Wrap the log viewer in a flex container
+
+
+ {/* Filter bar - fixed at top */}
+
+
+ {/* Log entries */}
+
+
+```
+
+**Key Changes:**
+- `flex flex-col` - Vertical flex container
+- `flex-shrink-0` - Filter bar doesn't shrink
+- `flex-1 min-h-0` - Log area grows to fill remaining space, `min-h-0` allows overflow scroll
+- Removed brittle `calc(100vh-300px)` in favor of flex layout
+
+**Phase 1B: Single-Line Compact Mode (Priority: High)**
+
+Add state and UI toggle:
+
+```tsx
+// Add after line ~55 (state declarations)
+const [compactMode, setCompactMode] = useState(false);
+
+// Add toggle in filter bar (after line ~395)
+
+```
+
+**Log Entry Scroll Behavior (UX Decision):**
+
+**Chosen: Option B - Truncate with Tooltip**
+
+Rationale: Per-entry horizontal scrolling (Option A) creates a poor UX with multiple scrollbars and makes it difficult to scan logs quickly. Truncation with tooltips provides:
+- Clean visual appearance
+- Full text available on hover
+- No horizontal scrollbar clutter
+- Better accessibility (screen readers announce full text)
+
+```tsx
+{/* Log entry container - truncate long messages with tooltip */}
+
+ {/* In compact mode, all spans are inline with flex-shrink-0 */}
+ {/* Timestamp */}
+
+ {formatTimestamp(log.timestamp)}
+
+ {/* Message - truncate in compact mode */}
+
+ {log.message}
+
+ {/* ... rest of spans */}
+
+
+// Helper function for tooltip
+const formatFullLogEntry = (log: LogEntry): string => {
+ return `${formatTimestamp(log.timestamp)} [${log.level}] ${log.client_ip || ''} ${log.message}`;
+};
+```
+
+**Phase 1C: Font Size/Density Control (Priority: Medium)**
+
+```tsx
+// State for density
+const [density, setDensity] = useState<'compact' | 'normal' | 'comfortable'>('compact');
+
+// Density dropdown in filter bar
+
+
+// Dynamic font class
+const fontSizeClass = {
+ compact: 'text-xs',
+ normal: 'text-sm',
+ comfortable: 'text-base',
+}[density];
+
+// Apply to container
+className={`... font-mono ${fontSizeClass} bg-black`}
+```
+
+### Testing Strategy
+
+**Unit Tests:** `frontend/src/components/__tests__/LiveLogViewer.test.tsx`
+- Test compact mode toggle renders single-line entries
+- Test density selection changes font class
+- Test responsive height classes applied
+- Test truncation with tooltip in compact mode
+
+**E2E Tests:** `tests/live-logs.spec.ts`
+- Verify compact mode toggle works
+- Verify tooltips show full log content on hover
+- Verify density selector changes log appearance
+- Visual regression tests for widescreen layout
+
+### Files to Modify
+
+| File | Changes |
+|------|---------|
+| [frontend/src/components/LiveLogViewer.tsx](../../frontend/src/components/LiveLogViewer.tsx#L435) | Responsive height, compact mode, density control |
+| `tests/live-logs.spec.ts` | E2E tests for new features |
+
+### Complexity Estimate
+
+- Phase 1A (Responsive): **S** (1-2 hours)
+- Phase 1B (Compact): **M** (3-4 hours)
+- Phase 1C (Density): **S** (1-2 hours)
+
+---
+
+## Issue 2: Caddy Import Not Working
+
+### Root Cause Analysis
+
+**Affected Files:**
+- [backend/internal/api/handlers/import_handler.go](../../backend/internal/api/handlers/import_handler.go)
+- [backend/internal/caddy/importer.go](../../backend/internal/caddy/importer.go)
+- [frontend/src/pages/ImportCaddy.tsx](../../frontend/src/pages/ImportCaddy.tsx)
+
+**Import Flow:**
+1. User uploads/pastes Caddyfile content
+2. Backend writes to temp file (`import/uploads/.caddyfile`)
+3. Calls `caddy adapt --config --adapter caddyfile` to convert to JSON
+4. Parses JSON to extract `reverse_proxy` handlers
+5. Returns hosts for review
+
+**Potential Failure Points Identified:**
+
+**Point A: Caddy Binary Not Available (Lines 12-17 of importer.go)**
+```go
+func (i *Importer) ValidateCaddyBinary() error {
+ _, err := i.executor.Execute(i.caddyBinaryPath, "version")
+ if err != nil {
+ return errors.New("caddy binary not found or not executable")
+ }
+ return nil
+}
+```
+- User's system may not have `caddy` in PATH
+- Docker container has it, but local dev might not
+
+**Point B: Complex Caddyfile Syntax (Lines 280-286 of importer.go)**
+```go
+if handler.Handler == "rewrite" {
+ host.Warnings = append(host.Warnings, "Rewrite rules not supported")
+}
+if handler.Handler == "file_server" {
+ host.Warnings = append(host.Warnings, "File server directives not supported")
+}
+```
+- Only `reverse_proxy` handlers are extracted
+- `file_server`, `rewrite`, `respond`, `redir` are not imported
+- Snippet blocks and macros may confuse the parser
+
+**Point C: Import Directives Require Multi-File (Lines 297-305 of import_handler.go)**
+```go
+if len(result.Hosts) == 0 {
+ imports := detectImportDirectives(req.Content)
+ if len(imports) > 0 {
+ c.JSON(http.StatusBadRequest, gin.H{
+ "error": "no sites found in uploaded Caddyfile; imports detected; please upload the referenced site files using the multi-file import flow",
+ "imports": imports,
+ })
+ return
+ }
+}
+```
+- If Caddyfile uses `import ./sites.d/*`, hosts are in external files
+- User must use multi-file upload flow but may not realize this
+
+**Point D: Silent Host Skipping (Lines 315-318 of importer.go)**
+```go
+if parsed.ForwardHost == "" || parsed.ForwardPort == 0 {
+ continue // Skip invalid entries
+}
+```
+- Hosts with no `reverse_proxy` backend are silently skipped
+- No feedback to user about which sites were ignored
+
+**Point E: Parse Errors Not Surfaced**
+- If `caddy adapt` fails, error is returned but may be cryptic
+- User sees "import failed: " without actionable guidance
+
+### Requirements (EARS Notation)
+
+**R2.1 - Import Directive Detection**
+WHEN user uploads a Caddyfile containing import directives,
+THE SYSTEM SHALL detect the import paths and prompt user to use multi-file import flow.
+
+**R2.2 - Parse Error Clarity**
+WHEN `caddy adapt` fails to parse the Caddyfile,
+THE SYSTEM SHALL return a user-friendly error message with the line number and suggestion.
+
+**R2.3 - Skipped Hosts Feedback**
+WHEN hosts are skipped due to missing `reverse_proxy` configuration,
+THE SYSTEM SHALL include a list of skipped domains with reasons in the response.
+
+**R2.4 - Supported Directives Documentation**
+WHEN displaying the import wizard,
+THE SYSTEM SHALL show a list of supported Caddyfile directives and known limitations.
+
+**R2.5 - Caddy Binary Validation**
+WHEN the import handler initializes,
+THE SYSTEM SHALL validate that the Caddy binary is available and return a clear error if not.
+
+### Implementation Approach
+
+**Phase 2A: Enhanced Error Messages (Priority: Critical)**
+
+**File:** `backend/internal/caddy/importer.go`
+
+```go
+// ParseCaddyfile - enhance error handling
+func (i *Importer) ParseCaddyfile(caddyfilePath string) ([]byte, error) {
+ output, err := i.executor.Execute(i.caddyBinaryPath, "adapt", "--config", caddyfilePath, "--adapter", "caddyfile")
+ if err != nil {
+ // Parse caddy error output for line numbers
+ errMsg := string(output)
+ if strings.Contains(errMsg, "line") {
+ return nil, fmt.Errorf("Caddyfile syntax error: %s", extractLineError(errMsg))
+ }
+ return nil, fmt.Errorf("failed to parse Caddyfile: %v", err)
+ }
+ return output, nil
+}
+
+func extractLineError(errOutput string) string {
+ // Extract "line X: error message" format from caddy output
+ re := regexp.MustCompile(`(?i)line\s+(\d+):\s*(.+)`)
+ if match := re.FindStringSubmatch(errOutput); len(match) > 2 {
+ return fmt.Sprintf("Line %s: %s", match[1], match[2])
+ }
+ return errOutput
+}
+```
+
+**Phase 2B: Skipped Hosts Feedback (Priority: High)**
+
+**File:** `backend/internal/caddy/importer.go`
+
+```go
+type ImportResult struct {
+ Hosts []ParsedHost
+ Skipped []SkippedHost // NEW: Add skipped hosts tracking
+ Warnings []string
+ ParsedAt time.Time
+}
+
+type SkippedHost struct {
+ DomainNames string `json:"domain_names"`
+ Reason string `json:"reason"`
+}
+
+// In ConvertToProxyHosts
+func ConvertToProxyHostsWithSkipped(parsedHosts []ParsedHost) ([]models.ProxyHost, []SkippedHost) {
+ hosts := make([]models.ProxyHost, 0)
+ skipped := make([]SkippedHost, 0)
+
+ for _, parsed := range parsedHosts {
+ if parsed.ForwardHost == "" || parsed.ForwardPort == 0 {
+ skipped = append(skipped, SkippedHost{
+ DomainNames: parsed.DomainNames,
+ Reason: "No reverse_proxy backend defined",
+ })
+ continue
+ }
+ // ... normal conversion
+ }
+ return hosts, skipped
+}
+```
+
+**File:** `backend/internal/api/handlers/import_handler.go`
+
+```go
+// In Upload handler response (line ~335)
+c.JSON(http.StatusOK, gin.H{
+ "session": gin.H{"id": sid, "state": "transient"},
+ "preview": transient,
+ "conflict_details": conflictDetails,
+ "skipped_hosts": skippedHosts, // NEW: Include in response
+})
+```
+
+**Phase 2C: Frontend Skipped Hosts Display (Priority: High)**
+
+**File:** `frontend/src/pages/ImportCaddy.tsx`
+
+```tsx
+// Add skipped hosts display in review step
+// Note: Use snake_case to match backend JSON field naming convention
+{importData.skipped_hosts?.length > 0 && (
+