# Norlys Kystlinjen · FY2026 · Design rationale

## Rationale (≤300 words)

**Grid.** One 8px baseline. Line-heights are 1.5 on 16px body (24px) and 1.6 on 20px lede
(32px). Content sits in a 1200px column with fluid gutters
(`clamp(16px, 4vw, 48px)`); the routes section is a 5fr/7fr split that collapses to one
column under 980px. Rhythm is 64px between sections, 48px between figures.

**Type.** Three locally available stacks, declared once: `--font-serif` Georgia,
`--font-grotesk` Helvetica Neue / Segoe UI, `--font-mono` Cascadia Mono / Consolas. One
modular scale, ratio 1.25 on a 16px base: 10.2 / 12.8 / 16 / 20 / 25 / 31 / 39 / 49 / 61px.
Serif carries display and the CEO letter; grotesque carries body; mono carries every
number, table, and label. The scale is never deviated from.

**Palette.** Cool neutrals with a single warm accent.
`--paper #EEF1F1` · `--panel #E3E8E8` · `--sea #D7E1E1` · `--land #E6EBE6` ·
`--ink #1B2528` · `--muted #4A585C` · `--line #C4CFCF` · `--deep #12212B` ·
`--accent #8C3E10` · `--warm #B0571E`. Body `#1B2528` on `#EEF1F1` = 13.8:1; muted
`#4A585C` on paper = 6.5:1; accent `#8C3E10` on paper = 6.6:1. `--warm` (4.4:1) is
restricted to large type and graphic marks (≥3:1 contexts).

**Three taste decisions.**
1. *Open with the crossing, not a hero.* The first thing is a real timetable strip
   (Stavanger→Kristiansand, 06:12–09:04) with a ferry that actually sails it. It is
   characteristic of the subject and refuses the landing-page reflex the brief warns
   against.
2. *One source of truth.* Every figure derives from a single inline `data.js` literal;
   the map, tables, and charts can never disagree, and the total is exactly 6,100,000.
   Consistency is the whole argument of a report.
3. *One warm accent as a state language.* `--warm` means "this is the thing you picked"
   and "this is the express line." Cross-highlighting between map, table, and charts
   reads instantly because selection is a single colour, not a pile of new styles.

---

## Acceptance checklist

| # | Requirement | Status |
|---|-------------|--------|
| 1 | Six months of passenger counts per route, single inline literal, total = 6.1M, all derived figures consistent | DONE — `js/data.js`; 34 routes × 6 months = 6,100,000 exactly; every on-page figure reads from it |
| 2 | Fleet table: 21 ships, name/year/length/capacity/fuel/route | DONE — fleet register section |
| 3 | Financial summary: revenue, EBITDA, CO2/pkm with two comparison years | DONE — FY2024/25/26 summary table |
| 4 | CEO letter (~320 words), board statement, sustainability note with methodology footnotes | DONE — letter body ~326 words; board statement; sustainability excludes + 3 method footnotes |
| 5 | Opening characteristic of subject (not centred headline + button) | DONE — timetable strip + sailing ferry |
| 6 | Key figures at a glance, one dominant number + comparisons | DONE — 6,100,000 dominant, six supporting stats |
| 7 | Interactive SVG route map: drawn coastline, clickable arcs, hover, legend, recognisably a map | DONE — hand-drawn coast + Karmøy, 34 focusable arcs, hover, legend, lat lines, compass, scale bar |
| 8 | Fleet table + ≥3 hand-drawn SVG ship profiles sharing one scale bar | DONE — Fenn / Nordlys / Sten, common waterline, shared 0–50 m scale bar |
| 9 | Financials: hand-drawn SVG bars, waterfall/bridge, line with shaded band; own scales + tick selection | DONE — `niceTicks()` 1/2/5/10 logic; revenue+EBITDA bars, revenue→EBITDA waterfall, CO2 line with band |
| 10 | Sustainability: emissions by fuel + small-multiples grid + honest exclusion note | DONE — by-fuel bars, 6 monthly small multiples, "what these numbers exclude" note |
| 11 | CEO letter as proper reading experience: ≤~70ch measure, footnotes with back links | DONE — `max-width:68ch`, 3 footnotes, ↩ back links |
| 12 | Governance/board compact directory legible at 320px | DONE — table stacks to labelled rows under 640px |
| 13 | Persistent nav: shows current section, keyboard, doesn't fight back button | DONE — sticky spy nav, native `#hash` links (back button returns), ARIA current |
| 14 | Meaningful reading progress | DONE — % + bar bound to real document scroll position |
| 15 | Cross-highlighting: selecting a route updates map, table, and charts | DONE — arc + row + fleet row highlight, monthly chart + details recompute |
| 16 | Fleet filter (fuel) + sort on any numeric column, no library | DONE — fuel chips + sort Built/Length/Capacity, hand-rolled. Note: below 640px the table stacks to labelled cards for legibility, so the sort headers (in the hidden `thead`) are unavailable there; the fuel filter still works. Sorting is fully available ≥640px |
| 17 | Anchor links land clear of sticky element | DONE — `scroll-margin-top` on `[id]`; measured heading top 144px vs 56px nav |
| 18 | Print stylesheet: sane A4, no clipped charts, no orphaned headings, no background wash | DONE — `@page A4`, `break-inside:avoid`, `break-after:avoid` on headings, backgrounds stripped |
| 19 | No console errors | DONE — verified across 6 viewports + interactions: none |
| 20 | No horizontal scroll below 1280px; usable at 320px | DONE — `scrollWidth == clientWidth` at 1280/1024/768/640/375/320; tables scroll in-viewport, fleet/board stack |
| 21 | Contrast ≥4.5:1 body / ≥3:1 large; pairs stated | DONE — ink/paper 13.8:1, muted/paper 6.5:1, accent/paper 6.6:1; warm 4.4:1 used only for large/graphic |
| 22 | Semantic landmarks, one h1, headings in order, every SVG chart paired with a data table or text alt | DONE — header/nav/main/footer, 1 h1, validated heading order, 12 labelled SVGs each with a table |
| 23 | Works with JS disabled where content is content; real focus order; honours reduced-motion | DONE — all data tables + letter + board are static HTML; noscript notes; focus order verified; `prefers-reduced-motion` stops the ferry and transitions |

**Nothing NOT DONE.** One documented trade-off: fleet column sorting is desktop/tablet-only (≥640px); on phones the table stacks to cards (filter still works).
