Commit graph

67 commits

Author SHA1 Message Date
ff368a8314 credits: rename to 'The Regulatory AI-Sandbox'
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 15:46:56 +02:00
9703d47407 credits: redesign supporter lockup + mobile parity pass
Rework the supporter credits ("Backed by" / "Part of") into a
three-tier lockup — a small uppercase label over an upright Newsreader
serif name, with the parent body / issuing authority in a quieter
serif beneath — so the credits read in the deck's editorial voice
instead of as a foreign sans-serif caption. Applied consistently
across all three views:

- Entrance welcome screen: replaced three separately position:fixed
  blocks (with hand-tuned top offsets) with one centred .welcome-credits
  lockup; drops the brittle magic-number stacking.
- Timeline hero ("Fenja introduction"): left-aligned .support-credit
  stack in place of the old .support / .support-bii lines.
- Mobile hero: matching .m-credit stack replacing .m-support / .m-backer.

Credits now read: Backed by Innofounder (Innovationsfonden); Part of
AI Lab (BioInnovation Institute); Part of The AI Regulatory Sandbox
(Datatilsynet & Digitaliseringsstyrelsen).

Also brings the mobile view to parity with the customer-facing desktop
deck: updated hero copy, platform-question framing, architecture layers,
Wiki deep-dive, deployment cards, and implementation roadmap; removes the
old "Join Project Bifrost" CTA + footer (mobile.js loses the join
handler, keeps the session check).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:08:19 +02:00
0c4b3a438e overview: pin-with-scrub-release on cards & roadmap; fix dot-nav
initCards and initRoadmap now use a pin-with-scrub-release pattern
instead of a simple scroll-tied fade. Each section fades in over ~50vh
as it approaches viewport centre, locks in place for 100vh of scroll
input (cards extends to 150vh and fades out while still pinned;
roadmap stays visible as the page ends), then releases. Scroll itself
is never blocked — wheel/keyboard/touch all advance scroll normally
against the pin budget. platform-cards is removed from bifrost's
sticky-damping list since the new pin handles the dwell.

Dot-nav fixes for the new pins:
- activatePage now also calls __platform.init() in the same tick as
  __bifrost.init(), so pin spacers exist before scrollTo reads
  target offsets. Previously platform's MutationObserver-driven init
  fired ~80ms after scrollTo, leaving roadmap.offsetTop pointing at
  the pre-spacer position (empty space between cards and roadmap).
- scrollTo walks the offsetParent chain via offsetTopWithin() instead
  of reading target.offsetTop directly. ScrollTrigger's pinSpacing
  wraps pinned sections in a pin-spacer with position:relative, which
  becomes the section's offsetParent and makes target.offsetTop
  return ~0 — collapsing every dot click to scrollY=0 (hero).
- getSceneAnchorOffset adds cases for platform-cards / platform-roadmap
  returning (section.height - vh) / 2, so the user lands exactly at
  the pin-engagement point with the full pin budget remaining.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 14:53:01 +02:00
8b277193d1 roadmap: replace section headline with 'One foundation, many use cases.'
Italic emphasis on 'many use cases' via the deck's existing <em>
treatment. Eyebrow and the rest of the section are untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 13:50:28 +02:00
bd4d2f5f33 roadmap: rename Wave cards to Knowledge/Tools/Agents, drop duration meta
Card faces and the expanded panel now show only title + subtitle.
Setup keeps its title; Wave 1/2/3+ become Knowledge, Tools, Agents
with updated subtitles. The .rm-mono meta line (e.g. "Once · 1-2
weeks") is removed from all four cards, along with the now-orphan
.rm-mono base rule and its is-expanded order override. Card
min-height drops to 132px and the column flex now centres the
title+subtitle pair so the leaner content sits balanced inside the
walnut surface; grid stretch keeps the row equal-height.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 13:26:55 +02:00
fb815768e2 customer-presentation: convert deck from Bifrost invitation to customer-facing
Why: pivot the experience from a personal invitation for Project Bifrost
participants to a customer-facing presentation that can be shown to
prospects like Novo Nordisk while still mentioning Bifrost in context.

Major changes:
- Entrance: re-worded title/body away from "invitation" into "introduction"; kept Fenja AI / Project Bifrost definition blocks.
- Timeline: page-sub reworked to also speak to highly-regulated private orgs (data, IP, regulated workflows, US-vendor dependency) alongside public sector.
- "Backed by Innovationsfonden" pairs with new "Part of BioInnovation Institute AI Lab" line on entrance and Scene 1 hero.
- Removed: stack-scene (4 capabilities) and words-scene ("This is why we've invited you") — archived at protected/_archive/stack-scene.html for restore.
- Removed: bifrost-join CTA + Innovationsfonden footer section.
- Inlined the standalone /deepdive architecture explainer into #overview-scroll after #bifrost-meaning; platform.js detects scroller and skips its own Lenis setup when integrated.
- New: Wiki deep-dive section (#wiki-deepdive) — scattered knowledge cluster → Fenja AI Compiler → layered page stack with citations, plus pinned scrubbed beat-by-beat reveal.
- New: Implementation roadmap section (#platform-roadmap) — four stage cards + GOVERN & SCALE band + footer, with click-to-expand card-morph (FLIP-based; same DOM element grows into the featured panel).
- Dot-nav: 4 → 8 entries — Welcome / Timeline / Fenja introduction / Project Bifrost / Architecture / Wiki / Deployment / Roadmap.
- Deployment options: scroll-tied fade-in for the whole section + sticky-damping at centre for a subtle dwell stop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 12:56:30 +02:00
e3439d6c8f deepdive: add Beat-5 'Everything Client-Managed' frame around platform stack
Why: closes the Beat-5 summary with a visible boundary + label so the
"client-managed" point lands visually, not just in copy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:11:05 +02:00
547515061c product-pages: add deepdive subpage and platform assets
Adds a new standalone /deepdive page with its own platform.css/platform.js,
wires it into the dot-nav as an external entry, and updates the dot-nav
docblock to reflect the new seven-entry layout. Also drops in BUSINESS.md
and reference material under architecture boxes/ and examples/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:28:23 +02:00
Arlind Ukshini
9f742928d5 fenjaops: surface engagement events on admin page
Adds GET /api/fenjaops/events and three new panels: per-type totals
+ device breakdown, per-user summary (logins / timeline views / last
seen), and a raw event log capped at the newest 500 rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 11:08:33 +02:00
Arlind Ukshini
10c614faae events: clarify 'forced' semantics in docs 2026-04-27 10:57:20 +02:00
Arlind Ukshini
32c88041ec events: document bin/events.js in CLAUDE.md, OPERATIONS.md, CHECKLIST.md 2026-04-27 10:54:04 +02:00
Arlind Ukshini
31bfd72b4c events: record timeline_view on GET /timeline with view+forced meta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 10:52:29 +02:00
Arlind Ukshini
9eafba547a events: record login event on POST /auth/login success 2026-04-27 10:49:44 +02:00
Arlind Ukshini
4afaad8b61 sessions: issueSession returns the new session id 2026-04-27 10:49:06 +02:00
Arlind Ukshini
88f78f4b50 events: add recordEvent helper 2026-04-27 10:48:32 +02:00
Arlind Ukshini
e141b16f1f events: source MOBILE_UA_RE from src/ua.js
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 10:47:54 +02:00
Arlind Ukshini
a756a1dcc3 events: document desktop fallback in UA parser 2026-04-27 10:46:47 +02:00
Arlind Ukshini
d974e865c2 events: add UA parser (device_type/os/browser)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 10:43:42 +02:00
Arlind Ukshini
51b0508561 events: validate --type and harden --limit parsing in CLI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 10:41:37 +02:00
Arlind Ukshini
6b5e2f1297 events: add bin/events.js CLI for reading the event log 2026-04-27 10:38:27 +02:00
Arlind Ukshini
0cc3dc808e events: add events table and prepared statements 2026-04-27 10:29:46 +02:00
Arlind Ukshini
44f7a8c5d7 docs: implementation plan for engagement event tracking
9 tasks: schema → CLI → UA parser → wire UA into server → recorder
→ issueSession refactor → login event → timeline_view event → docs.
Each task is a self-contained commit with verification steps using
curl + the new events CLI (no test framework on this project).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:08:19 +02:00
Arlind Ukshini
4d2ff042e3 docs: drop welcome_cta event from engagement tracking spec
There's only one welcome-step button (#welcome-continue, label "Start
the introduction"); the recent renames were sequential edits to the
same button, not two separate CTAs. The click immediately navigates to
/timeline so the subsequent timeline_view event already captures it.

Also clarified GET / no longer has a timeline branch, and pinned down
how session_id flows into the login event (refactor issueSession to
return its new ID).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:03:53 +02:00
Arlind Ukshini
9044573d01 docs: spec for engagement event tracking
Server-side landmark events (login, welcome_cta, timeline_view) into a
unified events table with device fields. CLI-only readout via
bin/events.js. bifrost_joins left in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 09:53:48 +02:00
Arlind Ukshini
2d06f8e513 /fenjaops: add Remove button for non-admin invites
- New endpoint: DELETE /api/fenjaops/invites/:email, behind
  requireAuth + requireAdmin. Guardrails:
    * refuses if the target email is an admin (demote first via
      bin/invite.js admin remove) — preserves the invariant that
      a compromised admin session can't lock everyone out;
    * refuses if the target email equals the caller's own —
      prevents self-inflicted lockouts from the UI;
    * deletes active sessions for the target email so the user
      is kicked out immediately instead of holding their 30-day
      cookie.
- Admin page: Invites table gains an "Action" column. Non-admin,
  non-self rows show a Remove button (quiet ink outline; crimson
  on hover to cue destructive intent). Admin and self rows show
  an em-dash. Click → browser confirm() → DELETE → load() to
  refresh counts + tables.
- admin.js fetches /auth/me alongside the other payloads so
  render can compare each row's email against the viewer's.
- PROJECT.md and CLAUDE.md updated: the "no web deletion"
  invariant is narrowed to "no web deletion of admins or self"
  to reflect the new capability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:23:12 +02:00
Arlind Ukshini
06fb151550 mobile: enlarge masthead logo, add breathing room, kill underline
.m-logo img grows 34px → 48px. Masthead padding 14px/18px → 28/18/26
so the logo sits with noticeably more space above and below.
text-decoration:none is now applied across :link/:visited/:hover/
:active variants (plus border-bottom:0 on both the anchor and the
<img>) to defeat any browser default underline on the tappable
logo, including Safari's quirky behaviours.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:08:27 +02:00
Arlind Ukshini
404c9f1dff welcome: rename CTA to "Start the introduction"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:07:16 +02:00
Arlind Ukshini
a750e6a0eb welcome: rename CTA to "Learn more about us"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:06:53 +02:00
Arlind Ukshini
d6a819fdb5 entrance: nudge welcome-backer ~10% further left under the logo
transform translateX -28% → -38% so the block centres more
closely under the fixed fenja wordmark at left:75%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:03:48 +02:00
Arlind Ukshini
6442d4c20b entrance: nudge welcome-backer 5px left
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:03:13 +02:00
Arlind Ukshini
010ed1492f entrance: nudge welcome-backer down and right, clear the logo
welcome-backer was overlapping the bottom of the fenja wordmark
now that the Innovationsfonden SVG grew to 24px (taller row, lower
visual centre). top bumped from +45px to +80px below viewport
mid, and transform translate from -50% to -28% so the block sits
slightly right of the logo's centreline instead of under it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:02:40 +02:00
Arlind Ukshini
789f1bbb17 backer: match text between pages, grow Innovationsfonden mark
Hero .support and welcome-backer now carry the same text sizing —
11px / weight 600 / uppercase / 0.18em tracking — so the lockup
reads identically on the entrance and on the overview hero.
Innovationsfonden SVG bumps 14→24px on the hero and 16→24px on
the welcome page, so the wordmark is meaningfully visible while
the accompanying label stays small.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:01:02 +02:00
Arlind Ukshini
0b07b2a8f6 entrance: "Backed by Innovationsfonden" below the welcome-logo
A small supporter lockup (uppercase "Backed by" + redrawn
Innovationsfonden wordmark, same treatment as the hero's .support
block) sits directly under the fixed welcome-logo at left:75%,
~45px below the logo's centre. Fades in with the welcome step via
the same body:has(#step-welcome.is-active) trigger the logo uses,
hidden on the email step and on narrow viewports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:00:19 +02:00
Arlind Ukshini
91cc7aa729 hero: center vertically, widen copy, +10% type, shrink backer
- #page-overview #hero padding rebalanced to clamp(3,8vh,6rem) top
  and clamp(4,10vh,7rem) bottom so the grid-centered content sits
  on the true vertical midline rather than being pushed upward by
  an asymmetric padding-bottom.
- .hero-copy max-width 58ch → 70ch (~+20%). .hero-lede follows to
  53ch (was 44ch). Headline size clamp(1.65rem,4vw,3.4rem) →
  clamp(1.8rem,4.4vw,3.75rem), lede follows in proportion.
- .support type + svg drop to half the previous (0.75x the base
  --step-sm, svg 27→14px). The "Backed by Innovationsfonden" line
  now reads as a quiet footnote instead of competing with the
  main copy above it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:58:56 +02:00
Arlind Ukshini
7e3f0e6ada hero: drop eyebrow, tighten type, indent copy column
- "Meet Fenja AI" eyebrow removed — headline + wordmark pairing
  is enough to anchor the scene.
- Type scale reduced inside #hero: title clamp(1.65rem, 4vw, 3.4rem)
  (was clamp(2rem, 5.1vw, 4.4rem) via --step-hero), lede
  clamp(1rem, 1.7vw, 1.4rem). Reads calmer and keeps the headline
  from overshadowing the lede.
- .hero-copy gets padding-left: clamp(2rem, 8vw, 8rem) so the text
  block floats in the same way the wordmark does in its column —
  symmetric breathing room on both sides of the viewport rather
  than the copy hugging the left edge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:54:47 +02:00
Arlind Ukshini
3e73313c3b hero: two-column layout with Fenja wordmark on the right
- Hero copy moves into a .hero-copy column on the left; a new
  .hero-mark column on the right holds the Fenja wordmark at
  clamp(200px, 22vw, 320px) wide — same position as the welcome
  page's .welcome-logo (~75% across the viewport).
- New eyebrow "Meet Fenja AI", headline "Trusted & Sovereign AI /
  built in Denmark, for Europe.", and expanded lede explaining
  that Fenja AI is both the company and the platform.
- "Supported by" → "Backed by". Both the wordmark font-size and
  the Innovationsfonden SVG get a 50% size bump (calc(--step-sm *
  1.5) on the text, 18px → 27px on the svg).
- Responsive: at <780px the grid collapses to a single column so
  the wordmark stacks below the copy on narrow desktop viewports
  (the mobile UA still gets the separate static mobile view).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:50:07 +02:00
Arlind Ukshini
a2cbf57ce2 stack-scene: centered title, per-card counters, earlier anchor
- Stack title bar moves from top-left next to the site-mark to
  centered at ~14vh so the title anchors visually to the cards
  below. Font size bumped to clamp(2rem, 3.6vw, 3rem).
- Counter ("1 / 4 … 4 / 4") relocates from the title bar into
  each .layer-card as a .card-counter element in the top-right of
  each card-box. No longer driven by ScrollTrigger onUpdate —
  each card carries its own number, so stacked + grid phases
  both read correctly without JS. Grid phase shrinks the counter
  so it doesn't compete with the per-cell label.
- SCENE_ANCHOR_OFFSET for stack-scene drops from 1800 back to 0,
  so clicking the "Capabilities" dot lands at the top of the
  pin — the title and first card come in together instead of
  starting mid-stack.

Welcome step: the "desktop experience" aside and its CSS are
removed. Users now see only the two definitions before the CTA.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:46:08 +02:00
Arlind Ukshini
43129717bf mobile: enlarge masthead logo and drop the anchor underline
.m-logo img 22px → 34px; .m-logo gets text-decoration:none so the
wrapping <a href="/"> doesn't paint a default underline under the
wordmark on Safari / any browser with link styles leaking through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:39:51 +02:00
Arlind Ukshini
8cfe72296b mobile: enlarge card icons and tuck into the corner
Icons grow 72px → 148px and shift to top:-18px right:-18px so they
overflow the card edges. .m-cap gets overflow:hidden so the bleed
is clipped at the tile boundary — icon reads as an ornament tucked
into the top-right corner rather than a floating sticker. Num,
eyebrow, and title rows bump their right padding from 92 to 120 so
the text still clears the icon's visible silhouette.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:39:27 +02:00
Arlind Ukshini
cd9bd71f4b mobile: drop the masthead logout button
Masthead is now the Fenja wordmark on its own, centred. The logout
handler and the .m-logout CSS are gone; mobile.js header comment
updated to reflect "two behaviours" (session check + join CTA).
Users who need to log out can do so from a desktop session or by
clearing cookies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:34:37 +02:00
Arlind Ukshini
1144095715 mobile: match desktop card palette, add stacked supporter footer
Capability cards now carry the desktop's per-layer colours —
sage / slate / clay / plum — with paper-toned text for contrast.
Icon, num, eyebrow, title, body all flipped to paper with per-
element alpha so the hierarchy still reads on the coloured tile.

Footer reintroduced as a stacked column of three supporter marks:
"Project Bifrost" serif lockup, Fenja AI wordmark, Innovationsfonden
mark. Centered, 26px gap, paper background, 44/56 top/bottom padding.
No "view desktop" link — the ?view=desktop query override still
works for anyone who needs it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:26:42 +02:00
Arlind Ukshini
21d4b23470 mobile: carousel timeline, hero wordmark, arc + illustrations
Timeline: 12 event tiles are now a horizontal scroll-snap carousel
instead of a vertical stack. Each card flex-basis: 84vw, snap-align
start, with paper-tile styling + a subtle shadow + "NN / 12" index
in the top-right. A "Swipe →" hint sits above the track. Zero JS —
pure CSS scroll-snap + -webkit-overflow-scrolling.

Hero: Fenja wordmark (36px) placed above the "For regulated
environments" eyebrow, whole section wrapped in a paper-2 strip
with 72/80px top/bottom padding and hairline rules top + bottom.
Sits visually as its own chapter instead of a continuation of the
previous section.

Transition strip ("How Fenja AI addresses this") gets vertical
rules above and below and 88px vertical padding so it reads as a
deliberate pause between the timeline and the platform sections.

Capability cards: 4 per-layer illustrations (ai.png, lightbulb,
blocs tools, agents.png — all already in fenja/illustrations/)
placed as 72×72 PNGs in the top-right of each card. Card content
padding-right bumped to 92px for the num/eyebrow/title rows so
long titles wrap around the icon instead of under it.

Project Bifrost: the desktop aurora arc reused as a static SVG
backdrop. Scaled to 190% viewport width (max 900px) so the arc
runs off both edges of the phone screen, matching the desktop's
"bridge spans the stage" feel.

Treasure-map stops: community.svg, council.svg, pilot.svg sit
as 220px-wide illustrations above each stop's text, replacing
the text-only mobile rendering.

Join CTA: now wrapped in an inset paper tile with a 1.5px ink
border, a shadow, and an accent hairline ornament floating above
the eyebrow. Enclosing section gets a paper-3 background to push
the tile forward. Button grows to 20px serif / 18×34 padding.

Section padding standardised to 56-120px vertical so sections
breathe. Body overflow-x:hidden on html/body as a safety net for
the wide arc + the carousel overflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:20:29 +02:00
Arlind Ukshini
2cfbebf61f mobile: drop the footer
The footer added a wordmark row and a "view desktop" escape hatch
below the Join confirmation. Neither is worth the visual weight on
a phone screen — the masthead already has the wordmark, and the
?view=desktop override still works as a manual URL. Page now ends
at the join confirmation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:08:39 +02:00
Arlind Ukshini
72590b08bc add mobile view at protected/mobile/ (UA-dispatched)
Desktop is a GSAP/Lenis/d3 animated experience that doesn't hold up
on phones. Rather than retrofitting media queries across 1200+ lines
of scroll-trigger code, add a completely isolated static mobile tree:

- protected/mobile/index.html — one-page static flow covering the
  intro, 12 timeline events, hero, 4 capability cards, Bifrost
  reveal, 3 participation stops, and Join CTA. All copy duplicated
  from the desktop HTML on purpose — a shared data module would
  re-couple the two trees.
- protected/mobile/mobile.css — paper/ink palette, all m-prefixed,
  zero cascade overlap with the desktop CSS.
- protected/mobile/mobile.js — 60-line client: /auth/me check,
  /api/bifrost-join POST + panel swap, /auth/logout. No GSAP, no
  Lenis, no d3.

Routing (server.js):
- GET /timeline now UA-dispatches via MOBILE_UA_RE. Phone UAs get
  the mobile page; everything else gets the desktop page.
- ?view=mobile and ?view=desktop query overrides take precedence
  over the UA sniff — for bad guesses or previewing the other
  version.
- Gating is unchanged: protected/mobile/ is inside protected/ so
  the existing requireAuth + express.static gate covers it.

Docs:
- CLAUDE.md §routing now lists the UA dispatch as step 4.
- PROJECT.md gets a new "Mobile view" section explaining the
  isolation rules (no shared JS/CSS, content duplicated manually).
- CHECKLIST.md gains section H0 with dispatch curl checks, render
  verification on a phone, and an isolation audit that fails if
  mobile classes leak into the desktop HTML or vice versa.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:03:13 +02:00
Arlind Ukshini
5f466e68a9 tune overview anchors + stack-title-bar placement
- SCENE_ANCHOR_OFFSET replaced with getSceneAnchorOffset() so the
  bifrost scene can compute its offset per-viewport instead of using
  a fixed px count. bifrost lands at offsetTop + 0.85 * vh so the arc
  and sub-headline are already drawn in; stack-scene drops from 2100
  to 1800 so the anchor lands mid-stack rather than on the 4th card's
  final beat.
- .stack-title-bar top drops from clamp(3.75rem, 7vh, 5.25rem) to
  clamp(1.25rem, 2.8vh, 1.85rem) so the title floats at the same
  vertical baseline as the fixed .site-mark wordmark in the top-left,
  instead of sitting below it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:53:40 +02:00
Arlind Ukshini
9b2c166b6c timeline + overview: pacing, labeling, and viewing guidance
Timeline page:
- .page-sub: final sentence wrapped in .page-sub-accent and styled
  crimson so the rhetorical beat lifts off the block.
- Scroll-to-begin hint is now toggled from applyScroll() against
  state.target, so it dismisses on first commit AND re-appears when
  the reader scrolls all the way back to the start. onWheel no longer
  hard-adds .hint-dismissed; the applyScroll toggle drives both ways.

Overview page:
- Hero scroll icon: size and presence bumped (2px line, 44px tall, 11px
  chevron, weight 600, color:var(--ink)) so it reads as a confident
  cue, not a whisper at the bottom of the hero.
- Architecture scene gains a title bar pinned with the scene: "The
  Fenja AI platform in four steps" with a 1/4 → 4/4 counter driven by
  the scroll-trigger onUpdate. Bar is placed below the site-mark's
  fixed position so the two don't collide.
- Dot-nav: dot size 5px → 10px (1.5px ring) for better click target +
  visual weight. Buttons for "Words" and "Participate" removed — the
  corresponding intermediate sections now map to their nearest
  surviving dot in bifrost.js's scroll-spy (words-scene →
  stack-scene, bifrost-meaning → bifrost).
- Renames: "Hero" → "Fenja introduction", "Architecture" →
  "Capabilities", "Bifrost" → "Project Bifrost".
- scrollTo() adds a per-scene SCENE_ANCHOR_OFFSET — stack-scene lands
  +2100px into its 5000px pin so the reader arrives on the fully
  stacked state instead of an empty pre-animation frame.

Welcome step (public/entrance.html):
- New .welcome-note callout between definitions and CTA advising
  desktop viewing and gentle scrolling so readers don't fly past
  animated sections before they've resolved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 09:45:24 +02:00
Arlind Ukshini
e10ee31beb /fenjaops: drop masthead subtitle
The form panel's own subheading already says "non-admin only", so the
separate admin-page subtitle was redundant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:08:16 +02:00
Arlind Ukshini
cbfb187d16 /fenjaops: admin-only form to invite non-admin users
- POST /api/fenjaops/invites on server.js (requireAuth+requireAdmin).
  Ignores any is_admin field in the body — always stores 0. Records
  the acting admin's email in invited_by so the audit trail shows
  who added whom (CLI adds still record "cli").
- admin/index.html: new "Invite a new user" form panel at the top
  (email + optional first name).
- admin/admin.js: wires the form submit to the POST, shows inline
  success/error, refreshes the tables on success.
- admin/admin.css: form styling matching the existing paper/ink
  palette; mobile stacks.
- Docs: CLAUDE.md, PROJECT.md, OPERATIONS.md, CHECKLIST.md, README.md
  all updated. New non-negotiable property in PROJECT.md: no web
  endpoint can set is_admin=1 or delete an invite — promotion +
  removal stay on bin/invite.js. New CHECKLIST.md section H2 covers
  the page's gating, the invite form, and an escalation-path audit.

Admin promotion and invite deletion remain CLI-only so a compromised
admin session cannot escalate or evict.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:07:47 +02:00
Arlind Ukshini
107284801b add hidden /fenjaops admin page (read-only) + is_admin invite flag
- new is_admin column on invites (migration 4) with DEFAULT 0
- requireAdmin middleware returns 404 for non-admins so the route's
  existence isn't leaked; path obscured as /fenjaops (not /admin)
- admin/ dir lives outside public/ and protected/; only reachable via
  the explicit gated mount + /api/fenjaops/{invites,joins} endpoints
- bin/invite.js gains `admin add|remove|list` subcommands
- OPERATIONS.md + CLAUDE.md + PROJECT.md document the hidden URL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:29:19 +02:00
Arlind Ukshini
88863183e1 update docs: minimal env, WSL deploy, join tracking, rsync excludes
- align auth docs with the simplified POST /auth/login flow
- drop CODE_PEPPER / SMTP / MAIL_FROM / mail.js / request-code references
- document the bifrost_joins table and bin/joins.js CLI
- OPERATIONS.md: WSL setup, exclude data/.env/node_modules on promote rsync
- INSTALL.md: 3-value /etc/fenja/env, drop SMTP prereq

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:10:08 +02:00