# ArchNest — Design Decisions & Lessons Learned

> This file captures all visual/UX decisions made during Glance page development.
> Apply these consistently to ALL future pages to avoid repeated iteration.

---

## Global Rules (Apply to Every Page)

### Sidebar
- **Expanded width**: 200px (matches mockup proportions — needs room for labels)
- **Collapsed width**: 64px (icon only)
- **User can manually collapse/expand** via toggle button (not just responsive)
- **Main content margin-left** must match sidebar width exactly

### Page Title (Top Bar)
- **Color**: Gold (#C8A434) — NOT white. Use inline `style={{ color: '#C8A434' }}` if Tailwind class doesn't apply
- **Font**: 18px, bold, uppercase, tracking-wide
- **No border on top bar** — blends into the page background

### Colors — Use Inline Styles When Tailwind Fails
- Tailwind v4 `@theme` custom colors (text-gold, bg-card, etc.) may not always apply
- If a color isn't rendering correctly, fall back to inline `style={{ color: '#C8A434' }}`
- Always verify visually after changes

### Content Alignment
- **All rows must share the same horizontal padding** (`px-6` applied once at the parent container level)
- **Do NOT** use different padding for different rows — this causes misalignment
- The hero banner, status cards, middle row, and bottom row must all line up left and right edges

### Hero Banner + KPI Overlap
- Banner height: 200px
- Status cards overlap via negative margin: `-mt-12`
- Banner image: `object-cover` with `object-position: center 25%` (show the top/skyline, not center)
- Cards use `backdrop-blur-sm` and `bg-card/95` for glass effect over the banner

### KPI Card Sizing
- KPI 1 (System Status) and KPI 4 (Network): **wider** — `1.3fr`
- KPI 2 (Infrastructure) and KPI 3 (Security): **standard** — `1fr`
- Grid: `grid-cols-[1.3fr_1fr_1fr_1.3fr]`
- Cards have compact padding: `p-4` (not p-5 or p-6)

### No Footer
- The mockup does NOT have a footer/status bar
- Do not add one unless explicitly requested

### Target Display
- Primary design target: **16-inch screen / 1920px width**
- Lots of horizontal space available — don't constrain content width unnecessarily
- Design should feel spacious, not cramped

### Typography Sizes (smaller than default)
- Card titles: 10-11px, uppercase, tracking-[1.5px], secondary color, font-medium
- Large numbers: 24-28px, bold, primary color
- Subtitles/labels: 10-11px, secondary color
- Body text in lists: 13px, primary color
- Timestamps: 11px, secondary color
- Breakdowns: 9-10px, secondary color

### Animations
- Card hover: border → gold, 0.2s ease
- Progress ring: animates from 0 to value in 1s
- Sparklines: draw animation 1s
- Progress bars: fill animation 0.8s

### Icons
- Source: Lucide React (imported per component, tree-shaken)
- Size: 14-18px depending on context
- Color: gold for active/accent, text-secondary for inactive
- Gold glow on active sidebar items: `shadow-[0_0_6px_rgba(200,164,52,0.5)]`

---

## Page-Specific Notes

### Glance Page
- No footer
- Status cards overlap hero banner
- Middle row: 3 equal-ish columns (30/40/30)
- Bottom row: 2 columns (65/35)
- Network Traffic card has its own background image at low opacity
- User avatar dropdown has: Profile, Appearance, Security, Help & Support, Sign Out

### Infrastructure Page
- Hero banner rendered at the **App.tsx layout level** (not per-page), so it can extend
  behind the sticky TopBar — conditional via `showHero = location.pathname === '/infrastructure'`.
  TopBar and the search input are transparent on hero routes so the banner shows through.
- Hero image: `object-position: center 5%` to reveal the full arch + sky; faded out via
  `linear-gradient` mask (vertical) + a `radial-gradient(ellipse 70% 100% at center, ...)`
  overlay (sides/corners) — borderless, blends into page background.
- Sub-tabs trimmed to **Overview only** (Compute → Tags are future work, not built yet).
- Status cards: `rgba(10,10,12,0.5)` background (more transparent than other pages),
  content centered (`justify-content/alignItems: center`) with a fixed row height (110px)
  so there's breathing room instead of empty space below left-aligned content.
- Middle row (`grid-cols-[1fr_1.6fr]`): **Resource Distribution** (donut) and **Node Status**
  (server tile grid). Both use the `/blank-kpi-bg.png` background art with a `cardDim`
  (semi-transparent dark overlay) + `cardVignette` (radial-gradient `closest-side` blend)
  combo — keeps the background pattern visible but subdued, with borders blended rather
  than hard-edged. Card titles are rendered as our own text, NOT baked into the image
  (baked-in labels got covered by the dim overlay).
- **Node Status** card: originally a world-map-style region dot plot, replaced with a
  4-column tile grid (one tile per server, colored status dot + name) — a world map
  didn't make sense for a small/single-site infra. Reuse this "small-scale" reasoning
  for any future map-like cards.
- Bottom row (`grid-cols-[1.4fr_1fr_1fr]`): Resource Trend / Cost Breakdown / Recent
  Activity — **left plain/regular**, no dim/vignette blending (explicit user preference,
  only the middle row gets the hero-style blend). Resource Trend uses the
  `/archnest-network-traffic-bg.png` background (plain, no dim/vignette) with 4 trend
  lines: blue `#3B82F6` (compute), orange `#E67E22` (storage), green `#2ECC71` (database),
  brown `#8B5E3C` (network).
- `cardVignette` radial-gradient must use the `closest-side` keyword (not a fixed `%`)
  — otherwise straight edges of the card don't reach full opacity and a hard border line
  remains visible (only corners fade correctly with a fixed percentage).

### BookNest Page
- Hero banner reused at the App.tsx layout level (`showHero` now includes `/booknest`),
  with page-specific tuning via small lookup maps in `App.tsx` keyed on `location.pathname`:
  `heroPaddingTop` (how far content sits below the hero top) and `heroObjectPosition`
  (horizontal/vertical crop of the arch image) — `70px` / `54% 8%` for BookNest vs.
  `72px` / `center 5%` for Infrastructure. Extend these maps rather than hardcoding a
  single value when a future page needs different hero framing.
- **Large hero title + subtitle**: unlike other pages, BookNest's TopBar title is NOT the
  small 18px uppercase label — it's rendered at 28px with a subtitle line ("Your Digital
  Library") underneath, driven by a new `pageSubtitles` map in `TopBar.tsx`. When a page
  has a subtitle, the header height grows from 56px → 72px (`TopBar.tsx`), and `App.tsx`'s
  `topBarHeight` lookup keeps the content section's `calc(100vh - Npx)` in sync — both
  must be updated together or the layout will clip/gap.
- **Stats row lives directly under the hero subtitle** (Links / Categories / Favorites),
  not in its own separate bar — matches the blueprint's hero-header block grouping.
- **"Quick Access" section label** added above the 5 quick-access category cards (gold,
  same `sectionTitle` style) — the row is intentionally pulled up via a small negative
  hero-padding tune so it slightly overlaps the bottom edge of the hero, like the blueprint.
- **"+ Add Bookmark" button**: same gold-fill button style as Infrastructure's "+ Add
  Resource", placed inline next to the "Quick Access" label rather than the page-stats row.
- **Right sidebar spans both grid rows** (`gridRow: '1 / span 2'` in a `gridTemplateRows:
  'auto 1fr'` grid) so the Favorites card can rise up near the hero while the main column's
  page-stats row stays in row 1 of column 1 only — keeps the two from overlapping/clipping.
  Negative margins were tried first and discarded: content pushed above the scroll
  container's natural top edge gets clipped by `overflow-y-auto`, so prefer reshaping the
  grid/flow over negative-margin hacks when something needs to "reach upward."
- **Sidebar cards stretch to match the main column's full height** so the last card's
  (Category Breakdown) bottom border lines up with the bottom of the bookmark groups grid:
  sidebar wrapper is `display:flex; flex-direction:column; height:100%` (grid's default
  `align-items: stretch` already gives it the matching height), and each card uses
  `flex: 1 0 auto` (Favorites gets `flex: 1.4 0 auto` to read as visibly taller, per
  explicit request) so they share the leftover vertical space instead of all packing tight
  at content height.
- lucide-react gotcha (see Global Rules candidate): the installed version does **not**
  export brand/wordmark icons (`Github`, `Gitlab`, `Linkedin`, `Youtube`) even though
  TypeScript's type declarations list them — `tsc --noEmit` stays clean while the page
  renders blank with a runtime `SyntaxError` only visible in the Vite dev log. Verify
  icon names against `Object.keys(require('lucide-react'))` before importing anything
  brand-flavored; substitutes used here: `GitBranch`/`GitFork` (GitHub/GitLab/Gitea),
  `SquarePlay` (YouTube), `Briefcase` (LinkedIn Learning).

### Settings Page
- No mockup image existed for this page — built directly from the blueprint's Page 6
  spec rather than iterating against a screenshot.
- Layout: fixed-width (200px) left nav listing the 6 sections (Profile, Appearance,
  Integrations, Notifications, Data & Backup, About) + a scrollable content panel on
  the right showing the active section. No hero banner (not in the blueprint spec for
  this page, and a settings page doesn't need one).
- Active section is local component state (a string id) mapped through a
  `sectionComponents` record to the corresponding section-renderer function — simplest
  approach for a page with no routing/deep-linking requirement.
- Shared style helpers (`cardBase`, `sectionTitle`, `labelStyle`, `inputStyle`) plus two
  small reusable components defined in the same file: `Toggle` (on/off pill switch) and
  `GoldButton` (gold-filled primary / danger-outline variant) — kept local to
  `Settings.tsx` rather than extracted, since no other page needs them yet.
- **Integrations** cards mask secret fields (API keys/tokens) behind dots with an eye
  icon to reveal/hide, plus a "Test Connection" button per card — matches the blueprint's
  explicit "masked secrets with eye toggle" instruction.
- **Avatar upload** (Profile section): the avatar circle is clickable and opens a hidden
  `<input type="file" accept="image/*">` via a `useRef` + `.click()` call rather than a
  visible file input — keeps the round avatar as the only visible control. On change,
  `FileReader.readAsDataURL` converts the selected image to a base64 data URL stored in
  component state, which becomes the circle's `backgroundImage` (cover-fit), replacing
  the "AO" initials fallback. A hover-only camera-icon overlay (Tailwind `group` /
  `group-hover:opacity-100`) signals the circle is clickable without cluttering the
  default state. This is a frontend-only preview (no backend upload endpoint exists yet).

---

## Backend (added once frontend reached "good enough" state)

- New `backend/` package (separate `package.json`/`tsconfig.json`, own `node_modules`,
  own Dockerfile) — Fastify + TypeScript, `better-sqlite3` for storage, deployed as a
  second container alongside the existing frontend container (`docker-compose.yml` now
  has `archnest` + `archnest-backend`, with a named volume for the SQLite file).
- Auth: single-user, JWT-based (`@fastify/jwt`). `POST /api/setup` creates the one user
  and only succeeds while the `users` table is empty — this is what powers the
  first-run enrollment page. `GET /api/system/setup-status` tells the frontend whether
  to show enrollment/login or the normal app.
- Integration credentials are split across two tables: `integrations` (type, name,
  status, non-secret `config_json`) and `secrets` (per-key AES-256-GCM-encrypted
  values, key derived from the `ARCHNEST_SECRET_KEY` env var) — keeps secrets out of any
  generic "list integrations" query/response by construction, not by remembering to
  redact a field.
- Each integration type has an adapter module under `backend/src/integrations/`
  exporting `testConnection(config, secrets)`; `registry.ts` maps `IntegrationType` →
  adapter. Only `uptime_kuma` and `docker` are real so far (simple HTTP health checks);
  the rest return a "not yet implemented" result until built out — this lets the
  Integrations UI and `POST /api/integrations/:id/test` endpoint work end-to-end for
  every type without blocking on every adapter being finished.
- Vite dev server proxies `/api` → `http://localhost:4000` (`vite.config.ts`) so the
  frontend can call relative `/api/...` paths in both dev and prod (prod routes `/api`
  to the backend container via NPM).
- Next steps (not yet done): build the enrollment/login frontend pages, strip the mock
  arrays out of Glance/Infrastructure/BookNest/Settings and replace with calls to this
  API, add bookmark category seeding.

## Future Integration Notes

### Live Provider Data (AWS, Linode, etc.)
- All KPI/status card data (resource counts, health, pricing, budgets, cost breakdowns,
  utilization, regions/map data) is currently mocked/static.
- The Infrastructure page (and likely Glance) should eventually integrate with real
  cloud provider APIs — AWS, Linode, or any other VPC/cloud provider — via user-supplied
  API keys, to pull live data such as:
  - Resource inventory/counts and health status
  - Pricing and budget/cost data (replacing the static Cost Breakdown numbers)
  - Resource utilization metrics
  - Region/datacenter info for the Infrastructure Map
- Design the data layer so it's provider-agnostic (a common interface/adapter per
  provider) since users may connect more than one provider's API key.
