docs: establish approved Phase 1 design + Phase 2 spec as project foundation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
# DESIGN_RESEARCH.md
|
||||
Portfolio Redesign — Phase 1 Research
|
||||
Connor Babbington · cesnimda.co.uk · July 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Audit of the existing site
|
||||
|
||||
**Current state:** WordPress + Avada (Fusion Builder) single-page site. Sections: Hero → About → Proficiencies → Experience → Projects → CV downloads → Contact form.
|
||||
|
||||
### What works
|
||||
- The content skeleton is correct: summary, skills, experience, projects, CV, contact — everything a recruiter needs exists somewhere.
|
||||
- Clear availability statement ("open to remote, hybrid, on-site") in the first viewport.
|
||||
- Both CV languages are downloadable from the hero.
|
||||
|
||||
### What fails against the goal ("decide to interview me")
|
||||
| Problem | Evidence | Impact |
|
||||
|---|---|---|
|
||||
| **Generic template look** | Avada theme, stock section headings ("About.", "Let's Talk.") | A hiring manager evaluating a *software engineer* sees a page-builder site — it signals "assembled", not "engineered". The single strongest negative signal on the current site. |
|
||||
| **No proof, only claims** | Skills are bare word-lists; experience is one paragraph; projects have no screenshots, no links, no architecture, no outcomes | Recruiters can't verify; engineers can't assess depth. Nothing differentiates from 10,000 similar CVs. |
|
||||
| **Projects undersold** | Job Tracker gets 2 sentences; InboxIntel absent entirely | These two projects are the *strongest* seniority evidence available (multi-service architecture, OAuth, AI integration, Docker, testing, security hardening) and they're invisible. |
|
||||
| **English only** | `lang="en-GB"`, no Norwegian content | Candidate lives in Tønsberg targeting the Norwegian market; a Norwegian hiring manager gets zero accommodation. Bokmål content is also a trust and effort signal ("aktiv språkutvikling" made visible). |
|
||||
| **Heavy, slow stack** | WP + Avada + W3TC minified bundles, lazyload shims | Poor Core Web Vitals undercut the "production-level thinking" message. The site itself should be a work sample. |
|
||||
| **No dark mode, no motion design, no personality** | Static template sections | Feels dated next to any modern product site. |
|
||||
| **Weak SEO/meta** | Empty `og:title`, description is a raw text dump of the hero | Sloppy details on the most detail-oriented artifact a developer publishes. |
|
||||
|
||||
**Conclusion:** the redesign is not a reskin — the site must *become the work sample*: fast, precise, bilingual, with projects presented as engineering case studies.
|
||||
|
||||
---
|
||||
|
||||
## 2. What makes engineer portfolios convincing (pattern research)
|
||||
|
||||
Synthesised from high-performing engineer portfolios (e.g. the genre exemplified by Brittany Chiang, Lee Robinson, Josh Comeau, Rauno Freiberg) and product marketing sites (Stripe, Linear, Vercel, Notion, Framer):
|
||||
|
||||
### Convincing signals
|
||||
1. **Specificity beats adjectives.** "Reduced manual spreadsheet workflows for a county council" beats "passionate problem-solver". Numbers, stack names, and constraints read as experience.
|
||||
2. **Case studies, not thumbnails.** Portfolios that convert treat projects like product launches: problem → approach → architecture → decisions/trade-offs → outcome → screenshots. Engineers hiring engineers read the *trade-offs* section first.
|
||||
3. **The site itself is evidence.** Fast load, keyboard access, tasteful motion, working dark mode, correct typography — hiring managers notice these subconsciously; senior ones notice consciously.
|
||||
4. **Restraint reads as seniority.** Junior portfolios over-animate. The Linear/Stripe lesson: one or two signature moves, executed perfectly, on a quiet base.
|
||||
5. **Low-friction verification.** CV download, LinkedIn, e-mail, code links reachable within one interaction from anywhere on the page.
|
||||
6. **A human face.** A real photo near the top measurably increases trust and recall; recruiters match the site to the LinkedIn profile.
|
||||
|
||||
### What makes visitors leave
|
||||
- Walls of unscannable text (recruiters scan, engineers skim then deep-read).
|
||||
- "Skills: HTML, CSS, JavaScript…" laundry lists with no context or proficiency framing.
|
||||
- Broken/missing project links, placeholder content, lorem-tier copy.
|
||||
- Slow first paint; janky scroll-hijacking; autoplaying anything.
|
||||
- No way to answer "can this person do *my* job?" within ~30 seconds.
|
||||
|
||||
### SaaS landing-page techniques worth borrowing (not copying)
|
||||
| Site | Technique to adapt |
|
||||
|---|---|
|
||||
| **Stripe** | Dense-but-calm information design; gradient used as a single identity moment, not everywhere; impeccable code-adjacent typography. |
|
||||
| **Linear** | Dark-first palette; short declarative sentences; motion only on state change; keyboard-first affordances. |
|
||||
| **Vercel** | Monochrome + one accent; triangle-precision spacing; "proof strip" (logos/metrics) directly under the hero. |
|
||||
| **Notion** | Friendly humanity inside a rigid grid; illustration used sparingly to soften. |
|
||||
| **Framer** | Scroll-linked reveals that never block reading; hero that demonstrates the product (here: the site demonstrates the engineer). |
|
||||
|
||||
---
|
||||
|
||||
## 3. Recruiter & hiring-manager evidence base
|
||||
|
||||
(Expanded in RECRUITER_BEHAVIOUR_ANALYSIS.md.)
|
||||
|
||||
- **Time budget:** eye-tracking studies of recruiters (Ladders, 2018) put initial CV scan at **~7.4 seconds**. A portfolio gets slightly more — roughly 20–50 seconds before a stay/leave decision — but the first viewport carries the decision.
|
||||
- **Scan pattern:** F-pattern / layered scanning (Nielsen Norman Group): name → title → location/availability → most recent role → skills. Layout must place these on the F.
|
||||
- **What they screen for:** role fit keywords, seniority, location/work-permit, availability, red flags (gaps, vagueness). The hero must answer: *who, what level, what stack, where, available?*
|
||||
- **Two-audience problem:** recruiters (non-technical, 30s, checklist-driven) and engineering managers/CTOs (technical, 3–10 min, depth-driven) visit the same URL. The IA must serve a **fast lane** (scannable hero + skills + CV button) and a **deep lane** (case studies, architecture, decisions) without either blocking the other.
|
||||
- **Norway-specific:** Norwegian hiring culture values understatement, concrete competence claims (jf. *janteloven* — avoid grandiosity), and language effort. "Norsk A2/B1, i aktiv utvikling" stated plainly is a positive signal, not a weakness to hide. Work-permit clarity ("gyldig oppholdstillatelse") removes the #1 recruiter objection for foreign candidates.
|
||||
|
||||
---
|
||||
|
||||
## 4. Accessibility baseline (non-negotiable, also a seniority signal)
|
||||
|
||||
- WCAG 2.2 AA: contrast ≥ 4.5:1 body text (≥ 3:1 large text/UI), visible focus states, target size ≥ 24px.
|
||||
- Full keyboard operability including the language switch and theme toggle.
|
||||
- `prefers-reduced-motion` honoured by *every* animation (motion system must define reduced variants, not just "off").
|
||||
- Semantic landmark structure; skip-link; alt text strategy for project screenshots.
|
||||
- Language correctness: `lang` per locale, `hreflang` alternates — this doubles as SEO.
|
||||
|
||||
---
|
||||
|
||||
## 5. Strategic conclusions → design brief
|
||||
|
||||
1. **Reposition the site as a product.** Name the artefact: it is "Connor Babbington — systems developer" presented with the craft of a SaaS launch page.
|
||||
2. **Projects become the centre of gravity.** Two full case studies (JobTrack, InboxIntel) + an infrastructure/homelab capability page. Each with architecture diagram, screenshots, decisions, and honest "what I'd improve".
|
||||
3. **Two-lane IA.** 30-second lane: hero → proof strip → skills → CV. 10-minute lane: case studies → experience → about.
|
||||
4. **Bilingual as a feature, not a toggle afterthought.** Norwegian content is written (not machine-mirrored), the switch is visible in the header, and URLs are language-scoped.
|
||||
5. **Honest seniority framing.** The CV says "mid-level, 8 years". The site should not claim "senior" in a title; it should *demonstrate* senior judgement (trade-off writing, production thinking, security notes) and use the framing "systems developer, 8+ years". Evidence over adjectives — this is also the safest posture for the Norwegian market.
|
||||
6. **Performance is part of the design.** The visual system is deliberately light: system-adjacent type stacks, no hero video, SVG-first graphics, motion via transform/opacity only.
|
||||
7. **The design direction is defined in DESIGN_SYSTEM.md as "Kontrollrom / Quiet Console"** — a Nordic-restrained, dark-first engineering aesthetic with a single aurora-teal accent and monospace metadata as its identity move. Unique, ownable, and cheap to render.
|
||||
@@ -0,0 +1,62 @@
|
||||
# RECRUITER_BEHAVIOUR_ANALYSIS.md
|
||||
How recruiters and hiring managers actually behave on candidate sites — and the design responses.
|
||||
|
||||
---
|
||||
|
||||
## 1. The decision funnel
|
||||
|
||||
```
|
||||
Application / LinkedIn / referral
|
||||
│ (click, if the link looks worth it)
|
||||
▼
|
||||
First viewport ······· ~5–10 s → stay or bounce
|
||||
▼
|
||||
Scan pass ············ ~20–45 s → "worth shortlisting?"
|
||||
▼
|
||||
Depth pass (technical) ~3–10 min → "worth interviewing?"
|
||||
▼
|
||||
CV download / e-mail / forward to hiring manager
|
||||
```
|
||||
|
||||
Each stage has different owners: the scan pass is usually a recruiter; the depth pass an engineering manager. **The same page must pass both.**
|
||||
|
||||
## 2. Evidence & observed patterns
|
||||
|
||||
1. **~7.4 seconds** median initial CV scan (Ladders eye-tracking, 2018). Web pages get marginally more, but the fixation order is stable: **name → title → location → recent employer → dates → skills**. Design response: those six items are literally the hero and proof strip, in that order.
|
||||
2. **F-pattern scanning** (NN/g): two horizontal sweeps near the top, then a vertical skim down the left edge. Design response: left-align key nouns; front-load sentences; section labels in the left rail on desktop; never bury critical facts mid-paragraph or right-aligned.
|
||||
3. **Layer-cake scanning:** on well-structured pages, scanners read *headings only*. Design response: headings must carry the argument alone. Test: read only the H2/H3s — do they say "experienced .NET/full-stack developer in Norway with production evidence, available now"? Ship only if yes.
|
||||
4. **Checklist screening:** recruiters screen *out* before they screen in. Top objections for this candidate profile: (a) work permit / right to work in Norway, (b) language, (c) seniority mismatch, (d) unverifiable claims. Design response: kill all four above the fold — permit ✓, "English native / Norsk B1" ✓, "8+ yrs" ✓, links to real running systems ✓.
|
||||
5. **Mobile share is significant** for first visits from LinkedIn (often 40–60%). Design response: mobile hero carries identical decision-facts; CV download works on phone (and the CV itself is ATS-parseable — see CONTENT_STRATEGY §6).
|
||||
6. **Trust markers ranked** (approximate weighting for a mid/senior dev profile):
|
||||
- Real employer names + durations ●●●
|
||||
- Screenshots of real, running software ●●●
|
||||
- Face photo matching LinkedIn ●●
|
||||
- Code/architecture detail ●● (hiring managers)
|
||||
- Domain + professional e-mail ●
|
||||
- Testimonials ○ (low value unless verifiable — omit rather than fake)
|
||||
7. **Exit triggers:** template look, broken links, "under construction", walls of text, unlabeled skill bars ("JavaScript 80%" — meaningless, reads junior), autoplay, scroll hijack, pop-ups.
|
||||
|
||||
## 3. The two-lane rule (governing IA principle)
|
||||
|
||||
- **Fast lane (P1/P3):** hero → proof strip → skills matrix → CV/contact. Complete in ≤ 45 s without a single click. Every fact scannable, nothing interactive required.
|
||||
- **Deep lane (P2/P4):** project case studies and experience detail, one click away, each readable in 3–5 minutes with skimmable structure (headings carry the argument; diagrams before prose).
|
||||
- Lanes must not interleave: no case-study prose on the homepage beyond a 2-line teaser; no marketing fluff inside case studies.
|
||||
|
||||
## 4. Norway-market specifics
|
||||
|
||||
- **Understatement wins.** "Senior rockstar ninja" framing actively harms with Norwegian evaluators. Use concrete competence statements: "Åtte års erfaring med systemutvikling i britisk offentlig sektor."
|
||||
- **Public sector experience is an asset** — Warwickshire County Council maps mentally to kommune/fylkeskommune IT. Frame it that way in Norwegian copy.
|
||||
- **Language effort is a first-class signal.** A natively-written Bokmål site says more than "Norwegian: B1" ever can. Never machine-mirror; write shorter, plainer Norwegian rather than translated English sentence structures.
|
||||
- **Practicalities beat charisma:** permit, location (Tønsberg — commutable to Oslo south corridor, Horten, Sandefjord tech), remote readiness, start availability.
|
||||
|
||||
## 5. Measurable success criteria for the design (Phase 1 acceptance)
|
||||
|
||||
| # | Criterion | Test |
|
||||
|---|---|---|
|
||||
| 1 | Identity, role, stack, location, permit, availability all visible in first desktop viewport (1440×900) and first two mobile screens | Mockup review |
|
||||
| 2 | Headings alone tell the hire story | Read H-tags only |
|
||||
| 3 | CV (correct language) reachable in ≤ 1 interaction from any scroll position | Mockup review |
|
||||
| 4 | A technical reader can find an architecture diagram in ≤ 2 clicks | Journey walkthrough |
|
||||
| 5 | Language switch discoverable in ≤ 5 s, state obvious | Mockup review |
|
||||
| 6 | Nothing moves without user intent except one hero identity moment (≤ 800 ms, reduced-motion safe) | Motion spec |
|
||||
| 7 | All text ≥ 4.5:1 contrast in both themes | Colour system audit |
|
||||
@@ -0,0 +1,55 @@
|
||||
# USER_PERSONAS.md
|
||||
Four personas drive every layout and content decision. Each has a *time budget*, a *question they must answer*, and a *conversion action*.
|
||||
|
||||
---
|
||||
|
||||
## P1 — "Silje", Tech Recruiter (agency or in-house), Oslo/Vestfold
|
||||
- **Context:** Screening 40+ profiles today. Found the site via LinkedIn or an application link. Likely on desktop, possibly mobile between meetings. Norwegian native; reads English fine but a Norwegian option signals local commitment.
|
||||
- **Time budget:** 20–45 seconds.
|
||||
- **Technical depth:** Low. Matches keywords to a job spec.
|
||||
- **Must answer:** Right title? Right stack keywords (C#, .NET, React, Docker)? Located where? Work permit? Available? Language level? CV downloadable?
|
||||
- **Conversion action:** Download CV (correct language) or copy e-mail/LinkedIn into ATS.
|
||||
- **Design implications:** Hero states role, stack, location, permit and availability *in text* (not images). CV buttons persistent (hero + sticky nav + footer). Norwegian toggle prominent. Phone-friendly one-thumb layout.
|
||||
- **Failure mode:** Any ambiguity about location/permit → skip candidate.
|
||||
|
||||
## P2 — "Martin", Engineering Manager, product company (Oslo/remote-EU)
|
||||
- **Context:** Recruiter shortlisted Connor; Martin has 5 minutes between meetings to decide interview/no-interview. Desktop, probably dark-mode IDE person.
|
||||
- **Time budget:** 3–7 minutes.
|
||||
- **Technical depth:** High. Reads architecture, judges trade-offs, opens dev tools out of habit.
|
||||
- **Must answer:** Can he build and *operate* real systems? Does he test? Does he think about security and maintenance? Will he communicate well with stakeholders?
|
||||
- **Conversion action:** Skim one case study end-to-end → recommend interview; possibly click through to code.
|
||||
- **Design implications:** Case studies with architecture diagrams, decision/trade-off sections, testing & security notes, honest "known limitations". Site performance and a11y are silent evidence. Metadata in monospace, correct semantics — he *will* view source.
|
||||
- **Failure mode:** Claims without proof; a slow template site ("if the portfolio is Avada, what will the codebase be?").
|
||||
|
||||
## P3 — "Anne", CTO / Head of IT at a Norwegian SME or kommune-adjacent supplier
|
||||
- **Context:** Small team, hires rarely, values dependability over rockstar energy. Found via application. Reads Bokmål by preference.
|
||||
- **Time budget:** 2–5 minutes, often on the Norwegian version.
|
||||
- **Technical depth:** Medium-high, but evaluates *risk*: will this person maintain legacy systems, talk to users, stay?
|
||||
- **Must answer:** Public-sector experience? Boring-technology competence (SQL, .NET, servers)? Stability and communication? Settled in Norway?
|
||||
- **Conversion action:** Read About + Experience → forward to colleague → invite to interview.
|
||||
- **Design implications:** Warwickshire County Council experience framed as *public-sector systems work* (highly relevant in NO). Understated tone (janteloven-compatible). Norwegian copy natively written, not translated-sounding. "Bosatt i Tønsberg, gyldig oppholdstillatelse" visible.
|
||||
- **Failure mode:** Grandiose self-labels, hype language, English-only.
|
||||
|
||||
## P4 — "Priya", Senior Engineer doing interview-loop prep (secondary persona)
|
||||
- **Context:** Interview is booked; she's preparing questions from his projects. Deep-reads one case study.
|
||||
- **Time budget:** 10+ minutes.
|
||||
- **Must answer:** What exactly did he build? Where are the interesting decisions to probe?
|
||||
- **Conversion action:** Arrives at interview with informed, favourable questions.
|
||||
- **Design implications:** Case studies must be accurate and honest — everything on the page is fair game in an interview. "What I'd do differently" sections turn probing questions into prepared wins.
|
||||
|
||||
---
|
||||
|
||||
## Persona → priority matrix
|
||||
|
||||
| Section | P1 Recruiter | P2 Eng Manager | P3 CTO (NO) | P4 Engineer |
|
||||
|---|---|---|---|---|
|
||||
| Hero (role/stack/location/permit) | ●●● | ●● | ●●● | ● |
|
||||
| Skills matrix | ●●● | ●● | ●● | ● |
|
||||
| CV download | ●●● | ● | ●● | ● |
|
||||
| Case studies | ● | ●●● | ●● | ●●● |
|
||||
| Experience timeline | ●● | ●● | ●●● | ● |
|
||||
| About (human) | ● | ● | ●●● | ● |
|
||||
| Contact | ●●● | ●● | ●●● | ● |
|
||||
| Norwegian locale | ●● | ○ | ●●● | ○ |
|
||||
|
||||
**Reading:** The homepage top half serves P1/P3; the middle and project pages serve P2/P4. Nothing P2 needs may push P1's answers below the fold.
|
||||
@@ -0,0 +1,57 @@
|
||||
# CANONICAL_CONTENT.md
|
||||
Master source of truth for all site + CV copy (English). Provided by Connor, July 2026. All Phase 2/3 content derives from this file; the Norwegian versions are written natively against these *facts*, not translated sentence-by-sentence.
|
||||
|
||||
---
|
||||
|
||||
## Identity & contact
|
||||
- **Name:** Connor Babbington
|
||||
- **E-mail:** connor.babbington@cesnimda.co.uk
|
||||
- **Phone:** +47 41 33 44 70
|
||||
- **Website:** cesnimda.co.uk
|
||||
- **LinkedIn:** via redirect `cesnimda.co.uk/Linkedin`
|
||||
- **Location:** Tønsberg, Norway — valid residence permit
|
||||
- **Languages:** English (native) · Norwegian A2/B1, actively developing
|
||||
- **Availability:** open to remote, hybrid, or on-site developer roles
|
||||
|
||||
## Profile (canonical facts behind the About section)
|
||||
- Systems developer, eight years in UK local government (2015–2023).
|
||||
- Full-stack: backend, frontend, and server administration.
|
||||
- Analyses and plans user needs; programs and tests with modern languages/frameworks; designs robust, scalable systems.
|
||||
- Collaborates with developers, designers, project managers, and customers.
|
||||
- Strong experience: Azure DevOps (CI/CD), GitHub.
|
||||
- Works well independently and in teams; structured and open-ended tasks.
|
||||
|
||||
> Site framing decision (approved direction): self-label is **"Systems Developer"** — the CV's "mid-level" qualifier is dropped everywhere (site *and* reworked CV); seniority is shown through evidence. "Eight years" stays because the apprenticeship was on-the-job development work.
|
||||
|
||||
## Experience
|
||||
|
||||
### Warwickshire County Council, UK — System Developer · 2015–2023
|
||||
- **2015–2017 was an apprenticeship** → progressed to full developer role. Site treatment: show this *as progression* on the timeline (`2015–2017 apprentice → 2017–2023 developer`) — it's an asset (grew into the role, retained 8 years), not something to bury.
|
||||
- Developed and maintained multiple full-stack applications: **C#, Python, Ruby on Rails, SQL, JavaScript**.
|
||||
- Delivered reliable, well-tested software; resolved stability issues.
|
||||
- Translated stakeholder requirements into solutions; adapted across languages/frameworks as projects changed.
|
||||
- Responsibilities: backend + frontend design/build on several projects; bug fixing for stability and performance; clean maintainable code with testing and documentation; collaborative problem-solving; guided team members on code/process best practices.
|
||||
- **Concrete anchor (confirmed):** worked on the highways/streetlight fault-reporting system used across the whole of Warwickshire (county-wide public-facing + internal use). Draft site bullet: *"Worked on the county-wide highways and streetlight fault-reporting system used across Warwickshire."* Exact user counts unknown — say "county-wide", never invent numbers.
|
||||
|
||||
### 2023 → now (confirmed)
|
||||
- Self-directed development: JobTrack, InboxIntel, self-hosted infrastructure lab.
|
||||
- **Norskkurs** — ongoing Norwegian language study. Gets a timeline node (`2023– · Norskkurs + full-stack product development · Tønsberg`); it substantiates the "aktiv språkutvikling" claim and fills the employment gap honestly.
|
||||
|
||||
### Side roles (confirmed: part-time alongside the council role — vape shop weekend days, bar evenings/weekend evenings)
|
||||
- Royal Vapes, UK — Sales Representative · 2017–2021 (customer service, client issues, communications, payments)
|
||||
- The Hodcarrier, UK — Bartender · 2016–2018 (operations, multitasking)
|
||||
- Nuffield Health, UK — Receptionist · 2014–2015 (front of house, organisation)
|
||||
|
||||
Site treatment per CONTENT_STRATEGY: compact one-liners, de-emphasised, grouped under "Earlier & alongside: customer-facing roles that shaped how I communicate."
|
||||
|
||||
## Education
|
||||
- Warwickshire College, UK · 2012–2015 — **Extended Diploma NVQ Level 3 in ICT** (programming, systems administration, IT support).
|
||||
|
||||
## Interests
|
||||
PC and board games (strategic thinking, problem solving), cooking, learning new skills. Site treatment: one plain line in About; no icons, no over-explanation.
|
||||
|
||||
## Open items — all resolved July 2026
|
||||
1. ~~Side roles part-time?~~ Confirmed: weekend days (shop) and evenings/weekend evenings (bar), alongside the council role.
|
||||
2. ~~Concrete Warwickshire detail?~~ Confirmed: county-wide highways/streetlight fault-reporting system (see Experience above).
|
||||
3. ~~2023→now?~~ Confirmed: self-development + norskkurs (see section above).
|
||||
4. "Systems Developer, 8+ years" positioning: **approved by Connor.**
|
||||
@@ -0,0 +1,82 @@
|
||||
# CONTENT_STRATEGY.md
|
||||
|
||||
## 1. Voice & tone
|
||||
|
||||
| Attribute | Do | Don't |
|
||||
|---|---|---|
|
||||
| **Plain-spoken** | "I build and run web systems." | "Passionate technologist crafting digital experiences." |
|
||||
| **Evidence-led** | "Replaced spreadsheet-driven reporting workflows for a UK county council." | "Extensive experience with stakeholders." |
|
||||
| **Understated confidence** (Norwegian-market calibrated) | "Eight years building and maintaining production systems." | "Senior rockstar engineer." |
|
||||
| **Honest** | "SQLite was the right call for a single-user app; I'd move to Postgres for multi-tenant." | Hiding limitations. |
|
||||
|
||||
**Seniority framing decision:** the site never self-labels "senior". Role line = **"Systems Developer"** (EN) / **"Systemutvikler"** (NO), qualified by "8+ years". Seniority is demonstrated through trade-off writing, security notes, and production thinking. This is honest against the CV ("mid-level, eight years"), safe in Norwegian culture, and *more* convincing to P2/P3 than a claimed title.
|
||||
|
||||
## 2. Hero copy (approved-draft level)
|
||||
|
||||
**EN**
|
||||
> **Connor Babbington**
|
||||
> Systems Developer — .NET, full-stack & infrastructure
|
||||
> I design, build and run web systems: eight-plus years delivering internal software for UK local government, now building full-stack products in Norway.
|
||||
> Chips: `Tønsberg, Norway` `Work permit ✓` `Open to remote / hybrid / on-site` `English native · Norsk B1`
|
||||
> CTAs: **[View projects]** **[Download CV]**
|
||||
|
||||
**NO (written natively, not translated)**
|
||||
> **Connor Babbington**
|
||||
> Systemutvikler — .NET, fullstack og infrastruktur
|
||||
> Jeg utvikler og drifter websystemer. Åtte års erfaring med fagsystemer i britisk offentlig sektor — nå bygger jeg fullstack-løsninger fra Tønsberg.
|
||||
> Chips: `Bosatt i Tønsberg` `Gyldig oppholdstillatelse` `Åpen for remote / hybrid / oppmøte` `Engelsk morsmål · Norsk B1`
|
||||
> CTAs: **[Se prosjekter]** **[Last ned CV]**
|
||||
|
||||
## 3. Section content rules
|
||||
|
||||
- **Skills:** grouped chips + one context line per group. Never percentage bars, never star ratings. Groups: *Development* (C#, .NET, Python, JavaScript/TypeScript, React, SQL) · *DevOps & Infrastructure* (Docker, Linux, CI/CD, Azure DevOps, nginx/reverse proxies, monitoring, self-hosting) · *Practices* (testing, security hardening, OAuth2 integrations, stakeholder communication, production support). Each chip must be defensible in an interview.
|
||||
- **Experience:** the 2015–2023 Warwickshire role gets 3–4 highlight bullets (outcomes, not duties), with the 2015–2017 apprenticeship shown as *progression* on the timeline (apprentice → developer — a retention/growth signal, not a footnote). Non-dev roles (sales, bartender, receptionist — held alongside/before the council role) collapse into one line — "Earlier & alongside: customer-facing roles that shaped how I communicate" — present for timeline honesty, de-emphasised visually. NO version frames council work as "fagsystemer for lokalforvaltning". Canonical facts: see CANONICAL_CONTENT.md.
|
||||
- **About:** 3 short paragraphs — (1) how I work, (2) the homelab/personal-projects habit as evidence of currency, (3) life in Norway + language journey + interests (games → strategic thinking framing kept light). Photo: the outdoor headshot (warm, approachable); the suit photo reserved for CV/LinkedIn.
|
||||
|
||||
## 4. Case-study content sources & angles
|
||||
|
||||
### JobTrack (`/projects/jobtrack`)
|
||||
- **Source:** `D:\Job tracker\website_details.md` (already portfolio-shaped — reuse its headline, features, use cases) + README (stack: React, ASP.NET Core (.NET 9), SQLite, FastAPI summarizer, Ollama, Docker, PWA share-target).
|
||||
- **Angle:** *product thinking + integration depth.* Story: "I had a real problem (job hunting), built a real tool, then hardened it like production software."
|
||||
- **Decision candidates:** SQLite vs Postgres for single-user; local AI (Ollama) vs cloud API (privacy + cost); PWA share-target vs native app; no offline cache by design (deploy freshness) — this one is *gold*: a deliberate anti-feature with reasoning.
|
||||
- **Security notes:** Google ID-token auth, ownership checks, secure file uploads.
|
||||
|
||||
### InboxIntel (`/projects/inboxintel`)
|
||||
- **Source:** repo README + docs/ARCHITECTURE.md.
|
||||
- **Angle:** *architecture discipline.* Story: "Clean Architecture in practice: four-layer .NET solution, background sync worker, safe-by-design destructive operations."
|
||||
- **Decision candidates:** Clean Architecture layering & dependency rule; encrypted-at-rest OAuth refresh tokens (Data Protection API); preview-then-confirm for all destructive cleanup; advisory-only AI layer; Polly retry/backoff against Gmail API.
|
||||
- **Honesty note:** README calls it a scaffold with marked extension points — the case study says "in active development" with a roadmap, and status chip `In development`. Never overclaim; P4 will probe.
|
||||
|
||||
### Homelab (`/projects/homelab`) — capability page, lighter template
|
||||
- Ubuntu + Docker services, reverse proxy, auth, monitoring, self-hosted Gitea (git.cesnimda.uk — itself proof), backups. Angle: *operations competence* — "I don't just deploy; I keep things running." Include a small topology diagram.
|
||||
|
||||
### The site itself (footer meta-link)
|
||||
- One short page/footnote: performance budget, accessibility choices, bilingual architecture. Written *after* implementation (Phase 3 content); designed now as a P2 hook.
|
||||
|
||||
## 5. Localisation strategy (content level)
|
||||
|
||||
1. **Write EN and NO as siblings, not source→target.** NO sentences should be shorter and plainer; Bokmål tolerates directness English pads out.
|
||||
2. **Equivalence, not identity:** the NO About may spend more words on the Norway/language story; the EN version more on UK career detail. Facts identical; emphasis local.
|
||||
3. **Glossary (fixed term pairs):** Systems Developer/Systemutvikler · case study/prosjektgjennomgang · public sector/offentlig sektor · work permit/oppholdstillatelse · experience/erfaring · skills/kompetanse · self-hosted/egendriftet. Keep product names (JobTrack, InboxIntel) and technology names untranslated.
|
||||
4. **Quality gate:** a native/fluent Bokmål speaker reviews all NO copy before launch (flagged as an explicit pre-launch task — the current CV's Norwegian has minor tells, e.g. "Ytret fremragende kundeservice" should be "Ytet…"; the site must be cleaner than the CV).
|
||||
5. **Tone parity:** understated in both; the NO version must never read as marketing-translated.
|
||||
|
||||
## 6. CV improvement recommendations (assets provided → must improve)
|
||||
|
||||
The current PDFs are two-column, heavily letter-spaced graphical CVs. Problems: (a) letter-spaced headings ("E X P E R I E N C E") and two-column order break ATS parsing badly — text extraction confirms scrambled reading order; (b) content undersells projects; (c) "mid-level" self-label undercuts an 8-year record.
|
||||
|
||||
**Recommendations (content design, no implementation):**
|
||||
1. Produce a **single-column, ATS-safe layout**: standard headings (Experience, Education, Skills, Projects), no letter-spacing tricks, real text, consistent date formats.
|
||||
2. Replace "Mid-level system developer" with **"Systems developer with eight years' experience"** in both languages.
|
||||
3. Add a **Projects section** (JobTrack, InboxIntel, homelab — 2 lines each with stack) — currently the CV omits his strongest recent evidence entirely.
|
||||
4. Split the 2015–2023 role into outcome bullets mirroring site copy (single source of truth with the site content model).
|
||||
5. Fix Norwegian errors ("Ytret" → "Ytet", "holdt baren oppdatert på lager" → "holdt baren velfylt"); native review pass.
|
||||
6. Add the website URL prominently; site and CV cross-promote.
|
||||
7. Keep a designed "pretty" variant for humans if desired, but the *download default* is the ATS-safe version. Filenames: `Connor-Babbington-CV-EN.pdf` / `Connor-Babbington-CV-NO.pdf`.
|
||||
|
||||
## 7. SEO content (conceptual)
|
||||
|
||||
- Title patterns: `Connor Babbington — Systems Developer (.NET, React) · Tønsberg, Norway` / `Connor Babbington — Systemutvikler (.NET, React) · Tønsberg`. Case studies: `JobTrack — case study · Connor Babbington`.
|
||||
- Meta descriptions handwritten per page per language (≤ 155 chars), answering "who/what/where".
|
||||
- Structured data (conceptual): `Person` on home (name, jobTitle, address locality, sameAs → LinkedIn/Gitea), `SoftwareSourceCode`/`CreativeWork` per case study.
|
||||
- OpenGraph: per-language OG images (name + role line + accent motif; generated per design system).
|
||||
@@ -0,0 +1,91 @@
|
||||
# INFORMATION_ARCHITECTURE.md
|
||||
|
||||
## 1. Site map
|
||||
|
||||
```
|
||||
/ Home (EN default)
|
||||
/no Hjem (Bokmål)
|
||||
│
|
||||
├── /projects Projects index (teaser cards) /no/prosjekter
|
||||
│ ├── /projects/jobtrack Case study: JobTrack /no/prosjekter/jobtrack
|
||||
│ ├── /projects/inboxintel Case study: InboxIntel /no/prosjekter/inboxintel
|
||||
│ └── /projects/homelab Capability page: Self-hosted /no/prosjekter/hjemmelab
|
||||
│ infrastructure lab
|
||||
├── /experience Experience & timeline /no/erfaring
|
||||
├── /about About + photo + languages + interests /no/om-meg
|
||||
├── /contact Contact (form + direct channels) /no/kontakt
|
||||
└── /cv CV hub (EN/NO download, web-readable /no/cv
|
||||
version, ATS note)
|
||||
404 Styled, bilingual, links home
|
||||
```
|
||||
|
||||
**Depth rule:** nothing is more than 2 levels deep. Home teases everything; dedicated pages carry depth. This replaces the current one-pager because case studies need URLs of their own (shareable by recruiters into ATS/Slack, individually indexable per language).
|
||||
|
||||
**Home remains a narrative one-page scroll** (hero → proof → skills → featured projects → experience summary → about teaser → contact CTA) so the P1 fast lane never requires navigation. Deep pages serve P2/P4.
|
||||
|
||||
## 2. Navigation model
|
||||
|
||||
**Header (persistent, all pages):**
|
||||
```
|
||||
[CB monogram] Projects Experience About Contact | [EN/NO] [☾/☀] [Download CV ▾]
|
||||
```
|
||||
- Monogram → home. 4 nav items max — recruiters don't explore menus.
|
||||
- `Download CV ▾` is a split-button: primary action downloads the CV matching current locale; the chevron reveals the other language. Persistent = criterion "CV in ≤1 interaction from any scroll position".
|
||||
- Header is sticky, shrinks on scroll (see SCROLL_EXPERIENCE.md); backdrop blurs over content.
|
||||
- Mobile: monogram + CV button + hamburger (full-screen overlay panel, large tap targets, language + theme controls inside the panel top).
|
||||
|
||||
**Footer (all pages):** e-mail, LinkedIn, git.cesnimda.uk, CV both languages, language switch repeat, "Built by me — [how this site works]" link (small meta-page or case-study footnote; a deliberate P2 hook).
|
||||
|
||||
**Local navigation inside case studies:** right-side "On this page" mini-TOC (desktop ≥1200px only): Problem · Architecture · Decisions · Security · Outcome · Next.
|
||||
|
||||
## 3. Homepage section order & rationale
|
||||
|
||||
| # | Section | Serves | First-fact placement |
|
||||
|---|---|---|---|
|
||||
| 1 | **Hero** — name, role line, 2-line summary, location/permit/availability chips, CTAs (View projects / Download CV), portrait | P1 P3 | Everything from RECRUITER §2.1 |
|
||||
| 2 | **Proof strip** — 4 compact stat/fact tiles: `8+ yrs experience` · `UK public sector` · `.NET / React / Docker` · `Based in Tønsberg, NO` | P1 | Reinforces scan pass |
|
||||
| 3 | **Skills matrix** — 3 columns (Development / DevOps & Infrastructure / Practices), grouped chips with context lines, no percentage bars | P1 P2 | Keyword match |
|
||||
| 4 | **Featured projects** — 2 large case-study cards (JobTrack, InboxIntel) + 1 slim homelab card; each: screenshot, 1-line problem, stack chips, "Read case study →" | P2 P4 | Deep-lane entry |
|
||||
| 5 | **Experience** — compact vertical timeline (System Developer 2015–2023 emphasised; earlier roles collapsed one-liners) | P1 P3 | Employment verification |
|
||||
| 6 | **About teaser** — photo, 3 sentences, language levels, "More about me →" | P3 | Human trust |
|
||||
| 7 | **Contact CTA band** — "Looking for a systems developer in Norway?" + e-mail button + form link | all | Conversion |
|
||||
|
||||
## 4. Case study page template (both projects share it)
|
||||
|
||||
```
|
||||
1. Header: project mark + name, one-line value prop, status chip (Active / In development),
|
||||
stack chips, links (live demo if applicable · repository)
|
||||
2. Hero screenshot (framed browser mock, themed)
|
||||
3. TL;DR box — 4 bullets: What / Why / Stack / My role ← the only part P1 reads
|
||||
4. Problem & context
|
||||
5. Architecture — diagram first, then short prose (frontend / API / data / workers / integrations)
|
||||
6. Key decisions & trade-offs — 3–5 numbered decisions, each: choice, alternative, why
|
||||
7. Security & production notes — auth, secrets, encryption-at-rest, backups, CI
|
||||
8. Screenshots gallery — 3–5 annotated captures
|
||||
9. What I'd improve next — honest, specific
|
||||
10. Footer nav: ← other project | All projects
|
||||
```
|
||||
|
||||
Content per project sourced from `website_details.md` (JobTrack) and InboxIntel README/docs — see CONTENT_STRATEGY.md §4.
|
||||
|
||||
## 5. Bilingual architecture (conceptual)
|
||||
|
||||
- **URL strategy:** path-prefix locales. English at root (`/projects/jobtrack`), Norwegian under `/no/` with **localised slugs** (`/no/prosjekter/jobtrack`). Rationale: root-EN keeps existing inbound links and international reach; `/no/` prefix is the conventional, SEO-clean pattern; localised slugs signal genuine Norwegian content.
|
||||
- **Language switch behaviour:** switches to the *same page* in the other language (per-page mapping table), never to the other homepage. Preference remembered for return visits; first visit may show a one-time, dismissible "Denne siden finnes på norsk" hint if the browser prefers Norwegian — never an auto-redirect (recruiters share links across languages; links must stay stable).
|
||||
- **Parity rule:** every page exists in both languages; content is *equivalent, not identical* (CONTENT_STRATEGY §5). If a page ever ships EN-first, the NO version shows a short native-Norwegian summary + "full versjon på engelsk" link — never machine output, never a broken switch.
|
||||
- **SEO (conceptual):** `hreflang` pairs on every page + `x-default` → EN; per-language titles/descriptions/OpenGraph; per-language sitemap entries; canonical per locale (no cross-language canonicals — the versions are alternates, not duplicates).
|
||||
|
||||
## 6. Content model (design-level, informs Phase 2 data model)
|
||||
|
||||
```
|
||||
Profile name, roleLine ×2 lang, summary ×2, chips (location, permit,
|
||||
availability, languages), photo variants, links
|
||||
SkillGroup title ×2, ordered skills[] {name, contextLine ×2 (optional)}
|
||||
Project slug ×2, name, valueProp ×2, status, stack[], links[],
|
||||
tldr ×2, sections[] ×2 (problem/architecture/decisions/
|
||||
security/next), media[] {image, alt ×2, caption ×2}, diagram
|
||||
ExperienceItem employer, role ×2, period, location, summary ×2,
|
||||
highlights[] ×2, emphasis (featured | compact)
|
||||
Page meta title ×2, description ×2, ogImage per lang
|
||||
```
|
||||
Every user-visible string exists per-language by construction — this is the "avoid duplication issues" answer: one structural source, two content channels, no forked page trees.
|
||||
@@ -0,0 +1,52 @@
|
||||
# USER_JOURNEYS.md
|
||||
Five journeys validated against the IA. Format: step → what they see → design element that makes it succeed.
|
||||
|
||||
---
|
||||
|
||||
## J1 · Recruiter Silje — LinkedIn → CV in ATS (target: < 60 s)
|
||||
1. Clicks site link on LinkedIn (mobile). → Hero: "Connor Babbington — Systems Developer · .NET / React / Docker · Tønsberg, Norway · Open to work." Portrait matches LinkedIn photo. *(Identity confirmed in ~3 s.)*
|
||||
2. Thumb-scrolls once. → Proof strip chips: 8+ yrs · UK public sector · work permit ✓ · EN native/NO B1. *(All screening objections cleared without reading prose.)*
|
||||
3. Scrolls skills matrix. → Keyword match against job spec: C#, .NET, SQL, React, Docker, CI/CD. *(Chips, not bars — parseable at a glance.)*
|
||||
4. Taps persistent **Download CV** in header. → Gets `Connor-Babbington-CV-EN.pdf` (locale-matched). *(Split-button defaults correctly; no hunting.)*
|
||||
5. Done — candidate shortlisted. **Exit quality: converted without ever leaving the fast lane.**
|
||||
|
||||
## J2 · Eng manager Martin — shortlist e-mail → interview recommendation (target: ≤ 7 min)
|
||||
1. Opens link on desktop, dark IDE habits → site respects `prefers-color-scheme`, loads instantly. *(First silent evidence.)*
|
||||
2. Skims hero, ignores chips, clicks **Projects** in nav. → Two case-study cards with real screenshots + stack chips. Picks InboxIntel (Gmail/OAuth catches his eye).
|
||||
3. Case study: reads TL;DR box, jumps via mini-TOC to **Architecture**. → Clean-architecture diagram: React SPA → ASP.NET API → PostgreSQL, background sync worker, Gmail API, token encryption. *(Diagram before prose — his reading order.)*
|
||||
4. Reads **Decisions & trade-offs** ("why a hosted worker instead of a queue", "why advisory-only AI"). → Judgement demonstrated, not claimed.
|
||||
5. Reads **Security & production notes** (encrypted refresh tokens, confirmed-flag destructive ops, integration tests asserting authz). → *This* is the interview trigger for a production-minded manager.
|
||||
6. Opens dev tools out of habit → semantic HTML, no framework soup, fast. Checks the footer "how this site works" link, smirks approvingly.
|
||||
7. Replies to recruiter: "Yes, bring him in — ask about the sync worker." **Exit quality: P4 (interviewer prep) journey pre-seeded.**
|
||||
|
||||
## J3 · CTO Anne (Norwegian) — application → forwarded to colleague (target: ≤ 5 min)
|
||||
1. Opens link from application e-mail. Browser prefers `nb-NO` → dismissible hint: "Denne siden finnes på norsk →". Clicks it. → `/no` with natively-written Bokmål. *(Effort signal received before any content.)*
|
||||
2. Hero: "Systemutvikler · åtte års erfaring fra britisk offentlig sektor · bosatt i Tønsberg." *(Public-sector frame + local residence = risk questions answered.)*
|
||||
3. Reads **Erfaring**: Warwickshire County Council role described in kommune-relatable terms (saksbehandlingssystemer, rapportering, drift).
|
||||
4. Reads **Om meg**: photo, plain-spoken 3 paragraphs, "norsk B1, i aktiv utvikling", interests. *(Understated tone; janteloven-compatible.)*
|
||||
5. Copies URL of `/no/prosjekter/jobtrack` into e-mail to her senior dev. *(Per-language, per-page URLs make forwarding work.)*
|
||||
**Exit quality: converted in Norwegian end-to-end; never saw English.**
|
||||
|
||||
## J4 · Engineer Priya — interview prep deep-read (target: 10+ min, accuracy critical)
|
||||
1. Arrives directly on `/projects/jobtrack` (link from Martin). Reads whole case study top-to-bottom.
|
||||
2. Mini-TOC lets her jump back to Architecture while reading Decisions. Screenshots are annotated so she can reference specific UI ("the Gmail import view").
|
||||
3. **What I'd improve next** gives her interview questions the candidate is *prepared for*.
|
||||
**Design requirement surfaced: every claim in case studies must be true and demoable.**
|
||||
|
||||
## J5 · Return visitor / language edge cases
|
||||
- **Language switch mid-page:** Anne's colleague (English-speaking) receives the `/no/...` link → header `EN` switch → lands on `/projects/jobtrack`, same scroll context (same page mapping, position preserved where feasible).
|
||||
- **Theme:** system default on first visit; manual toggle persists; no flash of wrong theme on load (Phase 2 must solve this; Phase 1 flags it as a perceived-quality requirement).
|
||||
- **404:** mistyped shared link → bilingual 404 with links to both homepages and projects index. Never a dead end for a recruiter.
|
||||
- **Slow connection (mobile, train to Oslo):** text renders first; screenshots lazy-load below fold with fixed aspect-ratio placeholders (no layout shift — see MOTION_GUIDELINES loading states).
|
||||
|
||||
---
|
||||
|
||||
## Journey-derived requirements checklist
|
||||
- [x] Locale-matched CV default with one-tap access anywhere (J1)
|
||||
- [x] Architecture diagram ≤ 2 clicks from entry (J2)
|
||||
- [x] Security/testing content in every case study (J2)
|
||||
- [x] Native Bokmål, public-sector framing (J3)
|
||||
- [x] Stable per-language deep links; switch maps page↔page (J3, J5)
|
||||
- [x] Honest, demoable case-study claims (J4)
|
||||
- [x] No auto-redirect on language; hint pattern only (J3, J5)
|
||||
- [x] Zero layout shift on image load (J5)
|
||||
@@ -0,0 +1,58 @@
|
||||
# COLOUR_SYSTEM.md
|
||||
|
||||
## 1. Palette philosophy
|
||||
|
||||
Near-monochrome surfaces + **one** accent ("aurora" teal-green — a nod to Norway without cliché flags/fjords) + **one** functional secondary (amber, status-only). The accent behaves like a **status LED**: it marks liveness, focus, and action — it is never decoration or large fills.
|
||||
|
||||
## 2. Core tokens
|
||||
|
||||
### Dark theme (design lead)
|
||||
| Token | Value | Contrast vs surface-0 | Use |
|
||||
|---|---|---|---|
|
||||
| `surface-0` | `#0B0E14` | — | Page bg (blue-black, not pure black) |
|
||||
| `surface-1` | `#11151D` | — | Cards |
|
||||
| `surface-2` | `#171C26` | — | Nested panels, TL;DR |
|
||||
| `ink` | `#E8ECF2` | 15.4:1 | Headings, body |
|
||||
| `ink-muted` | `#9AA4B2` | 7.1:1 | Secondary text, captions |
|
||||
| `ink-faint` | `#5C6675` | 3.4:1 | Disabled, decorative only (never body) |
|
||||
| `border` | `#E8ECF2` @ 8% | — | Hairlines |
|
||||
| `accent` | `#3ECFAE` | 9.8:1 | Links, primary buttons (dark text on it), status dots, trace motif, focus rings |
|
||||
| `accent-ink` | `#062A22` | 12:1 on accent | Text on accent fills |
|
||||
| `amber` | `#E5A93D` | 8.3:1 | `IN DEVELOPMENT` chips, form warnings only |
|
||||
| `danger` | `#E5606B` | 5.6:1 | Form errors only |
|
||||
|
||||
### Light theme
|
||||
| Token | Value | Contrast vs surface-0 | Use |
|
||||
|---|---|---|---|
|
||||
| `surface-0` | `#FAFAF8` | — | Warm paper white |
|
||||
| `surface-1` | `#FFFFFF` | — | Cards |
|
||||
| `surface-2` | `#F1F2EF` | — | Nested panels |
|
||||
| `ink` | `#171A20` | 15.9:1 | Text |
|
||||
| `ink-muted` | `#555E6B` | 6.8:1 | Secondary |
|
||||
| `border` | `#171A20` @ 10% | — | Hairlines |
|
||||
| `accent` | `#0E8C72` | 4.9:1 | Links, buttons (white text: 5.1:1), focus |
|
||||
| `amber` | `#8A6114` | 5.6:1 | Status text (paired w/ `#F5E5C2` chip bg) |
|
||||
| `danger` | `#B3323E` | 6.2:1 | Errors |
|
||||
|
||||
**Note:** the accent is *perceptually the same identity* in both themes but shifted per theme to hold WCAG AA (dark uses the luminous `#3ECFAE`; light uses the deepened `#0E8C72`). This is deliberate — never reuse the dark accent on white (2.1:1, fails).
|
||||
|
||||
## 3. Usage rules
|
||||
|
||||
1. **Accent budget:** ≤ 3 accent moments per viewport (e.g. hero: trace line + primary CTA + one chip dot). If a screen feels flat, fix hierarchy with type/spacing — not more accent.
|
||||
2. **Large fills:** accent may fill only buttons and chips; never section backgrounds or headings-as-blocks.
|
||||
3. **Text on accent:** always `accent-ink`/white per table; never accent-on-accent.
|
||||
4. **Status semantics (fixed):** accent dot = active/live · amber dot = in development · muted dot = archived. Consistent across chips, timeline, project cards.
|
||||
5. **Charts/diagrams:** surfaces + border + ink-muted, accent for the *one* flow being explained, amber for external systems (e.g. Gmail API node). Max 4 hues in any diagram.
|
||||
6. **Duotone photo treatment (dark):** shadows → `#0B0E14`, highlights → warm neutral; accent never tints skin.
|
||||
7. **Never colour-only meaning:** every status pairs dot + text label; errors pair colour + icon + message.
|
||||
|
||||
## 4. Gradients
|
||||
|
||||
Exactly one permitted gradient: hero background halo — radial, accent @ 6% → transparent, anchored behind the trace motif. Everything else flat. (The Stripe lesson: one identity moment, not a gradient system.)
|
||||
|
||||
## 5. Accessibility audit summary
|
||||
|
||||
- All body/secondary text ≥ 4.5:1 in both themes (see tables).
|
||||
- UI components/borders on interactive elements ≥ 3:1 against adjacent colours.
|
||||
- Focus ring `accent` at 2px passes 3:1 against both surface-0 values.
|
||||
- `ink-faint` is documented decorative-only. High-contrast/forced-colors mode: dot-grid and halo disabled, hairlines → CanvasText.
|
||||
@@ -0,0 +1,80 @@
|
||||
# DESIGN_SYSTEM.md
|
||||
Design direction: **"Kontrollrom"** (control room) — a quiet, Nordic-restrained engineering aesthetic.
|
||||
|
||||
---
|
||||
|
||||
## 1. Concept
|
||||
|
||||
The site should feel like **well-run production infrastructure given a public face**: calm surfaces, precise alignment, information-dense but never busy, one living accent colour that behaves like a status light. Not a marketing site pretending to be technical; a technical artefact polished until it feels premium.
|
||||
|
||||
Three ideas fused, none copied:
|
||||
1. **Nordic restraint** — generous whitespace, muted surfaces, understatement (matches the Norwegian market and the candidate's voice).
|
||||
2. **Console/ops vernacular as *seasoning*** — monospace metadata labels, status chips, subtle grid/blueprint texture. Used at ~10% intensity: labels, captions, chips — never full terminal cosplay, no fake CLIs, no green-on-black.
|
||||
3. **SaaS product craft** — case studies presented like product launch pages (framed screenshots, TL;DR boxes, proof strips).
|
||||
|
||||
**Litmus test for every design decision:** *"Would this look at home in the admin console of a product you trust?"* If it's decorative noise — cut it.
|
||||
|
||||
## 2. Identity elements
|
||||
|
||||
- **Monogram:** `CB` set in the mono face inside a rounded square, with a 2px accent tick in the corner — reads as both initials and a status LED. Used as favicon, header mark, OG-image anchor.
|
||||
- **Signature motif — "the trace":** a thin (1.5px) accent line that draws horizontally then steps (like an oscilloscope/PCB trace). Appears once in the hero (animated draw on load), as section-divider punctuation, and in the 404. This is the site's single ownable visual move.
|
||||
- **Status chips:** rounded-full, mono, 12px, used for facts (location, permit, availability) and project status (`ACTIVE`, `IN DEVELOPMENT`). The chip *is* the brand's information atom.
|
||||
- **Photography:** the outdoor headshot, duotone-treated in dark mode (slight cool grade) and natural in light mode; always in a hairline-bordered rounded frame with a mono caption (`tønsberg, norway · 2025`).
|
||||
|
||||
## 3. Surfaces & elevation
|
||||
|
||||
Flat-first. Depth via **hairline borders (1px, low-alpha)** and background steps, not drop shadows.
|
||||
|
||||
| Token | Dark | Light | Use |
|
||||
|---|---|---|---|
|
||||
| `surface-0` | #0B0E14 | #FAFAF8 | Page background |
|
||||
| `surface-1` | #11151D | #FFFFFF | Cards, panels |
|
||||
| `surface-2` | #171C26 | #F1F2EF | Nested/hover panels, code-ish blocks |
|
||||
| `border` | #FFFFFF @ 8% | #16181D @ 10% | All hairlines |
|
||||
| `border-strong` | #FFFFFF @ 16% | #16181D @ 20% | Focused/hover cards |
|
||||
|
||||
Shadows: only two, both subtle, only in light mode + overlays (`shadow-card`, `shadow-overlay`). Dark mode uses border-strong + surface step instead.
|
||||
Radius scale: `4px` (chips inner) · `8px` (buttons, inputs) · `12px` (cards) · `16px` (screenshot frames) · `full` (chips).
|
||||
|
||||
## 4. Grid & layout
|
||||
|
||||
- Max content width **1200px**; case-study prose measure capped at **72ch**.
|
||||
- 12-column grid, 24px gutters desktop; 4-column, 16px gutters mobile.
|
||||
- Left-rail section labels on desktop (mono, vertical rhythm anchor): `01 — ABOUT`, `02 — SKILLS`… echoing the current site's numbering, done properly.
|
||||
- Breakpoints: 480 / 768 / 1024 / 1280 / 1536.
|
||||
- Background texture: an extremely faint dot-grid (border-colour dots, 24px pitch) on `surface-0` hero + section headers only — the "blueprint" whisper. Disabled in forced-colors/high-contrast.
|
||||
|
||||
## 5. Core components (design-level inventory)
|
||||
|
||||
| Component | States to design | Notes |
|
||||
|---|---|---|
|
||||
| Header / nav | default, scrolled (compressed + blur), mobile overlay | see mockups M7 |
|
||||
| Split CV button | default, hover, open (menu shows both languages + file size) | locale-aware default |
|
||||
| Language switch | EN active, NO active, hover, focus, keyboard | segmented control, see M11 |
|
||||
| Theme toggle | dark, light, hover (icon morph sun⇄moon) | respects system on first load |
|
||||
| Status chip | fact, status-active (accent dot pulse ≤ 2 loops), status-dev (amber dot) | |
|
||||
| Skill group card | default, hover (border-strong) | chips inside |
|
||||
| Project card | default, hover (screenshot parallax-shift 4px + border glow), focus | whole card clickable, single tab stop |
|
||||
| Timeline item | featured (expanded), compact (one-liner), hover | |
|
||||
| TL;DR box | — | surface-2, accent left edge (3px) |
|
||||
| Architecture diagram frame | static SVG, nodes highlight on hover (desktop only) | must read perfectly with zero interaction |
|
||||
| Screenshot frame | loading (skeleton), loaded, lightbox open | browser-chrome mock, aspect-locked |
|
||||
| Form field | default, focus, filled, error, success | error text + icon, never colour-only |
|
||||
| Buttons | primary (accent), secondary (outline), ghost | see MICRO_INTERACTIONS |
|
||||
| Footer | — | dense, mono-flavoured, sitemap + channels |
|
||||
| 404 | — | trace motif breaks/frays; bilingual links home |
|
||||
|
||||
## 6. Iconography & illustration
|
||||
|
||||
- Icons: single stroke-icon set, 1.5px stroke, 20px grid (Lucide-class aesthetic — final set is a Phase 2 choice). Never filled+outlined mixed.
|
||||
- No stock illustration, no 3D blobs, no particles. The only "illustrations" are: the trace motif, architecture diagrams (drawn in-system: surface-2 nodes, border hairlines, accent flow arrows, mono labels), and framed product screenshots.
|
||||
|
||||
## 7. Theming rules
|
||||
|
||||
- Dark is the **design-lead theme** (P2 audience default; screenshots of both apps present well on dark), light must be equally finished — P1/P3 recruiters often browse light.
|
||||
- Every token has a pair; no theme-conditional layouts. Accent identical in both (passes contrast both ways — see COLOUR_SYSTEM).
|
||||
- First paint must match user preference without flash (implementation constraint flagged for Phase 2).
|
||||
|
||||
## 8. What this system deliberately avoids
|
||||
|
||||
Percentage skill bars · testimonial carousels · particle heroes · scroll-hijacking · fake terminals · gradient-everything · cookie-consent theatre (no tracking → no banner; a privacy footnote instead — itself a trust signal).
|
||||
@@ -0,0 +1,50 @@
|
||||
# MOTION_GUIDELINES.md
|
||||
|
||||
## 1. Motion philosophy
|
||||
|
||||
**"Motion confirms; it never performs."** Every animation must answer a user question — *did my click register? what changed? where did this come from?* — or it doesn't ship. One exception: the hero trace draw, the site's single identity moment.
|
||||
|
||||
This is the strongest anti-junior signal available: restraint executed perfectly.
|
||||
|
||||
## 2. Tokens
|
||||
|
||||
| Token | Duration | Easing | Use |
|
||||
|---|---|---|---|
|
||||
| `instant` | 80ms | linear | Colour/opacity on hover |
|
||||
| `quick` | 160ms | ease-out (0.2, 0, 0, 1) | Buttons, chips, toggles, focus |
|
||||
| `standard` | 240ms | ease-out | Card hover lift, menu open, theme cross-fade |
|
||||
| `entrance` | 400ms | ease-out, slight overshoot-free | Section reveals (opacity + 12px rise) |
|
||||
| `signature` | 700ms | ease-in-out | Hero trace draw only, once per page load |
|
||||
|
||||
Rules: nothing exceeds 700ms; entrances stagger max 3 items × 60ms; **only `transform` and `opacity` animate** (performance budget — no layout/box-shadow/filter animation); parallax capped at 4px (project-card screenshots).
|
||||
|
||||
## 3. Reduced motion (first-class variant, not an off-switch)
|
||||
|
||||
`prefers-reduced-motion: reduce` →
|
||||
- Entrances become pure opacity fades (120ms), no translation.
|
||||
- Trace motif renders complete, static.
|
||||
- Theme switch: instant swap.
|
||||
- Status-dot pulse: static dot.
|
||||
- Scroll behaviour: instant anchor jumps, no smooth scroll.
|
||||
Nothing is *removed* — content parity is exact.
|
||||
|
||||
## 4. Loading states
|
||||
|
||||
| Context | Design |
|
||||
|---|---|
|
||||
| Initial page | No spinner, no splash: text renders immediately (fonts swap-safe), hero trace draws as the "loaded" confirmation. Perceived performance *is* the loading design. |
|
||||
| Screenshots / gallery | Aspect-ratio-locked skeleton: `surface-2` block + faint dot-grid + mono caption already visible (`loading capture…` / `laster skjermbilde…`); cross-fade 240ms on load. Zero layout shift, ever. |
|
||||
| Form submit | Button label → inline three-dot mono ellipsis animation (·, ··, ···), button stays same width; success swaps band to confirmation panel (see MICRO_INTERACTIONS §form). |
|
||||
| Language switch | Content cross-fade 160ms; header/footer chrome does not blink (only text nodes swap). |
|
||||
| Lightbox open | Backdrop fade 240ms + image scale 0.98→1. |
|
||||
|
||||
## 5. Page & theme transitions
|
||||
|
||||
- Route changes: outgoing fade 120ms → incoming rise-fade 240ms, header persistent. No full-screen wipes.
|
||||
- Theme toggle: 240ms token cross-fade; the toggle icon morphs (sun⇄moon path) in `quick`. Screenshots swap themed variants where available (JobTrack/InboxIntel both have dark UIs — capture both if the apps support it, else keep one honest capture).
|
||||
|
||||
## 6. Scroll-linked motion (summary — full spec in SCROLL_EXPERIENCE.md)
|
||||
|
||||
- Section reveals trigger at 20% viewport entry, once (no re-trigger on scroll-up).
|
||||
- Header compresses 72→56px between 0–120px scroll, linear-mapped.
|
||||
- No scroll-jacking, no pinned scenes, no horizontal hijack — recruiters skim; the scrollbar must behave like a document's.
|
||||
@@ -0,0 +1,28 @@
|
||||
# SPACING_SYSTEM.md
|
||||
|
||||
## 1. Scale (8pt base, 4pt half-step)
|
||||
|
||||
| Token | px | Use |
|
||||
|---|---|---|
|
||||
| `space-1` | 4 | Chip inner gaps, icon-to-label |
|
||||
| `space-2` | 8 | Inside chips/buttons vertical, tight stacks |
|
||||
| `space-3` | 12 | Button horizontal padding unit, list gaps |
|
||||
| `space-4` | 16 | Card inner padding (mobile), paragraph gaps |
|
||||
| `space-5` | 24 | Card inner padding (desktop), grid gutters |
|
||||
| `space-6` | 32 | Between related blocks |
|
||||
| `space-7` | 48 | Between component groups |
|
||||
| `space-8` | 64 | Section inner top/bottom (mobile) |
|
||||
| `space-9` | 96 | Section rhythm (desktop) |
|
||||
| `space-10` | 128 | Hero breathing room, page top/bottom |
|
||||
|
||||
Only these ten values exist. No 20px, no 30px, no eyeballing.
|
||||
|
||||
## 2. Rules
|
||||
|
||||
1. **Section rhythm:** homepage sections separated by `space-9` desktop / `space-8` mobile; the mono section label sits `space-6` above its H2.
|
||||
2. **Proximity encodes relationship:** gap within a skill group (`space-2`) < gap between groups (`space-5`) < gap between sections (`space-9`). If two things feel related but aren't, increase the gap before adding a divider; hairlines are the *second* resort.
|
||||
3. **Cards:** `space-5` padding desktop, `space-4` mobile; media flush to card edge (screenshots bleed to the frame), text content padded.
|
||||
4. **Touch targets:** min 44×44px tap area on mobile (chips visually smaller but hit-area padded); nav/footer link spacing ≥ `space-3` vertically on mobile.
|
||||
5. **Header:** 72px tall default, compresses to 56px on scroll; content top-padding accounts for it (anchor scroll offset = header height + `space-5`).
|
||||
6. **Prose:** paragraph gap `space-4`; heading top `space-7`/bottom `space-3` (the 2:1 before/after rule from TYPOGRAPHY_GUIDE).
|
||||
7. **Density gradient:** marketing-calm at top of pages → denser in technical sections (case-study decision lists, footer). Density shift is achieved by stepping down *one* spacing token, never by shrinking type below `small`.
|
||||
@@ -0,0 +1,43 @@
|
||||
# TYPOGRAPHY_GUIDE.md
|
||||
|
||||
## 1. Type roles (three voices, strict casting)
|
||||
|
||||
| Role | Direction | Character | Used for |
|
||||
|---|---|---|---|
|
||||
| **Display / headings** | Modern grotesque with slight technical edge — *Space Grotesk-class* (alternatives in that register: General Sans, Hanken Grotesk) | Confident, engineered, not cold | H1–H3, nav, buttons, stat numbers |
|
||||
| **Body** | Highly-legible humanist/neo-grotesque — *Inter-class* (alternatives: Source Sans 3, Figtree) | Invisible, effortless at 16–18px | Paragraphs, lists, form fields |
|
||||
| **Mono (identity voice)** | *JetBrains Mono-class* (alternatives: IBM Plex Mono, Commit Mono) | The "console seasoning" | Section labels, chips, captions, metadata, diagram labels, footer |
|
||||
|
||||
Exact typeface licensing/loading = Phase 2. Design intent: **two families max loaded + mono**, variable weights preferred, `ø æ å` coverage mandatory in all three (Norwegian body text is first-class, not fallback).
|
||||
|
||||
## 2. Scale (1.25 major-third, 16px base)
|
||||
|
||||
| Token | Size / line-height | Weight | Tracking | Use |
|
||||
|---|---|---|---|---|
|
||||
| `display` | 61/1.05 (desktop) · 40/1.1 (mobile) | 600 | −0.02em | Hero name only |
|
||||
| `h1` | 49/1.1 · 34/1.15 | 600 | −0.015em | Page titles |
|
||||
| `h2` | 39/1.15 · 28/1.2 | 600 | −0.01em | Section titles |
|
||||
| `h3` | 31/1.2 · 24/1.25 | 550 | 0 | Card titles, case-study subsections |
|
||||
| `h4` | 20/1.35 | 550 | 0 | Minor headings |
|
||||
| `body-lg` | 18/1.6 | 400 | 0 | Hero summary, TL;DR, lead paragraphs |
|
||||
| `body` | 16/1.65 | 400 | 0 | Default prose |
|
||||
| `small` | 14/1.5 | 400 | 0 | Secondary info, form hints |
|
||||
| `mono-label` | 12/1.4 | 500 | +0.08em, UPPERCASE | Section labels (`01 — PROSJEKTER`), chips |
|
||||
| `mono-meta` | 13/1.5 | 400 | 0 | Captions, dates, file sizes |
|
||||
|
||||
**Rule: uppercase + letter-spacing lives only in `mono-label`.** (The old site letter-spaced everything; the CV letter-spaces headings into A T S soup. Reserve the effect and it becomes identity instead of noise.)
|
||||
|
||||
## 3. Rhythm & measure
|
||||
|
||||
- Prose measure 60–72ch; hero summary ≤ 55ch.
|
||||
- Space *before* a heading ≈ 2× space after (sections cohere downward).
|
||||
- Baseline rhythm on the 8pt grid — line-heights above are chosen to land on it at 16px base.
|
||||
- Norwegian headline audit: NO strings run ~10–15% longer and compounds don't hyphenate freely (`systemadministrasjon`); every headline layout must be tested with the NO string at the mobile breakpoint. Wrapping rule: allow hyphenation in body NO text, never in headings — reword instead.
|
||||
|
||||
## 4. Typographic details (the seniority tells)
|
||||
|
||||
- Real dashes (– —), curly quotes per language convention (EN “ ” · NO « »), non-breaking spaces before units and inside `8+ yrs`.
|
||||
- Numbers in stats use tabular lining figures (mono or `tnum`).
|
||||
- Links in prose: accent-coloured underline (2px offset), full colour+underline — never colour-only (a11y).
|
||||
- Code/tech names inline (C#, .NET) stay in body face — mono inline is reserved for actual paths/commands in case studies.
|
||||
- Focus states use a 2px accent outline + 2px offset, same in both themes.
|
||||
@@ -0,0 +1,50 @@
|
||||
# ANIMATION_CONCEPTS.md
|
||||
Named animation concepts, each mapped to the question it answers. All obey MOTION_GUIDELINES tokens & reduced-motion variants.
|
||||
|
||||
---
|
||||
|
||||
## A1 · "Trace draw" — hero identity moment
|
||||
On first paint: a 1.5px accent line draws left→right under the role line (700ms `signature`), steps down 8px mid-way (PCB-trace corner), and terminates in a 4px status dot that blinks once, then stays lit next to `Open to work`.
|
||||
*Question answered:* "the page is ready — and he's available."
|
||||
Reduced motion: line + lit dot render complete, static. Repeat visits: draws once per session only.
|
||||
|
||||
## A2 · "Section arrive"
|
||||
Each homepage section reveals on 20% viewport entry: opacity 0→1 + 12px rise (400ms), heading first, content children staggered 60ms (max 3 groups). Fires once per page load.
|
||||
*Question:* "what's new in view?" — creates reading rhythm without withholding content (text is present for search/reader modes regardless).
|
||||
|
||||
## A3 · "Card focus"
|
||||
Project cards on hover/focus-visible: border → `border-strong`, screenshot shifts up 4px (240ms), title gains accent underline draw (160ms), `Read case study →` arrow nudges 4px right.
|
||||
*Question:* "is this clickable, and what happens?"
|
||||
|
||||
## A4 · "Chip pulse"
|
||||
The `ACTIVE` status dot pulses twice (scale 1→1.4 opacity fade) when its chip first enters viewport, then rests lit. Amber `IN DEVELOPMENT` dots never pulse (calm hierarchy: live > in-progress).
|
||||
*Question:* "which of these is running right now?"
|
||||
|
||||
## A5 · "Diagram flow" (case-study architecture, desktop only)
|
||||
Architecture diagrams are static SVGs; hovering a node highlights its connected edges in accent (160ms) and dims unrelated nodes to 60%. No autoplay, no looping data-packet animations.
|
||||
*Question:* "what talks to what?"
|
||||
Mobile/touch + reduced motion: full static diagram, always fully legible without interaction.
|
||||
|
||||
## A6 · "Theme morph"
|
||||
Sun⇄moon icon path-morph (160ms) + global 240ms token cross-fade. The hero trace re-tints without redrawing.
|
||||
|
||||
## A7 · "Language cross-fade"
|
||||
On EN⇄NO switch: text nodes fade out 80ms / in 160ms; layout containers keep dimensions during swap (pre-measured against longer NO strings) so nothing jumps. The switch control itself slides its active-segment thumb (160ms).
|
||||
*Question:* "same page, other language — nothing lost."
|
||||
|
||||
## A8 · "Timeline grow"
|
||||
Experience timeline's vertical hairline draws downward as the section enters (400ms); period dots fade in with their rows.
|
||||
*Question:* "read this top-down, newest first."
|
||||
|
||||
## A9 · "Form confirm"
|
||||
On successful submit, the form panel cross-fades to a confirmation card: accent check icon draws (240ms stroke), message + "I usually reply within a day" (`Jeg svarer vanligvis innen en dag`).
|
||||
*Question:* "did it actually send?" — the current site's broken-looking send feedback (raw � glyphs) is exactly what this replaces.
|
||||
|
||||
## A10 · "404 fray"
|
||||
The trace motif runs, stutters, and frays into dots at the break point; mono caption `route not found — no such page` / `fant ikke siden`. Static in reduced motion.
|
||||
*Purpose:* the one place allowed personality-forward motion — errors are where craft shows.
|
||||
|
||||
---
|
||||
|
||||
### Explicitly rejected concepts (documented so Phase 3 doesn't re-invent them)
|
||||
Typewriter hero text (junior tell) · particle backgrounds · 3D tilt cards · scroll-pinned storytelling · animated skill counters · magnetic buttons (see CURSOR_INTERACTIONS) · autoplaying screenshot carousels.
|
||||
@@ -0,0 +1,25 @@
|
||||
# CURSOR_INTERACTIONS.md
|
||||
|
||||
## Verdict: near-zero custom cursor work — by design
|
||||
|
||||
Custom cursors (dot followers, magnetic buttons, cursor-morphing) were evaluated and **rejected** for this site. Reasons, documented so the decision survives future redesign itch:
|
||||
|
||||
1. **Audience mismatch.** P1 recruiters on trackpads/mobile never see it; P2 engineering managers disproportionately *dislike* cursor gimmicks (input latency, hijacked expectations).
|
||||
2. **Brand mismatch.** "Kontrollrom" promises precision and restraint; a floating blob promises an agency reel.
|
||||
3. **Cost.** Pointer-following JS is the classic source of main-thread jank — the exact failure mode this site must never exhibit.
|
||||
|
||||
## What ships instead (native cursor semantics, used *correctly*)
|
||||
|
||||
| Context | Cursor | Note |
|
||||
|---|---|---|
|
||||
| Links, buttons, cards, TOC | `pointer` | Whole project card = pointer (it's one link) |
|
||||
| Fact chips (non-interactive) | `default` | Honest affordance — no fake clickability |
|
||||
| Text/prose | `text` | Never suppressed |
|
||||
| Screenshot in lightbox (zoomable) | `zoom-in` / `zoom-out` | The one "delight" allowed |
|
||||
| Click-to-copy e-mail | `copy` | Native, semantically perfect, zero JS cost |
|
||||
| Diagram nodes with hover-highlight | `default` + visual highlight | Hover is enhancement, not interaction — pointer would over-promise |
|
||||
| Disabled controls | `not-allowed` | |
|
||||
|
||||
## The single permitted flourish
|
||||
|
||||
On the hero, the status dot at the end of the trace shows a `crosshair`-style precision hover halo (pure CSS, 160ms) revealing a mono tooltip: `status: open to work` / `status: åpen for muligheter`. Desktop-only easter egg; invisible to touch, absent under reduced motion, no positional JS. If it ever measures as jank — cut it without debate.
|
||||
@@ -0,0 +1,54 @@
|
||||
# MICRO_INTERACTIONS.md
|
||||
Component-level interaction specs. Every interactive element defines: rest / hover / active / focus-visible / disabled, keyboard behaviour, and touch behaviour.
|
||||
|
||||
---
|
||||
|
||||
## Buttons
|
||||
- **Primary (accent fill):** hover → brightness +6% and 1px rise (transform); active → rise removed (press); focus-visible → 2px accent ring, 2px offset; disabled → surface-2 + ink-faint, no events.
|
||||
- **Secondary (outline):** hover → border-strong + surface-1 fill.
|
||||
- **Ghost (nav, footer):** hover → ink (from ink-muted) + accent underline draw.
|
||||
- Touch: no hover states persist; active state on touchstart (≤80ms feedback).
|
||||
|
||||
## Split CV button (header)
|
||||
- Primary zone downloads locale CV; label: `Download CV` / `Last ned CV`, mono-meta suffix shows size `· pdf 180 kB`.
|
||||
- Chevron zone (separate 44px hit area) opens a 2-item menu: `English (pdf)` / `Norsk (pdf)` with a subtle flag-free language tag (`EN`/`NO` mono chips — no flag icons anywhere; flags conflate language and nationality).
|
||||
- Keyboard: button reachable in tab order; chevron = separate stop; menu arrows + Esc. Download triggers a one-time toast-less confirmation: button icon swaps to check for 1.5s.
|
||||
|
||||
## Language switch (header, segmented control)
|
||||
- Two segments `EN | NO`, active segment has surface-1 thumb + ink text; inactive = ink-muted. Hover inactive → ink. Thumb slides 160ms on change (A7).
|
||||
- Keyboard: single tab stop, arrow keys toggle, Enter/Space activates. `aria` as a proper toggle group; screen reader announces "Norsk — bytt språk / English — switch language" (label in *target* language, the convention that works when you can't read the current one).
|
||||
- Mobile: inside menu overlay header, full-width segmented control, same behaviour.
|
||||
- Never a dropdown (two options), never flags, never auto-switching.
|
||||
|
||||
## Theme toggle
|
||||
- Icon-only button, morphing sun⇄moon; tooltip on hover-delay 600ms (`Switch to light mode` / `Bytt til lyst modus` — target-state phrasing).
|
||||
- First visit follows system; once touched, choice persists and system changes no longer override (standard expectation).
|
||||
|
||||
## Nav links
|
||||
- Rest: ink-muted → hover: ink + 2px accent underline drawing left→right (160ms). Current-page: ink + static accent underline + `aria-current`.
|
||||
- Scroll-spy on homepage: section currently in view marks its nav item (underline at 60% opacity) — subtle, no jumping.
|
||||
|
||||
## Chips
|
||||
- Fact chips: non-interactive, no hover (cursor default — honesty about affordance).
|
||||
- Stack chips on project cards: hover shows a 600ms-delay tooltip with one context line ("React 18 — SPA frontend"); tap on mobile: no-op (tooltip content lives in case-study text instead).
|
||||
|
||||
## Project cards
|
||||
- Whole card = one link (single tab stop); internal "Read case study →" is decorative affordance, not a second link. Hover per A3. Focus-visible: ring around entire card.
|
||||
- Screenshot inside frames: `alt` text written per language, meaningful ("JobTrack applications table with status filters", not "screenshot").
|
||||
|
||||
## Timeline items
|
||||
- Featured item expanded by default. Compact one-liners: entire row hover → surface-1 tint; they are *not* expandable (content lives in CV/experience page — avoid hidden-content traps for scanners).
|
||||
|
||||
## Form (contact)
|
||||
- Fields: 2px border; focus → accent border + ring; label always visible above (no placeholder-as-label). Error on blur-validate: danger border + icon + message under field (`Please enter a valid e-mail` / `Skriv inn en gyldig e-postadresse`); error summary focus-moved on failed submit.
|
||||
- Submit per A9. Honey-pot antispam invisible to AT (no CAPTCHA — friction kills recruiter conversion).
|
||||
- Beside the form, equal visual weight: direct e-mail (click-to-copy with `copied ✓ / kopiert ✓` mono feedback) and LinkedIn — many recruiters won't touch forms.
|
||||
|
||||
## Screenshot lightbox
|
||||
- Click/Enter opens; Esc, backdrop-click, or × closes; arrows navigate gallery; focus trapped; caption + `n/N` mono counter. Mobile: pinch-zoom enabled, swipe between captures.
|
||||
|
||||
## "On this page" mini-TOC (case studies, ≥1200px)
|
||||
- Items = ghost links with scroll-spy accent tick; click = smooth scroll (instant under reduced motion) with header-offset anchor.
|
||||
|
||||
## Copy affordances
|
||||
- E-mail and `git.cesnimda.uk` in footer/contact: click-to-copy with mono `copied ✓` swap (1.5s). Phone number: plain `tel:` link, no copy gimmick.
|
||||
@@ -0,0 +1,45 @@
|
||||
# SCROLL_EXPERIENCE.md
|
||||
|
||||
## 1. Governing rule
|
||||
|
||||
**The scrollbar is a contract.** Native scroll physics everywhere: no hijacking, no pinned scenes, no snap-scrolling on prose, no horizontal sections. Recruiters skim with momentum-flicks; anything that interferes reads as hostile.
|
||||
|
||||
## 2. Homepage scroll narrative (desktop)
|
||||
|
||||
| Scroll position | What happens | Why |
|
||||
|---|---|---|
|
||||
| 0 (load) | Hero static except trace draw (A1) | Decision facts readable instantly |
|
||||
| 0–120px | Header compresses 72→56px, gains blur backdrop + hairline bottom border (linear-mapped to scroll, not toggled — no pop) | Reclaims space, signals "we're moving" |
|
||||
| Each section at 20% entry | A2 section arrive, once | Reading rhythm |
|
||||
| Proof strip enter | Chip dots light in stagger (accent pulse A4) | Draws eye across the four facts |
|
||||
| Projects enter | Cards rise in 2-stagger; screenshots have 4px max parallax offset relative to card scroll | Depth without gimmick |
|
||||
| Experience enter | Timeline draw (A8) | Guides top-down reading |
|
||||
| Contact band enter | No animation — band is high-contrast already | Calm before conversion action |
|
||||
| Any position | Header CV button + nav always available | ≤1 interaction to CV rule |
|
||||
|
||||
Scroll-spy updates nav underline per section. No "back to top" button on homepage (header is sticky; document is ~5 viewports); case studies (longer) get one after 3 viewports, ghost-style, bottom-right.
|
||||
|
||||
## 3. Case-study scroll
|
||||
|
||||
- Mini-TOC (≥1200px) tracks reading position with an accent tick; no progress bars (this is a document, not an article funnel — but see next point).
|
||||
- Long-form sections use generous `space-9` rhythm; architecture diagram gets full-width breakout (max 1200px) beyond the 72ch prose measure.
|
||||
- Images lazy-load 1 viewport ahead with aspect-locked skeletons — **zero cumulative layout shift** is a hard requirement.
|
||||
- Section anchors offset by header height + 24px; anchor links in TL;DR jump correctly.
|
||||
|
||||
## 4. Mobile scroll
|
||||
|
||||
- No parallax, no scroll-linked header compression (fixed 56px slim header from the start); section reveals still fire (opacity-dominant).
|
||||
- Sticky elements budget: header only. No sticky CTAs covering content (thumb-reach conversion handled by the contact band + header CV icon-button).
|
||||
- Overscroll behaviour default; PWA-style rubber-banding untouched.
|
||||
- Gallery swipe is the only horizontal gesture, clearly contained inside the lightbox.
|
||||
|
||||
## 5. Deep-link & restoration behaviour
|
||||
|
||||
- Anchor navigation (nav click, TOC, TL;DR links): smooth scroll 400ms max — distance-capped so cross-page-length jumps don't take seconds; reduced motion → instant.
|
||||
- Back/forward restores scroll position (browser default — do not fight it).
|
||||
- Language switch preserves scroll ratio where section structure matches (A7); if the target page differs structurally, land at top of the equivalent section rather than a mismatched pixel offset.
|
||||
|
||||
## 6. Performance constraints on scroll effects
|
||||
|
||||
- All scroll-linked effects (header compression, spy, reveals) via passive observers/transforms — no scroll-handler layout reads (jank = instant credibility loss with P2).
|
||||
- Reveal thresholds via IntersectionObserver semantics (conceptually); everything degrades to "visible" when JS is absent — **content is never gated behind scroll animation.**
|
||||
@@ -0,0 +1,148 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 1420" font-family="Segoe UI, Arial, sans-serif">
|
||||
<defs>
|
||||
<radialGradient id="halo" cx="30%" cy="18%" r="55%">
|
||||
<stop offset="0%" stop-color="#3ECFAE" stop-opacity="0.07"/>
|
||||
<stop offset="100%" stop-color="#3ECFAE" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<pattern id="dots" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<circle cx="1" cy="1" r="1" fill="#E8ECF2" opacity="0.05"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<!-- page -->
|
||||
<rect width="1440" height="1420" fill="#0B0E14"/>
|
||||
<rect width="1440" height="640" fill="url(#dots)"/>
|
||||
<rect width="1440" height="640" fill="url(#halo)"/>
|
||||
|
||||
<!-- ===== HEADER 72px ===== -->
|
||||
<line x1="0" y1="72" x2="1440" y2="72" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<!-- monogram -->
|
||||
<rect x="120" y="20" width="32" height="32" rx="8" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="136" y="41" fill="#E8ECF2" font-family="Consolas, monospace" font-size="13" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<circle cx="148" cy="24" r="2" fill="#3ECFAE"/>
|
||||
<!-- nav -->
|
||||
<text x="420" y="41" fill="#9AA4B2" font-size="14">Projects</text>
|
||||
<text x="510" y="41" fill="#9AA4B2" font-size="14">Experience</text>
|
||||
<text x="620" y="41" fill="#9AA4B2" font-size="14">About</text>
|
||||
<text x="695" y="41" fill="#9AA4B2" font-size="14">Contact</text>
|
||||
<!-- language segmented -->
|
||||
<rect x="1000" y="22" width="88" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="1003" y="25" width="41" height="22" rx="11" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="1023" y="40" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="1066" y="40" fill="#9AA4B2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
<!-- theme toggle -->
|
||||
<circle cx="1122" cy="36" r="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<path d="M1126 30 a7 7 0 1 0 3 9 a6 6 0 0 1 -3 -9" fill="#9AA4B2"/>
|
||||
<!-- CV split button -->
|
||||
<rect x="1156" y="20" width="164" height="32" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1216" y="41" fill="#062A22" font-size="13" font-weight="600" text-anchor="middle">Download CV</text>
|
||||
<line x1="1290" y1="26" x2="1290" y2="46" stroke="#062A22" stroke-opacity="0.3"/>
|
||||
<path d="M1300 33 l6 6 l6 -6" fill="none" stroke="#062A22" stroke-width="1.8"/>
|
||||
|
||||
<!-- ===== HERO ===== -->
|
||||
<text x="120" y="196" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">SYSTEMS DEVELOPER · TØNSBERG, NORWAY</text>
|
||||
<text x="120" y="262" fill="#E8ECF2" font-size="61" font-weight="600" letter-spacing="-1">Connor Babbington</text>
|
||||
<text x="120" y="316" fill="#E8ECF2" font-size="26" font-weight="500">I design, build and run web systems.</text>
|
||||
<text x="120" y="356" fill="#9AA4B2" font-size="18">Eight-plus years delivering internal software for UK local government —</text>
|
||||
<text x="120" y="384" fill="#9AA4B2" font-size="18">now building full-stack products in Norway.</text>
|
||||
|
||||
<!-- trace motif -->
|
||||
<path d="M120 424 H360 l12 12 H560" fill="none" stroke="#3ECFAE" stroke-width="1.5"/>
|
||||
<circle cx="566" cy="436" r="4" fill="#3ECFAE"/>
|
||||
<text x="582" y="441" fill="#9AA4B2" font-family="Consolas, monospace" font-size="12">status: open to work</text>
|
||||
|
||||
<!-- chips -->
|
||||
<g font-family="Consolas, monospace" font-size="12">
|
||||
<rect x="120" y="466" width="150" height="30" rx="15" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="195" y="485" fill="#9AA4B2" text-anchor="middle">Tønsberg, Norway</text>
|
||||
<rect x="282" y="466" width="128" height="30" rx="15" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="346" y="485" fill="#9AA4B2" text-anchor="middle">Work permit ✓</text>
|
||||
<rect x="422" y="466" width="200" height="30" rx="15" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="522" y="485" fill="#9AA4B2" text-anchor="middle">Remote / hybrid / on-site</text>
|
||||
<rect x="634" y="466" width="212" height="30" rx="15" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="740" y="485" fill="#9AA4B2" text-anchor="middle">English native · Norsk B1</text>
|
||||
</g>
|
||||
|
||||
<!-- CTAs -->
|
||||
<rect x="120" y="530" width="156" height="44" rx="8" fill="#3ECFAE"/>
|
||||
<text x="198" y="557" fill="#062A22" font-size="15" font-weight="600" text-anchor="middle">View projects</text>
|
||||
<rect x="292" y="530" width="196" height="44" rx="8" fill="none" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="390" y="557" fill="#E8ECF2" font-size="15" text-anchor="middle">Download CV · pdf</text>
|
||||
|
||||
<!-- portrait -->
|
||||
<rect x="1020" y="150" width="300" height="375" rx="16" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<circle cx="1170" cy="290" r="72" fill="#242B38"/>
|
||||
<path d="M1080 525 q90 -110 180 0" fill="#242B38"/>
|
||||
<text x="1170" y="300" fill="#5C6675" font-size="13" text-anchor="middle">[ outdoor headshot ]</text>
|
||||
<text x="1020" y="552" fill="#5C6675" font-family="Consolas, monospace" font-size="12">tønsberg, norway · 2025</text>
|
||||
|
||||
<!-- ===== PROOF STRIP ===== -->
|
||||
<g>
|
||||
<rect x="120" y="620" width="285" height="96" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="144" y="662" fill="#E8ECF2" font-size="28" font-weight="600">8+ yrs</text>
|
||||
<text x="144" y="692" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">EXPERIENCE</text>
|
||||
<rect x="425" y="620" width="285" height="96" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="449" y="662" fill="#E8ECF2" font-size="22" font-weight="600">UK public sector</text>
|
||||
<text x="449" y="692" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">BACKGROUND</text>
|
||||
<rect x="730" y="620" width="285" height="96" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="754" y="662" fill="#E8ECF2" font-size="22" font-weight="600">.NET · React · Docker</text>
|
||||
<text x="754" y="692" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">CORE STACK</text>
|
||||
<rect x="1035" y="620" width="285" height="96" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="1059" y="662" fill="#E8ECF2" font-size="22" font-weight="600">Based in Norway</text>
|
||||
<text x="1059" y="692" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">WORK PERMIT ✓</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== SKILLS SECTION ===== -->
|
||||
<text x="120" y="828" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">01 — SKILLS</text>
|
||||
<text x="120" y="880" fill="#E8ECF2" font-size="39" font-weight="600">What I work with</text>
|
||||
<text x="120" y="916" fill="#9AA4B2" font-size="16">A practical profile: business value, maintainability, dependable delivery.</text>
|
||||
|
||||
<!-- three cards -->
|
||||
<g>
|
||||
<rect x="120" y="956" width="384" height="360" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="144" y="996" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">01 — DEVELOPMENT</text>
|
||||
<text x="144" y="1032" fill="#9AA4B2" font-size="14">Backend-leaning full-stack work in the</text>
|
||||
<text x="144" y="1052" fill="#9AA4B2" font-size="14">.NET ecosystem.</text>
|
||||
<g font-family="Consolas, monospace" font-size="12" fill="#E8ECF2">
|
||||
<rect x="144" y="1080" width="52" height="28" rx="14" fill="#171C26"/><text x="170" y="1098" text-anchor="middle">C#</text>
|
||||
<rect x="204" y="1080" width="64" height="28" rx="14" fill="#171C26"/><text x="236" y="1098" text-anchor="middle">.NET</text>
|
||||
<rect x="276" y="1080" width="76" height="28" rx="14" fill="#171C26"/><text x="314" y="1098" text-anchor="middle">Python</text>
|
||||
<rect x="144" y="1118" width="110" height="28" rx="14" fill="#171C26"/><text x="199" y="1136" text-anchor="middle">TypeScript</text>
|
||||
<rect x="262" y="1118" width="66" height="28" rx="14" fill="#171C26"/><text x="295" y="1136" text-anchor="middle">React</text>
|
||||
<rect x="144" y="1156" width="56" height="28" rx="14" fill="#171C26"/><text x="172" y="1174" text-anchor="middle">SQL</text>
|
||||
<rect x="208" y="1156" width="110" height="28" rx="14" fill="#171C26"/><text x="263" y="1174" text-anchor="middle">JavaScript</text>
|
||||
</g>
|
||||
|
||||
<rect x="528" y="956" width="384" height="360" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="552" y="996" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">02 — DEVOPS & INFRASTRUCTURE</text>
|
||||
<text x="552" y="1032" fill="#9AA4B2" font-size="14">I deploy and operate what I build —</text>
|
||||
<text x="552" y="1052" fill="#9AA4B2" font-size="14">professionally and at home.</text>
|
||||
<g font-family="Consolas, monospace" font-size="12" fill="#E8ECF2">
|
||||
<rect x="552" y="1080" width="76" height="28" rx="14" fill="#171C26"/><text x="590" y="1098" text-anchor="middle">Docker</text>
|
||||
<rect x="636" y="1080" width="66" height="28" rx="14" fill="#171C26"/><text x="669" y="1098" text-anchor="middle">Linux</text>
|
||||
<rect x="710" y="1080" width="66" height="28" rx="14" fill="#171C26"/><text x="743" y="1098" text-anchor="middle">CI/CD</text>
|
||||
<rect x="552" y="1118" width="130" height="28" rx="14" fill="#171C26"/><text x="617" y="1136" text-anchor="middle">Azure DevOps</text>
|
||||
<rect x="690" y="1118" width="150" height="28" rx="14" fill="#171C26"/><text x="765" y="1136" text-anchor="middle">Reverse proxies</text>
|
||||
<rect x="552" y="1156" width="120" height="28" rx="14" fill="#171C26"/><text x="612" y="1174" text-anchor="middle">Monitoring</text>
|
||||
<rect x="680" y="1156" width="130" height="28" rx="14" fill="#171C26"/><text x="745" y="1174" text-anchor="middle">Self-hosting</text>
|
||||
</g>
|
||||
|
||||
<rect x="936" y="956" width="384" height="360" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="960" y="996" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">03 — PRACTICES</text>
|
||||
<text x="960" y="1032" fill="#9AA4B2" font-size="14">Production thinking: testing, security,</text>
|
||||
<text x="960" y="1052" fill="#9AA4B2" font-size="14">and honest communication.</text>
|
||||
<g font-family="Consolas, monospace" font-size="12" fill="#E8ECF2">
|
||||
<rect x="960" y="1080" width="86" height="28" rx="14" fill="#171C26"/><text x="1003" y="1098" text-anchor="middle">Testing</text>
|
||||
<rect x="1054" y="1080" width="130" height="28" rx="14" fill="#171C26"/><text x="1119" y="1098" text-anchor="middle">OAuth2 flows</text>
|
||||
<rect x="960" y="1118" width="180" height="28" rx="14" fill="#171C26"/><text x="1050" y="1136" text-anchor="middle">Security hardening</text>
|
||||
<rect x="1148" y="1118" width="150" height="28" rx="14" fill="#171C26"/><text x="1223" y="1136" text-anchor="middle">Prod. support</text>
|
||||
<rect x="960" y="1156" width="220" height="28" rx="14" fill="#171C26"/><text x="1070" y="1174" text-anchor="middle">Stakeholder comms</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- annotations -->
|
||||
<g font-family="Consolas, monospace" font-size="11" fill="#E5A93D">
|
||||
<text x="120" y="106">A1: trace draws on load (700ms), dot blinks once → stays lit</text>
|
||||
<text x="920" y="106">hover: nav underline draw · CV button = locale default + chevron menu</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,94 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 760" font-family="Segoe UI, Arial, sans-serif">
|
||||
<defs>
|
||||
<radialGradient id="haloL" cx="30%" cy="18%" r="55%">
|
||||
<stop offset="0%" stop-color="#0E8C72" stop-opacity="0.06"/>
|
||||
<stop offset="100%" stop-color="#0E8C72" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<pattern id="dotsL" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<circle cx="1" cy="1" r="1" fill="#171A20" opacity="0.05"/>
|
||||
</pattern>
|
||||
<filter id="cardShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#171A20" flood-opacity="0.06"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<rect width="1440" height="760" fill="#FAFAF8"/>
|
||||
<rect width="1440" height="640" fill="url(#dotsL)"/>
|
||||
<rect width="1440" height="640" fill="url(#haloL)"/>
|
||||
|
||||
<!-- HEADER -->
|
||||
<line x1="0" y1="72" x2="1440" y2="72" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<rect x="120" y="20" width="32" height="32" rx="8" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.2"/>
|
||||
<text x="136" y="41" fill="#171A20" font-family="Consolas, monospace" font-size="13" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<circle cx="148" cy="24" r="2" fill="#0E8C72"/>
|
||||
<text x="420" y="41" fill="#555E6B" font-size="14">Projects</text>
|
||||
<text x="510" y="41" fill="#555E6B" font-size="14">Experience</text>
|
||||
<text x="620" y="41" fill="#555E6B" font-size="14">About</text>
|
||||
<text x="695" y="41" fill="#555E6B" font-size="14">Contact</text>
|
||||
<rect x="1000" y="22" width="88" height="28" rx="14" fill="#F1F2EF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<rect x="1003" y="25" width="41" height="22" rx="11" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.2"/>
|
||||
<text x="1023" y="40" fill="#171A20" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="1066" y="40" fill="#555E6B" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
<circle cx="1122" cy="36" r="14" fill="#F1F2EF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<circle cx="1122" cy="36" r="5" fill="#555E6B"/>
|
||||
<g stroke="#555E6B" stroke-width="1.4">
|
||||
<line x1="1122" y1="26" x2="1122" y2="29"/><line x1="1122" y1="43" x2="1122" y2="46"/>
|
||||
<line x1="1112" y1="36" x2="1115" y2="36"/><line x1="1129" y1="36" x2="1132" y2="36"/>
|
||||
</g>
|
||||
<rect x="1156" y="20" width="164" height="32" rx="8" fill="#0E8C72"/>
|
||||
<text x="1216" y="41" fill="#FFFFFF" font-size="13" font-weight="600" text-anchor="middle">Download CV</text>
|
||||
<line x1="1290" y1="26" x2="1290" y2="46" stroke="#FFFFFF" stroke-opacity="0.4"/>
|
||||
<path d="M1300 33 l6 6 l6 -6" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
|
||||
|
||||
<!-- HERO (Norwegian locale shown — demonstrates NO string lengths + light theme) -->
|
||||
<text x="120" y="196" fill="#0E8C72" font-family="Consolas, monospace" font-size="12" letter-spacing="2">SYSTEMUTVIKLER · TØNSBERG</text>
|
||||
<text x="120" y="262" fill="#171A20" font-size="61" font-weight="600" letter-spacing="-1">Connor Babbington</text>
|
||||
<text x="120" y="316" fill="#171A20" font-size="26" font-weight="500">Jeg utvikler og drifter websystemer.</text>
|
||||
<text x="120" y="356" fill="#555E6B" font-size="18">Åtte års erfaring med fagsystemer i britisk offentlig sektor —</text>
|
||||
<text x="120" y="384" fill="#555E6B" font-size="18">nå bygger jeg fullstack-løsninger fra Tønsberg.</text>
|
||||
|
||||
<path d="M120 424 H360 l12 12 H560" fill="none" stroke="#0E8C72" stroke-width="1.5"/>
|
||||
<circle cx="566" cy="436" r="4" fill="#0E8C72"/>
|
||||
<text x="582" y="441" fill="#555E6B" font-family="Consolas, monospace" font-size="12">status: åpen for muligheter</text>
|
||||
|
||||
<g font-family="Consolas, monospace" font-size="12">
|
||||
<rect x="120" y="466" width="158" height="30" rx="15" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<text x="199" y="485" fill="#555E6B" text-anchor="middle">Bosatt i Tønsberg</text>
|
||||
<rect x="290" y="466" width="200" height="30" rx="15" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<text x="390" y="485" fill="#555E6B" text-anchor="middle">Gyldig oppholdstillatelse</text>
|
||||
<rect x="502" y="466" width="230" height="30" rx="15" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<text x="617" y="485" fill="#555E6B" text-anchor="middle">Remote / hybrid / oppmøte</text>
|
||||
<rect x="744" y="466" width="230" height="30" rx="15" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<text x="859" y="485" fill="#555E6B" text-anchor="middle">Engelsk morsmål · Norsk B1</text>
|
||||
</g>
|
||||
|
||||
<rect x="120" y="530" width="156" height="44" rx="8" fill="#0E8C72"/>
|
||||
<text x="198" y="557" fill="#FFFFFF" font-size="15" font-weight="600" text-anchor="middle">Se prosjekter</text>
|
||||
<rect x="292" y="530" width="196" height="44" rx="8" fill="none" stroke="#171A20" stroke-opacity="0.2"/>
|
||||
<text x="390" y="557" fill="#171A20" font-size="15" text-anchor="middle">Last ned CV · pdf</text>
|
||||
|
||||
<!-- portrait -->
|
||||
<rect x="1020" y="150" width="300" height="375" rx="16" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1" filter="url(#cardShadow)"/>
|
||||
<circle cx="1170" cy="290" r="72" fill="#E4E6E1"/>
|
||||
<path d="M1080 525 q90 -110 180 0" fill="#E4E6E1"/>
|
||||
<text x="1170" y="300" fill="#8A93A0" font-size="13" text-anchor="middle">[ outdoor headshot — natural grade ]</text>
|
||||
<text x="1020" y="552" fill="#8A93A0" font-family="Consolas, monospace" font-size="12">tønsberg, norge · 2025</text>
|
||||
|
||||
<!-- proof strip -->
|
||||
<g filter="url(#cardShadow)">
|
||||
<rect x="120" y="620" width="285" height="96" rx="12" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<rect x="425" y="620" width="285" height="96" rx="12" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<rect x="730" y="620" width="285" height="96" rx="12" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
<rect x="1035" y="620" width="285" height="96" rx="12" fill="#FFFFFF" stroke="#171A20" stroke-opacity="0.1"/>
|
||||
</g>
|
||||
<text x="144" y="662" fill="#171A20" font-size="28" font-weight="600">8+ år</text>
|
||||
<text x="144" y="692" fill="#8A93A0" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">ERFARING</text>
|
||||
<text x="449" y="662" fill="#171A20" font-size="22" font-weight="600">Offentlig sektor (UK)</text>
|
||||
<text x="449" y="692" fill="#8A93A0" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">BAKGRUNN</text>
|
||||
<text x="754" y="662" fill="#171A20" font-size="22" font-weight="600">.NET · React · Docker</text>
|
||||
<text x="754" y="692" fill="#8A93A0" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">KJERNESTACK</text>
|
||||
<text x="1059" y="662" fill="#171A20" font-size="22" font-weight="600">Bosatt i Norge</text>
|
||||
<text x="1059" y="692" fill="#8A93A0" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">OPPHOLDSTILLATELSE ✓</text>
|
||||
|
||||
<text x="120" y="106" fill="#8A6114" font-family="Consolas, monospace" font-size="11">M2: light theme + Norwegian locale — same geometry as M1, token swap only; accent deepened to #0E8C72 for AA contrast</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.9 KiB |
@@ -0,0 +1,125 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 1720" font-family="Segoe UI, Arial, sans-serif">
|
||||
<rect width="1440" height="1720" fill="#0B0E14"/>
|
||||
|
||||
<!-- header (compressed 56px scrolled state) -->
|
||||
<rect width="1440" height="56" fill="#0B0E14" fill-opacity="0.9"/>
|
||||
<line x1="0" y1="56" x2="1440" y2="56" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="120" y="14" width="28" height="28" rx="7" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="134" y="33" fill="#E8ECF2" font-family="Consolas, monospace" font-size="12" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<text x="430" y="34" fill="#E8ECF2" font-size="13">Projects</text>
|
||||
<line x1="430" y1="40" x2="478" y2="40" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<text x="510" y="34" fill="#9AA4B2" font-size="13">Experience</text>
|
||||
<text x="610" y="34" fill="#9AA4B2" font-size="13">About</text>
|
||||
<text x="680" y="34" fill="#9AA4B2" font-size="13">Contact</text>
|
||||
<rect x="1176" y="13" width="144" height="30" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1238" y="33" fill="#062A22" font-size="12" font-weight="600" text-anchor="middle">Download CV ▾</text>
|
||||
|
||||
<!-- case study header -->
|
||||
<text x="120" y="140" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">CASE STUDY · PERSONAL PRODUCT</text>
|
||||
<text x="120" y="204" fill="#E8ECF2" font-size="49" font-weight="600" letter-spacing="-0.7">JobTrack</text>
|
||||
<rect x="340" y="172" width="92" height="28" rx="14" fill="#11151D" stroke="#3ECFAE" stroke-opacity="0.4"/>
|
||||
<circle cx="358" cy="186" r="3.5" fill="#3ECFAE"/>
|
||||
<text x="370" y="191" fill="#3ECFAE" font-family="Consolas, monospace" font-size="11">ACTIVE</text>
|
||||
<text x="120" y="248" fill="#9AA4B2" font-size="18">A job-application workspace covering the full journey from application to follow-up.</text>
|
||||
|
||||
<g font-family="Consolas, monospace" font-size="12" fill="#E8ECF2">
|
||||
<rect x="120" y="276" width="72" height="28" rx="14" fill="#171C26"/><text x="156" y="294" text-anchor="middle">.NET 9</text>
|
||||
<rect x="200" y="276" width="66" height="28" rx="14" fill="#171C26"/><text x="233" y="294" text-anchor="middle">React</text>
|
||||
<rect x="274" y="276" width="110" height="28" rx="14" fill="#171C26"/><text x="329" y="294" text-anchor="middle">TypeScript</text>
|
||||
<rect x="392" y="276" width="76" height="28" rx="14" fill="#171C26"/><text x="430" y="294" text-anchor="middle">SQLite</text>
|
||||
<rect x="476" y="276" width="86" height="28" rx="14" fill="#171C26"/><text x="519" y="294" text-anchor="middle">FastAPI</text>
|
||||
<rect x="570" y="276" width="76" height="28" rx="14" fill="#171C26"/><text x="608" y="294" text-anchor="middle">Ollama</text>
|
||||
<rect x="654" y="276" width="76" height="28" rx="14" fill="#171C26"/><text x="692" y="294" text-anchor="middle">Docker</text>
|
||||
</g>
|
||||
<text x="1200" y="294" fill="#3ECFAE" font-size="14">Repository ↗</text>
|
||||
|
||||
<!-- hero screenshot: browser frame -->
|
||||
<rect x="120" y="336" width="1200" height="520" rx="16" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<rect x="120" y="336" width="1200" height="40" rx="16" fill="#11151D"/>
|
||||
<rect x="120" y="360" width="1200" height="16" fill="#11151D"/>
|
||||
<circle cx="148" cy="356" r="5" fill="#5C6675"/><circle cx="166" cy="356" r="5" fill="#5C6675"/><circle cx="184" cy="356" r="5" fill="#5C6675"/>
|
||||
<rect x="560" y="346" width="320" height="20" rx="10" fill="#171C26"/>
|
||||
<text x="720" y="360" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="middle">jobtrack.local</text>
|
||||
<!-- mock app content -->
|
||||
<rect x="150" y="400" width="220" height="426" rx="8" fill="#11151D"/>
|
||||
<rect x="390" y="400" width="900" height="60" rx="8" fill="#11151D"/>
|
||||
<text x="410" y="436" fill="#9AA4B2" font-size="14">Applications · filters · search</text>
|
||||
<g fill="#11151D">
|
||||
<rect x="390" y="474" width="900" height="52" rx="6"/>
|
||||
<rect x="390" y="534" width="900" height="52" rx="6"/>
|
||||
<rect x="390" y="594" width="900" height="52" rx="6"/>
|
||||
<rect x="390" y="654" width="900" height="52" rx="6"/>
|
||||
<rect x="390" y="714" width="900" height="52" rx="6"/>
|
||||
</g>
|
||||
<g font-family="Consolas, monospace" font-size="11">
|
||||
<rect x="1160" y="488" width="92" height="24" rx="12" fill="#0F2B24"/><text x="1206" y="504" fill="#3ECFAE" text-anchor="middle">INTERVIEW</text>
|
||||
<rect x="1160" y="548" width="92" height="24" rx="12" fill="#2E2410"/><text x="1206" y="564" fill="#E5A93D" text-anchor="middle">WAITING</text>
|
||||
<rect x="1160" y="608" width="92" height="24" rx="12" fill="#171C26"/><text x="1206" y="624" fill="#9AA4B2" text-anchor="middle">APPLIED</text>
|
||||
</g>
|
||||
<text x="720" y="800" fill="#5C6675" font-size="13" text-anchor="middle">[ 2× capture: applications table with status filters — seeded demo data ]</text>
|
||||
|
||||
<!-- TL;DR box -->
|
||||
<rect x="120" y="896" width="800" height="180" rx="12" fill="#171C26"/>
|
||||
<rect x="120" y="896" width="3" height="180" fill="#3ECFAE"/>
|
||||
<text x="148" y="932" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">TL;DR</text>
|
||||
<text x="148" y="964" fill="#E8ECF2" font-size="15"><tspan font-weight="600">What</tspan> Full-stack job-application tracker: pipeline, correspondence, attachments, analytics</text>
|
||||
<text x="148" y="992" fill="#E8ECF2" font-size="15"><tspan font-weight="600">Why</tspan> Real problem — my own job search needed production-grade tooling</text>
|
||||
<text x="148" y="1020" fill="#E8ECF2" font-size="15"><tspan font-weight="600">Stack</tspan> React + ASP.NET Core + SQLite · FastAPI/Ollama AI service · Docker Compose</text>
|
||||
<text x="148" y="1048" fill="#E8ECF2" font-size="15"><tspan font-weight="600">Role</tspan> Everything: product, backend, frontend, ops, security</text>
|
||||
|
||||
<!-- mini TOC -->
|
||||
<g font-size="13">
|
||||
<text x="1080" y="932" fill="#5C6675" font-family="Consolas, monospace" font-size="11" letter-spacing="1.5">ON THIS PAGE</text>
|
||||
<text x="1080" y="964" fill="#9AA4B2">Problem</text>
|
||||
<rect x="1064" y="982" width="2" height="16" fill="#3ECFAE"/>
|
||||
<text x="1080" y="994" fill="#E8ECF2">Architecture</text>
|
||||
<text x="1080" y="1024" fill="#9AA4B2">Decisions</text>
|
||||
<text x="1080" y="1054" fill="#9AA4B2">Security</text>
|
||||
<text x="1080" y="1084" fill="#9AA4B2">Screenshots</text>
|
||||
<text x="1080" y="1114" fill="#9AA4B2">What's next</text>
|
||||
</g>
|
||||
|
||||
<!-- Architecture section -->
|
||||
<text x="120" y="1160" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">ARCHITECTURE</text>
|
||||
<text x="120" y="1204" fill="#E8ECF2" font-size="31" font-weight="600">Four services, one compose file</text>
|
||||
|
||||
<!-- diagram -->
|
||||
<rect x="120" y="1240" width="1200" height="300" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<!-- nodes -->
|
||||
<g>
|
||||
<rect x="170" y="1330" width="180" height="72" rx="8" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="260" y="1360" fill="#E8ECF2" font-size="14" text-anchor="middle">React SPA</text>
|
||||
<text x="260" y="1382" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="middle">nginx · PWA</text>
|
||||
|
||||
<rect x="440" y="1330" width="200" height="72" rx="8" fill="#171C26" stroke="#3ECFAE" stroke-opacity="0.5"/>
|
||||
<text x="540" y="1360" fill="#E8ECF2" font-size="14" text-anchor="middle">ASP.NET Core API</text>
|
||||
<text x="540" y="1382" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="middle">.NET 9 · auth · rules</text>
|
||||
|
||||
<rect x="730" y="1276" width="180" height="64" rx="8" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="820" y="1302" fill="#E8ECF2" font-size="14" text-anchor="middle">SQLite + files</text>
|
||||
<text x="820" y="1322" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EF Core · attachments</text>
|
||||
|
||||
<rect x="730" y="1364" width="180" height="64" rx="8" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="820" y="1390" fill="#E8ECF2" font-size="14" text-anchor="middle">AI service</text>
|
||||
<text x="820" y="1410" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="middle">FastAPI → Ollama</text>
|
||||
|
||||
<rect x="1000" y="1330" width="180" height="72" rx="8" fill="#171C26" stroke="#E5A93D" stroke-opacity="0.5"/>
|
||||
<text x="1090" y="1360" fill="#E8ECF2" font-size="14" text-anchor="middle">Gmail API</text>
|
||||
<text x="1090" y="1382" fill="#8A6114" font-family="Consolas, monospace" font-size="11" text-anchor="middle">external · OAuth2</text>
|
||||
</g>
|
||||
<!-- edges -->
|
||||
<g stroke="#3ECFAE" stroke-width="1.5" fill="none">
|
||||
<path d="M350 1366 H440"/>
|
||||
<path d="M640 1352 L730 1308"/>
|
||||
<path d="M640 1380 L730 1396"/>
|
||||
</g>
|
||||
<path d="M640 1366 H960 M960 1366 H1000" stroke="#E5A93D" stroke-width="1.5" stroke-dasharray="4 4" fill="none"/>
|
||||
<text x="380" y="1356" fill="#5C6675" font-family="Consolas, monospace" font-size="10">/api</text>
|
||||
<text x="930" y="1356" fill="#8A6114" font-family="Consolas, monospace" font-size="10">import</text>
|
||||
<text x="148" y="1276" fill="#5C6675" font-family="Consolas, monospace" font-size="11">hover: node highlights its edges, others dim to 60% (desktop only — fully legible static)</text>
|
||||
|
||||
<!-- decisions preview -->
|
||||
<text x="120" y="1600" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">KEY DECISIONS & TRADE-OFFS</text>
|
||||
<text x="120" y="1644" fill="#E8ECF2" font-size="16"><tspan fill="#3ECFAE" font-family="Consolas, monospace">01</tspan> Local AI (Ollama) over a cloud API — privacy of job-search data + zero per-call cost, traded against setup complexity.</text>
|
||||
<text x="120" y="1676" fill="#E8ECF2" font-size="16"><tspan fill="#3ECFAE" font-family="Consolas, monospace">02</tspan> No offline cache in the PWA — deliberate: frequent deploys made stale builds a worse failure than offline gaps.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
@@ -0,0 +1,133 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 1240" font-family="Segoe UI, Arial, sans-serif">
|
||||
<rect width="900" height="1240" fill="#05070B"/>
|
||||
<text x="60" y="48" fill="#E5A93D" font-family="Consolas, monospace" font-size="12">M4: mobile 390px — homepage (left) · nav overlay open (right)</text>
|
||||
|
||||
<!-- ============ PHONE 1: homepage ============ -->
|
||||
<rect x="60" y="80" width="390" height="1100" rx="24" fill="#0B0E14" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
|
||||
<!-- slim header 56 -->
|
||||
<line x1="60" y1="136" x2="450" y2="136" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="84" y="94" width="28" height="28" rx="7" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="98" y="113" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<!-- CV icon button -->
|
||||
<rect x="330" y="94" width="40" height="28" rx="7" fill="#3ECFAE"/>
|
||||
<path d="M350 100 v10 m-4 -4 l4 4 l4 -4 M344 114 h12" stroke="#062A22" stroke-width="1.6" fill="none"/>
|
||||
<!-- burger -->
|
||||
<g stroke="#E8ECF2" stroke-width="1.6">
|
||||
<line x1="390" y1="102" x2="414" y2="102"/><line x1="390" y1="110" x2="414" y2="110"/><line x1="390" y1="118" x2="414" y2="118"/>
|
||||
</g>
|
||||
|
||||
<!-- hero -->
|
||||
<text x="84" y="192" fill="#3ECFAE" font-family="Consolas, monospace" font-size="10" letter-spacing="1.5">SYSTEMS DEVELOPER · NORWAY</text>
|
||||
<text x="84" y="234" fill="#E8ECF2" font-size="34" font-weight="600">Connor</text>
|
||||
<text x="84" y="272" fill="#E8ECF2" font-size="34" font-weight="600">Babbington</text>
|
||||
<text x="84" y="308" fill="#9AA4B2" font-size="15">I design, build and run web systems.</text>
|
||||
<text x="84" y="332" fill="#9AA4B2" font-size="15">8+ years in UK local government —</text>
|
||||
<text x="84" y="356" fill="#9AA4B2" font-size="15">now building full-stack in Norway.</text>
|
||||
<path d="M84 384 H240 l8 8 H330" fill="none" stroke="#3ECFAE" stroke-width="1.5"/>
|
||||
<circle cx="335" cy="392" r="3.5" fill="#3ECFAE"/>
|
||||
|
||||
<!-- chips 2 rows -->
|
||||
<g font-family="Consolas, monospace" font-size="10.5">
|
||||
<rect x="84" y="410" width="128" height="26" rx="13" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="148" y="427" fill="#9AA4B2" text-anchor="middle">Tønsberg, Norway</text>
|
||||
<rect x="220" y="410" width="108" height="26" rx="13" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="274" y="427" fill="#9AA4B2" text-anchor="middle">Work permit ✓</text>
|
||||
<rect x="84" y="444" width="150" height="26" rx="13" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="159" y="461" fill="#9AA4B2" text-anchor="middle">Remote/hybrid/on-site</text>
|
||||
<rect x="242" y="444" width="150" height="26" rx="13" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="317" y="461" fill="#9AA4B2" text-anchor="middle">EN native · NO B1</text>
|
||||
</g>
|
||||
|
||||
<!-- stacked CTAs full width, 48px -->
|
||||
<rect x="84" y="494" width="342" height="48" rx="8" fill="#3ECFAE"/>
|
||||
<text x="255" y="524" fill="#062A22" font-size="15" font-weight="600" text-anchor="middle">View projects</text>
|
||||
<rect x="84" y="552" width="342" height="48" rx="8" fill="none" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="255" y="582" fill="#E8ECF2" font-size="15" text-anchor="middle">Download CV · pdf</text>
|
||||
|
||||
<!-- proof 2x2 -->
|
||||
<g>
|
||||
<rect x="84" y="628" width="165" height="72" rx="10" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="100" y="660" fill="#E8ECF2" font-size="18" font-weight="600">8+ yrs</text>
|
||||
<text x="100" y="682" fill="#5C6675" font-family="Consolas, monospace" font-size="9" letter-spacing="1">EXPERIENCE</text>
|
||||
<rect x="261" y="628" width="165" height="72" rx="10" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="277" y="660" fill="#E8ECF2" font-size="15" font-weight="600">Public sector</text>
|
||||
<text x="277" y="682" fill="#5C6675" font-family="Consolas, monospace" font-size="9" letter-spacing="1">BACKGROUND</text>
|
||||
<rect x="84" y="710" width="165" height="72" rx="10" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="100" y="742" fill="#E8ECF2" font-size="15" font-weight="600">.NET · React</text>
|
||||
<text x="100" y="764" fill="#5C6675" font-family="Consolas, monospace" font-size="9" letter-spacing="1">CORE STACK</text>
|
||||
<rect x="261" y="710" width="165" height="72" rx="10" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<text x="277" y="742" fill="#E8ECF2" font-size="15" font-weight="600">In Norway</text>
|
||||
<text x="277" y="764" fill="#5C6675" font-family="Consolas, monospace" font-size="9" letter-spacing="1">PERMIT ✓</text>
|
||||
</g>
|
||||
|
||||
<!-- project card -->
|
||||
<text x="84" y="836" fill="#3ECFAE" font-family="Consolas, monospace" font-size="10" letter-spacing="1.5">02 — PROJECTS</text>
|
||||
<rect x="84" y="856" width="342" height="290" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="84" y="856" width="342" height="170" rx="12" fill="#171C26"/>
|
||||
<rect x="84" y="1010" width="342" height="16" fill="#171C26"/>
|
||||
<text x="255" y="948" fill="#5C6675" font-size="11" text-anchor="middle">[ JobTrack screenshot 16:10 ]</text>
|
||||
<rect x="342" y="872" width="70" height="24" rx="12" fill="#0B0E14" stroke="#3ECFAE" stroke-opacity="0.4"/>
|
||||
<circle cx="356" cy="884" r="3" fill="#3ECFAE"/>
|
||||
<text x="366" y="889" fill="#3ECFAE" font-family="Consolas, monospace" font-size="9.5">ACTIVE</text>
|
||||
<text x="104" y="1058" fill="#E8ECF2" font-size="18" font-weight="600">JobTrack</text>
|
||||
<text x="104" y="1082" fill="#9AA4B2" font-size="13">A workspace for the whole job-application journey.</text>
|
||||
<g font-family="Consolas, monospace" font-size="10" fill="#9AA4B2">
|
||||
<rect x="104" y="1096" width="56" height="22" rx="11" fill="#171C26"/><text x="132" y="1111" text-anchor="middle">.NET 9</text>
|
||||
<rect x="168" y="1096" width="52" height="22" rx="11" fill="#171C26"/><text x="194" y="1111" text-anchor="middle">React</text>
|
||||
<rect x="228" y="1096" width="60" height="22" rx="11" fill="#171C26"/><text x="258" y="1111" text-anchor="middle">Docker</text>
|
||||
</g>
|
||||
<text x="104" y="1136" fill="#3ECFAE" font-size="13">Read case study →</text>
|
||||
|
||||
<!-- ============ PHONE 2: nav overlay ============ -->
|
||||
<rect x="480" y="80" width="390" height="1100" rx="24" fill="#0B0E14" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<rect x="480" y="80" width="390" height="1100" rx="24" fill="#11151D" fill-opacity="0.98"/>
|
||||
|
||||
<!-- overlay header -->
|
||||
<rect x="504" y="94" width="28" height="28" rx="7" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="518" y="113" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<g stroke="#E8ECF2" stroke-width="1.8">
|
||||
<line x1="812" y1="98" x2="832" y2="118"/><line x1="832" y1="98" x2="812" y2="118"/>
|
||||
</g>
|
||||
|
||||
<!-- language + theme row -->
|
||||
<rect x="504" y="152" width="240" height="44" rx="22" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="508" y="156" width="116" height="36" rx="18" fill="#0B0E14" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="566" y="179" fill="#E8ECF2" font-family="Consolas, monospace" font-size="13" text-anchor="middle">EN</text>
|
||||
<text x="686" y="179" fill="#9AA4B2" font-family="Consolas, monospace" font-size="13" text-anchor="middle">NO</text>
|
||||
<circle cx="800" cy="174" r="22" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<path d="M806 166 a9 9 0 1 0 4 12 a8 8 0 0 1 -4 -12" fill="#9AA4B2"/>
|
||||
|
||||
<!-- nav stack: large targets -->
|
||||
<g font-size="28" font-weight="600">
|
||||
<text x="504" y="292" fill="#E8ECF2">Home</text>
|
||||
<text x="504" y="368" fill="#E8ECF2">Projects</text>
|
||||
<text x="504" y="444" fill="#E8ECF2">Experience</text>
|
||||
<text x="504" y="520" fill="#E8ECF2">About</text>
|
||||
<text x="504" y="596" fill="#E8ECF2">Contact</text>
|
||||
</g>
|
||||
<g font-family="Consolas, monospace" font-size="11" fill="#5C6675">
|
||||
<text x="504" y="316">/</text>
|
||||
<text x="504" y="392">/projects · 3</text>
|
||||
<text x="504" y="468">/experience</text>
|
||||
<text x="504" y="544">/about</text>
|
||||
<text x="504" y="620">/contact</text>
|
||||
</g>
|
||||
<line x1="504" y1="660" x2="846" y2="660" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
|
||||
<!-- CV buttons -->
|
||||
<rect x="504" y="692" width="342" height="48" rx="8" fill="#3ECFAE"/>
|
||||
<text x="675" y="722" fill="#062A22" font-size="15" font-weight="600" text-anchor="middle">Download CV — English</text>
|
||||
<rect x="504" y="752" width="342" height="48" rx="8" fill="none" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="675" y="782" fill="#E8ECF2" font-size="15" text-anchor="middle">Last ned CV — Norsk</text>
|
||||
|
||||
<!-- contact shortcuts -->
|
||||
<g font-family="Consolas, monospace" font-size="13" fill="#9AA4B2">
|
||||
<text x="504" y="852">connor.babbington@cesnimda.co.uk ⧉</text>
|
||||
<text x="504" y="888">linkedin ↗</text>
|
||||
<text x="504" y="924">git.cesnimda.uk ↗</text>
|
||||
</g>
|
||||
|
||||
<text x="504" y="1150" fill="#5C6675" font-family="Consolas, monospace" font-size="11">overlay: full-screen, body scroll locked, Esc/× closes,</text>
|
||||
<text x="504" y="1168" fill="#5C6675" font-family="Consolas, monospace" font-size="11">focus trapped · targets ≥ 44px</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
@@ -0,0 +1,132 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 1040" font-family="Segoe UI, Arial, sans-serif">
|
||||
<rect width="1440" height="1040" fill="#05070B"/>
|
||||
<text x="60" y="44" fill="#E5A93D" font-family="Consolas, monospace" font-size="12">M5: navigation & control states</text>
|
||||
|
||||
<!-- (a) default header -->
|
||||
<text x="60" y="86" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(a) default · 72px</text>
|
||||
<rect x="60" y="100" width="1320" height="72" fill="#0B0E14" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="90" y="120" width="32" height="32" rx="8" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="106" y="141" fill="#E8ECF2" font-family="Consolas, monospace" font-size="13" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<text x="380" y="141" fill="#9AA4B2" font-size="14">Projects</text>
|
||||
<text x="470" y="141" fill="#9AA4B2" font-size="14">Experience</text>
|
||||
<text x="580" y="141" fill="#9AA4B2" font-size="14">About</text>
|
||||
<text x="655" y="141" fill="#9AA4B2" font-size="14">Contact</text>
|
||||
<rect x="960" y="122" width="88" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="963" y="125" width="41" height="22" rx="11" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="983" y="140" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="1026" y="140" fill="#9AA4B2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
<circle cx="1082" cy="136" r="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="1116" y="120" width="164" height="32" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1176" y="141" fill="#062A22" font-size="13" font-weight="600" text-anchor="middle">Download CV</text>
|
||||
<line x1="1250" y1="126" x2="1250" y2="146" stroke="#062A22" stroke-opacity="0.3"/>
|
||||
<path d="M1258 133 l6 6 l6 -6" fill="none" stroke="#062A22" stroke-width="1.8"/>
|
||||
|
||||
<!-- (b) scrolled -->
|
||||
<text x="60" y="216" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(b) scrolled · 56px · backdrop blur + hairline · hover on "Projects" (underline draw) · current page "About"</text>
|
||||
<rect x="60" y="230" width="1320" height="56" fill="#0B0E14" fill-opacity="0.85" stroke="#E8ECF2" stroke-opacity="0.12"/>
|
||||
<rect x="90" y="244" width="28" height="28" rx="7" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="104" y="263" fill="#E8ECF2" font-family="Consolas, monospace" font-size="12" font-weight="bold" text-anchor="middle">CB</text>
|
||||
<text x="380" y="264" fill="#E8ECF2" font-size="13">Projects</text>
|
||||
<line x1="380" y1="271" x2="410" y2="271" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<text x="470" y="264" fill="#9AA4B2" font-size="13">Experience</text>
|
||||
<text x="570" y="264" fill="#E8ECF2" font-size="13">About</text>
|
||||
<line x1="570" y1="271" x2="608" y2="271" stroke="#3ECFAE" stroke-width="2" stroke-opacity="0.6"/>
|
||||
<text x="645" y="264" fill="#9AA4B2" font-size="13">Contact</text>
|
||||
<rect x="1136" y="243" width="144" height="30" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1198" y="263" fill="#062A22" font-size="12" font-weight="600" text-anchor="middle">Download CV ▾</text>
|
||||
|
||||
<!-- (c) CV split open -->
|
||||
<text x="60" y="336" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(c) CV split-button open · locale default first · file sizes · keyboard: ↓↑ Esc</text>
|
||||
<rect x="60" y="350" width="164" height="32" rx="8" fill="#3ECFAE"/>
|
||||
<text x="120" y="371" fill="#062A22" font-size="13" font-weight="600" text-anchor="middle">Download CV</text>
|
||||
<line x1="194" y1="356" x2="194" y2="376" stroke="#062A22" stroke-opacity="0.3"/>
|
||||
<path d="M202 366 l6 -6 l6 6" fill="none" stroke="#062A22" stroke-width="1.8"/>
|
||||
<rect x="60" y="392" width="280" height="104" rx="12" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<rect x="68" y="402" width="264" height="40" rx="8" fill="#11151D"/>
|
||||
<rect x="80" y="412" width="30" height="20" rx="10" fill="#0F2B24"/>
|
||||
<text x="95" y="426" fill="#3ECFAE" font-family="Consolas, monospace" font-size="10" text-anchor="middle">EN</text>
|
||||
<text x="122" y="427" fill="#E8ECF2" font-size="13">English CV</text>
|
||||
<text x="322" y="427" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="end">pdf · 180 kB</text>
|
||||
<rect x="80" y="454" width="30" height="20" rx="10" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="95" y="468" fill="#9AA4B2" font-family="Consolas, monospace" font-size="10" text-anchor="middle">NO</text>
|
||||
<text x="122" y="469" fill="#9AA4B2" font-size="13">Norsk CV</text>
|
||||
<text x="322" y="469" fill="#5C6675" font-family="Consolas, monospace" font-size="11" text-anchor="end">pdf · 172 kB</text>
|
||||
|
||||
<!-- (d) language switch states -->
|
||||
<text x="480" y="336" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(d) language switch · EN active / NO active / focus-visible · thumb slides 160ms · aria toggle group</text>
|
||||
<g>
|
||||
<rect x="480" y="352" width="88" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="483" y="355" width="41" height="22" rx="11" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="503" y="370" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="546" y="370" fill="#9AA4B2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
|
||||
<rect x="600" y="352" width="88" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="644" y="355" width="41" height="22" rx="11" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="620" y="370" fill="#9AA4B2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="664" y="370" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
|
||||
<rect x="720" y="352" width="88" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="718" y="350" width="92" height="32" rx="16" fill="none" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<rect x="723" y="355" width="41" height="22" rx="11" fill="#171C26" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="743" y="370" fill="#E8ECF2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">EN</text>
|
||||
<text x="786" y="370" fill="#9AA4B2" font-family="Consolas, monospace" font-size="11" text-anchor="middle">NO</text>
|
||||
</g>
|
||||
|
||||
<!-- (e) first-visit language hint -->
|
||||
<text x="480" y="430" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(e) first-visit hint (browser prefers nb-NO) · shown once · dismissible · never auto-redirects</text>
|
||||
<rect x="480" y="444" width="620" height="44" rx="8" fill="#171C26" stroke="#3ECFAE" stroke-opacity="0.3"/>
|
||||
<text x="500" y="471" fill="#E8ECF2" font-size="13">Denne siden finnes på norsk</text>
|
||||
<rect x="700" y="452" width="66" height="28" rx="8" fill="#3ECFAE"/>
|
||||
<text x="733" y="471" fill="#062A22" font-size="12" font-weight="600" text-anchor="middle">Bytt</text>
|
||||
<text x="1076" y="472" fill="#9AA4B2" font-size="14" text-anchor="end">✕</text>
|
||||
|
||||
<!-- (f) buttons row -->
|
||||
<text x="60" y="560" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(f) buttons: primary rest/hover/focus · secondary · ghost · disabled</text>
|
||||
<rect x="60" y="576" width="150" height="44" rx="8" fill="#3ECFAE"/>
|
||||
<text x="135" y="603" fill="#062A22" font-size="14" font-weight="600" text-anchor="middle">Primary</text>
|
||||
<rect x="230" y="572" width="150" height="44" rx="8" fill="#54DBBC"/>
|
||||
<text x="305" y="599" fill="#062A22" font-size="14" font-weight="600" text-anchor="middle">Hover +1px ↑</text>
|
||||
<rect x="400" y="576" width="150" height="44" rx="8" fill="#3ECFAE"/>
|
||||
<rect x="396" y="572" width="158" height="52" rx="10" fill="none" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<text x="475" y="603" fill="#062A22" font-size="14" font-weight="600" text-anchor="middle">Focus ring</text>
|
||||
<rect x="590" y="576" width="150" height="44" rx="8" fill="none" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="665" y="603" fill="#E8ECF2" font-size="14" text-anchor="middle">Secondary</text>
|
||||
<text x="790" y="603" fill="#9AA4B2" font-size="14">Ghost link</text>
|
||||
<line x1="790" y1="610" x2="856" y2="610" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<rect x="900" y="576" width="150" height="44" rx="8" fill="#171C26"/>
|
||||
<text x="975" y="603" fill="#5C6675" font-size="14" text-anchor="middle">Disabled</text>
|
||||
|
||||
<!-- (g) chips & status -->
|
||||
<text x="60" y="690" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(g) status semantics: accent=active · amber=in development · muted=archived (dot + label, never colour-only)</text>
|
||||
<g font-family="Consolas, monospace" font-size="11">
|
||||
<rect x="60" y="706" width="92" height="28" rx="14" fill="#11151D" stroke="#3ECFAE" stroke-opacity="0.4"/>
|
||||
<circle cx="78" cy="720" r="3.5" fill="#3ECFAE"/>
|
||||
<text x="90" y="725" fill="#3ECFAE">ACTIVE</text>
|
||||
<rect x="168" y="706" width="150" height="28" rx="14" fill="#11151D" stroke="#E5A93D" stroke-opacity="0.4"/>
|
||||
<circle cx="186" cy="720" r="3.5" fill="#E5A93D"/>
|
||||
<text x="198" y="725" fill="#E5A93D">IN DEVELOPMENT</text>
|
||||
<rect x="334" y="706" width="104" height="28" rx="14" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<circle cx="352" cy="720" r="3.5" fill="#5C6675"/>
|
||||
<text x="364" y="725" fill="#9AA4B2">ARCHIVED</text>
|
||||
</g>
|
||||
|
||||
<!-- (h) form states -->
|
||||
<text x="60" y="800" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(h) form: rest · focus · error (icon + message, danger ≥4.5:1) · submit-sending · success panel</text>
|
||||
<text x="60" y="832" fill="#9AA4B2" font-size="13">E-mail</text>
|
||||
<rect x="60" y="842" width="260" height="44" rx="8" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<text x="340" y="832" fill="#E8ECF2" font-size="13">E-mail</text>
|
||||
<rect x="340" y="842" width="260" height="44" rx="8" fill="#11151D" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<text x="620" y="832" fill="#E8ECF2" font-size="13">E-mail</text>
|
||||
<rect x="620" y="842" width="260" height="44" rx="8" fill="#11151D" stroke="#E5606B" stroke-width="2"/>
|
||||
<text x="620" y="908" fill="#E5606B" font-size="12">⚠ Please enter a valid e-mail address</text>
|
||||
<rect x="920" y="842" width="180" height="44" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1010" y="869" fill="#062A22" font-family="Consolas, monospace" font-size="14" text-anchor="middle">···</text>
|
||||
<rect x="1130" y="830" width="250 " height="70" rx="12" fill="#171C26" stroke="#3ECFAE" stroke-opacity="0.4"/>
|
||||
<text x="1156" y="860" fill="#3ECFAE" font-size="14">✓ Message sent</text>
|
||||
<text x="1156" y="882" fill="#9AA4B2" font-size="12">I usually reply within a day.</text>
|
||||
|
||||
<!-- (i) skip link -->
|
||||
<text x="60" y="972" fill="#5C6675" font-family="Consolas, monospace" font-size="11">(i) skip-link, visible on first Tab</text>
|
||||
<rect x="60" y="984" width="220" height="40" rx="8" fill="#3ECFAE"/>
|
||||
<text x="170" y="1009" fill="#062A22" font-size="13" font-weight="600" text-anchor="middle">Skip to content ↵</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,91 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 1560" font-family="Segoe UI, Arial, sans-serif">
|
||||
<rect width="1440" height="1560" fill="#0B0E14"/>
|
||||
<text x="120" y="44" fill="#E5A93D" font-family="Consolas, monospace" font-size="12">M6: homepage lower half — projects · experience timeline · about teaser · contact band</text>
|
||||
|
||||
<!-- ===== PROJECTS ===== -->
|
||||
<text x="120" y="110" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">02 — PROJECTS</text>
|
||||
<text x="120" y="162" fill="#E8ECF2" font-size="39" font-weight="600">Built, shipped, and running</text>
|
||||
|
||||
<!-- card 1: JobTrack (hover state shown) -->
|
||||
<rect x="120" y="200" width="588" height="470" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.16"/>
|
||||
<rect x="120" y="200" width="588" height="280" rx="12" fill="#171C26"/>
|
||||
<rect x="120" y="464" width="588" height="16" fill="#171C26"/>
|
||||
<text x="414" y="348" fill="#5C6675" font-size="12" text-anchor="middle">[ JobTrack — applications table screenshot, 4px hover shift ]</text>
|
||||
<rect x="600" y="220" width="88" height="26" rx="13" fill="#0B0E14" stroke="#3ECFAE" stroke-opacity="0.4"/>
|
||||
<circle cx="616" cy="233" r="3.5" fill="#3ECFAE"/>
|
||||
<text x="628" y="238" fill="#3ECFAE" font-family="Consolas, monospace" font-size="10">ACTIVE</text>
|
||||
<text x="148" y="524" fill="#E8ECF2" font-size="24" font-weight="600">JobTrack</text>
|
||||
<line x1="148" y1="532" x2="248" y2="532" stroke="#3ECFAE" stroke-width="2"/>
|
||||
<text x="148" y="556" fill="#9AA4B2" font-size="14">One workspace for the whole job-application journey —</text>
|
||||
<text x="148" y="576" fill="#9AA4B2" font-size="14">pipeline, correspondence, attachments, AI summaries.</text>
|
||||
<g font-family="Consolas, monospace" font-size="11" fill="#9AA4B2">
|
||||
<rect x="148" y="594" width="60" height="24" rx="12" fill="#171C26"/><text x="178" y="610" text-anchor="middle">.NET 9</text>
|
||||
<rect x="216" y="594" width="56" height="24" rx="12" fill="#171C26"/><text x="244" y="610" text-anchor="middle">React</text>
|
||||
<rect x="280" y="594" width="66" height="24" rx="12" fill="#171C26"/><text x="313" y="610" text-anchor="middle">SQLite</text>
|
||||
<rect x="354" y="594" width="66" height="24" rx="12" fill="#171C26"/><text x="387" y="610" text-anchor="middle">Ollama</text>
|
||||
<rect x="428" y="594" width="66" height="24" rx="12" fill="#171C26"/><text x="461" y="610" text-anchor="middle">Docker</text>
|
||||
</g>
|
||||
<text x="148" y="646" fill="#3ECFAE" font-size="14">Read case study →→</text>
|
||||
|
||||
<!-- card 2: InboxIntel (rest state) -->
|
||||
<rect x="732" y="200" width="588" height="470" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<rect x="732" y="200" width="588" height="280" rx="12" fill="#171C26"/>
|
||||
<rect x="732" y="464" width="588" height="16" fill="#171C26"/>
|
||||
<text x="1026" y="348" fill="#5C6675" font-size="12" text-anchor="middle">[ InboxIntel — draggable analytics dashboard screenshot ]</text>
|
||||
<rect x="1156" y="220" width="146" height="26" rx="13" fill="#0B0E14" stroke="#E5A93D" stroke-opacity="0.4"/>
|
||||
<circle cx="1172" cy="233" r="3.5" fill="#E5A93D"/>
|
||||
<text x="1184" y="238" fill="#E5A93D" font-family="Consolas, monospace" font-size="10">IN DEVELOPMENT</text>
|
||||
<text x="760" y="524" fill="#E8ECF2" font-size="24" font-weight="600">InboxIntel</text>
|
||||
<text x="760" y="556" fill="#9AA4B2" font-size="14">Gmail analytics and safe bulk cleanup — Clean Architecture,</text>
|
||||
<text x="760" y="576" fill="#9AA4B2" font-size="14">encrypted OAuth tokens, preview-before-delete by design.</text>
|
||||
<g font-family="Consolas, monospace" font-size="11" fill="#9AA4B2">
|
||||
<rect x="760" y="594" width="60" height="24" rx="12" fill="#171C26"/><text x="790" y="610" text-anchor="middle">.NET 8</text>
|
||||
<rect x="828" y="594" width="100" height="24" rx="12" fill="#171C26"/><text x="878" y="610" text-anchor="middle">PostgreSQL</text>
|
||||
<rect x="936" y="594" width="56" height="24" rx="12" fill="#171C26"/><text x="964" y="610" text-anchor="middle">React</text>
|
||||
<rect x="1000" y="594" width="96" height="24" rx="12" fill="#171C26"/><text x="1048" y="610" text-anchor="middle">Gmail API</text>
|
||||
</g>
|
||||
<text x="760" y="646" fill="#3ECFAE" font-size="14">Read case study →</text>
|
||||
|
||||
<!-- slim homelab card -->
|
||||
<rect x="120" y="694" width="1200" height="92" rx="12" fill="#11151D" stroke="#E8ECF2" stroke-opacity="0.08"/>
|
||||
<g stroke="#3ECFAE" stroke-width="1.3" fill="none" opacity="0.8">
|
||||
<rect x="152" y="722" width="24" height="16" rx="2"/><rect x="152" y="744" width="24" height="16" rx="2"/>
|
||||
<path d="M176 730 h20 v22 h-20 M186 730 v-8 h30"/>
|
||||
</g>
|
||||
<text x="240" y="732" fill="#E8ECF2" font-size="18" font-weight="600">Self-hosted infrastructure lab</text>
|
||||
<text x="240" y="758" fill="#9AA4B2" font-size="14">Ubuntu · Docker services · reverse proxy · auth · monitoring · Gitea (this site's own git remote)</text>
|
||||
<text x="1180" y="746" fill="#3ECFAE" font-size="14">Explore →</text>
|
||||
|
||||
<!-- ===== EXPERIENCE ===== -->
|
||||
<text x="120" y="884" fill="#3ECFAE" font-family="Consolas, monospace" font-size="12" letter-spacing="2">03 — EXPERIENCE</text>
|
||||
<text x="120" y="936" fill="#E8ECF2" font-size="39" font-weight="600">Where I've worked</text>
|
||||
|
||||
<!-- timeline spine -->
|
||||
<line x1="160" y1="972" x2="160" y2="1270" stroke="#E8ECF2" stroke-opacity="0.12" stroke-width="1.5"/>
|
||||
<!-- featured -->
|
||||
<circle cx="160" cy="990" r="5" fill="#3ECFAE"/>
|
||||
<text x="196" y="996" fill="#E8ECF2" font-size="20" font-weight="600">System Developer — Warwickshire County Council</text>
|
||||
<text x="196" y="1020" fill="#5C6675" font-family="Consolas, monospace" font-size="12">2015 – 2023 · UK public sector · full-stack</text>
|
||||
<text x="196" y="1050" fill="#9AA4B2" font-size="15">· Replaced spreadsheet-heavy workflows with reliable internal systems used across the council</text>
|
||||
<text x="196" y="1076" fill="#9AA4B2" font-size="15">· Built and maintained full-stack applications in C#, Python, Ruby on Rails, SQL and JavaScript</text>
|
||||
<text x="196" y="1102" fill="#9AA4B2" font-size="15">· Owned deployments, permissions hardening and production troubleshooting on live systems</text>
|
||||
<!-- compact items -->
|
||||
<circle cx="160" cy="1150" r="4" fill="#5C6675"/>
|
||||
<text x="196" y="1156" fill="#9AA4B2" font-size="14">2017 – 2021 · Sales Representative · Royal Vapes</text>
|
||||
<circle cx="160" cy="1188" r="4" fill="#5C6675"/>
|
||||
<text x="196" y="1194" fill="#9AA4B2" font-size="14">2016 – 2018 · Bartender · The Hodcarrier</text>
|
||||
<circle cx="160" cy="1226" r="4" fill="#5C6675"/>
|
||||
<text x="196" y="1232" fill="#9AA4B2" font-size="14">2014 – 2015 · Receptionist · Nuffield Health</text>
|
||||
<circle cx="160" cy="1264" r="4" fill="#5C6675"/>
|
||||
<text x="196" y="1270" fill="#9AA4B2" font-size="14">2012 – 2015 · Extended Diploma NVQ L3 in ICT · Warwickshire College</text>
|
||||
<text x="196" y="1300" fill="#5C6675" font-family="Consolas, monospace" font-size="12">earlier customer-facing roles — kept for honesty, de-emphasised · timeline draws on scroll (A8)</text>
|
||||
|
||||
<!-- ===== CONTACT BAND ===== -->
|
||||
<rect x="120" y="1360" width="1200" height="140" rx="16" fill="#171C26"/>
|
||||
<text x="160" y="1418" fill="#E8ECF2" font-size="28" font-weight="600">Looking for a systems developer in Norway?</text>
|
||||
<text x="160" y="1450" fill="#9AA4B2" font-size="15">I usually reply within a day — e-mail is fastest.</text>
|
||||
<rect x="880" y="1404" width="300" height="48" rx="8" fill="#3ECFAE"/>
|
||||
<text x="1030" y="1434" fill="#062A22" font-size="15" font-weight="600" text-anchor="middle">connor.babbington@cesnimda.co.uk ⧉</text>
|
||||
<text x="1216" y="1434" fill="#062A22" font-size="0">copy</text>
|
||||
<text x="1230" y="1432" fill="#9AA4B2" font-size="14">Contact form →</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,116 @@
|
||||
# MOCKUPS.md
|
||||
Master mockup document. High-fidelity SVG boards live beside this file (`M1…M6.svg`); this document specifies every required state, including those not economical to board. All measurements reference SPACING_SYSTEM tokens; colours reference COLOUR_SYSTEM tokens.
|
||||
|
||||
Boards:
|
||||
- **M1** `M1-home-desktop-dark.svg` — homepage hero + proof strip + skills (dark, 1440)
|
||||
- **M2** `M2-home-desktop-light.svg` — same region, light theme
|
||||
- **M3** `M3-case-study-dark.svg` — JobTrack case-study page (dark, 1440)
|
||||
- **M4** `M4-mobile-dark.svg` — mobile homepage + open nav overlay (390)
|
||||
- **M5** `M5-header-states.svg` — navigation states, language switch, CV split button
|
||||
- **M6** `M6-projects-experience-contact.svg` — homepage lower half (projects cards, timeline, contact band)
|
||||
|
||||
---
|
||||
|
||||
## 1. Homepage — hero + narrative flow (M1/M2)
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────┐
|
||||
│ HEADER 72px: [CB●] Projects Experience About Contact │ EN|NO ☾ [CV ▾] │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ (faint dot-grid + accent halo, top-left biased) │
|
||||
│ │
|
||||
│ mono-label: SYSTEMS DEVELOPER · TØNSBERG, NORWAY ┌──────────┐ │
|
||||
│ display: Connor Babbington │ portrait │ │
|
||||
│ h3-weight: I design, build and run web systems. │ hairline │ │
|
||||
│ body-lg: Eight-plus years delivering internal │ frame, │ │
|
||||
│ software for UK local government — now │ mono │ │
|
||||
│ building full-stack products in Norway. │ caption │ │
|
||||
│ ── trace line ──┐___________ ● status: open to work └──────────┘ │
|
||||
│ │
|
||||
│ chips: [Tønsberg, Norway] [Work permit ✓] [Remote/hybrid/on-site] │
|
||||
│ [English native · Norsk B1] │
|
||||
│ CTAs: [■ View projects] [□ Download CV · pdf] │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ PROOF STRIP (4 tiles, surface-1, hairline): 8+ YRS · PUBLIC SECTOR · │
|
||||
│ .NET/REACT/DOCKER · BASED IN NORWAY — mono labels, stat numerals │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
- Hero text column 7/12 cols; portrait 4/12, offset 1. Portrait: outdoor headshot, 4:5, radius 16, duotone in dark.
|
||||
- Scroll behaviour: header compresses at 120px; sections reveal per A2.
|
||||
- Hover states: CTAs per MICRO_INTERACTIONS buttons; portrait has no hover (non-interactive).
|
||||
|
||||
## 2. Skills section (M1 lower)
|
||||
|
||||
Three `surface-1` cards, 4/12 each, mono-labels `01 — DEVELOPMENT` / `02 — DEVOPS & INFRASTRUCTURE` / `03 — PRACTICES`; one context sentence (ink-muted) + chip rows. Card hover: border-strong. No proficiency bars.
|
||||
|
||||
## 3. Projects section — case-study cards (M6)
|
||||
|
||||
Two large cards (6/12 each): screenshot bleeding to top frame edge (browser-chrome mock), status chip overlaid top-right (`ACTIVE` accent / `IN DEVELOPMENT` amber), h3 title, 1-line problem statement, stack chips, ghost link `Read case study →`. Third slim full-width card for homelab: no screenshot; small topology glyph + one line. Hover per A3 (4px screenshot shift, border glow, arrow nudge).
|
||||
|
||||
## 4. Experience timeline (M6)
|
||||
|
||||
Left hairline spine with period dots. Featured item (System Developer, Warwickshire County Council, 2015–2023): expanded — role h4, employer + location mono-meta, 3 outcome bullets. Earlier roles: compact one-liners at 60% emphasis (`2017–2021 · Sales Representative · Royal Vapes` etc.). Education node at bottom. Timeline draws on entry (A8).
|
||||
|
||||
## 5. About section
|
||||
|
||||
7/12 text (3 short paragraphs per CONTENT_STRATEGY), 4/12 photo (same portrait, natural grade in light). Language chips repeated (`English — native` `Norsk — B1, aktiv utvikling`). Interests one mono-meta line, no icons.
|
||||
|
||||
## 6. Contact (M6 band + page)
|
||||
|
||||
Homepage band: `surface-2`, h2 "Looking for a systems developer in Norway?" / NO: "Ser dere etter en systemutvikler?", primary e-mail button (click-to-copy), secondary "Contact form →".
|
||||
Contact page: 6/12 form (name/e-mail/message, labels above, per MICRO_INTERACTIONS form), 5/12 direct channels card: e-mail (copy), LinkedIn, git.cesnimda.uk, response-time note. Success state per A9.
|
||||
|
||||
## 7. Navigation states (M5)
|
||||
|
||||
Boards show: (a) default header, (b) scrolled/compressed with blur, (c) CV split-button open (EN/NO menu with file sizes), (d) language switch EN-active vs NO-active with sliding thumb, (e) focus-visible rings on nav item + toggle, (f) mobile header + full-screen overlay (nav stack, language segmented control, theme toggle, CV buttons, contact shortcuts).
|
||||
|
||||
## 8. Mobile layouts (M4)
|
||||
|
||||
390px reference. Order: slim header (56px: monogram · CV icon-button · burger) → mono-label + name (40px) → summary → chips (wrap, 2 rows) → CTAs stacked full-width → proof strip 2×2 tiles → skills cards stacked → project cards full-width (screenshot 16:10) → timeline (spine indented 16px) → about → contact band → footer. Touch targets ≥44px; no parallax; reveals opacity-only.
|
||||
|
||||
## 9. Dark + light (M1 vs M2)
|
||||
|
||||
Identical geometry; token swap only. Light: shadows-card active on cards, dot-grid at 5% instead of 8%, accent `#0E8C72`, portrait natural. Boards demonstrate both to prove parity.
|
||||
|
||||
## 10. Loading states
|
||||
|
||||
Specified in MOTION_GUIDELINES §4; visually: skeleton = `surface-2` rounded block, aspect-locked, mono caption `loading capture…` bottom-left, no shimmer sweep (shimmer reads as ad-tech; static + cross-fade is calmer). Form submit dots and language cross-fade per A7/A9.
|
||||
|
||||
## 11. Language switch UX (M5 detail)
|
||||
|
||||
- Segmented `EN | NO` control, header right cluster, thumb slide 160ms.
|
||||
- Switching maps current page → same page other locale (URL strategy per IA §5); content cross-fade per A7; scroll ratio preserved.
|
||||
- First-visit hint (browser prefers nb): dismissible one-line banner under header: `Denne siden finnes på norsk → [Bytt] [×]` — shown once, never auto-redirect.
|
||||
- All chrome strings localise with content (nav labels, CV button, form labels, alt text, skip-link).
|
||||
|
||||
## 12. Case-study page (M3)
|
||||
|
||||
```
|
||||
┌ header ────────────────────────────────────────────────────────────────┐
|
||||
│ mono-label: CASE STUDY · PERSONAL PRODUCT │
|
||||
│ h1: JobTrack [ACTIVE ●] │
|
||||
│ body-lg: A job-application workspace covering the full journey │
|
||||
│ from application to follow-up. │
|
||||
│ chips: .NET 9 · React · TypeScript · SQLite · FastAPI · Ollama · │
|
||||
│ Docker links: [Repository ↗] │
|
||||
├─ hero screenshot (browser frame, full-bleed to 1200) ─────────────────┤
|
||||
├─ TL;DR box (surface-2, 3px accent left edge): What/Why/Stack/Role ────┤
|
||||
│ ┌ mini-TOC (sticky) ──┐ │
|
||||
│ ## Problem & context (72ch) │ Problem │ │
|
||||
│ ## Architecture │ Architecture ● │ │
|
||||
│ [diagram breakout 1200px] │ Decisions │ │
|
||||
│ ## Key decisions & trade-offs │ Security │ │
|
||||
│ numbered blocks: choice/alt/why │ Outcome │ │
|
||||
│ ## Security & production notes │ Next │ │
|
||||
│ ## Screenshots (lightbox gallery) └─────────────────────┘ │
|
||||
│ ## What I'd improve next │
|
||||
├─ footer nav: ← InboxIntel | All projects ─────────────────────────────┤
|
||||
```
|
||||
Diagram style per COLOUR_SYSTEM §3.5: surface nodes, accent flow, amber external systems (Gmail API), mono labels.
|
||||
|
||||
## 13. Asset integration notes
|
||||
|
||||
- **Portraits:** outdoor headshot → hero/about; square crop → favicon-adjacent/OG fallback; suit photo → not used on site (kept for LinkedIn/CV).
|
||||
- **JobTrack media plan** (from `website_details.md`): applications table, job-details dialog, Gmail import view, dashboard analytics — 4 captures + 1 architecture diagram + workflow diagram.
|
||||
- **InboxIntel media plan:** dashboard grid, cleanup preview/confirm flow, architecture diagram (Clean Architecture layers), sync-worker sequence sketch.
|
||||
- Screenshots need a capture pass at 2× on seeded demo data — **flagged as a Phase 3 prerequisite task** (no real personal job-hunt data on the public site; seed fictional companies).
|
||||
@@ -0,0 +1,38 @@
|
||||
# Portfolio Redesign — Phase 1 Deliverables (Design, no code)
|
||||
|
||||
Status: **complete, awaiting approval** · July 2026
|
||||
|
||||
## Index
|
||||
|
||||
### 01 — Research & strategy
|
||||
- [DESIGN_RESEARCH.md](01-research/DESIGN_RESEARCH.md) — audit of cesnimda.co.uk, portfolio/SaaS pattern research, strategic conclusions
|
||||
- [USER_PERSONAS.md](01-research/USER_PERSONAS.md) — recruiter, eng manager, Norwegian CTO, interviewer
|
||||
- [RECRUITER_BEHAVIOUR_ANALYSIS.md](01-research/RECRUITER_BEHAVIOUR_ANALYSIS.md) — scan behaviour, two-lane rule, Norway specifics, acceptance criteria
|
||||
|
||||
### 02 — UX structure
|
||||
- [INFORMATION_ARCHITECTURE.md](02-ux/INFORMATION_ARCHITECTURE.md) — site map, nav model, bilingual URL strategy, content model
|
||||
- [USER_JOURNEYS.md](02-ux/USER_JOURNEYS.md) — five validated journeys + requirements checklist
|
||||
- [CONTENT_STRATEGY.md](02-ux/CONTENT_STRATEGY.md) — voice, hero copy drafts (EN/NO), case-study angles, CV improvement plan
|
||||
- [CANONICAL_CONTENT.md](02-ux/CANONICAL_CONTENT.md) — master CV facts (provided by Connor, July 2026); single source for all copy
|
||||
|
||||
### 03 — Visual design system ("Kontrollrom")
|
||||
- [DESIGN_SYSTEM.md](03-visual/DESIGN_SYSTEM.md) — direction, identity, surfaces, components
|
||||
- [TYPOGRAPHY_GUIDE.md](03-visual/TYPOGRAPHY_GUIDE.md) · [COLOUR_SYSTEM.md](03-visual/COLOUR_SYSTEM.md) · [SPACING_SYSTEM.md](03-visual/SPACING_SYSTEM.md) · [MOTION_GUIDELINES.md](03-visual/MOTION_GUIDELINES.md)
|
||||
|
||||
### 04 — Interaction design
|
||||
- [ANIMATION_CONCEPTS.md](04-interaction/ANIMATION_CONCEPTS.md) — A1–A10 named concepts + rejected list
|
||||
- [MICRO_INTERACTIONS.md](04-interaction/MICRO_INTERACTIONS.md) — per-component states & keyboard behaviour
|
||||
- [SCROLL_EXPERIENCE.md](04-interaction/SCROLL_EXPERIENCE.md) · [CURSOR_INTERACTIONS.md](04-interaction/CURSOR_INTERACTIONS.md)
|
||||
|
||||
### 05 — Mockups
|
||||
- [MOCKUPS.md](05-mockups/MOCKUPS.md) — master spec covering all 11 required mockup scopes
|
||||
- Boards: M1 home desktop dark · M2 home desktop light (NO locale) · M3 case study · M4 mobile + nav overlay · M5 header/nav/control states · M6 projects/timeline/contact
|
||||
|
||||
## Key decisions requiring sign-off before Phase 2
|
||||
1. **Positioning:** "Systems Developer, 8+ years" — no "senior" self-label; seniority shown via evidence (CONTENT_STRATEGY §1)
|
||||
2. **IA:** multi-page (home + case studies + about/experience/contact/cv) replacing the one-pager (IA §1)
|
||||
3. **Bilingual URLs:** EN at root, NO under `/no/` with localised slugs; switch maps page↔page; no auto-redirect (IA §5)
|
||||
4. **Design direction:** "Kontrollrom" — dark-first, aurora-teal single accent, mono metadata, trace motif (DESIGN_SYSTEM)
|
||||
5. **InboxIntel framed as `IN DEVELOPMENT`** (honest status) (CONTENT_STRATEGY §4)
|
||||
6. **CV rework:** ATS-safe single-column variant becomes the download default (CONTENT_STRATEGY §6)
|
||||
7. **Screenshot capture pass on seeded demo data** = Phase 3 prerequisite (MOCKUPS §13)
|
||||
Reference in New Issue
Block a user