SEO
- New Settings > SEO admin page (seo_settings in app_config): indexing switch,
Google/Bing verification, X handle, JSON-LD identity (Person/Organization,
sameAs), per-locale keywords, readiness checklist and open links for
sitemap.xml / robots.txt / manifest.
- robots.txt is now dynamic: disallows admin, api, success and coming-soon
paths; blocks everything while indexing is off or maintenance is on.
- sitemap.xml carries hreflang alternates per URL, lists only categories with
published projects, and is empty while hidden.
- Metadata: robots + verification meta, og:locale in de_DE/en_US/ar_AR form,
alternateLocale, twitter site/creator, project cover as OG image with
article type, noindex on /success and /coming-soon.
- JSON-LD: WebSite + publisher graph on all public pages, CreativeWork per
project (view-mode independent).
Security
- Maintenance bypass now requires a correctly signed admin cookie; the
middleware previously only checked the cookie existed. Token helpers moved
to lib/admin-session-token.ts (shared by proxy.ts and lib/admin-auth.ts).
- Media uploads: magic-byte validation against the declared type, SVG
sanitization (script/handlers/foreignObject/javascript: rejected), upload
folder sanitized, kind inferred from the real file.
- Media route: fixed prefix-based path check that accepted sibling
directories, unknown extensions return 404, nosniff header, CSP sandbox on
SVG, gif content type added.
- External media URLs: protocol-relative (//host) URLs rejected.
Portfolio
- Project and category slugs share /portfolio/[slug]; saving now rejects a
slug already used on the other side instead of silently shadowing it.
Tooling/docs
- Lint: ignore scripts/legacy-prisma-seed.cjs, drop unused import.
- New docs/SEO.md; FEATURES, ARCHITECTURE (Drizzle instead of Prisma), admin
spec and CLAUDE.md updated.
- Tests for all of the above (unit + integration); suite green.
Replace the flat two-column layout with a guided 4-step wizard
(Grundlagen -> Inhalte -> Abschnitte -> Medien) using the existing wizard
progress model:
- A top stepper shows each step's number/label and completion (check), and
jumps to any step on click.
- One step is visible at a time in a single comfortable centered column; the
others stay mounted but hidden, so every named field still submits.
- Footer navigation: Zurueck / Save as draft / Weiter, with Save Project on
the final step.
No field-name or save-logic changes. All 375 tests pass; tsc and eslint clean.
Experiment: remove the AppCard wrappers around Basic Information, Localized
Content, Sections and Cover/Assets, plus the action bar, so the form content
sits directly on the page. Section headers now carry a thin underline and the
action bar a top divider to keep structure without card chrome. Two-column
layout preserved. No field-name or save-logic changes.
- Remove the project summary/header card (project name, badges, progress);
it added no value while creating a project. Move the on-submit validation
alert down to the action bar.
- Lay the whole form out in two columns: Basic Information + Localized
Content, then Sections + Cover/Assets. Basic Information fields cap at two
columns and the Cover/Assets card stacks inside its column.
All 375 tests pass; tsc and eslint clean.
Fix the deeply nested cards in the project form (screenshot: Cover Media >
Cover > preview card, with duplicate labels):
- MediaFieldPicker no longer wraps itself in AppCards. It renders one flat
bordered box laid out in two columns (preview | controls), dropping two
nested card layers and the duplicate title.
- Cover Media and Assets now sit side by side in a two-column grid instead
of stacked with a separator.
All 375 tests pass; tsc and eslint clean.
Reorganize the project form for a lighter, more professional flow without
touching field names or the save action (all data preserved):
- LocaleInputs: drop the per-locale card wrappers for a tight 3-column
labelled grid; shorter textareas.
- Sections and Assets: collapse each item into an accordion (closed by
default) and show a proper empty state when there are none.
- Slim the header to badges + title + a compact progress indicator instead
of a stats grid; drop the assets stats sidebar.
- Tighten section paddings/spacing (lg -> md) throughout.
All 375 tests pass; tsc and eslint clean.
Replace the card grid with a compact data table (cover, project, category,
year, status, layout, actions) and a proper empty state that adapts to an
active category filter. Actions expose a public-view link plus the existing
edit/delete menu. No data-layer or save changes.
The double-layer AppCard look showcased in the UI Kit was only applied on
the public frontend; every admin surface was pinned to layer=single and
stayed flat. Convert the admin section/container cards to the default
layered look, keeping compact metric tiles, toolbars, image tiles and
nested leaf cards single.
Add AppCard contentClassName so content utilities (space-y-*, grid, flex)
keep applying to the inner shell after the switch to double layer.
- Add a Save as draft button that bypasses the completeness gate: the user
can save partial work and finish later
- projectDraftInputSchema makes the user-facing copy (title, summary, service
label, client) optional; the action auto-generates a slug from the title
(or draft-<timestamp>), defaults the year to the current year, and forces
the project unpublished
- Drafts drop not-yet-valid sections/assets instead of failing the save
- Pass intent via a hidden field set on click so the server reliably sees it
- Tests: draft schema accepts empty copy a strict save rejects, and still
requires a slug and a valid year
- Remove the tab navigation, per-step sidebar, and Previous/Next buttons;
render Basics, Localized Content, Sections and Assets stacked on a single
scrollable page
- Drop the now-unused wizard step machinery (currentStep state, wizardSteps,
StepStateBadge, step stat card) and related imports
- Keep the same fields, validation, and save gating (Basics + Localized
Content required; sections/assets optional)
- Fix critical bug: TabsContent unmounted inactive panels, so the project
form only submitted the active tab's fields and could never save; add an
opt-in forceMount that keeps panels mounted (hidden) and use it on all
four project-form tabs
- Make Sections and Assets optional: a project saves with just Basics and
Localized Content; new projects start with no sections/assets, and the
last one can now be removed
- Update wizard progress so empty sections/assets steps count as complete
- Update save hint copy and step labels to reflect the optional steps
- Correct .claude/launch.json dev port to 3014
- Update portfolio-form-progress tests for the optional-steps behaviour
- Replace animated blob/wave/noise backdrop with clean static gradient
(radial ellipse highlights + linear fade) in hero-motion-backdrop
- Remove all framer-motion from background layer; delete unused CSS
classes: hero-blob, hero-sheet-glow, hero-sheet-wave, hero-sheet-noise,
hero-title-shift keyframe, and responsive blob media queries
- Add framer-motion stagger entrance to HeroContentMotion, HeroMotionItem,
and HeroTitle (slide-up + fade on mount)
- Refactor coming-soon page to use HeroContentMotion/HeroMotionItem
instead of MotionFade (whileInView unreliable above the fold)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>