---
phase: 14-auth-pages
plan: "01"
type: execute
wave: 1
depends_on: []
files_modified:
  - backend/static/logo_dark.png
  - backend/static/logo_white.png
  - backend/internal/web/ui/auth.css
  - backend/templates/auth_components.templ
  - backend/templates/auth_layout.templ
autonomous: true
requirements: [AUTH-UI-01, AUTH-UI-02, AUTH-UI-03]

must_haves:
  truths:
    - "logo_dark.png and logo_white.png exist under backend/static/"
    - "`just generate` completes without error after the new templ files are created"
    - "auth.css contains all auth card, animated background, gsi-material-button, and animation CSS extracted from go-backend"
    - "AnimatedBackground templ component emits exactly 35 .background-logo elements each with a single /static/logo_dark.png img"
    - "GoogleButton templ component renders the gsi-material-button structure with the Google SVG and 'Sign in with Google' label"
    - "AuthLayout templ component compiles and provides the .login-screen / .card-wrap / .auth-card-shell HTML shell"
  artifacts:
    - path: "backend/static/logo_dark.png"
      provides: "Logo asset for AnimatedBackground and brand header"
    - path: "backend/internal/web/ui/auth.css"
      provides: "All auth page CSS: layout, animated background, auth card, Google button, animations"
      contains: ".gsi-material-button"
    - path: "backend/templates/auth_components.templ"
      provides: "AnimatedBackground (35 elements), GoogleButton (gsi-material-button), AuthDivider components"
      exports: ["AnimatedBackground", "GoogleButton", "AuthDivider"]
    - path: "backend/templates/auth_layout.templ"
      provides: "Standalone auth page HTML shell with .login-screen wrapper"
      exports: ["AuthLayout"]
  key_links:
    - from: "backend/templates/auth_layout.templ"
      to: "backend/templates/auth_components.templ"
      via: "@AnimatedBackground() call"
      pattern: "AnimatedBackground"
    - from: "backend/internal/web/ui/auth.css"
      to: "backend/tailwind.input.css"
      via: "@import already present from Phase 13"
      pattern: "auth.css"
---

## Phase Goal

**As a** user opening the login page, **I want to** see a visually polished auth page with animated background and branded card, **so that** signing in feels like a professional product experience.

<objective>
Create the auth page foundation: copy logo assets, replace auth.css with full auth card + animation CSS from go-backend, and create the new templ components (AnimatedBackground, GoogleButton, AuthDivider, AuthLayout). These artifacts are consumed by Plan 02 when it migrates the existing login/signup pages.

Purpose: Plan 01 delivers all new files without touching existing pages. Plan 02 (wave 2) wires them in.
Output: logo_dark.png + logo_white.png copied; auth.css replaced (~500 lines); auth_components.templ created (35-element animated bg + Google button + divider); auth_layout.templ created (standalone HTML shell).
</objective>

<execution_context>
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.claude/get-shit-done/workflows/execute-plan.md
@/Users/arthur.belleville/Documents/perso/projects/xtablo-source/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/ROADMAP.md
@.planning/phases/14-auth-pages/14-CONTEXT.md
@.planning/phases/14-auth-pages/14-UI-SPEC.md
@.planning/phases/14-auth-pages/14-PATTERNS.md

<interfaces>
<!-- Key types and patterns the executor needs. -->

From backend/templates/layout.templ (pattern for AuthLayout):
```go
package templates

import (
    "backend/internal/auth"
    "backend/internal/web/ui"
)

templ Layout(title string, user *auth.User, csrfToken string) {
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="utf-8"/>
            <title>{ title }</title>
            <link rel="stylesheet" href="/static/tailwind.css"/>
        </head>
        <body ...>
            { children... }
            <script src="/static/htmx.min.js" defer></script>
        </body>
    </html>
}
```

AuthLayout signature (from PATTERNS.md):
```go
templ AuthLayout(title string, csrfToken string) { ... }
// No auth.User param — auth pages are always unauthenticated
// No sortable.min.js or discussion-sse.js — not needed on auth pages
```

From go-backend/internal/web/views/auth_components.templ (reference — package name changes):
- AnimatedBackground: 35 .background-logo elements; single img per element (Phase 14 uses logo_dark.png only, no light-only/dark-only pair)
- GoogleButton: accepts (href string, configured bool); <a> when configured, <button disabled> when not
- AuthDivider: .divider-row with .divider-line / "or" pill / .divider-line

go-backend logo paths use /logo_dark.png — backend uses /static/logo_dark.png (note path difference)
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Copy logo assets and replace auth.css</name>
  <files>backend/static/logo_dark.png, backend/static/logo_white.png, backend/internal/web/ui/auth.css</files>
  <read_first>
    - backend/internal/web/ui/auth.css (current 62-line stub being replaced)
    - go-backend/static/styles.css lines 1062–1510 (auth CSS to extract: .login-screen, .background-layer, .background-logo, .logo-asset, .size-* utilities, .opacity-* utilities, .bg-01 through .bg-35, .card-wrap, .card-glow, .auth-card-shell, .auth-card-topbar, .brand-header, .brand-logo, .title-group, .auth-body, .login-form, .divider-row, .divider-line, .divider-pill, .signup-copy, .signup-link, .status-slot, .status-banner, .status-success, .status-error, .gsi-material-button and all sub-classes)
    - go-backend/static/styles.css lines 2533–2947 (all @keyframes definitions and .animate-* utility classes)
    - backend/tailwind.input.css (confirm auth.css import is already present)
  </read_first>
  <action>
    1. Copy binary files verbatim: `cp go-backend/static/logo_dark.png backend/static/logo_dark.png` and `cp go-backend/static/logo_white.png backend/static/logo_white.png`.

    2. Replace backend/internal/web/ui/auth.css entirely. The new file must contain these sections in order (extracted from go-backend/static/styles.css — do NOT copy app-level rules like .app-shell, .dashboard-shell, .sidebar-*, etc.):

       Section A — Page shell (lines ~1062–1079, auth-only portion): `.login-screen` with min-height 100vh, flex center, background var(--gradient-shell), overflow hidden, padding 2rem 1rem, position relative. Do NOT include `.app-shell` rule.

       Section B — Animated background (lines ~1081–1155): `.background-layer`, `.background-logo`, `.logo-asset`, all `.size-06` through `.size-20` utilities (13 classes), all `.opacity-02` through `.opacity-05` utilities (4 classes), all `.bg-01` through `.bg-35` position rules (35 classes).

       Section C — Card wrapper (lines ~1157–1182): `.card-wrap`, `.card-glow`, `.auth-card-shell` (padding: 1.25rem verbatim — not a design token).

       Section D — Card internals (lines ~1184–1425): `.auth-card-topbar`, `.brand-header`, `.brand-logo`, `.title-group`, `.title-group h1` (clamp font-size), `.auth-body`, `.login-form`, `.divider-row`, `.divider-line`, `.divider-pill`, `.signup-copy`, `.signup-link` (including hover state), `.status-slot`, `.status-banner`, `.status-success`, `.status-error`. Skip `.back-home-link`, `.theme-toggle-button`, `.new-experience-link-wrap`, `.forgot-password-row`, `.submit-button`, `.field-stack` (these are go-backend specifics not used in backend's template pattern). Also skip `.field-stack` since inputs use ui-input from Phase 13.

       Section E — Google button (lines ~1427–1509): `.gsi-material-button` and ALL sub-class rules verbatim. Add `display: block; text-decoration: none;` to `.gsi-material-button` to handle the `<a>` variant (go-backend uses `<button>` only, backend uses `<a>` when configured).

       Section F — Keep existing auth-provider rules at bottom for any non-Google controls: `.auth-provider-stack`, `.auth-provider-button`, `.auth-provider-button:hover`, `.auth-provider-button:focus-visible`, `.auth-provider-button-disabled`, `.auth-provider-separator`, `.auth-provider-separator span`, `.auth-provider-separator em` (copy verbatim from the current auth.css stub).

       Section G — Keyframes (lines ~2533–2882): all @keyframes definitions verbatim.

       Section H — Animation utilities (lines ~2883–2947): all .animate-* utility classes verbatim.

    3. No change to backend/tailwind.input.css — the @import "./internal/web/ui/auth.css" line was added in Phase 13 and remains valid.
  </action>
  <verify>
    <automated>
      ls backend/static/logo_dark.png backend/static/logo_white.png &&
      grep -c 'gsi-material-button' backend/internal/web/ui/auth.css &&
      grep -c '@keyframes' backend/internal/web/ui/auth.css &&
      grep -c 'animate-move-right-slow' backend/internal/web/ui/auth.css &&
      grep 'login-screen' backend/internal/web/ui/auth.css &&
      grep 'auth-card-shell' backend/internal/web/ui/auth.css &&
      grep 'signup-link' backend/internal/web/ui/auth.css
    </automated>
  </verify>
  <acceptance_criteria>
    - backend/static/logo_dark.png exists (binary file, non-zero size)
    - backend/static/logo_white.png exists (binary file, non-zero size)
    - auth.css contains `.gsi-material-button` (grep -c returns >= 1)
    - auth.css contains >= 10 @keyframes definitions (grep -c '@keyframes' returns >= 10)
    - auth.css contains `.animate-move-right-slow` (at least one animate-* utility)
    - auth.css contains `.login-screen` rule
    - auth.css contains `.auth-card-shell` with `padding: 1.25rem`
    - auth.css contains `.signup-link` rule
    - auth.css does NOT contain `.app-shell` (wrong rule from go-backend)
    - auth.css does NOT contain `.dashboard-shell` (wrong rule from go-backend)
  </acceptance_criteria>
  <done>Logo assets exist in backend/static/, auth.css is fully replaced with all auth card, animated background, Google button, and animation CSS (~500+ lines total).</done>
</task>

<task type="auto">
  <name>Task 2: Create auth_components.templ and auth_layout.templ</name>
  <files>backend/templates/auth_components.templ, backend/templates/auth_layout.templ</files>
  <read_first>
    - backend/templates/layout.templ (pattern for HTML shell structure, script tags, head elements)
    - go-backend/internal/web/views/auth_components.templ (AnimatedBackground 35 elements, GoogleButton structure, AuthDivider — port verbatim with two adaptations: image path /logo_dark.png → /static/logo_dark.png, remove light-only/dark-only pairs)
    - backend/templates/auth_login.templ (current file — to understand existing AuthProviderButtons type used by GoogleButton)
    - .planning/phases/14-auth-pages/14-PATTERNS.md (AnimatedBackground element class list: bg-01..bg-35 with outer/inner class assignments per slot)
  </read_first>
  <action>
    1. Create backend/templates/auth_components.templ with package declaration `package templates` and three components:

       AnimatedBackground(): Port all 35 elements from go-backend/internal/web/views/auth_components.templ lines 108–158. Adaptations:
       - Change all img src from "/logo_dark.png" to "/static/logo_dark.png"
       - Replace every two-image (light-only/dark-only) pattern with a single img element using just the classes from the light-only image (drop the dark-only img entirely; Phase 14 uses logo_dark.png only per D-AB02 / DEFERRED section)
       - Parent div carries aria-hidden="true" on .background-layer
       - Exact class assignments per slot per 14-PATTERNS.md element table (bg-01 through bg-35)

       GoogleButton(href string, configured bool): Renders the Material Design button structure.
       - When configured=true: `<a class="gsi-material-button" href={ templ.SafeURL(href) }>` wrapping .gsi-material-button-state div, .gsi-material-button-content-wrapper div containing .gsi-material-button-icon div (Google SVG 4 paths), `<span class="gsi-material-button-contents">Sign in with Google</span>`, `<span class="visually-hidden">Sign in with Google</span>`.
       - When configured=false: `<button type="button" class="gsi-material-button" disabled aria-disabled="true">` with same inner structure.
       - Google SVG paths (verbatim from go-backend): #EA4335 (path M24 9.5...), #4285F4 (path M46.98 24.55...), #FBBC05 (path M10.53 28.59...), #34A853 (path M24 48...), none fill (path M0 0h48v48H0z).
       - Label text: "Sign in with Google" (English per D-G03; go-backend uses French — do not copy French text).

       AuthDivider(): Single component, no params.
       - `<div class="divider-row">` containing `.divider-line` div, `<span class="divider-pill">or</span>`, `.divider-line` div.
       - "or" in English (not "Ou continuer avec" from go-backend per D-G03 English convention).

    2. Create backend/templates/auth_layout.templ with package declaration `package templates` and signature:
       `templ AuthLayout(title string, csrfToken string)`
       - No import of "backend/internal/auth" — auth pages are always unauthenticated (no *auth.User param)
       - Full HTML shell: `<!DOCTYPE html>`, `<html lang="en">`, head with charset/viewport meta, `<title>{ title }</title>`, `<link rel="stylesheet" href="/static/tailwind.css"/>`
       - Body: `<div class="login-screen">` containing `@AnimatedBackground()`, `<div class="card-wrap">` containing `<div class="card-glow"></div>` and `<div class="auth-card-shell">{ children... }</div>`
       - Script: `<script src="/static/htmx.min.js" defer></script>` — auth forms are HTMX-driven. Do NOT include sortable.min.js or discussion-sse.js.
       - csrfToken parameter is carried through { children... } — the layout itself does not embed a CSRF field; only the form fragments do.
  </action>
  <verify>
    <automated>
      cd backend && just generate 2>&1 | tail -5 &&
      grep -c 'background-logo' backend/templates/auth_components.templ &&
      grep 'Sign in with Google' backend/templates/auth_components.templ &&
      grep 'AnimatedBackground' backend/templates/auth_layout.templ &&
      grep 'login-screen' backend/templates/auth_layout.templ
    </automated>
  </verify>
  <acceptance_criteria>
    - `just generate` completes with exit code 0 (no templ compile errors)
    - auth_components.templ contains exactly 35 occurrences of "background-logo" (one per slot)
    - auth_components.templ contains "Sign in with Google" (English label, D-G03)
    - auth_components.templ does NOT contain "Continuer avec Google" or "Ou continuer avec" (French text from go-backend)
    - auth_components.templ contains "/static/logo_dark.png" (not "/logo_dark.png")
    - auth_components.templ does NOT contain "light-only" or "dark-only" classes (deferred per DEFERRED section)
    - auth_layout.templ contains `@AnimatedBackground()`
    - auth_layout.templ contains `login-screen`
    - auth_layout.templ contains `htmx.min.js`
    - auth_layout.templ does NOT contain "auth.User" (no user parameter)
    - auth_layout.templ does NOT contain "sortable.min.js"
    - `go test ./backend/... -count=1` passes (or `cd backend && go test ./... -count=1`)
  </acceptance_criteria>
  <done>auth_components.templ and auth_layout.templ exist in backend/templates/, `just generate` compiles both files without error, and all existing tests remain green.</done>
</task>

</tasks>

<threat_model>
## Trust Boundaries

| Boundary | Description |
|----------|-------------|
| Static file serving | logo_dark.png served at /static/logo_dark.png — binary file from go-backend source, no user input |
| Template rendering | auth_components.templ and auth_layout.templ render server-controlled static content only |

## STRIDE Threat Register

| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-14-01-01 | Spoofing | logo_dark.png static file | accept | File is served from backend/static/ with no user-controlled path; binary asset copied from trusted go-backend source |
| T-14-01-02 | Tampering | auth.css in tailwind build | accept | CSS is build-time artifact; no runtime user input affects CSS output; tailwind.css is generated once and served as static |
| T-14-01-03 | Information Disclosure | AnimatedBackground aria-hidden | mitigate | .background-layer carries aria-hidden="true" per accessibility contract; decorative logos not exposed to screen readers |
| T-14-01-04 | Elevation of Privilege | AuthLayout no-auth check | accept | AuthLayout has no auth.User param by design; pages using it are always unauthenticated; handler-level RedirectIfAuthed middleware handles the actual gate |
</threat_model>

<verification>
After plan 01 completes:
1. `ls backend/static/logo_dark.png backend/static/logo_white.png` — both files exist
2. `wc -l backend/internal/web/ui/auth.css` — file is > 200 lines (full extraction)
3. `grep -c 'background-logo' backend/templates/auth_components.templ` — returns 35
4. `grep 'AnimatedBackground' backend/templates/auth_layout.templ` — matches
5. `cd backend && just generate` — exits 0
6. `cd backend && go test ./... -count=1` — all tests pass (no regressions from new files)
</verification>

<success_criteria>
- logo_dark.png and logo_white.png present in backend/static/
- auth.css contains full auth card CSS, gsi-material-button CSS, all @keyframes, and all .animate-* utilities (file > 200 lines)
- auth_components.templ: AnimatedBackground with exactly 35 background-logo elements using /static/logo_dark.png, GoogleButton with English label, AuthDivider with "or"
- auth_layout.templ: standalone HTML shell calling @AnimatedBackground(), no auth.User param
- `just generate` exits 0
- All existing tests pass unchanged
</success_criteria>

<output>
After completion, create `.planning/phases/14-auth-pages/14-01-SUMMARY.md`
</output>
