02 · The frame

Two hairlines, 1320px apart

The defining structure of the whole system. Look at the left and right edges of this page: those lines are not one element. Every horizontal band — nav, main, footer — draws its own side borders at the same width, so they read as continuous while horizontal rules cross them.

01

The idea

The effect is a technical drawing: content sits inside a measured frame rather than floating on a page. Three things make it read as deliberate rather than as a stray container.

  1. The rails never break.

    Every band repeats them at the same max-width. A band that omits the pair puts a visible gap in the line, and the notches will then appear to dock onto nothing. This is the most common way to get the frame wrong.

  2. Everything is 1px.

    No 2px borders, no shadow standing in for an edge, no rounded corners on the frame. The only radius in the system is 3px, on the outer edge of a docking notch.

  3. Rules are full-bleed; content need not be.

    A horizontal rule spans the whole band, edge to edge. The text inside it can sit in a narrower centred column. Making a rule match the text column collapses the effect entirely.

02

Rails

One rule, repeated on every band. That is the entire mechanism.

.shell,
.nav-inner,
.hero-footer,
.shell-foot-rule {
  max-width: var(--maxw);        /* 1320px */
  margin: 0 auto;
  border-left: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
}
Bands at reduced scale — the rails are continuous, the rules cross them
JourneyMRONav · 64px
Content band · .shell
Footer · 64px
The one failure mode. A full-width band that forgets the pair — a promo strip, a cookie bar, a section with its own background — cuts the rail into segments. If a band must be full-bleed, put the background on an outer element and the rails on an inner one at --maxw.
03

Grid background

Fixed behind everything, masked with a radial gradient so it fades out below the fold. It establishes the 96px module the layout implies without ever being the thing you look at. It is on this page — look past the content at the top of the viewport.

.grid-bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--hairline-faint) 1px, transparent 1px),
    linear-gradient(90deg, var(--hairline-faint) 1px, transparent 1px);
  background-size: 96px 96px;
  mask-image: radial-gradient(ellipse 120% 75% at 50% 0%, #000 38%, transparent 80%);
  opacity: 0.5;
}

It is the first element in <body>, and .shell carries z-index: 1 so content sits above it. pointer-events: none is not optional — without it the grid eats every click on the page.

04

Docking notches

The signature detail: page-turn controls that dock onto the rails from outside the frame, appearing fused to it. There are two live on this page — at the far left and right edges, vertically centred. The right one breathes. Try the arrow keys.

DeclarationWhy it is there
- 1px in the calc()Laps the notch's 1px border onto the rail. Without it there is a 1px gap and it reads as a floating button.
border-right: noneOn the left notch, the side meeting the rail must not draw its own border or you get a doubled 2px line. Mirrored on the right.
border-radius: 3px 0 0 3pxRounded on the outer edge only. The docked edge stays square.
32px × 120pxTall and narrow — reads as a tab on the frame, not a button beside it.
color: rgba(120,145,165,.5)The chevron is dim at rest; the accent is spent on hover only.
[hidden] { display: none !important }Lets JS toggle el.hidden = true at the end of a sequence. The !important beats the base display: flex.
@media (max-width: 760px)Below the frame width there is no rail to dock to, so both notches go.
Markup
<!-- direct children of <body>, outside the frame -->
<button type="button" class="svc7-arrow svc7-arrow-left" aria-label="Previous">
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
    <path d="M15 6l-6 6 6 6"/>
  </svg>
</button>
<a href="/services" class="svc7-arrow svc7-arrow-right svc7-arrow-breathe" aria-label="Next">
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1">
    <path d="M9 6l6 6-6 6"/>
  </svg>
</a>

stroke-width="1" matters — the chevron is hairline-weight to match the frame. A button for an in-page move, an anchor when it navigates; nothing keys off the tag or the id.

Keyboard

Left/right arrows flash the notch for 170ms, then .click() it. The handler never contains navigation logic, so mouse and keyboard cannot diverge. It is inert while typing in a field, while a dialog is open, and below 760px where the notches are gone. Shipped in journeymro.js; nothing to wire up.

Self-check
  • The notch's border touches the rail — no gap, no doubled line.
  • Rounded on the outer edge only; the docked edge is square.
  • Dim at rest, accent on hover.
  • Arrow keys flash the notch before the turn runs.
  • Below 760px both notches are gone and the arrow keys are inert.
  • No browser focus ring after clicking one and then pressing a key.
  • prefers-reduced-motion stops the breathe pulse.
05

Reticles

Crosshair ticks for a panel corner. A technical-drawing mark, not a decoration — at most one or two per screen, or they become wallpaper.

.reticle .tl / .tr / .bl / .br
Panel · reticles at all four corners
06

Page skeleton

Order matters — the rails only line up if every band is present and at the same width.

<body>
  <div class="grid-bg"></div>

  <header class="nav">
    <div class="nav-inner">…</div>
  </header>

  <!-- drawer is a SIBLING of header, never nested inside it -->
  <div class="nav-drawer" id="drawer" hidden>…</div>

  <!-- notches: fixed, outside the frame -->
  <button class="svc7-arrow svc7-arrow-left">…</button>
  <button class="svc7-arrow svc7-arrow-right">…</button>

  <main class="shell page-main">
    <div class="page-scroll" tabindex="0">…</div>
  </main>

  <div class="page-rule"></div>
  <div class="hero-footer"><span>© 2026 JOURNEYMRO</span></div>
</body>

Use .page-flow instead of .page-main for a page that scrolls normally — this one does.

07

Viewport-locked pages

A page that must not scroll locks to the viewport and scrolls internally, which is what keeps the rails and the footer fixed while content moves.

.page-main {
  min-height: calc(100vh - var(--chrome));   /* --chrome is 130px */
  max-height: calc(100vh - var(--chrome));
  display: flex;
  flex-direction: column;
  padding: clamp(1rem, 2.5vh, 1.8rem) var(--gutter) clamp(1rem, 4vh, 2.5rem);
  overflow: hidden;
}
.page-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
Count both hairlines. 64 (nav) + 1 (nav border) + 1 (page rule) + 64 (footer) = 130. Subtracting 129 overflows the viewport by exactly 1px and puts a scrollbar on a page that is supposed to be fixed. That is what --chrome is for.
Give .page-scroll tabindex="0" and focus it. The page itself no longer scrolls, so arrow keys, Page Up/Down and space would otherwise do nothing at all. Suppress its focus ring — .scroll-y already does.
Below 760px the lock is dropped automatically. A phone's address bar resizes the viewport mid-scroll, so 100vh is a lie there and a locked page either clips its own footer or grows a second scrollbar. See Mobile.