From 3c0030c9a376a270f5be9e1a4dbc5fb43b12cc36 Mon Sep 17 00:00:00 2001 From: Sam Rolfe Date: Sun, 11 Oct 2026 19:58:25 +1100 Subject: [PATCH] Phase 1.2: session login + empty HTMX dashboard - Session login for the single user Sam: server-side session store with an HMAC-signed session cookie, POST /login, POST /logout, and double-submit CSRF on every form post. Unauthenticated requests to / and /partials/* redirect to /login?next=... - Empty dashboard shell in the Family Home Lab design system: left nav (category and folder placeholders), centre list, right detail pane. - Copy tokens.css and the self-hosted Inter fonts from the family-home-lab design system into web/static; vendor htmx 2.0.4. No cross-project imports at runtime. - Dark mode via prefers-color-scheme plus a persisted toggle; phone-first layout with 44px touch targets, hairline borders, 8px grid, pill CTAs. - Config: EMAIL_AGENT_PASSWORD for the login password, falling back to EMAIL_AGENT_SESSION_SECRET so one configured secret is enough. Co-Authored-By: Paperclip --- .env.example | 5 +- README.md | 35 ++-- cmd/email-agent/main.go | 4 +- docker-compose.yml | 1 + internal/config/config.go | 5 + internal/server/auth.go | 284 ++++++++++++++++++++++++++++++ internal/server/server.go | 124 +++++++------ internal/server/server_test.go | 197 ++++++++++++++++++++- internal/session/session.go | 196 +++++++++++++++++++++ internal/session/session_test.go | 78 ++++++++ web/embed.go | 23 +++ web/static/app.css | 293 +++++++++++++++++++++++++++++++ web/static/fonts/inter-400.woff2 | Bin 0 -> 23692 bytes web/static/fonts/inter-500.woff2 | Bin 0 -> 24368 bytes web/static/fonts/inter-600.woff2 | Bin 0 -> 24304 bytes web/static/fonts/inter-700.woff2 | Bin 0 -> 24352 bytes web/static/htmx.min.js | 1 + web/static/tokens.css | 110 ++++++++++++ web/templates/dashboard.html | 97 ++++++++++ web/templates/inbox.html | 5 + web/templates/login.html | 45 +++++ 21 files changed, 1423 insertions(+), 80 deletions(-) create mode 100644 internal/server/auth.go create mode 100644 internal/session/session.go create mode 100644 internal/session/session_test.go create mode 100644 web/embed.go create mode 100644 web/static/app.css create mode 100644 web/static/fonts/inter-400.woff2 create mode 100644 web/static/fonts/inter-500.woff2 create mode 100644 web/static/fonts/inter-600.woff2 create mode 100644 web/static/fonts/inter-700.woff2 create mode 100644 web/static/htmx.min.js create mode 100644 web/static/tokens.css create mode 100644 web/templates/dashboard.html create mode 100644 web/templates/inbox.html create mode 100644 web/templates/login.html diff --git a/.env.example b/.env.example index f8b418e..1f13f52 100644 --- a/.env.example +++ b/.env.example @@ -15,8 +15,11 @@ EMAIL_AGENT_LOG_LEVEL=info EMAIL_AGENT_PUBLIC_BASE_URL=https://mail.lab.audasmedia.com.au # --- Session / auth -------------------------------------------------------- -# Generate with: openssl rand -hex 32 +# Session cookie signing key. Generate with: python3 -c 'import secrets;print(secrets.token_hex(32))' EMAIL_AGENT_SESSION_SECRET=change-me +# Login password for the single user (Sam). If unset, the session secret above +# doubles as the password. +EMAIL_AGENT_PASSWORD= # --- Integrations (used from later phases) --------------------------------- JEV_BASE_URL= diff --git a/README.md b/README.md index 0d9bdde..486ccbf 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,22 @@ pre-planning material and is not authoritative. ## Status -**Phase 1.1 — Go service skeleton.** Complete when this file was written: -config from the environment, SQLite schema init, `GET /health`, Dockerfile and -docker-compose. Mail fetch, classification, login, and the HTMX screens come in -later phases. +**Phase 1.2 — Session login + empty dashboard.** The Go skeleton (Phase 1.1) +plus a single-user session login, a copied Family Home Lab `tokens.css`, and an +HTMX dashboard shell (left nav, centre list, right detail pane) with dark mode. +Mail fetch, classification, and the rest come in later phases. ## Layout ``` cmd/email-agent/ service entry point internal/config/ environment-driven configuration +internal/session/ server-side session store + signed cookie values internal/store/ SQLite open + schema (internal/store/schema.sql) -internal/server/ HTTP routes and middleware +internal/server/ HTTP routes, auth middleware, HTMX handlers internal/version/ build metadata (set via -ldflags) +web/static/ embedded design tokens, Inter fonts, htmx, app.css +web/templates/ embedded server-rendered HTML Dockerfile multi-stage, CGO-free build docker-compose.yml this project's own container and image ``` @@ -42,7 +45,8 @@ Key variables: | `EMAIL_AGENT_PORT` | `8790` | Listen port (used when `EMAIL_AGENT_ADDR` is unset) | | `EMAIL_AGENT_DB_PATH` | `data/email_agent.db` | SQLite file | | `EMAIL_AGENT_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error` | -| `EMAIL_AGENT_SESSION_SECRET` | _(empty)_ | Session cookie signing key (Phase 1.2) | +| `EMAIL_AGENT_SESSION_SECRET` | _(empty)_ | Session cookie signing key. If unset, an ephemeral key is used (sessions reset on restart) | +| `EMAIL_AGENT_PASSWORD` | _(empty)_ | Login password for Sam. If unset, `EMAIL_AGENT_SESSION_SECRET` doubles as the password | ## Run locally @@ -65,11 +69,20 @@ Caddy on `.35` proxies `mail.lab.audasmedia.com.au` to this port. ## Endpoints (Phase 1) -| Method | Path | Purpose | -|---|---|---| -| `GET` | `/health` | Liveness + database check, JSON | -| `GET` | `/readyz` | Readiness, JSON | -| `GET` | `/` | Placeholder dashboard (real screens in Phase 1.2) | +| Method | Path | Auth | Purpose | +|---|---|---|---| +| `GET` | `/health` | public | Liveness + database check, JSON | +| `GET` | `/readyz` | public | Readiness, JSON | +| `GET` | `/login` | public | Sign-in form (deep-indigo band) | +| `POST` | `/login` | public | CSRF-checked password sign-in, sets the session cookie | +| `POST` | `/logout` | session | Destroys the session, returns to `/login` | +| `GET` | `/` | session | Empty dashboard shell | +| `GET` | `/partials/inbox` | session | HTMX fragment for the centre list | +| `GET` | `/static/…` | public | Embedded `tokens.css`, `app.css`, Inter fonts, `htmx.min.js` | + +Unauthenticated requests to `/` and `/partials/…` redirect to `/login?next=…`. +Form posts use a double-submit CSRF token (`email_agent_csrf` cookie plus a +hidden `csrf` field). ## Database diff --git a/cmd/email-agent/main.go b/cmd/email-agent/main.go index 7025622..f27ba1f 100644 --- a/cmd/email-agent/main.go +++ b/cmd/email-agent/main.go @@ -60,8 +60,8 @@ func run(cfg *config.Config, logger *slog.Logger) error { "env", cfg.Env, ) - if cfg.SessionSecret == "" { - logger.Warn("EMAIL_AGENT_SESSION_SECRET is not set; set it before enabling login") + if cfg.SessionSecret == "" && cfg.Password == "" { + logger.Warn("no EMAIL_AGENT_PASSWORD or EMAIL_AGENT_SESSION_SECRET set; login is disabled until one is configured") } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) diff --git a/docker-compose.yml b/docker-compose.yml index 7760bf3..57a0a94 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,7 @@ services: # (see ~/.config/environment.d/10-secrets.conf) or a gitignored .env file, # and are never committed. EMAIL_AGENT_SESSION_SECRET: ${EMAIL_AGENT_SESSION_SECRET:-} + EMAIL_AGENT_PASSWORD: ${EMAIL_AGENT_PASSWORD:-} JEV_BASE_URL: ${JEV_BASE_URL:-} JEV_API_KEY: ${JEV_API_KEY:-} OMNIROUTE_BASE_URL: ${OMNIROUTE_BASE_URL:-} diff --git a/internal/config/config.go b/internal/config/config.go index f244e5a..a19e371 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -42,6 +42,10 @@ type Config struct { // service has a single configuration surface. SessionSecret string + // Password is the single-user login password. When it is empty the service + // falls back to SessionSecret so one configured secret is enough. Secret. + Password string + // LogLevel controls slog verbosity: debug, info, warn, error. LogLevel string @@ -75,6 +79,7 @@ func Load() (*Config, error) { c := &Config{ Env: getenvDefault("EMAIL_AGENT_ENV", DefaultEnv), SessionSecret: os.Getenv("EMAIL_AGENT_SESSION_SECRET"), + Password: os.Getenv("EMAIL_AGENT_PASSWORD"), LogLevel: strings.ToLower(getenvDefault("EMAIL_AGENT_LOG_LEVEL", "info")), PublicBaseURL: strings.TrimRight(getenvDefault("EMAIL_AGENT_PUBLIC_BASE_URL", "http://localhost:8790"), "/"), JEVBaseURL: strings.TrimRight(os.Getenv("JEV_BASE_URL"), "/"), diff --git a/internal/server/auth.go b/internal/server/auth.go new file mode 100644 index 0000000..c0ff3d3 --- /dev/null +++ b/internal/server/auth.go @@ -0,0 +1,284 @@ +package server + +import ( + "context" + "crypto/subtle" + "net/http" + "net/url" + "strings" + "time" + + "gitea.lab.audasmedia.com.au/sam/email_agent/internal/session" + "gitea.lab.audasmedia.com.au/sam/email_agent/internal/version" +) + +const ( + // csrfCookieName holds the double-submit CSRF token. It is independent of + // the session so the login form (which has no session yet) is protected too. + csrfCookieName = "email_agent_csrf" + sessionTTL = session.DefaultTTL +) + +// ctxKey is the private context key type for request-scoped values. +type ctxKey int + +const sessionCtxKey ctxKey = iota + +// requireAuth redirects unauthenticated requests to the login screen and +// otherwise puts the session on the request context. +func (s *Server) requireAuth(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + sess, ok := s.currentSession(r) + if !ok { + target := "/login?next=" + url.QueryEscape(r.URL.RequestURI()) + http.Redirect(w, r, target, http.StatusSeeOther) + return + } + next(w, r.WithContext(context.WithValue(r.Context(), sessionCtxKey, sess))) + } +} + +func (s *Server) currentSession(r *http.Request) (session.Session, bool) { + c, err := r.Cookie(session.CookieName) + if err != nil || c.Value == "" { + return session.Session{}, false + } + return s.sessions.Lookup(c.Value) +} + +func sessionFrom(r *http.Request) session.Session { + sess, _ := r.Context().Value(sessionCtxKey).(session.Session) + return sess +} + +// loginSecret is the password the single user must supply. EMAIL_AGENT_PASSWORD +// wins; otherwise the session secret doubles as the password so a single +// configured secret is enough. +func (s *Server) loginSecret() string { + if s.cfg.Password != "" { + return s.cfg.Password + } + return s.cfg.SessionSecret +} + +// csrfToken returns the current double-submit token, minting one when absent. +func (s *Server) csrfToken(w http.ResponseWriter, r *http.Request) string { + if c, err := r.Cookie(csrfCookieName); err == nil && c.Value != "" { + return c.Value + } + tok, err := session.RandomToken(32) + if err != nil { + // crypto/rand failing is fatal for security; surface it rather than + // issuing an unprotected form. + s.logger.Error("csrf token generation failed", "error", err) + return "" + } + http.SetCookie(w, &http.Cookie{ + Name: csrfCookieName, + Value: tok, + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + Secure: s.cfg.IsProduction(), + MaxAge: int(sessionTTL.Seconds()), + }) + return tok +} + +// csrfOK verifies the double-submit token from the form or the X-CSRF-Token +// header. +func (s *Server) csrfOK(r *http.Request) bool { + c, err := r.Cookie(csrfCookieName) + if err != nil || c.Value == "" { + return false + } + tok := r.FormValue("csrf") + if tok == "" { + tok = r.Header.Get("X-CSRF-Token") + } + return subtle.ConstantTimeCompare([]byte(tok), []byte(c.Value)) == 1 +} + +func (s *Server) setSessionCookie(w http.ResponseWriter, value string, expires time.Time) { + http.SetCookie(w, &http.Cookie{ + Name: session.CookieName, + Value: value, + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + Secure: s.cfg.IsProduction(), + Expires: expires, + MaxAge: int(sessionTTL.Seconds()), + }) +} + +func (s *Server) clearSessionCookie(w http.ResponseWriter) { + http.SetCookie(w, &http.Cookie{ + Name: session.CookieName, + Value: "", + Path: "/", + HttpOnly: true, + SameSite: http.SameSiteLaxMode, + Secure: s.cfg.IsProduction(), + MaxAge: -1, + }) +} + +type loginData struct { + PartOfDay string + Error string + Next string + CSRFToken string + Configured bool +} + +func (s *Server) renderLogin(w http.ResponseWriter, r *http.Request, code int, errMsg, next string) { + // Mint the CSRF cookie before the status line, or Set-Cookie is dropped. + csrf := s.csrfToken(w, r) + w.Header().Set("Content-Type", "text/html; charset=utf-8") + w.WriteHeader(code) + data := loginData{ + PartOfDay: partOfDay(time.Now()), + Error: errMsg, + Next: next, + CSRFToken: csrf, + Configured: s.loginSecret() != "", + } + if err := s.tmpl.ExecuteTemplate(w, "login.html", data); err != nil { + s.logger.Error("render login", "error", err) + } +} + +// handleLoginGet renders the sign-in form. +func (s *Server) handleLoginGet(w http.ResponseWriter, r *http.Request) { + if _, ok := s.currentSession(r); ok { + http.Redirect(w, r, cleanNext(r.URL.Query().Get("next")), http.StatusSeeOther) + return + } + s.renderLogin(w, r, http.StatusOK, "", cleanNext(r.URL.Query().Get("next"))) +} + +// handleLoginPost validates the CSRF token and password, then starts a session. +// POST (not GET) means the password is never placed in the URL or browser +// history. +func (s *Server) handleLoginPost(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + s.renderLogin(w, r, http.StatusBadRequest, "Could not read the sign-in form.", "/") + return + } + next := cleanNext(r.FormValue("next")) + + if !s.csrfOK(r) { + s.renderLogin(w, r, http.StatusForbidden, "Your sign-in page expired. Please try again.", next) + return + } + + secret := s.loginSecret() + if secret == "" { + s.renderLogin(w, r, http.StatusServiceUnavailable, "Login is not configured yet.", next) + return + } + + if subtle.ConstantTimeCompare([]byte(r.FormValue("password")), []byte(secret)) != 1 { + s.logger.Warn("login failed", "remote", r.RemoteAddr) + s.renderLogin(w, r, http.StatusUnauthorized, "Incorrect password.", next) + return + } + + sess, cookie, err := s.sessions.New("Sam") + if err != nil { + s.logger.Error("create session", "error", err) + http.Error(w, "could not start a session", http.StatusInternalServerError) + return + } + s.setSessionCookie(w, cookie, sess.ExpiresAt) + s.logger.Info("login ok", "user", sess.User, "remote", r.RemoteAddr) + http.Redirect(w, r, next, http.StatusSeeOther) +} + +// handleLogout destroys the session and returns to the login screen. +func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) { + _ = r.ParseForm() + if !s.csrfOK(r) { + http.Error(w, "invalid CSRF token", http.StatusForbidden) + return + } + if c, err := r.Cookie(session.CookieName); err == nil && c.Value != "" { + s.sessions.Destroy(c.Value) + } + s.clearSessionCookie(w) + http.Redirect(w, r, "/login", http.StatusSeeOther) +} + +type navItem struct { + Name string + Color string +} + +type dashboardData struct { + Greeting string + User string + CSRFToken string + Version string + Categories []navItem + Folders []navItem +} + +// handleDashboard renders the empty inbox shell. +func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) { + sess := sessionFrom(r) + data := dashboardData{ + Greeting: partOfDay(time.Now()), + User: sess.User, + CSRFToken: s.csrfToken(w, r), + Version: version.Version, + // Placeholders until FR-14 (editable categories) and FR-9 (folders) + // land. Colours are the design system's decorative sticker palette. + Categories: []navItem{ + {Name: "Action", Color: "var(--primary)"}, + {Name: "Important", Color: "var(--sticker-orange)"}, + {Name: "Info", Color: "var(--sticker-sky)"}, + {Name: "Spam", Color: "var(--sticker-red)"}, + {Name: "Newsletter", Color: "var(--sticker-purple)"}, + {Name: "Auto-reply", Color: "var(--sticker-teal)"}, + }, + Folders: []navItem{ + {Name: "Replied-to", Color: "var(--sticker-green)"}, + {Name: "Delete", Color: "var(--sticker-red)"}, + {Name: "Saved", Color: "var(--sticker-teal)"}, + }, + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := s.tmpl.ExecuteTemplate(w, "dashboard.html", data); err != nil { + s.logger.Error("render dashboard", "error", err) + } +} + +// handleInboxPartial is the HTMX fragment for the centre list. Phase 1.2 has no +// mail yet, so it renders the empty state. +func (s *Server) handleInboxPartial(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := s.tmpl.ExecuteTemplate(w, "inbox.html", nil); err != nil { + s.logger.Error("render inbox partial", "error", err) + } +} + +// cleanNext only allows same-site, absolute-path redirects. +func cleanNext(next string) string { + if next == "" || !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") { + return "/" + } + return next +} + +// partOfDay returns the time-aware greeting prefix. +func partOfDay(t time.Time) string { + switch h := t.Hour(); { + case h < 12: + return "Good morning" + case h < 18: + return "Good afternoon" + default: + return "Good evening" + } +} diff --git a/internal/server/server.go b/internal/server/server.go index b078024..67f7097 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,45 +1,66 @@ // Package server wires the HTTP surface of the Email Agent. // -// Phase 1 exposes liveness/readiness endpoints and a placeholder dashboard. -// Login and the real HTMX screens arrive in Phase 1.2. +// Phase 1.2 exposes a session login and an empty HTMX dashboard shell in the +// Family Home Lab design system. Mail fetch arrives in Phase 2. package server import ( "context" + "crypto/rand" "encoding/json" "errors" "fmt" "html/template" + "io/fs" "log/slog" + "mime" "net/http" "time" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/config" + "gitea.lab.audasmedia.com.au/sam/email_agent/internal/session" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/store" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/version" + "gitea.lab.audasmedia.com.au/sam/email_agent/web" ) +func init() { + // Go's mime table has no woff2 entry; the self-hosted Inter fonts need it. + _ = mime.AddExtensionType(".woff2", "font/woff2") +} + // Server holds the HTTP dependencies. type Server struct { - cfg *config.Config - store *store.Store - logger *slog.Logger - http *http.Server - index *template.Template + cfg *config.Config + store *store.Store + logger *slog.Logger + http *http.Server + tmpl *template.Template + sessions *session.Manager } // New builds a Server. It does not start listening. func New(cfg *config.Config, st *store.Store, logger *slog.Logger) (*Server, error) { - index, err := template.New("index").Parse(indexHTML) + tmpl, err := template.ParseFS(web.TemplateFS, "templates/*.html") if err != nil { - return nil, fmt.Errorf("parse index template: %w", err) + return nil, fmt.Errorf("parse templates: %w", err) + } + + key, err := sessionKey(cfg, logger) + if err != nil { + return nil, fmt.Errorf("session key: %w", err) + } + sessions, err := session.NewManager(key, session.DefaultTTL) + if err != nil { + return nil, fmt.Errorf("session manager: %w", err) } s := &Server{ - cfg: cfg, - store: st, - logger: logger, - index: index, + cfg: cfg, + store: st, + logger: logger, + tmpl: tmpl, + sessions: sessions, } s.http = &http.Server{ Addr: cfg.Addr, @@ -52,12 +73,43 @@ func New(cfg *config.Config, st *store.Store, logger *slog.Logger) (*Server, err return s, nil } +// sessionKey returns the cookie signing key. In production it comes from +// EMAIL_AGENT_SESSION_SECRET; with no secret configured the service still +// starts with an ephemeral key so it is usable, but sessions do not survive a +// restart and the operator sees a warning. +func sessionKey(cfg *config.Config, logger *slog.Logger) ([]byte, error) { + if cfg.SessionSecret != "" { + return []byte(cfg.SessionSecret), nil + } + key := make([]byte, 32) + if _, err := rand.Read(key); err != nil { + return nil, err + } + logger.Warn("EMAIL_AGENT_SESSION_SECRET is not set; using an ephemeral session key (sessions reset on restart)") + return key, nil +} + // Handler returns the root HTTP handler with middleware applied. func (s *Server) Handler() http.Handler { + staticFS, err := fs.Sub(web.StaticFS, "static") + if err != nil { + // web.StaticFS is a compile-time constant; this cannot fail at runtime. + panic(fmt.Sprintf("static assets: %v", err)) + } + static := http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))) + mux := http.NewServeMux() mux.HandleFunc("GET /health", s.handleHealth) mux.HandleFunc("GET /readyz", s.handleReady) - mux.HandleFunc("GET /{$}", s.handleIndex) + mux.Handle("GET /static/", static) + + mux.HandleFunc("GET /login", s.handleLoginGet) + mux.HandleFunc("POST /login", s.handleLoginPost) + mux.HandleFunc("POST /logout", s.handleLogout) + + mux.HandleFunc("GET /{$}", s.requireAuth(s.handleDashboard)) + mux.HandleFunc("GET /partials/inbox", s.requireAuth(s.handleInboxPartial)) + return s.recoverPanic(s.logRequests(mux)) } @@ -138,19 +190,6 @@ func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]string{"status": "ready"}) } -// handleIndex renders a placeholder dashboard. The real login + inbox screens -// are Phase 1.2. -func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=utf-8") - data := struct { - Version string - Env string - }{Version: version.Version, Env: s.cfg.Env} - if err := s.index.Execute(w, data); err != nil { - s.logger.Error("render index", "error", err) - } -} - func writeJSON(w http.ResponseWriter, code int, v any) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(code) @@ -195,34 +234,3 @@ func (r *statusRecorder) WriteHeader(code int) { r.status = code r.ResponseWriter.WriteHeader(code) } - -const indexHTML = ` - - - - -Email Agent - - - -
-
-

Email Agent

-

Phase 1 skeleton is running. Version {{.Version}} ({{.Env}}).

-
-

Login and the inbox arrive in Phase 1.2. Liveness is at - /health.

-
- - -` diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 4a0f8f6..3d3d0c7 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -6,20 +6,28 @@ import ( "log/slog" "net/http" "net/http/httptest" + "net/url" "path/filepath" + "regexp" + "strings" "testing" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/config" + "gitea.lab.audasmedia.com.au/sam/email_agent/internal/session" "gitea.lab.audasmedia.com.au/sam/email_agent/internal/store" ) +const testPassword = "test-password" + func newTestServer(t *testing.T) *Server { t.Helper() cfg := &config.Config{ - Env: "test", - Addr: ":0", - DBPath: filepath.Join(t.TempDir(), "email_agent.db"), - LogLevel: "error", + Env: "test", + Addr: ":0", + DBPath: filepath.Join(t.TempDir(), "email_agent.db"), + LogLevel: "error", + SessionSecret: "test-session-secret", + Password: testPassword, } st, err := store.Open(context.Background(), cfg.DBPath) if err != nil { @@ -65,9 +73,9 @@ func TestReadyz(t *testing.T) { } } -func TestIndexRendersHTML(t *testing.T) { +func TestStaticTokensServed(t *testing.T) { srv := newTestServer(t) - req := httptest.NewRequest(http.MethodGet, "/", nil) + req := httptest.NewRequest(http.MethodGet, "/static/tokens.css", nil) rec := httptest.NewRecorder() srv.Handler().ServeHTTP(rec, req) @@ -75,7 +83,180 @@ func TestIndexRendersHTML(t *testing.T) { if rec.Code != http.StatusOK { t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) } - if ct := rec.Header().Get("Content-Type"); ct == "" { - t.Error("Content-Type header is empty") + if !strings.Contains(rec.Body.String(), "--canvas-soft") { + t.Error("tokens.css does not contain the design tokens") } } + +func TestUnauthenticatedRedirectsToLogin(t *testing.T) { + srv := newTestServer(t) + req := httptest.NewRequest(http.MethodGet, "/", nil) + rec := httptest.NewRecorder() + + srv.Handler().ServeHTTP(rec, req) + + if rec.Code != http.StatusSeeOther { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusSeeOther) + } + if loc := rec.Header().Get("Location"); !strings.HasPrefix(loc, "/login") { + t.Errorf("Location = %q, want /login…", loc) + } +} + +func TestLoginPageRenders(t *testing.T) { + srv := newTestServer(t) + req := httptest.NewRequest(http.MethodGet, "/login", nil) + rec := httptest.NewRecorder() + + srv.Handler().ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK) + } + body := rec.Body.String() + if !strings.Contains(body, "Sign in") || !strings.Contains(body, `name="csrf"`) { + t.Error("login page is missing the form or CSRF field") + } + if findCookie(t, rec.Result().Cookies(), csrfCookieName) == nil { + t.Error("login page did not set the CSRF cookie") + } +} + +func TestLoginFlowAndDashboard(t *testing.T) { + srv := newTestServer(t) + h := srv.Handler() + + // 1. Load the login page to obtain the CSRF token. + rec := httptest.NewRecorder() + h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/login", nil)) + csrf := csrfFromBody(t, rec.Body.String()) + csrfCookie := findCookie(t, rec.Result().Cookies(), csrfCookieName) + + // 2. Sign in with the right password. + rec = httptest.NewRecorder() + h.ServeHTTP(rec, loginRequest(csrf, testPassword, csrfCookie)) + if rec.Code != http.StatusSeeOther { + t.Fatalf("login status = %d, want %d (body: %s)", rec.Code, http.StatusSeeOther, rec.Body.String()) + } + sessionCookie := findCookie(t, rec.Result().Cookies(), session.CookieName) + if sessionCookie == nil { + t.Fatal("login did not set the session cookie") + } + + // 3. The dashboard renders with the session cookie. + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.AddCookie(sessionCookie) + rec = httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("dashboard status = %d, want %d", rec.Code, http.StatusOK) + } + body := rec.Body.String() + for _, want := range []string{"Good ", "Inbox", "Categories", "Folders", "Log out"} { + if !strings.Contains(body, want) { + t.Errorf("dashboard body missing %q", want) + } + } +} + +func TestLoginWrongPassword(t *testing.T) { + srv := newTestServer(t) + h := srv.Handler() + + rec := httptest.NewRecorder() + h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/login", nil)) + csrf := csrfFromBody(t, rec.Body.String()) + csrfCookie := findCookie(t, rec.Result().Cookies(), csrfCookieName) + + rec = httptest.NewRecorder() + h.ServeHTTP(rec, loginRequest(csrf, "wrong-password", csrfCookie)) + if rec.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusUnauthorized) + } + if findCookie(t, rec.Result().Cookies(), session.CookieName) != nil { + t.Error("a session cookie was issued for a failed login") + } +} + +func TestLoginRejectsMissingCSRF(t *testing.T) { + srv := newTestServer(t) + + form := url.Values{"password": {testPassword}} + req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + rec := httptest.NewRecorder() + srv.Handler().ServeHTTP(rec, req) + + if rec.Code != http.StatusForbidden { + t.Fatalf("status = %d, want %d", rec.Code, http.StatusForbidden) + } +} + +func TestLogoutDestroysSession(t *testing.T) { + srv := newTestServer(t) + h := srv.Handler() + + rec := httptest.NewRecorder() + h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/login", nil)) + csrf := csrfFromBody(t, rec.Body.String()) + csrfCookie := findCookie(t, rec.Result().Cookies(), csrfCookieName) + + rec = httptest.NewRecorder() + h.ServeHTTP(rec, loginRequest(csrf, testPassword, csrfCookie)) + sessionCookie := findCookie(t, rec.Result().Cookies(), session.CookieName) + if sessionCookie == nil { + t.Fatal("login did not set the session cookie") + } + + // Log out, carrying both the CSRF and session cookies. + form := url.Values{"csrf": {csrf}} + req := httptest.NewRequest(http.MethodPost, "/logout", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.AddCookie(csrfCookie) + req.AddCookie(sessionCookie) + rec = httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != http.StatusSeeOther { + t.Fatalf("logout status = %d, want %d", rec.Code, http.StatusSeeOther) + } + + // The old session no longer reaches the dashboard. + req = httptest.NewRequest(http.MethodGet, "/", nil) + req.AddCookie(sessionCookie) + rec = httptest.NewRecorder() + h.ServeHTTP(rec, req) + if rec.Code != http.StatusSeeOther { + t.Fatalf("dashboard after logout = %d, want %d", rec.Code, http.StatusSeeOther) + } +} + +func loginRequest(csrf, password string, csrfCookie *http.Cookie) *http.Request { + form := url.Values{"password": {password}, "csrf": {csrf}} + req := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if csrfCookie != nil { + req.AddCookie(csrfCookie) + } + return req +} + +var csrfFieldRE = regexp.MustCompile(`name="csrf" value="([^"]*)"`) + +func csrfFromBody(t *testing.T, body string) string { + t.Helper() + m := csrfFieldRE.FindStringSubmatch(body) + if len(m) < 2 || m[1] == "" { + t.Fatalf("no CSRF token in body") + } + return m[1] +} + +func findCookie(t *testing.T, cookies []*http.Cookie, name string) *http.Cookie { + t.Helper() + for _, c := range cookies { + if c.Name == name && c.Value != "" { + return c + } + } + return nil +} diff --git a/internal/session/session.go b/internal/session/session.go new file mode 100644 index 0000000..4ec2755 --- /dev/null +++ b/internal/session/session.go @@ -0,0 +1,196 @@ +// Package session implements the Email Agent's server-side session store and +// the signed cookie format used to reference a session. +// +// Sessions live in the process (there is one user and one container), so the +// cookie only ever carries an opaque, HMAC-signed identifier. Nothing about the +// user or the session state is stored client-side. +package session + +import ( + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/subtle" + "encoding/base64" + "encoding/hex" + "strings" + "sync" + "time" +) + +// CookieName is the name of the session cookie. +const CookieName = "email_agent_session" + +// DefaultTTL is how long a session stays valid. +const DefaultTTL = 30 * 24 * time.Hour + +// Session is a single logged-in session. +type Session struct { + ID string + User string + CreatedAt time.Time + ExpiresAt time.Time +} + +// Store is an in-memory, concurrency-safe session store with lazy expiry. +type Store struct { + mu sync.Mutex + sessions map[string]Session + ttl time.Duration + now func() time.Time +} + +// NewStore returns an empty store. A non-positive ttl falls back to DefaultTTL. +func NewStore(ttl time.Duration) *Store { + if ttl <= 0 { + ttl = DefaultTTL + } + return &Store{ + sessions: make(map[string]Session), + ttl: ttl, + now: time.Now, + } +} + +// Create makes a new session for user and returns it. +func (s *Store) Create(user string) (Session, error) { + id, err := RandomToken(32) + if err != nil { + return Session{}, err + } + now := s.now() + sess := Session{ + ID: id, + User: user, + CreatedAt: now, + ExpiresAt: now.Add(s.ttl), + } + s.mu.Lock() + defer s.mu.Unlock() + s.gcLocked() + s.sessions[id] = sess + return sess, nil +} + +// Get returns the session for id if it exists and has not expired. +func (s *Store) Get(id string) (Session, bool) { + s.mu.Lock() + defer s.mu.Unlock() + sess, ok := s.sessions[id] + if !ok { + return Session{}, false + } + if s.now().After(sess.ExpiresAt) { + delete(s.sessions, id) + return Session{}, false + } + return sess, true +} + +// Delete removes a session. +func (s *Store) Delete(id string) { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.sessions, id) +} + +// Len returns the number of live (not-yet-swept) sessions. +func (s *Store) Len() int { + s.mu.Lock() + defer s.mu.Unlock() + return len(s.sessions) +} + +func (s *Store) gcLocked() { + now := s.now() + for id, sess := range s.sessions { + if now.After(sess.ExpiresAt) { + delete(s.sessions, id) + } + } +} + +// Manager pairs a session store with the HMAC key that signs cookie values. +type Manager struct { + store *Store + key []byte +} + +// NewManager builds a Manager. An empty key is replaced with a random key so +// the manager is always safe to use; the caller should normally supply one. +func NewManager(key []byte, ttl time.Duration) (*Manager, error) { + if len(key) == 0 { + k, err := randomBytes(32) + if err != nil { + return nil, err + } + key = k + } + return &Manager{store: NewStore(ttl), key: key}, nil +} + +// New creates a session and returns it with the signed cookie value to set. +func (m *Manager) New(user string) (Session, string, error) { + sess, err := m.store.Create(user) + if err != nil { + return Session{}, "", err + } + return sess, m.sign(sess.ID), nil +} + +// Lookup resolves a signed cookie value to a live session. +func (m *Manager) Lookup(cookieValue string) (Session, bool) { + id, ok := m.verify(cookieValue) + if !ok { + return Session{}, false + } + return m.store.Get(id) +} + +// Destroy deletes the session referenced by a signed cookie value. +func (m *Manager) Destroy(cookieValue string) { + if id, ok := m.verify(cookieValue); ok { + m.store.Delete(id) + } +} + +func (m *Manager) sign(id string) string { + mac := hmac.New(sha256.New, m.key) + mac.Write([]byte(id)) + return id + "." + hex.EncodeToString(mac.Sum(nil)) +} + +func (m *Manager) verify(value string) (string, bool) { + i := strings.LastIndexByte(value, '.') + if i <= 0 || i == len(value)-1 { + return "", false + } + id, sigHex := value[:i], value[i+1:] + sig, err := hex.DecodeString(sigHex) + if err != nil { + return "", false + } + mac := hmac.New(sha256.New, m.key) + mac.Write([]byte(id)) + if subtle.ConstantTimeCompare(sig, mac.Sum(nil)) != 1 { + return "", false + } + return id, true +} + +// RandomToken returns a URL-safe random string carrying n bytes of entropy. +func RandomToken(n int) (string, error) { + b, err := randomBytes(n) + if err != nil { + return "", err + } + return base64.RawURLEncoding.EncodeToString(b), nil +} + +func randomBytes(n int) ([]byte, error) { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + return nil, err + } + return b, nil +} diff --git a/internal/session/session_test.go b/internal/session/session_test.go new file mode 100644 index 0000000..7ab8807 --- /dev/null +++ b/internal/session/session_test.go @@ -0,0 +1,78 @@ +package session + +import ( + "testing" + "time" +) + +func TestManagerRoundTrip(t *testing.T) { + m, err := NewManager([]byte("test-key"), time.Hour) + if err != nil { + t.Fatalf("NewManager() error = %v", err) + } + + sess, cookie, err := m.New("Sam") + if err != nil { + t.Fatalf("New() error = %v", err) + } + if sess.User != "Sam" { + t.Errorf("User = %q, want Sam", sess.User) + } + + got, ok := m.Lookup(cookie) + if !ok { + t.Fatal("Lookup() = not ok, want a live session") + } + if got.ID != sess.ID { + t.Errorf("Lookup ID = %q, want %q", got.ID, sess.ID) + } + + m.Destroy(cookie) + if _, ok := m.Lookup(cookie); ok { + t.Error("Lookup() after Destroy() = ok, want expired") + } +} + +func TestManagerRejectsTamperedCookie(t *testing.T) { + m, err := NewManager([]byte("key-a"), time.Hour) + if err != nil { + t.Fatalf("NewManager() error = %v", err) + } + _, cookie, err := m.New("Sam") + if err != nil { + t.Fatalf("New() error = %v", err) + } + + other, err := NewManager([]byte("key-b"), time.Hour) + if err != nil { + t.Fatalf("NewManager() error = %v", err) + } + if _, ok := other.Lookup(cookie); ok { + t.Error("Lookup() with a different key = ok, want rejected") + } + + for _, bad := range []string{"", "garbage", "abc.", ".abc", "a.b.c", cookie + "x"} { + if _, ok := m.Lookup(bad); ok { + t.Errorf("Lookup(%q) = ok, want rejected", bad) + } + } +} + +func TestStoreExpiry(t *testing.T) { + st := NewStore(time.Minute) + base := time.Now() + st.now = func() time.Time { return base } + + sess, err := st.Create("Sam") + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if _, ok := st.Get(sess.ID); !ok { + t.Fatal("Get() = not ok before expiry") + } + + st.now = func() time.Time { return base.Add(2 * time.Minute) } + if _, ok := st.Get(sess.ID); ok { + t.Error("Get() = ok after expiry, want expired") + } +} diff --git a/web/embed.go b/web/embed.go new file mode 100644 index 0000000..be68476 --- /dev/null +++ b/web/embed.go @@ -0,0 +1,23 @@ +// Package web holds the server-rendered templates and static assets for the +// Email Agent UI. +// +// The assets are embedded in the binary so the container image needs no +// separate file tree and cannot drift from the code. +// +// tokens.css and the Inter woff2 files are copied verbatim from the Family Home +// Lab design system (AGENTS.md §6); they are compiled into this project rather +// than imported across projects. +package web + +import "embed" + +// StaticFS holds the /static tree: design tokens, self-hosted Inter fonts, +// htmx, and the application stylesheet. +// +//go:embed static +var StaticFS embed.FS + +// TemplateFS holds the server-rendered HTML templates. +// +//go:embed templates +var TemplateFS embed.FS diff --git a/web/static/app.css b/web/static/app.css new file mode 100644 index 0000000..d0ca346 --- /dev/null +++ b/web/static/app.css @@ -0,0 +1,293 @@ +/* ============================================================ + Email Agent — application styles + Built on Family Home Lab tokens.css (do not edit tokens here). + Base unit: 8px. Touch targets >= 44px. Pill CTAs, 4px inputs. + ============================================================ */ + +*, +*::before, +*::after { box-sizing: border-box; } + +html, body { height: 100%; } + +body { + margin: 0; + font: var(--type-body-md); + color: var(--ink-secondary); + background: var(--canvas-soft); + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, p { margin: 0; } + +a { color: var(--primary); text-decoration: none; } +a:hover { text-decoration: underline; } + +:focus-visible { + outline: 2px solid var(--primary); + outline-offset: 2px; +} + +/* ---------- primitives ---------- */ + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-xs); + min-height: 44px; + padding: 0 var(--space-lg); + border: 1px solid transparent; + border-radius: var(--rounded-full); + font: var(--type-button); + cursor: pointer; + transition: transform .08s ease, background-color .15s ease, box-shadow .15s ease; + white-space: nowrap; +} +.btn:active { transform: scale(.97); } + +.btn-primary { + background: var(--primary); + color: var(--on-primary); +} +.btn-primary:hover { background: var(--primary-active); } + +.btn-utility { + background: var(--surface); + color: var(--ink); + border-radius: var(--rounded-md); + min-height: 44px; + padding: 0 var(--space-md); + box-shadow: var(--shadow-level-1); + border: 1px solid var(--hairline); +} +.btn-utility:hover { background: var(--surface-2); } + +.btn-block { width: 100%; } + +.text-input { + width: 100%; + min-height: 44px; + padding: var(--space-xs) var(--space-sm); + background: var(--surface); + color: var(--ink); + font: var(--type-body-md); + border: 1px solid var(--hairline); + border-radius: var(--rounded-xs); +} +.text-input:focus { + outline: none; + border-color: var(--primary); + box-shadow: var(--shadow-level-1); +} + +.field { display: flex; flex-direction: column; gap: var(--space-xxs); } +.field > label { font: var(--type-caption); color: var(--ink-muted); } + +.pill { + display: inline-flex; + align-items: center; + gap: var(--space-xxs); + padding: 4px 8px; + border-radius: var(--rounded-full); + font: var(--type-eyebrow); + background: var(--surface); + border: 1px solid var(--hairline); + color: var(--ink-muted); +} + +.hairline { border: 1px solid var(--hairline); } + +.muted { color: var(--ink-muted); } +.faint { color: var(--ink-faint); } + +/* ---------- login ---------- */ + +.login-body { + background: var(--secondary); +} + +.login-wrap { + min-height: 100dvh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-xl); + padding: var(--space-lg); +} + +.login-greeting { + font: var(--type-display-1); + color: #fff; + text-align: center; + letter-spacing: -1.5px; +} + +.auth-card { + width: 100%; + max-width: 400px; + background: var(--surface); + border-radius: var(--rounded-lg); + box-shadow: var(--shadow-level-2); + padding: var(--space-xxl) var(--space-lg); + display: flex; + flex-direction: column; + gap: var(--space-lg); +} + +.auth-brand { + font: var(--type-eyebrow); + color: var(--ink-faint); + letter-spacing: .125px; + text-transform: uppercase; +} + +.auth-card h1, .auth-card h2 { + font: var(--type-heading-1); + color: var(--ink); +} +.auth-card h2 { font: var(--type-heading-2); } + +.auth-card p { font: var(--type-body-sm); color: var(--ink-muted); } + +.auth-form { display: flex; flex-direction: column; gap: var(--space-md); } + +.alert { + padding: var(--space-xs) var(--space-sm); + border-radius: var(--rounded-xs); + font: var(--type-body-sm); + border: 1px solid var(--hairline); + background: var(--surface-2); + color: var(--ink-secondary); +} +.alert-error { border-color: var(--sticker-red); color: var(--sticker-red); } + +/* ---------- dashboard chrome ---------- */ + +.nav-bar { + position: sticky; + top: 0; + z-index: 10; + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-md); + padding: var(--space-xs) var(--space-md); + min-height: 56px; + background: var(--surface); + border-bottom: 1px solid var(--hairline); +} +.nav-bar .wordmark { font: var(--type-title); color: var(--ink); } +.nav-bar .nav-actions { display: flex; align-items: center; gap: var(--space-xs); } + +.page { max-width: var(--container-max); margin: 0 auto; padding: var(--space-lg) var(--space-md); } + +.greeting { font: var(--type-heading-1); color: var(--ink); margin-bottom: var(--space-lg); } + +.shell { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-md); + align-items: start; +} + +@media (min-width: 760px) { + .shell { grid-template-columns: 220px minmax(0, 1fr); } +} +@media (min-width: 1080px) { + .shell { grid-template-columns: 220px minmax(0, 1fr) 340px; } +} + +.panel { + background: var(--surface); + border: 1px solid var(--hairline); + border-radius: var(--rounded-lg); + overflow: hidden; +} + +.panel-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--space-xs); + padding: var(--space-sm) var(--space-md); + border-bottom: 1px solid var(--hairline); +} +.panel-head h2 { font: var(--type-title); color: var(--ink); } + +.panel-body { padding: var(--space-md); } + +/* left nav: vertical list on desktop, horizontal scroller on phone */ +.nav-groups { display: flex; flex-direction: column; gap: var(--space-md); padding: var(--space-md); } + +@media (max-width: 759px) { + .nav-groups { + flex-direction: row; + overflow-x: auto; + gap: var(--space-md); + padding: var(--space-xs) var(--space-sm); + -webkit-overflow-scrolling: touch; + } + .nav-group { min-width: max-content; } + .nav-group-items { flex-direction: row; } +} + +.nav-group-title { + font: var(--type-eyebrow); + color: var(--ink-faint); + text-transform: uppercase; + letter-spacing: .125px; + margin-bottom: var(--space-xs); +} +.nav-group-items { display: flex; flex-direction: column; gap: 2px; } + +.nav-item { + display: flex; + align-items: center; + gap: var(--space-xs); + min-height: 44px; + padding: 0 var(--space-sm); + border-radius: var(--rounded-sm); + font: var(--type-body-sm); + color: var(--ink-secondary); +} +.nav-item:hover { background: var(--surface-2); text-decoration: none; } +.nav-item .dot { + width: 8px; height: 8px; border-radius: var(--rounded-full); + background: var(--ink-faint); flex: 0 0 auto; +} +.nav-item .count { margin-left: auto; font: var(--type-caption); color: var(--ink-faint); } + +/* centre list */ +.msg-list { display: flex; flex-direction: column; } +.msg-row { + display: flex; + align-items: center; + gap: var(--space-sm); + min-height: 64px; + padding: var(--space-sm) var(--space-md); + border-bottom: 1px solid var(--hairline); +} +.msg-row:last-child { border-bottom: 0; } + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-sm); + text-align: center; + padding: var(--space-xxl) var(--space-lg); +} +.empty-state .empty-title { font: var(--type-title); color: var(--ink); } +.empty-state .empty-copy { font: var(--type-body-sm); color: var(--ink-muted); max-width: 32ch; } + +.detail-body { padding: var(--space-lg) var(--space-md); } + +/* dark-mode toggle shows the right glyph */ +.theme-toggle .icon-dark { display: none; } +[data-theme="dark"] .theme-toggle .icon-dark { display: inline; } +[data-theme="dark"] .theme-toggle .icon-light { display: none; } + +/* subtle, non-decorative loading pulse (no spinners) */ +.htmx-request .empty-state { opacity: .55; transition: opacity .15s ease; } diff --git a/web/static/fonts/inter-400.woff2 b/web/static/fonts/inter-400.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..33002f12853a3d37d7b7eba39623fae3bca94a3b GIT binary patch literal 23692 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 aUbWq0We;bU+VQC=KKz{j$8G)n1^@ua=Md%q literal 0 HcmV?d00001 diff --git a/web/static/fonts/inter-600.woff2 b/web/static/fonts/inter-600.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fb50a02b2af82000d87d8c8b8161aa254b3b1593 GIT binary patch literal 24304 zcmaI7W2`Vd&@H-c+qP}nwr$(CZQHhO+qRx<HucIudE`dq6HAjhX?@`*y9se zCesj3a7Y`qU;`jZ_El2jXSpE?Ic4HpitQ zm4Vr!oU^KL^7r@0DLIY-R|P8~UHu2n8-yTh0xhppNSrtv239@i);eg3q{Ob+cn1y2 z8=*lRPWg9am+ZYVIDStz={Y7+BT`)!R@xAF6M4ctqNG%;$f#Q2DaVSnXCwI3Vl*C^ zDG`6xCL5M^fx!aw)5$8rNg@~yVkx;q$}Yild<^!z`5rX;i!bRSa$u+(?B+quB^ZJXV1SZAIKQT!fpxc(?)61oA{vup+Zy8eNN2t#_1|Bv47Zr{8~4AMrqiB<3<@VruV zetW2QvrM)veNBJ|s&L+j0WS~%EieURFkfdGf?r`EjY3q4QeH)ZQw69uH~*;{Q}dAR z?_aMR*WZtSJ~0zRFf#|}zXO;afBE)%+>D|BtCJV4QjtovoBOM?f&NZ6d-5B$XG?~4 zF1uuLe$InjH%;7uign<);nTNOfQapzYwVnnZhx=DWHdcD!`3$z(+Hy;2Hqtu2u&w$ zIlPb-q21=8Bo!@GN}IS))Y$1)Fkrl!%6F7#mQ{!~diCiJA1Fk@I%WWhEWGDZ8XYZl zOAtk=B~<9W>V5L(TnO08aTHMecfYUm_i7VEiDU4`O@8>(!o%W5FRRg%2n=)!XE#6w zd1E?{GS+9kWlw@;NHRjd-B5%q!Yr>q&=rcsP_MKl<+z78VA$~YXI;6QzTIAsgzpB* zW;d@gp`uzDxO+CUHOuik3&AnGV` ze`jOp+I*sWcB&HXB$aM-`g`^E2M*%nBLpI7g-$?PKLi@6Gv~-WDjhTgH!01Th%Q`} zBfQ8h|F6j9vPq`$vBQG9+ji#ZAAR=jcwu*;cFNaa73~k0YL$@PSm4k!6^arcL^*$zNjSVtLr_UXWONT{->0}HWhYo)^iIU#q z?-bGb(t=V73M><07@P;*+j6@vw`?R4lY+8^%%Z*~DLU5K72>4*icWvF&mTAI<B-2`cCoI~myN;Zqr`%|4#; zNVe%bZ7&IxSO4` z;78QE0b=vi3nmu+w5Q@v-l2(IqLI>LKHq?Tze}tY| zL3W1;r;)95YNQp$k-m+Dtm%U=OMkm2aLQLi) zdzoMF^~RF4xBWastqh|kb%;Tz#pdfTf5aYP9_;0_8DdrE61nS__hfoYf^l^R!LZov ze-{!5U=M0JKvkd$C)nj8oaYtiamju{KUslcC_sn^s-OW8TY)12nu05&5Cb9nC>SFO z--`m3N_f*sn}YiW!Zdxiu*DL(boWq!t^;9;G+pS@iJC}1CX9$Yexl<60s`z6q%A~Y zxl}t|&*_+uqFrd9Oh9$QnJ*FQCawk&ly2VBn4(z&P`G#E#$mlebzAqXrpBY9e|fRQmV z_rdP)2{%M6c>4_2S{)gQq-tJte%FmIm3KY*TCHBCtol5PtW@5%J$4JejH~D1MkUK$ z%+&q0)$t$JT`b7d&r2CEKVh-GwL^1Tm4rex$P4}}d#%@xoiCGub>;Bp2S7&kfe)-$ zFTS|7a;Jh#Eg&|@Js5_exgZwNw=d>ULJbGSmX#-G|Fp(^sa_p$rqkCiY3kco5)Y-K zEtTqI@Rr^E&*k+A`%YJT)iPomRcd{ubSJXvUU%P>kG+}9qnW5Xml)1FP(HCwm8hWx z4u(h;qGN(xjSERtCLN#Am$HrdTicYPi<)Ami%aGNaQNV1FbbIx2hnNY638X_Qq2BF z%ayJ8W%N2VU$FowoX5(_8;n+AFL`FFNy`ba)U6vxUW_|XC*!`HEknz*p z(mSi_!qnfs(+KWQ7j^aBb>kRI9scL&Kd7q63>kt*36ml;ty>fp0dOh@E9V|!T$}yf zZg7i4!zWlrx~beNs@G+;joj4pqO|`Kv8?=eVH|88EAun1xVCBy4O^iusN^G{y&2Q} zv-N|EQ~lDV{OG2!IZZG`Ri#Cc4o`b%TsMX`nZ?oY!s;I_9N~tE>RT!u78! zFXqH+k8Pm8KRf^vir>PCTOZE+ke-4lQ0(|4T|9y&T?$p|&7WQdv2i*64irvdJ5M0! z_D>39M&r}5zBzy;_63(=iB(VkVF`9>^=MF#jcV{1kl5~`=9>5AAcBqg;z0|iE6*mn zsM{Cy(r5gZb4RM9l-ZHvIWnwWM(4L+^r5`81)D@bAzs-Gkvo$53$OnZGFxm3t@bHW z(29L1%aigAB&?}IY)%>=i)ZBoHLi8_rR7o^!BHU@M6SxbbR-7vs(|rtr~&_AbA6|A&b~8BJft6gbF7 z0#AyPjto?EE;wC_@Qis#YL>et|4*)5qGmw-NwH2pHLN~~QhCr~fX_yyRt-kxL8z|# zN*(l)s!MML+3rtGgfQu zQ%G|;vULsj8Zi@AXmg{OLV?3xEk!5_F%#)hh01EGM8mTTVNkZ1@V0HL$QG$pH|WY_ z1@2*svewPKOA(LimhgDgE2Ax4CdTAA>#@`D2QU0_WSQY1nhBT)rwU?Br3g>|=y0hS z0a^ta8<+PvLf#k`=hV{2Fz9W|bFeg`UFw6NghH(ONfb*wXNj2G`AYVAM*yA|RI7NQ z2p`8i1BeU_R3@g#Ziy&aNMeC0G>vjO0({&H=~UAvn3y}3l1>I48y{V80wt8}r{a_r z84IRTu}rxB_@zP^lP=sooB&P2z?(iEeFznIYur_~Do>Fq9Ms8FgCvGtEJZj%fJH>7f&Uy59au~C}TauhTMQ&i)>9VD$G_`tBC;)|>yGADaJiTb;w|7V|U=t8R zo@0ZI?`5kJt(O|A)mGV>8w$SU;6?U5wr z$^Z8JWatG93d%)^n6yDWBCFs^`++0kZZJP5o%+^p9Nt7r(R3rSGP45CrrynFX^O*Q z*looX6|t}MZHxF%)Y6HD@*zO_pgC{GV)BFlx_BCo@63I41u?-p2?D3ypz4UO_@S9Z-5MSjlS%^jJlS6X@P-{qoi~mv?p` zuZ}3rojxuwYpWYvo2XLESfAZqp@XJT6uY?^_f2jX+YZ^%my2qjB6cRXUFLy@Q!PoU3wq2iMqu zubAyNcm-8|6q|ByvE2sKGRFRns_hw-eJ>TbYX597Z|vVvzUz-#IG9KOks)w!gekS2j$)e@U0BPI$`-3^`3nXctN)?4x_S zAyUQbMdW?>YClV=MDA}FwZ!FNo@Bnj?boLAD|H&nRu{sLgf6kelKhE~a{IZ*!+t&* zs_|uqXQ_eg$@?Pcw%zRbXPbc}MfGYc-WV9j(;L?z`pH_XEtKr}^GxMxtI2;B1!hL! z6SeF#^Lr;54(xr8)Fa2Buhj%z1*%wdkGbwNP`0jiXV|v4L$tl92&$NmSAdO-jxUgi zW=igj9Jw2q$8D&UY7P&%cznuz7KikS##{!a(8;{%ucWAWRvQ(Keu{l~nmtSm{>^`4 zscNd4lh04CC;?VCZTnYYvp4+nirH%wC}f%|ux&{qXmU6jxB6Y0)%4n{MAZ{0J`g&j z*uwtHiu`IoEiXvQ2e0yeN`|z%aG=;dXxm$2KOq;+wKo)?47xCaWb)e>)d|xYN73Zw z_!m4!v{l+mQ#13LA8j~6wi*ICB~do6fHTVNbd0C?tldHm*GS);1jLEv_I-8p>@HSB z(#|s$$So6tm-!0av)1d!Dc0i0W1&Yr`L*%N)m!U(q}lCcP`CoUK^-9wxW(+dK7c=0@B0>TbN+P(s8np@_Z^k0jDpgC3zN6M$(P z#(p9w-9KR>!i>Q)9(eg;H({rfWvf1ND9miY;p1iCyjIO@3_ex z5GCU9kuVqq1jJ$$6KT*`k0Py@ACXPc0AagAj<<~Y1AK}juhGdPn@*N+l8M9VGKB`# zC=P9!u=2Q8AK43p1IqCrUiKOl@_i8SN*iJr=%GZU5lnnZP>3S&fJqsQBO#Q0%x4JD zYVT!DVKU6Ic-VLd@`MtFXM#f^gtO>?ktni~F&a=uH6sZmp zcb(wZPXLF7el);f1o6D0tQVYJ=z1AJVuO5xE zjtH3HP;jC+R5V7+B~vMw%4RZ|yjLvkdL`L^74Cr<#!EBfb57$CZw_tYezgX5)b4R5Wh&0nus1 zDbl{A2i&S%79t`uPo&CBq*g>$MHl$D6%FSS!BBdk7+Tg2toXTniq%?M{?uWq6vh=g zuqM@B6#5qnL5vZMQamLX2+HN+RqgFB5l*(m2m!lvZERL2UERM$D$iFy}E-FO~EB1&sl`3Pm(nps9`sd0|fEVsosJ5FiZeH?mx zs_$3rfx^Cz_?U@N^(7A0Oc=Y_RHfrHiUWx5r7)oa{ZD~XAUTb)0 ztl4Zh9mfkEm&4(FI5q2X#}kre-sgZgrCPCKl7H5dp9u_S)ex&v*n(l+q+5`qPFV^5#SF`V7p_>_RnaPda>=i@FZ4i^uJT(+Q1Et4c=Gsit)f3>4}GTOuAG&=lGA zVmdlk)YLhHo%ef-w;fA&@l;><{O$XAr*}hk?!)rmllkvzb8)md-|n7=+V)rzRb$jH zn@bjNr@Wl8YuzNoJZj7l>e1}U##p>MOPj5k_X?JkXY?`Mv#JG%czF5dN3FtYk%gYLZ<5Z*QQ+cgY)Ird-quv0B|1k;}ZFy zYd4LhjXcS?i!KQ%4_y-06TG%+tCW^ybqQ%P+Dj8fiOE`Q(vNmr*vEy5v?-fr(IPG|{P) z(rq}Mk0SW)!tNH?1G)M~Le7ob1Ms>%Hb*f}99d%J)Mh~%5um-g z;mwyYu#O?}50nWv5(r>9oDxeq{)CsUCOs9*ah4=9U@|r$ghU#*>r2I=;t*(#Mv| zdkrlpJ_wjSDm=YBs_oF!O~fOq4s21E6hmT%NG5K3@_xyWopX_V9U9-SC-~!q@VP&Q zdH~2UY65=XOi~MCfTv@oj6c%P+P;gJ6C7JIK_}GcFeH0qvuqi@QEZ@k^PJfL?d$c{ zfx}C zL{)e8s;agXXqOuo4tZMME4sH)G)GA(xtcjlMY^0n^g%_16+;8ZtBamDeaRWYQ4NGR zKtXWYmRdF?0UQz0D3pq0!{tKBXt+Yk7R#hY0y6P@!eSsQm5Swq8Ia0mGnx%1rd5_U z8%@UZfyCr~cFl~H9XB^=Xs-ODQO zlJ$%c5~tRSEXa*A!%HRm{2U%6CLkgzEHE;P7a${5oz;b<)9Ly30)tCY&9aet% z-+vF=IavL*>+mwzEDr`Iu7aJ9M>e*tey+C2bc_1l>qbOb@l1W#1B6o;vfmTL|aRz{-_wjxX-bMX@~kSiCqJNs!zi;s7=FWz;4}!GLV7 zeC#M*7x)n$yaTyfi4=)CUIXim-1I7l%-ce2=<)+aRiq<(p;8ppk1z=!?$(G06LpC^ z1rcclo#nt4!K4nugBF{?sU=`X$EKNFHYU{*xtX(kZJmR}tS2xUQ*)@0gi0GNEBvS%oh5Oyt3cp=ee~RgQecQJ2gmjTm*Ywop6sraTp{h>*0U z$wRKB%)*YJmqMwGrQh5#18)v0W-+N=rM3x|P^i)=Yz{#fs(w<thT`FE^Ow-3;XKB-O@$MmyR$R zM22DgnIzVY5wuipC=$tc=?s{N{`Xo6`?N(iq=K}5A266InAvs3ukL<*KN}T+-xn== zdL*vE&=;*Vs8kNoyl)|eOqygJNrV)dL=nk{(3vb*gel07aj6iikR40cZ_q^(!#~2W z`sh|*PNH*S`AV}c6SOn;HwW4_QPUO=f4(3#6Z9tLkB%m~34XiQH z`n;_6EDB*jq6>uS`lv1aqwhaI5s!yv3wbynjYI^DiOq^Xtl2AyLFPM1Cxyr~ zr-#{tKI|0lqO@MN$Y#Pek!}^CdE5PPFdh%$fH{U0H;HBCHc7nEHPKNPt%k%*j1fum ziD8LV?~gjSYATX*HNn&rQzO+Tgd!e#C%@@=YPwp*;O|DbAsW7(J{LV6h(bi91&R_p z%64!7z|kyw_CAq*o;!WlwuVOVD%pP&g}L)ym{S!Y9mO6kn6?(1cY7yYOGRXQOBM1> zpi5vSgae|Wvk3qI&&2|oT?0mDjfxbPz($ti0VVx2%1RI`v=*jxr2-sgm%f_P+U^DR zz222KwDsHZDftb%Z3TB+e zT9bfJy(zYJwL~$NvElDarsw4nll%Wk4hfmR`>w+D$IA3BAkX4DY;_tnrA@UtwG5iOrQTjkRnXvj zdvAw;e;0wEaOZaR)Q1bu(5PZG43$(cI4^yPq|7+x_wPjR#Rg&`h^@54x~gbc^wKJ; z%(Ci#KoM0$!IdjlzI^#Vn0)yK%zM}T6(F+N8#E@9B$7k|0AOZjKF@LPq#H>hk^J9H z;(wbY{}c6ppdkJK3?%t~XAIt@vPtSTXJ=3X5BbKNE%{F}r1zl?XTN(gh7=9Jgr_6WD zr#SZZ+A0cUjK4NE#RqF_SB(>$BY@Yw;d&~j9U^fCmwSuU4{D?;KOzlP;6zh>?? zIc9#)n^_o^i{x@xjaC#RYl4r#_9y^i&}oLp@Ue_#Hu;P;C24MkW@eIK^I#_osx?YcLjLC5_6IiMCYNgxBQ ze&K6kQFp?@25e;rTpt-8g9%D_#me*CTUMKbg=gY zUjqtPII!u;;Fib?N6)6Ag6iJqS9f+Xl*nkaa^9h{`elgNxa7EHyRQ>*XT)OVyj(X; zB)nC&`{SDyE3mfvVr9hJJkyl<@>uC%`C`AYK-)?e z(PUY*C^WGMIHzE{tA&aotV*Pi$I~GgFERsR1bQF=$O5Dyf>KukL9~)*I8usXQ)>ey ziR>$hjMStB5=peumW!kobtp?_5=(muQifC{Hecx>B`I&{Qj-`;$qQ~uV3;5&kI?kU zaI`53rY`GwDkO$+icQ0FeZJmyFW6U$7WHIcEu0mre&4KN2|0jF8-s0}ykhGx_0+2n zI7hfQbznZJ5oj1Tjy5BAu40B{uwfa(ZKXdla()7Wvgp7xe+z8IIFCT`VvD~ryt*16d?DV4(lbfgU z{XF9(nnNy=@%E|T6@&3*px7SN_Bh_0HsHeE`+a%Nnelq2{3pw)Jdt*}B>Ov06WtC4 zefAgM@2bDF{hHVLycTAy(N4UUuF3B8L)$xu(yzN*r$}!{LBD#qIjr+J4@u+_ytdm5 z6TtZ5hU+A{FBi4et3|hD(t~eaNBEEc*z~u)>P5hx{mBOI^~p$*(NAPEEHT7V8OpMM=7b_O8bhS(r_ z3$Abzdo_WDbdbRv&Q1WSckG9Hw|0a#LUWHu zo_5;NnMjT^94GdpWVM?&g|}!MLRn`F;$RyhY?I1xNHiEi7pL^Qp;Wy_Vm2GjM^ee{ z3(ulah~<)0l$fe(m5X#zY1_7nC;h-(I8Jk->$(o(_~3cjEJnEVV?Q;c=jjEx4l0Z# zfm9t|H4=9)3IVwJp;hUjqKM0!ms0os(19lMCDv{tClak7#;qq*P7gp%td0=J)2Wkv zi$Ew0xC-q7>36`Z=ebi}=VVQF<&OQ}kzSB}7*I=OD0IngT- zullx9N$xldk}{7q?fV1e0^zYFb%5%x?odAkm~;#sh|~Z5M2a ziAt$#*UiCg0t_O8^oi+Oi!N!x zvXTX~=14;WB8JL#b5RxG2!)vz-bt1&NChmR+KeQC`^cax<%Bwhuk0r6T(-<6sk5A# zqGwp>YmkWOOPb`)sYnOpN)OC+F+a-Um&U;Z0b~M%Awjet3<5-<2}VLT*vgqKJf3dR z#OzEQOtf8%aw_Ap-5NpF?CtW6bQj z4rYzUcxw>22U^39`N41y*3Z_Z?r$Z-#apj|xU_>Xk%fd#Xd1w&v)LgYx8w;Buc==f zGKGdmp=%FXWt(H;YN#mR`Ky!{b&0@V4>%iIsok6MI)r zRmYO0Y9mK+$3mvWRE+r8q~2$h>lbSLB5Th#PEV@#SCw+Ky(pp4dtE5++qn^)mWRG9 z$x@p0vTNlbf}*^UZcsFhIs%|#U?4DO7j+0mqNP5D$NrFIzT_J0O*8hS;kC7HH5!nb z6IN?C7$t=LLnfV7g;vTKiiQ+D2VrZ$9fmb6s4`NK*{;0WPPD#8JNOy1&Y5}b=*ro7 zZWY?JRHM3YN!t+@)joiaE?j)PvQYdHsAO(!D>yKeA1=k4iIy?}6NNhuPJhAbUp5zx zjhVkZe>ukIM{CoftYROFl=vo)Bb2Li(Kl6$+q@wF16|Vd(u5v%2dPc}lW6t*2BI?8 z`gWoDtdJ<#p%}fuQE%ILB2-eo@{I#^NqreJtl;Vwf11TtYgs{|0j3o4DqDO>jcwP_ zu^J{%t5nf`^wDJB04ToMtpdvRLHTEp1agYBc~K=v*jtsV7a?A}y(cOlSMvU$g_Olc z?UX*xaGD4)enr`j=$Y>)^{1m>Ya|Lv+@8EVq{R&;W{w)bKrT&2zb%NoHMTT_eQHcD zCG55YFZpDG9#;eFluOeH&Z$kaZFLTMZL~rNPD!u(*&mKA+D4MQEHY7JK`;qylmJHG zr1qR$SQL5=1Ad~8^>p-eGJx~a>Ar1OwmHjS~l3W-L9U|KE-lXZM4@G z$=R8ZNEi{t7%s<#Ev-Ox53Bk4R)Nh#30t|%s*e9p>}mvW?9$5?(-hW3`#A_+*n2rx z>aJp=kX26?ce4K!0hinY!D%6zCDsO$LM2%KA`XY8^?p1u__EeT@DPpOJ;U1~NKTq$ zK#~P(UpUzQ_NrwY@B006Mfo|JFQ7PY$Xf>-wcpm}cb55#;I(lxkex9HN2VeY?q}3o z648kzUj~%i6%vzfJypErcWOPmU7d&JO!#@%1^yk}DAmsp2U6(hQ$ zbO#bfa2y_I`@Nh`K+mmJu{#~b<_DXVC*`{-fzTr9FbbRY=_h1T7)S2C7q|56Z9MCg zUZKWMRi!%k(@=|MwcoNX?d**mlLOEM6UaoIq6V5@t8O~&oFfSnp;J+1@0bfKb3;#< zFi~$}4vRWUd)40`f5h-*G^5pb-w`GIgUO3{uqZX_iI~Lfw~39)OXybG5s}mUHbEKl zWb@f#2CXH~VlQ!oLpRPT{`x=-0;ttke-AbHYk!%a!0T&9p#S0;c9E0}ZMbMMBsyss z+Tl=T$qfamjC*e&lTxoP*vzG|G11Mcxw{Z6e|I#s)(#1GdlK1FE_67fcF))CXHDlN zzqa4I5M_!qn<0I|v4<9BMf-qo%2}E)rK^0q0(H;KT-_Y}xNt}@7$ySSp&_?E?6h=rr-QsEbUkFm6k;Ekm&L)YMt*Us5T^ii_IJ{P)n%Yk-}M$JWap}sll10; zm7H@i9!Lj4Y|j-1%JsKvU(IxUsOcCq1Xi@WWZ(`07dD4db({1e1<>y!MN$#TI_XIX z(+7;m$%gzK=|lHc!(Eor*wG@DGNS1p&(`S)UFX5}Tlmgz#r5^Sb@}mU<9EYZ-tUKs zX@x8jtZ?9efBE_vE(t;=IPrnjaRncGbV4RA=+qW{kHn4|1_fyMg~+m5%z4KY(Vb)| zFV^U^4M2ep^P+-%b!~Ugy<0WdU>~4_G}`4atf}zWE))aFKJE%0>Pl6erTgQmk%8jN zpYiZ&JOXqD;e0S-$}R!J@0tP3=m)F0>pa{!U(vtJRI1-wiCu4;ilh1hR{j};{BOS= zFT*OOyq0pV#qi71JT*2$O$eO`b&t>n_9K3Y(jecaD@e#FD~xztG7utQHK{GoSFH7ot4 zEcskp`q4}(3Qs<-j*l^-t0S80aqIWnMGC9a5#-E%9F#!I(NRJoY0dT z0=@!Rc08e-teD!tMKu1YxPermGB`s$Gfr@o$r+xLrHT>Sork^DB(QN|k(Rp2&D*I| zj(wTNE@de$-eh@VdWG+~D5_Rb0eBSVdR@6=S~-Yw%@0!OIR8zG*4c7E*Qmwa)B~+- zG=KL%^se|f>cA)vReDX@T0gwu#ir3Z5Ex0^P zS|+j5{y~^d2^2K+#NWo~yX-GXy(~2N0^5gy&M^JxHn(TkHcZ~^cfkO~{-dF*(X+1i zRA1(A<^rbaDbj_<7JRzv>xSkE7%f`97Z#nWghAX(7$&gYlp)XhI2x?UiIJwJBro3U zY^|M+`j7Wp5Nsx9#Ehg6sL_YlOf9bj0*i-k3nz+q=k({EgURa8(jCO6mCM?$*tZOJ zP>_V{u+{lX)c$;aOfTh>v1r-XWTYZwTSc7NDbaOtYyAcRV7u^DedJdu@W_fU3qdiIrm>bxw?pFglpZZ9(}SGiHZKveJX zh3asQ{vS?x=q?Xdzeo=fnNCj8R@ZnXA_225gO=qkmHfFJX`fH7zJ;F<`W!jG1-^Ex7#$U$*TtKQ6uB4@o#J2$INxK`dP)X$>!G1Z(9GntUn<=CjXwE zkkv`kiz!goEwjD|>-ud6`xC3rB26OgkB=*h-+1%X8f1Mdm0n_%TVH@aHT#8jsI9nh zog`O??w0F)x>`|Mfucpv%&Ao@*R0tDn%c|1;xgYbF!yc0o^y}O^LwIvx@qGeGU5P) zpEF;x*FgOE`U%j$g9@%igjWK(XDorUJ=c5)T$arY8ZACfdf5r9xXx5c_J)NMp(6CGS2$^Us4#0>qhS+mSzW3@o{o5ZsB$f}}*ib3eXF)OI~ zpTn!}AC$L5VJ`a&aka5&jxT~0y)%%+CLX&NPq1$Ftrrsn?oiOv_J8HO7$AO|x|+ED zM&GKxH~o;bSNZzhC*G1ux;TrwC?N^rXPhM67JtUQ$YulRp?6*Mm8M*!&Az~P93n6v z-|oWe8{2`yy#;P}@1VbJciuIfcj=(ZIg6BOi_9&9t$*lrFY0>VXt~>-cj=&IF_oOw6jz3VZU3L4veQ$s=U=p1Da*W_a+FSgP_ zf|!F=?Ij!h&diP0V8XV{=DnIOPkg$OSaPeIxk8Ums#}ip961XW!SqsWx!DD+^5)#z zq0Vqn#V?=SUN(mK&~`58no=5Hb(G?v74EU*p+nKWSL(eb|7X>VmMLf&s)uY zpXTFbgW)Y7{BuZ7MUIpbOLLu7U`xUv%|%)_P-*Drk2;3I<1))B8*MG0E}FK|EZ-%? zu7p!V3NMSXpp&1DsexFmj)(8xb(P-l4$&ZQBJBPQ<=Dl`YDV zZ8k_6XWx>N=rMklRE)U%x<J%p!?M-XI+OZJlMQ&e_EKDOCO(Y|RnKCEx z`_9({o%Xy6k+YPvNbsNl?s}WMP_dJz)n4}{D+vlAg%Lcqgl7Xr5Br>>3Rx8ht=E|B z>KKe!DQJ8th{LESQHaEmlM#(DlnW4X)7R26b2&^IKU%CQ9+=h=K(w{eVWGfDs^iO+{O!ri4C;F(8jfKoJkrrxQuz|Pp_ zOi2-$AeH7+cSg}puL=6gxd|61!Zf50|F87=1g07rhGaft&GrCd3gK7ogOZVCI88e z^pZpiXpxUTPZ~x<2SdSEh7EwV}nAFoHgWr=9 zTWQ#0KV7}tkIBtk=NqV_=#iOWw>|g-;YXQ7Zd{OP0&yocy>=W0+r6anmDpZ{Ah;~S zuS?LL&a+V!-!X*r|1^$%XJ^}voqI(&)B7UpJ0Hu)O9`}z>K9Kh^O}c09Q-k5GRkG; zSWc#X0@gF04S$CLt(_%AFpRbt^VWYJFtUS(!NIo!R>e$YRXcXmorw=OXT+c{;g$1( z?Vl>e{QID1ygdFRuL27hBS_xhH6!#3FW0KI;GTxMdfokW0NZSITBpa?gl@+op)6%z z_dxINnbM$7O*NB+r#qLIyw^cfv=iN$VS6^p#k=&9tz|N|G9p>`m#e#bch;Tce9re* zOUiDQ!!mELTYRu2&vX0Mf4^T=noja~=X&++Wg@ToQ?J;{_$P*O@Ldr+Y7luYXq~6J z@CG8WUQ4RrlqiS;b9Sz|!`XK^_j}tvMFGP3E+myalO%!xadh}8sG#$|4qGPZfSzW} zg2aS!^!~!NsZKWYV0P|cE}Yd*jcR(6Kg4wdiz-lSAyDnXKFwB`t^3n2k?-DW4Cw<` z@qMuaM~i|^!;!%l`qIk#)m3Wx`_q{1e9U52hlf!$Y&w+i@vK224v)0H`GgVwlbp=| zZC4%51M6~HJFAwWw8De$_l;899n5_-&rweQzrbU)pZ% z?{+qslxC#jT&9}}9Y@MY;31hh#2M#|&N9 z#oy=9<<9oWgAl(W|8YTaUSF&3{K?D7`0*-E=<3%W_{`qDtLZ6I1LS^r*MD z)|;Wsp*0bIUjJiT&(hjb^M|&&`8us^WuJjU8Iok=^2bufmy#Gbh>$$J-)I^)ecnN6 z!NJ8OlY}EXX#R<0{F0S{iYoI{VQcN5ThN=Ovf{1VwM{QspeSvTg1WZ<$plRKHqWcI zpZ_e6i{Ctjk>hrlHP!k9M2k)svc^U;|^>9h{thkDJky3Z*Ubgjl%lIER~0HsmIcq@_*Oi`wmi+AWm({05RrEjZ}DFzyMD^a51tZ z#MODg@wRS$e4PKsJI)sb_I0RV)kT>bvZ|jxv~N2#-=m3 zV>)}hQY!K$?ee1hR}qHi_-NQ+!V_6R6=A{zojAgRgqN)XVU9mo9wIhUxEP^9nj)Yg zbl7OYLPW=kV)A$mwlr1Veo=GN2|D$DB8Gd~Etowaqv#7zZPF<4)>YT~SWbT`Og5E$ zayXpUht<=4$ER+&A5Jb*5hE-aK~dBgFlU6aFhNQ~wpdXf1ot)h5GgeG>4o?fKRFr6 zPY&IAeGQt!PfpgCis@Q0e@@phP?0_<^~^$_GYp!3F)&j-s{Z;Xm1Pc?q+gauQep1e z(1-F{Aw8wTcR;J|54jwRE}r|yX|R^abd@w1{9?L4lSaW}>RL`vxP6p^@G3bgOCXX@;;ksg>5)R~eP5BiHgSEJcVB6!SN=-3~SUiFJ|mO~JS6~1CS0h}~? zmF=aW4`n|xI{LBzrJ)Z9F#HckX1X_xmTWb}%o!mK-EN$_xaR3TF7G>1PG=}6oL*MvZ!b9LckM?81=7JIF`xpFXAW>(Mg0aoyA6x$9(^c+{^8+0 zzkaFvwG@puv4@`#Jd}!yCxrGn6jZWj*m$PluPh^)jy@vV3f;65{WW~8m&Des*Yv)Y3{4(e5`XK$frk)!Y_JUI|jZ^ z0=^h^)1>N^U8z0^Q_PnBsa>)yqVHLIQE3vU0Fq-p>HE7T=GJfpg{89VP^vRpgvvk_{#l{t)vD!swm*!MmEtapeHmcst6coy<@n>yi7YBj^ zF~L|0&uOBxCi>T-Xa6TZes*KR>1nC`nWNHe*UxQ_y~OI+rFW%cE^p{KG^yE^FGwdq zYnG&h>C>SsdKw8<#Nr3kuV8~Ko4I{+tsta%t@8uDaglQ4X8ZzjtF~lUIL~Ph%z^*f z=e>?SazgxFdHHK8M~=Ui78ZgssSis(969l96`a@1XtU-Ein8WrG#bsUMm<+lpq`)A zdjC~YG55H<(kdZ)?omnEqdDT)s*=k2x$?5|xATv>I*%U%-z7VpHvBzgD+8y(W5Bv) z{pP@y}R=+ zoVBoWX9Yyg|M>o6V%@W~XMil9`SAJ!Ky|XASoX*mG>8ZFA88zhVtK=3fGnSS_4?JX z9cUnq#-PQbYZHxuV{Y&GCyH5EjKPS+uO^WMOozXG{}SoKUwXeJvt4_&28IUB%IK%- zxG_XMj6wL}qDLr>19*L=rOeZ{Tmhvpf`Yaw^?(u>CL#zqYG~2?T>To%EKD`QKHG?3 zMsW2d|AOzcYk&6a?WUz?;G5bl9)FGqx~)oK3VI#viqCRA{u<%;T9%5*?-r0-1tLFg zy-9J>{Woc%XP({jC}l$PHG^YfVOvm3^EIHO`w_c)K5^m}0IUg)#Ap|V1B1dvt=58q zH?=x(1_xt2Kp#XwoA!22&MgB1n$&9MW=+oRSKEi9nty7G{&}*~2Ey_TqQT%$Cg-5< z)u`@$|M4+1+atRSY=4ksUcZr|5v}8<0r*Wpe4jfK(>ymr4fYGD&~GB=$0d z;_Xd__%clKfj~e+U&yQ78zdHSE5qoS(NcMGuRqU{PLr{&?J*CdBPlc?-1?4aH#se# zJ5Y!=Iw{fUG)B?;$i1(#$UO+8-~0W+Jf>$x0;7++l_sb09G!DW&ULw8i8& z***;Ax@{+_^Sl4+P7xe39NhL`@7)G2;t9LwW!B62li%b6%b=o8so7cM)b2obNF<6L z6b%8r(Vs^k1r4P;jra~Qfws-kF6>(V#~lj zA~f{IcIzt*XT}PFWc}V<*Z|rge_Rm6Z`fioZOm9uHCUl=KfsMIR>V`7Ht0G~YZYA_ zz?Yb!F9(zML4vVChagx4hF8Z6M2Go$G9W$__XCp{6B1t)#^M*EFv@u^Sm>7Yg#g)Q1>~c6Q9*HC?(rh|Oa0oj9!%1q1kW?eEh=3qsL}(}}BFG;bj!^jh z2!Y%U2)Jktxep4N>@{dchBfxW`C|wDaWJ>EDC-C5I4nLLi_D{OqRMJ$lE9q3Dbd)W z@)pspE}8g<*I~DR%6S8ag zo_BFqd@#q572Zy{m|fLQM=djsQF7^jfB^qMHSD-*g?lc}DYBx)2aUbrc~_82`-7?l z=5`@uGW2N(5ujHE!tmTuMd2~Lxsg9qJRKq7U z@#%bGa}7^%m=G*Q(BXkH5 z2Aj6Ent=a42mp?fwMF}%e{6y8_4j8GXTa;aoez2MT^~oMrR`lfYjKGHLfqSKwtN3W zWIN7IECFGu0aHJpnzq)}Rcvi`Wzzb-%*1UA;+USyji0L!o)@}q0>@%Dj?(iZL+w}3_CR_$*f`*ROK z9|^dI#4G2m@Mt0=hFp{ zU9wd^5Fc-o*=Q0jG;RS43)x?aME2+31t27KOW%%(wCMe-RL=f(xPe<`NwsOO)n@w! z99_9X!>|Sfx@`C}ZyN`a7(KHJQ|2824;NBcKN4d9F9(h?p{+*ptM{zuJj4?QvHx7K zsp;{v8}*+$e{HIzb#)~;yEduPZ=rIV!Y>KfqbcFc^ytCkFddu@{&C0OruZr5Q}8^vc0@r}o{9sI)7a zKCIc~BK13WslcE;(wJzMhz0&)0$$gC^O?2x&hx^pFhhz6?D&Id>J?>3h0LPrzweAu-Cad;%tC~@oTy1Y?>RO!11lJv(!2E%ymD{79 z1yGE1p)ec-2^xsx1A2lSOO))1xA?7Ty8!L@|6j52Z>uQCXOy4W|NK`sp1NS$&MNwvlhO&Zq?EE8N;Mgn%dA>~%fUSDKS^yF{r7k@4Ok$=hE zMBIYM_Pfv=11fEeq$0!L++it~D0{+o-BArb zxUVwDOdA>fy1KYlamt}g*XbYjfZUkepE>d`?Q+KaLD_A!9FM2#xwravLu#-03`%TI zx)a;3D%J8jlu|YQ5!Z<5m4&*+1iNfS^~W$rckE7QvMXV7%nsM1IuvV1g{B~|wby$y z-2)PIPd@dg38^+cb5eVM059ME0drFSY)cfDu0T1Ml4x;^+m zi9R~xSG@mAlw-8_ckOr<1x!Z+)4-|Y6-lhfNZAw-i!rX}){wI1xqg+Gi=ErMo4Lr+ z#G>2bYgAVo^TYq(bliJqTLk1>J;>La;~H|z0oS7;RA~#tI#=vOwS$u=V;tF9HhM?D zZ$jzgk>jx={A@n##ylcc#G^dM$eOqrrdJTN47Od#bHDfQxmAvsL6(|9+RPwLW)OuL zgkMHV-(N@B0-kqP?rl1A-Psz}ngH_-9G`pgua;5fH`yyeiDi#6*KKQO06oq=5)OShKPC6|azA)!v(Q*#)BksT1*@`c6FfGfsQB@NR z!-I43yq&hq*gAJ#)Oc+HJ-v5JpCjI9{u^kDJ>zjrfkoSC@g09@33nQVEhl)D{tqZK zKA*CmXXek8rEKMpn+T@lEw`k|O59sI{;I3X7!|!`qtlMM%+aA)nyoou?uk@N-deS& zDgGCx@e&Jf_+Tb{W!ADZG#mKdYr^F)0ut#4C93>9;z)3{AR#rtVL{!3bRgcur^WB7 zIQ%o_#Q@#`5=~;UWMQq6TH&ut?2;J%{r`YX(ZY_8PYHkzLO>RuD9^bEjJg*gOsOY{J zJn+yX{RTYt#8ZQwdG6-I3k`2Tij2iD?T>)Zi_zj9sSP( z-?!sE!@l)sQ2pO~^8YS8tO3OMHU9PK{8Q`v-bPFUwi$08_Gp097XmmQOs}|w(Vz3- zRQta-h6O$w1J8R8(`T`k*Q6-f)oLO3hr*>VW(sBmfgO-FapJPIpx;uCO#Z^gun(*T zNbV=^1fEkGRm#M}YBa_J!b{~;wraW6t@r0L9ww-hB?4-Pw#(p%=tof%#B0jmcq8GY(`b0cwhxV1gu+Rua zASsdEOJ*K_7&^)6wbfXXt7_s*a|kUHjrar=rK6|5tqpR&mqJpIS0D9_2X&8`w_ibB zJJbG+fIQV(52)iHZUlNXQr*Mmwa^`Uf2s80WB*Z?iK#kvxYp8AB&WYWY^km#tOkZb zyIf`*3DP-ZUuu414}i%|&FQ|;Ohc0nDPcslW)$>-6)+IIwq7z(LIop7r8m9;s>wNW zDzVflSM-M6TuXvB39_XXg-jC8l+eQWpy6JLzmja9#8ExIUd5fFXysiz^P->jVd%8? zeqxW+Ttm>Ge8UVuB{Q0V`ALTtTj_uRFxMC@M>v2HBu* z71=uqv&Z`I1v6sR4)hZvLKWXu6#Cd@QsSawN8}ljszyW;a$=`TH;yXkeBTg(p5c2CJYXC)(zKBms%Hjy`@b2&AK+5z9^l% zWu|B{1^+}xRCRXuP*2`<{Ypz__6-$fr_Wv0!&-Fm;PB1uPou)R`-IB@ke)pa)l=>uQbD*-Y1$WU{qKM&&nUR7YaoLVUE@$E;z>*H*>4t+Gu zH$G}Z!Ee(9M<{e|TiDNmDLpwq?;#z!#tndG;^{&HC7-PPl>(MdHSzuSStNK7cJP!6cL+M zk&)68(rcsWQ}ytgUx=s{EWLW*ei_<&KBCMO@e;CLEPOR1R-5K#J@z-)sYl;D1YPwWJR%yPtBLwDzoN$((YdNX*Dy8hnPP&tp((M{CjhVEr z=pf1$#C){j6;+a zFdN|PwyUG-LV@d8_kcG5F9F^Gd~jK`dYJ=M2k1bq4q$m@-J56Y-boT+9{>R`0fCul zTWt;S8BQ$<*RwtEah(a^&z80W$kqTJ23!tY0o)q+GVqRhfzuY@5@J^?2fzo@82zR5 z^)BDmPhZOOtl-wFwEt+f4Ov5=7Kp?Yi2|8;lj&{U__n>y0PytO!{Ufm`ioCJFJiZ4G zu96~2ykxUT#9L{Zg{HERN+RGEFI07eLZ+K-vH5NAGD{_r2!SQ#>-UsmrA1~*Cb3vr z0_T;dO6SCqrITtgnI%%vv<|(;();OKNReh~ZgumrJWR`^Tb6xCg=|=`Vr434OD!W| z&h8R|lD&m20gu5A7Q(OWeCHY{*jY+C_a86K)V7e%XlPQFv|eO{tm`nlkA*I1u_v1a z_Gwcrx7b3X<3T!jV&g2`S2gOH($uySNhPqbaJ9Il5tOCp__D(N#<1!0t$&jaxm&dW z2z?3K9n$4L3kMXjpdy%HP~2@0TjVdp7+|EwVmEb%iCE*8^no#x5lpO1!;NI-cKF@V z>sC}oF6qmh#n>fQmgAXNS?&6pYRRA`cT6{HOC46}zPs*4M34U1CS8V%a5)_@EwgOc z4s2p&7rw38H#G;3Jn&FrfdiZ*a?Imga{3KrI`^FO$i)Ze@`QzhN659ANO@Uig_V(8 z9tE{zNgA3fD(~oh<|DmHp4<<{+ z7O-CsR)OJ7RFKRR5+xNZSDrnxWyvX73Z>MJ3Q1L{d8J7&tPGiLHoq+D!mTY`POIfx zGa8CjM3_)y2o!ZXMLX-`XeU-N;U4$Ndo&x=+E_t6d@2M~KdQc4tHM2&h!=-Uh03pR z%GKDS_5<(_db_$Wh|2BLZ2#9pvDQ4|S3do%IO&wr&cs04OQaDCn;Unr7SQXgbI!Zq zqDwlXxx32}Te z>a=R{-QVXv{npSdzh=5y6Hgu#a<3xAO02ib?)uzslY$<2=#l=;H{h`+o*MMbb1%H~ zYFHM!hk$>azmofA%y;8{_~}<;z8gnZYKAfc9(&@M=lSiA34cvSt$zC5&^{HK|OlP=38C&YZVUTM8PjPH$yxV{2#cU@(d}zgH4$GP|CR zXwEmd@gXkI1cPHlGXsecGV7Zj_XQL)-W|&1He{NHya-ldR#!?QE5QUq{1Koq+wA8H zRM<0VaY^BnVsa#aN~To6hmqa6N|&)Hr=vjCitmINS)5-F9|Fgo1m`Y8UY+hIw{`X> z&Hx)94JaxINe;9s=2{O3C&<+jl?vX3z(lx-!%G08cbz;JrpfR-HOOXhX~gZnDn;9# zcoIs$3fqcA6B~L|U>6*%rokIU&D#`Y))^=8$fJ&0O|jy;rVFd2A@C$&&`|kcJfPJU zWFCA4QCBOGqd*#ZN&hH0-GP>E@b~e=DDaVjE{9y?iT@mP(en|9Nn~LPomn*Yla?^A z6l8{NkjdV+Dx0L{aU0WRpB}@quLn!*S<}>fJN%)r?%@$1$z2*YhxG4^P6~7)eCPKi zuvkckv!$s_c3GQ*0v){;`DpO;TTBOUj+iSqc8n{x1s@!j$<-jsdSN*D}wP4%#79sV|@kQDvuX1BQiTWw5vr=6hYS9enN*0P;i z7yw%c>!uoT>)Av8mG;-?b*osVs$+o;uA4;5_WUbt!XbObeIe=NDPe=7vdquLr}(7i z4%JKhBui57@3J|aZKjF@-rF7C7e@e=AmpN1TAfvDF3bGfT~qVUE?Gt0%3dv1RR-T= zz91fr()a{?krZWe#9nCvX2GGsiMI4mtY*IZ`8Xb)>fS4^` zFo?k_0w}ewCW-(!zyT_y1_DF{8~}g{0MHu%4PXo)W(B%}6j^N(c{oj%WmHz`CyH6x zvtoNnh`8@f+0c{_IU|`7jP$!ro0DsV6ad?!jJz_)S+x|#)d)LMSs;q0w?k2znzyp_ z{xhHMg-WL`dtz4GsVu@YA6o_;Kh8mAH_UZaEH@*xY#`DtVBk4WbzRgj=0KD^x3=3# zr{rOTfv&4M1nvX5Y{zST49xi-A|Y*U^BrYM9?22;&y;;}+D)dXZmr16_SHMqdv*IE z@H*Jf`VQ_5KwW$=H%1iBJ9So4mV<`m5@A^>o(7Lu&#^7!pcCffJ{2CVErQEYbZ@q5 k7@ny5k!;?xs;nlt$ST{%$(02xLVGynhq literal 0 HcmV?d00001 diff --git a/web/static/fonts/inter-700.woff2 b/web/static/fonts/inter-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..12b51d7709cff88edbe49b94412d240b90126ee3 GIT binary patch literal 24352 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 r}else{return null}}if(o&&(o==="*"||o.split(" ").indexOf(n)>=0)){return"unset"}}return r}function re(t,n){let r=null;o(t,function(e){return!!(r=i(t,ue(e),n))});if(r!=="unset"){return r}}function h(e,t){const n=e instanceof Element&&(e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector);return!!n&&n.call(e,t)}function T(e){const t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;const n=t.exec(e);if(n){return n[1].toLowerCase()}else{return""}}function q(e){const t=new DOMParser;return t.parseFromString(e,"text/html")}function L(e,t){while(t.childNodes.length>0){e.append(t.childNodes[0])}}function A(e){const t=ne().createElement("script");se(e.attributes,function(e){t.setAttribute(e.name,e.value)});t.textContent=e.textContent;t.async=false;if(Q.config.inlineScriptNonce){t.nonce=Q.config.inlineScriptNonce}return t}function N(e){return e.matches("script")&&(e.type==="text/javascript"||e.type==="module"||e.type==="")}function I(e){Array.from(e.querySelectorAll("script")).forEach(e=>{if(N(e)){const t=A(e);const n=e.parentNode;try{n.insertBefore(t,e)}catch(e){O(e)}finally{e.remove()}}})}function P(e){const t=e.replace(/]*)?>[\s\S]*?<\/head>/i,"");const n=T(t);let r;if(n==="html"){r=new DocumentFragment;const i=q(e);L(r,i.body);r.title=i.title}else if(n==="body"){r=new DocumentFragment;const i=q(t);L(r,i.body);r.title=i.title}else{const i=q('");r=i.querySelector("template").content;r.title=i.title;var o=r.querySelector("title");if(o&&o.parentNode===r){o.remove();r.title=o.innerText}}if(r){if(Q.config.allowScriptTags){I(r)}else{r.querySelectorAll("script").forEach(e=>e.remove())}}return r}function oe(e){if(e){e()}}function t(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function k(e){return typeof e==="function"}function D(e){return t(e,"Object")}function ie(e){const t="htmx-internal-data";let n=e[t];if(!n){n=e[t]={}}return n}function M(t){const n=[];if(t){for(let e=0;e=0}function le(e){return e.getRootNode({composed:true})===document}function F(e){return e.trim().split(/\s+/)}function ce(e,t){for(const n in t){if(t.hasOwnProperty(n)){e[n]=t[n]}}return e}function S(e){try{return JSON.parse(e)}catch(e){O(e);return null}}function B(){const e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function U(t){try{const e=new URL(t);if(e){t=e.pathname+e.search}if(!/^\/$/.test(t)){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function e(e){return vn(ne().body,function(){return eval(e)})}function j(t){const e=Q.on("htmx:load",function(e){t(e.detail.elt)});return e}function V(){Q.logger=function(e,t,n){if(console){console.log(t,e,n)}}}function _(){Q.logger=null}function u(e,t){if(typeof e!=="string"){return e.querySelector(t)}else{return u(ne(),e)}}function x(e,t){if(typeof e!=="string"){return e.querySelectorAll(t)}else{return x(ne(),e)}}function E(){return window}function z(e,t){e=y(e);if(t){E().setTimeout(function(){z(e);e=null},t)}else{c(e).removeChild(e)}}function ue(e){return e instanceof Element?e:null}function $(e){return e instanceof HTMLElement?e:null}function J(e){return typeof e==="string"?e:null}function f(e){return e instanceof Element||e instanceof Document||e instanceof DocumentFragment?e:null}function K(e,t,n){e=ue(y(e));if(!e){return}if(n){E().setTimeout(function(){K(e,t);e=null},n)}else{e.classList&&e.classList.add(t)}}function G(e,t,n){let r=ue(y(e));if(!r){return}if(n){E().setTimeout(function(){G(r,t);r=null},n)}else{if(r.classList){r.classList.remove(t);if(r.classList.length===0){r.removeAttribute("class")}}}}function W(e,t){e=y(e);e.classList.toggle(t)}function Z(e,t){e=y(e);se(e.parentElement.children,function(e){G(e,t)});K(ue(e),t)}function g(e,t){e=ue(y(e));if(e&&e.closest){return e.closest(t)}else{do{if(e==null||h(e,t)){return e}}while(e=e&&ue(c(e)));return null}}function l(e,t){return e.substring(0,t.length)===t}function Y(e,t){return e.substring(e.length-t.length)===t}function ge(e){const t=e.trim();if(l(t,"<")&&Y(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function p(t,r,n){if(r.indexOf("global ")===0){return p(t,r.slice(7),true)}t=y(t);const o=[];{let t=0;let n=0;for(let e=0;e"){t--}}if(n0){const r=ge(o.shift());let e;if(r.indexOf("closest ")===0){e=g(ue(t),ge(r.substr(8)))}else if(r.indexOf("find ")===0){e=u(f(t),ge(r.substr(5)))}else if(r==="next"||r==="nextElementSibling"){e=ue(t).nextElementSibling}else if(r.indexOf("next ")===0){e=pe(t,ge(r.substr(5)),!!n)}else if(r==="previous"||r==="previousElementSibling"){e=ue(t).previousElementSibling}else if(r.indexOf("previous ")===0){e=me(t,ge(r.substr(9)),!!n)}else if(r==="document"){e=document}else if(r==="window"){e=window}else if(r==="body"){e=document.body}else if(r==="root"){e=m(t,!!n)}else if(r==="host"){e=t.getRootNode().host}else{s.push(r)}if(e){i.push(e)}}if(s.length>0){const e=s.join(",");const c=f(m(t,!!n));i.push(...M(c.querySelectorAll(e)))}return i}var pe=function(t,e,n){const r=f(m(t,n)).querySelectorAll(e);for(let e=0;e=0;e--){const o=r[e];if(o.compareDocumentPosition(t)===Node.DOCUMENT_POSITION_FOLLOWING){return o}}};function ae(e,t){if(typeof e!=="string"){return p(e,t)[0]}else{return p(ne().body,e)[0]}}function y(e,t){if(typeof e==="string"){return u(f(t)||document,e)}else{return e}}function xe(e,t,n,r){if(k(t)){return{target:ne().body,event:J(e),listener:t,options:n}}else{return{target:y(e),event:J(t),listener:n,options:r}}}function ye(t,n,r,o){Vn(function(){const e=xe(t,n,r,o);e.target.addEventListener(e.event,e.listener,e.options)});const e=k(n);return e?n:r}function be(t,n,r){Vn(function(){const e=xe(t,n,r);e.target.removeEventListener(e.event,e.listener)});return k(n)?n:r}const ve=ne().createElement("output");function we(e,t){const n=re(e,t);if(n){if(n==="this"){return[Se(e,t)]}else{const r=p(e,n);if(r.length===0){O('The selector "'+n+'" on '+t+" returned no matches!");return[ve]}else{return r}}}}function Se(e,t){return ue(o(e,function(e){return te(ue(e),t)!=null}))}function Ee(e){const t=re(e,"hx-target");if(t){if(t==="this"){return Se(e,"hx-target")}else{return ae(e,t)}}else{const n=ie(e);if(n.boosted){return ne().body}else{return e}}}function Ce(t){const n=Q.config.attributesToSettle;for(let e=0;e0){s=e.substring(0,e.indexOf(":"));n=e.substring(e.indexOf(":")+1)}else{s=e}o.removeAttribute("hx-swap-oob");o.removeAttribute("data-hx-swap-oob");const r=p(t,n,false);if(r){se(r,function(e){let t;const n=o.cloneNode(true);t=ne().createDocumentFragment();t.appendChild(n);if(!Re(s,e)){t=f(n)}const r={shouldSwap:true,target:e,fragment:t};if(!he(e,"htmx:oobBeforeSwap",r))return;e=r.target;if(r.shouldSwap){qe(t);_e(s,e,e,t,i);Te()}se(i.elts,function(e){he(e,"htmx:oobAfterSwap",r)})});o.parentNode.removeChild(o)}else{o.parentNode.removeChild(o);fe(ne().body,"htmx:oobErrorNoTarget",{content:o})}return e}function Te(){const e=u("#--htmx-preserve-pantry--");if(e){for(const t of[...e.children]){const n=u("#"+t.id);n.parentNode.moveBefore(t,n);n.remove()}e.remove()}}function qe(e){se(x(e,"[hx-preserve], [data-hx-preserve]"),function(e){const t=te(e,"id");const n=ne().getElementById(t);if(n!=null){if(e.moveBefore){let e=u("#--htmx-preserve-pantry--");if(e==null){ne().body.insertAdjacentHTML("afterend","
");e=u("#--htmx-preserve-pantry--")}e.moveBefore(n,null)}else{e.parentNode.replaceChild(n,e)}}})}function Le(l,e,c){se(e.querySelectorAll("[id]"),function(t){const n=ee(t,"id");if(n&&n.length>0){const r=n.replace("'","\\'");const o=t.tagName.replace(":","\\:");const e=f(l);const i=e&&e.querySelector(o+"[id='"+r+"']");if(i&&i!==e){const s=t.cloneNode();Oe(t,i);c.tasks.push(function(){Oe(t,s)})}}})}function Ae(e){return function(){G(e,Q.config.addedClass);kt(ue(e));Ne(f(e));he(e,"htmx:load")}}function Ne(e){const t="[autofocus]";const n=$(h(e,t)?e:e.querySelector(t));if(n!=null){n.focus()}}function a(e,t,n,r){Le(e,n,r);while(n.childNodes.length>0){const o=n.firstChild;K(ue(o),Q.config.addedClass);e.insertBefore(o,t);if(o.nodeType!==Node.TEXT_NODE&&o.nodeType!==Node.COMMENT_NODE){r.tasks.push(Ae(o))}}}function Ie(e,t){let n=0;while(n0}function $e(e,t,r,o){if(!o){o={}}e=y(e);const i=o.contextElement?m(o.contextElement,false):ne();const n=document.activeElement;let s={};try{s={elt:n,start:n?n.selectionStart:null,end:n?n.selectionEnd:null}}catch(e){}const l=xn(e);if(r.swapStyle==="textContent"){e.textContent=t}else{let n=P(t);l.title=n.title;if(o.selectOOB){const u=o.selectOOB.split(",");for(let t=0;t0){E().setTimeout(c,r.settleDelay)}else{c()}}function Je(e,t,n){const r=e.getResponseHeader(t);if(r.indexOf("{")===0){const o=S(r);for(const i in o){if(o.hasOwnProperty(i)){let e=o[i];if(D(e)){n=e.target!==undefined?e.target:n}else{e={value:e}}he(n,i,e)}}}else{const s=r.split(",");for(let e=0;e0){const s=o[0];if(s==="]"){e--;if(e===0){if(n===null){t=t+"true"}o.shift();t+=")})";try{const l=vn(r,function(){return Function(t)()},function(){return true});l.source=t;return l}catch(e){fe(ne().body,"htmx:syntax:error",{error:e,source:t});return null}}}else if(s==="["){e++}if(tt(s,n,i)){t+="(("+i+"."+s+") ? ("+i+"."+s+") : (window."+s+"))"}else{t=t+s}n=o.shift()}}}function C(e,t){let n="";while(e.length>0&&!t.test(e[0])){n+=e.shift()}return n}function rt(e){let t;if(e.length>0&&Ye.test(e[0])){e.shift();t=C(e,Qe).trim();e.shift()}else{t=C(e,v)}return t}const ot="input, textarea, select";function it(e,t,n){const r=[];const o=et(t);do{C(o,w);const l=o.length;const c=C(o,/[,\[\s]/);if(c!==""){if(c==="every"){const u={trigger:"every"};C(o,w);u.pollInterval=d(C(o,/[,\[\s]/));C(o,w);var i=nt(e,o,"event");if(i){u.eventFilter=i}r.push(u)}else{const a={trigger:c};var i=nt(e,o,"event");if(i){a.eventFilter=i}C(o,w);while(o.length>0&&o[0]!==","){const f=o.shift();if(f==="changed"){a.changed=true}else if(f==="once"){a.once=true}else if(f==="consume"){a.consume=true}else if(f==="delay"&&o[0]===":"){o.shift();a.delay=d(C(o,v))}else if(f==="from"&&o[0]===":"){o.shift();if(Ye.test(o[0])){var s=rt(o)}else{var s=C(o,v);if(s==="closest"||s==="find"||s==="next"||s==="previous"){o.shift();const h=rt(o);if(h.length>0){s+=" "+h}}}a.from=s}else if(f==="target"&&o[0]===":"){o.shift();a.target=rt(o)}else if(f==="throttle"&&o[0]===":"){o.shift();a.throttle=d(C(o,v))}else if(f==="queue"&&o[0]===":"){o.shift();a.queue=C(o,v)}else if(f==="root"&&o[0]===":"){o.shift();a[f]=rt(o)}else if(f==="threshold"&&o[0]===":"){o.shift();a[f]=C(o,v)}else{fe(e,"htmx:syntax:error",{token:o.shift()})}C(o,w)}r.push(a)}}if(o.length===l){fe(e,"htmx:syntax:error",{token:o.shift()})}C(o,w)}while(o[0]===","&&o.shift());if(n){n[t]=r}return r}function st(e){const t=te(e,"hx-trigger");let n=[];if(t){const r=Q.config.triggerSpecsCache;n=r&&r[t]||it(e,t,r)}if(n.length>0){return n}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,ot)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function lt(e){ie(e).cancelled=true}function ct(e,t,n){const r=ie(e);r.timeout=E().setTimeout(function(){if(le(e)&&r.cancelled!==true){if(!gt(n,e,Mt("hx:poll:trigger",{triggerSpec:n,target:e}))){t(e)}ct(e,t,n)}},n.pollInterval)}function ut(e){return location.hostname===e.hostname&&ee(e,"href")&&ee(e,"href").indexOf("#")!==0}function at(e){return g(e,Q.config.disableSelector)}function ft(t,n,e){if(t instanceof HTMLAnchorElement&&ut(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"&&String(ee(t,"method")).toLowerCase()!=="dialog"){n.boosted=true;let r,o;if(t.tagName==="A"){r="get";o=ee(t,"href")}else{const i=ee(t,"method");r=i?i.toLowerCase():"get";o=ee(t,"action");if(o==null||o===""){o=ne().location.href}if(r==="get"&&o.includes("?")){o=o.replace(/\?[^#]+/,"")}}e.forEach(function(e){pt(t,function(e,t){const n=ue(e);if(at(n)){b(n);return}de(r,o,n,t)},n,e,true)})}}function ht(e,t){const n=ue(t);if(!n){return false}if(e.type==="submit"||e.type==="click"){if(n.tagName==="FORM"){return true}if(h(n,'input[type="submit"], button')&&(h(n,"[form]")||g(n,"form")!==null)){return true}if(n instanceof HTMLAnchorElement&&n.href&&(n.getAttribute("href")==="#"||n.getAttribute("href").indexOf("#")!==0)){return true}}return false}function dt(e,t){return ie(e).boosted&&e instanceof HTMLAnchorElement&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function gt(e,t,n){const r=e.eventFilter;if(r){try{return r.call(t,n)!==true}catch(e){const o=r.source;fe(ne().body,"htmx:eventFilter:error",{error:e,source:o});return true}}return false}function pt(l,c,e,u,a){const f=ie(l);let t;if(u.from){t=p(l,u.from)}else{t=[l]}if(u.changed){if(!("lastValue"in f)){f.lastValue=new WeakMap}t.forEach(function(e){if(!f.lastValue.has(u)){f.lastValue.set(u,new WeakMap)}f.lastValue.get(u).set(e,e.value)})}se(t,function(i){const s=function(e){if(!le(l)){i.removeEventListener(u.trigger,s);return}if(dt(l,e)){return}if(a||ht(e,l)){e.preventDefault()}if(gt(u,l,e)){return}const t=ie(e);t.triggerSpec=u;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(l)<0){t.handledFor.push(l);if(u.consume){e.stopPropagation()}if(u.target&&e.target){if(!h(ue(e.target),u.target)){return}}if(u.once){if(f.triggeredOnce){return}else{f.triggeredOnce=true}}if(u.changed){const n=event.target;const r=n.value;const o=f.lastValue.get(u);if(o.has(n)&&o.get(n)===r){return}o.set(n,r)}if(f.delayed){clearTimeout(f.delayed)}if(f.throttle){return}if(u.throttle>0){if(!f.throttle){he(l,"htmx:trigger");c(l,e);f.throttle=E().setTimeout(function(){f.throttle=null},u.throttle)}}else if(u.delay>0){f.delayed=E().setTimeout(function(){he(l,"htmx:trigger");c(l,e)},u.delay)}else{he(l,"htmx:trigger");c(l,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:u.trigger,listener:s,on:i});i.addEventListener(u.trigger,s)})}let mt=false;let xt=null;function yt(){if(!xt){xt=function(){mt=true};window.addEventListener("scroll",xt);window.addEventListener("resize",xt);setInterval(function(){if(mt){mt=false;se(ne().querySelectorAll("[hx-trigger*='revealed'],[data-hx-trigger*='revealed']"),function(e){bt(e)})}},200)}}function bt(e){if(!s(e,"data-hx-revealed")&&X(e)){e.setAttribute("data-hx-revealed","true");const t=ie(e);if(t.initHash){he(e,"revealed")}else{e.addEventListener("htmx:afterProcessNode",function(){he(e,"revealed")},{once:true})}}}function vt(e,t,n,r){const o=function(){if(!n.loaded){n.loaded=true;he(e,"htmx:trigger");t(e)}};if(r>0){E().setTimeout(o,r)}else{o()}}function wt(t,n,e){let i=false;se(r,function(r){if(s(t,"hx-"+r)){const o=te(t,"hx-"+r);i=true;n.path=o;n.verb=r;e.forEach(function(e){St(t,e,n,function(e,t){const n=ue(e);if(g(n,Q.config.disableSelector)){b(n);return}de(r,o,n,t)})})}});return i}function St(r,e,t,n){if(e.trigger==="revealed"){yt();pt(r,n,t,e);bt(ue(r))}else if(e.trigger==="intersect"){const o={};if(e.root){o.root=ae(r,e.root)}if(e.threshold){o.threshold=parseFloat(e.threshold)}const i=new IntersectionObserver(function(t){for(let e=0;e0){t.polling=true;ct(ue(r),n,e)}else{pt(r,n,t,e)}}function Et(e){const t=ue(e);if(!t){return false}const n=t.attributes;for(let e=0;e", "+e).join(""));return o}else{return[]}}function Tt(e){const t=g(ue(e.target),"button, input[type='submit']");const n=Lt(e);if(n){n.lastButtonClicked=t}}function qt(e){const t=Lt(e);if(t){t.lastButtonClicked=null}}function Lt(e){const t=g(ue(e.target),"button, input[type='submit']");if(!t){return}const n=y("#"+ee(t,"form"),t.getRootNode())||g(t,"form");if(!n){return}return ie(n)}function At(e){e.addEventListener("click",Tt);e.addEventListener("focusin",Tt);e.addEventListener("focusout",qt)}function Nt(t,e,n){const r=ie(t);if(!Array.isArray(r.onHandlers)){r.onHandlers=[]}let o;const i=function(e){vn(t,function(){if(at(t)){return}if(!o){o=new Function("event",n)}o.call(t,e)})};t.addEventListener(e,i);r.onHandlers.push({event:e,listener:i})}function It(t){ke(t);for(let e=0;eQ.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){fe(ne().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function Vt(t){if(!B()){return null}t=U(t);const n=S(localStorage.getItem("htmx-history-cache"))||[];for(let e=0;e=200&&this.status<400){he(ne().body,"htmx:historyCacheMissLoad",i);const e=P(this.response);const t=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;const n=Ut();const r=xn(n);kn(e.title);qe(e);Ve(n,t,r);Te();Kt(r.tasks);Bt=o;he(ne().body,"htmx:historyRestore",{path:o,cacheMiss:true,serverResponse:this.response})}else{fe(ne().body,"htmx:historyCacheMissLoadError",i)}};e.send()}function Wt(e){zt();e=e||location.pathname+location.search;const t=Vt(e);if(t){const n=P(t.content);const r=Ut();const o=xn(r);kn(t.title);qe(n);Ve(r,n,o);Te();Kt(o.tasks);E().setTimeout(function(){window.scrollTo(0,t.scroll)},0);Bt=e;he(ne().body,"htmx:historyRestore",{path:e,item:t})}else{if(Q.config.refreshOnHistoryMiss){window.location.reload(true)}else{Gt(e)}}}function Zt(e){let t=we(e,"hx-indicator");if(t==null){t=[e]}se(t,function(e){const t=ie(e);t.requestCount=(t.requestCount||0)+1;e.classList.add.call(e.classList,Q.config.requestClass)});return t}function Yt(e){let t=we(e,"hx-disabled-elt");if(t==null){t=[]}se(t,function(e){const t=ie(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","");e.setAttribute("data-disabled-by-htmx","")});return t}function Qt(e,t){se(e.concat(t),function(e){const t=ie(e);t.requestCount=(t.requestCount||1)-1});se(e,function(e){const t=ie(e);if(t.requestCount===0){e.classList.remove.call(e.classList,Q.config.requestClass)}});se(t,function(e){const t=ie(e);if(t.requestCount===0){e.removeAttribute("disabled");e.removeAttribute("data-disabled-by-htmx")}})}function en(t,n){for(let e=0;en.indexOf(e)<0)}else{e=e.filter(e=>e!==n)}r.delete(t);se(e,e=>r.append(t,e))}}function on(t,n,r,o,i){if(o==null||en(t,o)){return}else{t.push(o)}if(tn(o)){const s=ee(o,"name");let e=o.value;if(o instanceof HTMLSelectElement&&o.multiple){e=M(o.querySelectorAll("option:checked")).map(function(e){return e.value})}if(o instanceof HTMLInputElement&&o.files){e=M(o.files)}nn(s,e,n);if(i){sn(o,r)}}if(o instanceof HTMLFormElement){se(o.elements,function(e){if(t.indexOf(e)>=0){rn(e.name,e.value,n)}else{t.push(e)}if(i){sn(e,r)}});new FormData(o).forEach(function(e,t){if(e instanceof File&&e.name===""){return}nn(t,e,n)})}}function sn(e,t){const n=e;if(n.willValidate){he(n,"htmx:validation:validate");if(!n.checkValidity()){t.push({elt:n,message:n.validationMessage,validity:n.validity});he(n,"htmx:validation:failed",{message:n.validationMessage,validity:n.validity})}}}function ln(n,e){for(const t of e.keys()){n.delete(t)}e.forEach(function(e,t){n.append(t,e)});return n}function cn(e,t){const n=[];const r=new FormData;const o=new FormData;const i=[];const s=ie(e);if(s.lastButtonClicked&&!le(s.lastButtonClicked)){s.lastButtonClicked=null}let l=e instanceof HTMLFormElement&&e.noValidate!==true||te(e,"hx-validate")==="true";if(s.lastButtonClicked){l=l&&s.lastButtonClicked.formNoValidate!==true}if(t!=="get"){on(n,o,i,g(e,"form"),l)}on(n,r,i,e,l);if(s.lastButtonClicked||e.tagName==="BUTTON"||e.tagName==="INPUT"&&ee(e,"type")==="submit"){const u=s.lastButtonClicked||e;const a=ee(u,"name");nn(a,u.value,o)}const c=we(e,"hx-include");se(c,function(e){on(n,r,i,ue(e),l);if(!h(e,"form")){se(f(e).querySelectorAll(ot),function(e){on(n,r,i,e,l)})}});ln(r,o);return{errors:i,formData:r,values:An(r)}}function un(e,t,n){if(e!==""){e+="&"}if(String(n)==="[object Object]"){n=JSON.stringify(n)}const r=encodeURIComponent(n);e+=encodeURIComponent(t)+"="+r;return e}function an(e){e=qn(e);let n="";e.forEach(function(e,t){n=un(n,t,e)});return n}function fn(e,t,n){const r={"HX-Request":"true","HX-Trigger":ee(e,"id"),"HX-Trigger-Name":ee(e,"name"),"HX-Target":te(t,"id"),"HX-Current-URL":ne().location.href};bn(e,"hx-headers",false,r);if(n!==undefined){r["HX-Prompt"]=n}if(ie(e).boosted){r["HX-Boosted"]="true"}return r}function hn(n,e){const t=re(e,"hx-params");if(t){if(t==="none"){return new FormData}else if(t==="*"){return n}else if(t.indexOf("not ")===0){se(t.slice(4).split(","),function(e){e=e.trim();n.delete(e)});return n}else{const r=new FormData;se(t.split(","),function(t){t=t.trim();if(n.has(t)){n.getAll(t).forEach(function(e){r.append(t,e)})}});return r}}else{return n}}function dn(e){return!!ee(e,"href")&&ee(e,"href").indexOf("#")>=0}function gn(e,t){const n=t||re(e,"hx-swap");const r={swapStyle:ie(e).boosted?"innerHTML":Q.config.defaultSwapStyle,swapDelay:Q.config.defaultSwapDelay,settleDelay:Q.config.defaultSettleDelay};if(Q.config.scrollIntoViewOnBoost&&ie(e).boosted&&!dn(e)){r.show="top"}if(n){const s=F(n);if(s.length>0){for(let e=0;e0?o.join(":"):null;r.scroll=u;r.scrollTarget=i}else if(l.indexOf("show:")===0){const a=l.slice(5);var o=a.split(":");const f=o.pop();var i=o.length>0?o.join(":"):null;r.show=f;r.showTarget=i}else if(l.indexOf("focus-scroll:")===0){const h=l.slice("focus-scroll:".length);r.focusScroll=h=="true"}else if(e==0){r.swapStyle=l}else{O("Unknown modifier in hx-swap: "+l)}}}}return r}function pn(e){return re(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&ee(e,"enctype")==="multipart/form-data"}function mn(t,n,r){let o=null;Ft(n,function(e){if(o==null){o=e.encodeParameters(t,r,n)}});if(o!=null){return o}else{if(pn(n)){return ln(new FormData,qn(r))}else{return an(r)}}}function xn(e){return{tasks:[],elts:[e]}}function yn(e,t){const n=e[0];const r=e[e.length-1];if(t.scroll){var o=null;if(t.scrollTarget){o=ue(ae(n,t.scrollTarget))}if(t.scroll==="top"&&(n||o)){o=o||n;o.scrollTop=0}if(t.scroll==="bottom"&&(r||o)){o=o||r;o.scrollTop=o.scrollHeight}}if(t.show){var o=null;if(t.showTarget){let e=t.showTarget;if(t.showTarget==="window"){e="body"}o=ue(ae(n,e))}if(t.show==="top"&&(n||o)){o=o||n;o.scrollIntoView({block:"start",behavior:Q.config.scrollBehavior})}if(t.show==="bottom"&&(r||o)){o=o||r;o.scrollIntoView({block:"end",behavior:Q.config.scrollBehavior})}}}function bn(r,e,o,i){if(i==null){i={}}if(r==null){return i}const s=te(r,e);if(s){let e=s.trim();let t=o;if(e==="unset"){return null}if(e.indexOf("javascript:")===0){e=e.slice(11);t=true}else if(e.indexOf("js:")===0){e=e.slice(3);t=true}if(e.indexOf("{")!==0){e="{"+e+"}"}let n;if(t){n=vn(r,function(){return Function("return ("+e+")")()},{})}else{n=S(e)}for(const l in n){if(n.hasOwnProperty(l)){if(i[l]==null){i[l]=n[l]}}}}return bn(ue(c(r)),e,o,i)}function vn(e,t,n){if(Q.config.allowEval){return t()}else{fe(e,"htmx:evalDisallowedError");return n}}function wn(e,t){return bn(e,"hx-vars",true,t)}function Sn(e,t){return bn(e,"hx-vals",false,t)}function En(e){return ce(wn(e),Sn(e))}function Cn(t,n,r){if(r!==null){try{t.setRequestHeader(n,r)}catch(e){t.setRequestHeader(n,encodeURIComponent(r));t.setRequestHeader(n+"-URI-AutoEncoded","true")}}}function On(t){if(t.responseURL&&typeof URL!=="undefined"){try{const e=new URL(t.responseURL);return e.pathname+e.search}catch(e){fe(ne().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function R(e,t){return t.test(e.getAllResponseHeaders())}function Rn(t,n,r){t=t.toLowerCase();if(r){if(r instanceof Element||typeof r==="string"){return de(t,n,null,null,{targetOverride:y(r)||ve,returnPromise:true})}else{let e=y(r.target);if(r.target&&!e||r.source&&!e&&!y(r.source)){e=ve}return de(t,n,y(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:e,swapOverride:r.swap,select:r.select,returnPromise:true})}}else{return de(t,n,null,null,{returnPromise:true})}}function Hn(e){const t=[];while(e){t.push(e);e=e.parentElement}return t}function Tn(e,t,n){let r;let o;if(typeof URL==="function"){o=new URL(t,document.location.href);const i=document.location.origin;r=i===o.origin}else{o=t;r=l(t,document.location.origin)}if(Q.config.selfRequestsOnly){if(!r){return false}}return he(e,"htmx:validateUrl",ce({url:o,sameHost:r},n))}function qn(e){if(e instanceof FormData)return e;const t=new FormData;for(const n in e){if(e.hasOwnProperty(n)){if(e[n]&&typeof e[n].forEach==="function"){e[n].forEach(function(e){t.append(n,e)})}else if(typeof e[n]==="object"&&!(e[n]instanceof Blob)){t.append(n,JSON.stringify(e[n]))}else{t.append(n,e[n])}}}return t}function Ln(r,o,e){return new Proxy(e,{get:function(t,e){if(typeof e==="number")return t[e];if(e==="length")return t.length;if(e==="push"){return function(e){t.push(e);r.append(o,e)}}if(typeof t[e]==="function"){return function(){t[e].apply(t,arguments);r.delete(o);t.forEach(function(e){r.append(o,e)})}}if(t[e]&&t[e].length===1){return t[e][0]}else{return t[e]}},set:function(e,t,n){e[t]=n;r.delete(o);e.forEach(function(e){r.append(o,e)});return true}})}function An(o){return new Proxy(o,{get:function(e,t){if(typeof t==="symbol"){const r=Reflect.get(e,t);if(typeof r==="function"){return function(){return r.apply(o,arguments)}}else{return r}}if(t==="toJSON"){return()=>Object.fromEntries(o)}if(t in e){if(typeof e[t]==="function"){return function(){return o[t].apply(o,arguments)}}else{return e[t]}}const n=o.getAll(t);if(n.length===0){return undefined}else if(n.length===1){return n[0]}else{return Ln(e,t,n)}},set:function(t,n,e){if(typeof n!=="string"){return false}t.delete(n);if(e&&typeof e.forEach==="function"){e.forEach(function(e){t.append(n,e)})}else if(typeof e==="object"&&!(e instanceof Blob)){t.append(n,JSON.stringify(e))}else{t.append(n,e)}return true},deleteProperty:function(e,t){if(typeof t==="string"){e.delete(t)}return true},ownKeys:function(e){return Reflect.ownKeys(Object.fromEntries(e))},getOwnPropertyDescriptor:function(e,t){return Reflect.getOwnPropertyDescriptor(Object.fromEntries(e),t)}})}function de(t,n,r,o,i,D){let s=null;let l=null;i=i!=null?i:{};if(i.returnPromise&&typeof Promise!=="undefined"){var e=new Promise(function(e,t){s=e;l=t})}if(r==null){r=ne().body}const M=i.handler||Dn;const X=i.select||null;if(!le(r)){oe(s);return e}const c=i.targetOverride||ue(Ee(r));if(c==null||c==ve){fe(r,"htmx:targetError",{target:te(r,"hx-target")});oe(l);return e}let u=ie(r);const a=u.lastButtonClicked;if(a){const L=ee(a,"formaction");if(L!=null){n=L}const A=ee(a,"formmethod");if(A!=null){if(A.toLowerCase()!=="dialog"){t=A}}}const f=re(r,"hx-confirm");if(D===undefined){const K=function(e){return de(t,n,r,o,i,!!e)};const G={target:c,elt:r,path:n,verb:t,triggeringEvent:o,etc:i,issueRequest:K,question:f};if(he(r,"htmx:confirm",G)===false){oe(s);return e}}let h=r;let d=re(r,"hx-sync");let g=null;let F=false;if(d){const N=d.split(":");const I=N[0].trim();if(I==="this"){h=Se(r,"hx-sync")}else{h=ue(ae(r,I))}d=(N[1]||"drop").trim();u=ie(h);if(d==="drop"&&u.xhr&&u.abortable!==true){oe(s);return e}else if(d==="abort"){if(u.xhr){oe(s);return e}else{F=true}}else if(d==="replace"){he(h,"htmx:abort")}else if(d.indexOf("queue")===0){const W=d.split(" ");g=(W[1]||"last").trim()}}if(u.xhr){if(u.abortable){he(h,"htmx:abort")}else{if(g==null){if(o){const P=ie(o);if(P&&P.triggerSpec&&P.triggerSpec.queue){g=P.triggerSpec.queue}}if(g==null){g="last"}}if(u.queuedRequests==null){u.queuedRequests=[]}if(g==="first"&&u.queuedRequests.length===0){u.queuedRequests.push(function(){de(t,n,r,o,i)})}else if(g==="all"){u.queuedRequests.push(function(){de(t,n,r,o,i)})}else if(g==="last"){u.queuedRequests=[];u.queuedRequests.push(function(){de(t,n,r,o,i)})}oe(s);return e}}const p=new XMLHttpRequest;u.xhr=p;u.abortable=F;const m=function(){u.xhr=null;u.abortable=false;if(u.queuedRequests!=null&&u.queuedRequests.length>0){const e=u.queuedRequests.shift();e()}};const B=re(r,"hx-prompt");if(B){var x=prompt(B);if(x===null||!he(r,"htmx:prompt",{prompt:x,target:c})){oe(s);m();return e}}if(f&&!D){if(!confirm(f)){oe(s);m();return e}}let y=fn(r,c,x);if(t!=="get"&&!pn(r)){y["Content-Type"]="application/x-www-form-urlencoded"}if(i.headers){y=ce(y,i.headers)}const U=cn(r,t);let b=U.errors;const j=U.formData;if(i.values){ln(j,qn(i.values))}const V=qn(En(r));const v=ln(j,V);let w=hn(v,r);if(Q.config.getCacheBusterParam&&t==="get"){w.set("org.htmx.cache-buster",ee(c,"id")||"true")}if(n==null||n===""){n=ne().location.href}const S=bn(r,"hx-request");const _=ie(r).boosted;let E=Q.config.methodsThatUseUrlParams.indexOf(t)>=0;const C={boosted:_,useUrlParams:E,formData:w,parameters:An(w),unfilteredFormData:v,unfilteredParameters:An(v),headers:y,target:c,verb:t,errors:b,withCredentials:i.credentials||S.credentials||Q.config.withCredentials,timeout:i.timeout||S.timeout||Q.config.timeout,path:n,triggeringEvent:o};if(!he(r,"htmx:configRequest",C)){oe(s);m();return e}n=C.path;t=C.verb;y=C.headers;w=qn(C.parameters);b=C.errors;E=C.useUrlParams;if(b&&b.length>0){he(r,"htmx:validation:halted",C);oe(s);m();return e}const z=n.split("#");const $=z[0];const O=z[1];let R=n;if(E){R=$;const Z=!w.keys().next().done;if(Z){if(R.indexOf("?")<0){R+="?"}else{R+="&"}R+=an(w);if(O){R+="#"+O}}}if(!Tn(r,R,C)){fe(r,"htmx:invalidPath",C);oe(l);return e}p.open(t.toUpperCase(),R,true);p.overrideMimeType("text/html");p.withCredentials=C.withCredentials;p.timeout=C.timeout;if(S.noHeaders){}else{for(const k in y){if(y.hasOwnProperty(k)){const Y=y[k];Cn(p,k,Y)}}}const H={xhr:p,target:c,requestConfig:C,etc:i,boosted:_,select:X,pathInfo:{requestPath:n,finalRequestPath:R,responsePath:null,anchor:O}};p.onload=function(){try{const t=Hn(r);H.pathInfo.responsePath=On(p);M(r,H);if(H.keepIndicators!==true){Qt(T,q)}he(r,"htmx:afterRequest",H);he(r,"htmx:afterOnLoad",H);if(!le(r)){let e=null;while(t.length>0&&e==null){const n=t.shift();if(le(n)){e=n}}if(e){he(e,"htmx:afterRequest",H);he(e,"htmx:afterOnLoad",H)}}oe(s);m()}catch(e){fe(r,"htmx:onLoadError",ce({error:e},H));throw e}};p.onerror=function(){Qt(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:sendError",H);oe(l);m()};p.onabort=function(){Qt(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:sendAbort",H);oe(l);m()};p.ontimeout=function(){Qt(T,q);fe(r,"htmx:afterRequest",H);fe(r,"htmx:timeout",H);oe(l);m()};if(!he(r,"htmx:beforeRequest",H)){oe(s);m();return e}var T=Zt(r);var q=Yt(r);se(["loadstart","loadend","progress","abort"],function(t){se([p,p.upload],function(e){e.addEventListener(t,function(e){he(r,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});he(r,"htmx:beforeSend",H);const J=E?null:mn(p,r,w);p.send(J);return e}function Nn(e,t){const n=t.xhr;let r=null;let o=null;if(R(n,/HX-Push:/i)){r=n.getResponseHeader("HX-Push");o="push"}else if(R(n,/HX-Push-Url:/i)){r=n.getResponseHeader("HX-Push-Url");o="push"}else if(R(n,/HX-Replace-Url:/i)){r=n.getResponseHeader("HX-Replace-Url");o="replace"}if(r){if(r==="false"){return{}}else{return{type:o,path:r}}}const i=t.pathInfo.finalRequestPath;const s=t.pathInfo.responsePath;const l=re(e,"hx-push-url");const c=re(e,"hx-replace-url");const u=ie(e).boosted;let a=null;let f=null;if(l){a="push";f=l}else if(c){a="replace";f=c}else if(u){a="push";f=s||i}if(f){if(f==="false"){return{}}if(f==="true"){f=s||i}if(t.pathInfo.anchor&&f.indexOf("#")===-1){f=f+"#"+t.pathInfo.anchor}return{type:a,path:f}}else{return{}}}function In(e,t){var n=new RegExp(e.code);return n.test(t.toString(10))}function Pn(e){for(var t=0;t0){E().setTimeout(e,x.swapDelay)}else{e()}}if(f){fe(o,"htmx:responseError",ce({error:"Response Status Error Code "+s.status+" from "+i.pathInfo.requestPath},i))}}const Mn={};function Xn(){return{init:function(e){return null},getSelectors:function(){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,n){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,n,r){return false},encodeParameters:function(e,t,n){return null}}}function Fn(e,t){if(t.init){t.init(n)}Mn[e]=ce(Xn(),t)}function Bn(e){delete Mn[e]}function Un(e,n,r){if(n==undefined){n=[]}if(e==undefined){return n}if(r==undefined){r=[]}const t=te(e,"hx-ext");if(t){se(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){r.push(e.slice(7));return}if(r.indexOf(e)<0){const t=Mn[e];if(t&&n.indexOf(t)<0){n.push(t)}}})}return Un(ue(c(e)),n,r)}var jn=false;ne().addEventListener("DOMContentLoaded",function(){jn=true});function Vn(e){if(jn||ne().readyState==="complete"){e()}else{ne().addEventListener("DOMContentLoaded",e)}}function _n(){if(Q.config.includeIndicatorStyles!==false){const e=Q.config.inlineStyleNonce?` nonce="${Q.config.inlineStyleNonce}"`:"";ne().head.insertAdjacentHTML("beforeend"," ."+Q.config.indicatorClass+"{opacity:0} ."+Q.config.requestClass+" ."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ."+Q.config.requestClass+"."+Q.config.indicatorClass+"{opacity:1; transition: opacity 200ms ease-in;} ")}}function zn(){const e=ne().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function $n(){const e=zn();if(e){Q.config=ce(Q.config,e)}}Vn(function(){$n();_n();let e=ne().body;kt(e);const t=ne().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){const t=e.target;const n=ie(t);if(n&&n.xhr){n.xhr.abort()}});const n=window.onpopstate?window.onpopstate.bind(window):null;window.onpopstate=function(e){if(e.state&&e.state.htmx){Wt();se(t,function(e){he(e,"htmx:restored",{document:ne(),triggerEvent:he})})}else{if(n){n(e)}}};E().setTimeout(function(){he(e,"htmx:load",{});e=null},0)});return Q}(); \ No newline at end of file diff --git a/web/static/tokens.css b/web/static/tokens.css new file mode 100644 index 0000000..1516a04 --- /dev/null +++ b/web/static/tokens.css @@ -0,0 +1,110 @@ +/* ============================================================ + Family Home Lab — Design Tokens + Source: DESIGN.md (Notion design language) + Only structural/semantic tokens here. All values map 1:1. + ============================================================ */ + +/* Inter (@fontsource) — self-hosted substitute for NotionInter */ +@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url("/static/fonts/inter-400.woff2") format("woff2"); } +@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url("/static/fonts/inter-500.woff2") format("woff2"); } +@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url("/static/fonts/inter-600.woff2") format("woff2"); } +@font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url("/static/fonts/inter-700.woff2") format("woff2"); } + +:root { + /* --- Surface --- */ + --canvas-soft: #f6f5f4; + --surface: #ffffff; + --surface-2: #f2f2f1; + --hairline: #e6e6e6; + + /* --- Text --- */ + --ink: #000000; + --ink-secondary: #31302e; + --ink-muted: #615d59; + --ink-faint: #a39e98; + + /* --- Structural accents --- */ + --primary: #0075de; /* the ONLY action colour */ + --primary-active: #005bab; + --on-primary: #ffffff; + --secondary: #213183; /* deep indigo — login band + greeting only */ + + /* --- Sticker palette (decorative only) --- */ + --sticker-sky: #62aef0; /* chat */ + --sticker-purple: #d6b6f6; /* image */ + --sticker-pink: #ff64c8; /* video */ + --sticker-orange: #dd5b00; /* audio */ + --sticker-teal: #2a9d99; /* docs */ + --sticker-green: #1aae39; /* status: online/saved */ + --sticker-red: #d33a2b; /* status: failed / error (ON_OFF.md decision) */ + --sticker-purple-deep: #391c57; /* illustration only */ + --sticker-orange-deep: #793400; /* illustration only */ + + /* --- Typography (Inter, self-hosted via @font-face) --- */ + --font-family: "Inter", -apple-system, system-ui, "Segoe UI", + Helvetica, Arial, sans-serif; + + /* Font-family is baked into each token below: the CSS `font` shorthand + resets font-family to the UA default (serif) unless a family is written, + and it cannot carry letter-spacing — so weights/sizes ship inline and the + family is appended per token. */ + --type-display-1: 700 48px/1.05 var(--font-family); + --type-heading-1: 700 32px/1.1 var(--font-family); + --type-heading-2: 700 22px/1.27 var(--font-family); + --type-title: 600 17px/1.4 var(--font-family); + --type-body-md: 400 15px/1.5 var(--font-family); + --type-body-sm: 400 14px/1.4 var(--font-family); + --type-button: 500 15px/1.4 var(--font-family); + --type-caption: 400 13px/1.43 var(--font-family); + --type-eyebrow: 600 12px/1.33 var(--font-family); + + /* --- Layout (base unit 8px) --- */ + --space-xxs: 4px; + --space-xs: 8px; + --space-sm: 12px; + --space-md: 16px; + --space-lg: 24px; + --space-xl: 28px; + --space-xxl: 32px; + + --container-max: 1200px; + + /* --- Elevation --- */ + --shadow-level-1: rgba(0, 0, 0, 0.01) 0 0.175px 1px, + rgba(0, 0, 0, 0.02) 0 0.8px 3px, + rgba(0, 0, 0, 0.027) 0 2px 8px, + rgba(0, 0, 0, 0.04) 0 4px 18px; + --shadow-level-2: rgba(0, 0, 0, 0.0122) 0 0.175px 1px, + rgba(0, 0, 0, 0.024) 0 0.8px 3px, + rgba(0, 0, 0, 0.032) 0 2px 8px, + rgba(0, 0, 0, 0.047) 0 4px 18px, + rgba(0, 0, 0, 0.05) 0 23px 52px; + + /* --- Shapes --- */ + --rounded-xs: 4px; + --rounded-sm: 5px; + --rounded-md: 8px; + --rounded-lg: 12px; + --rounded-full: 9999px; +} + +/* Dark mode — flip the semantic tokens only (components use var(--…)). */ +[data-theme="dark"] { + --canvas-soft: #141518; + --surface: #1c1e22; + --surface-2: #24262c; + --hairline: #33363d; + --ink: #f5f6f7; + --ink-secondary: #dcdee2; + --ink-muted: #a9adb6; + --ink-faint: #727680; + --primary: #3d8be0; + --primary-active: #5d9ff0; + --on-primary: #ffffff; + --secondary: #10142a; + --shadow-level-1: rgba(0, 0, 0, 0.45) 0 1px 3px; + --shadow-level-2: rgba(0, 0, 0, 0.55) 0 5px 22px; +} + +/* Numerals: enable OpenType lnum */ +html { font-feature-settings: "lnum" 1; } \ No newline at end of file diff --git a/web/templates/dashboard.html b/web/templates/dashboard.html new file mode 100644 index 0000000..ddda854 --- /dev/null +++ b/web/templates/dashboard.html @@ -0,0 +1,97 @@ + + + + + + Email Agent + + + + + + + +
+ + + + diff --git a/web/templates/inbox.html b/web/templates/inbox.html new file mode 100644 index 0000000..09b7388 --- /dev/null +++ b/web/templates/inbox.html @@ -0,0 +1,5 @@ +
+
No messages yet
+

Mail fetch arrives in Phase 2. New messages will appear here, grouped by category, with a recommended action on each row.

+ IMAP scan not connected +
diff --git a/web/templates/login.html b/web/templates/login.html new file mode 100644 index 0000000..8a31624 --- /dev/null +++ b/web/templates/login.html @@ -0,0 +1,45 @@ + + + + + + Sign in · Email Agent + + + + + +
+

{{.PartOfDay}}, Sam

+
+
+

Email Agent

+

Sign in

+

The family inbox triage desk.

+
+ + {{if .Error}}{{end}} + {{if not .Configured}}
Login is not configured. Set EMAIL_AGENT_PASSWORD (or EMAIL_AGENT_SESSION_SECRET) on the host.
{{end}} + + + + +
+ + +
+ + +
+
+ +
+

{{.Greeting}}, Sam

+ +
+ + +
+
+

Inbox

+ +
+
+
+ + +
+