Skip to content

Design specification · June 2026

The PreBizSys design system, complete.

Everything needed to produce pages that match the PreBizSys design language exactly: tokens, type, spacing, breakpoints, components, motion, images, performance budgets, and copy rules. This page renders the system live and carries every copy-paste value. Nothing in your output should invent a value that is not on this page.

1 · Identity snapshot

Who this system speaks for.

PreBizSys (Premier Business Systems) gets under-digitized local and small businesses found: on Google, on the map, and in AI answers.

The wordmark

The wordmark is text, never an image: the name in the display face, bold, tight tracking, ending in an orange full stop. On dark grounds the name is white; on light grounds it is graphite. The mono sub-label rides underneath in uppercase. The sub-label hides at the smallest breakpoint (below 561px).

Brand lockup, dark ground (canonical)

PreBizSys.
PartTreatment
Name "PreBizSys"display face · weight 700 · 1.35rem · letter-spacing -0.02em · white on dark, graphite on light
Stop "."always --pbs-orange (#F25C05), weight 700: the one brand mark
Sub-label"PREMIER BUSINESS SYSTEMS" · mono face · 0.62rem · letter-spacing 0.18em · uppercase · muted

The voice, in three lines

01

A confident, transparent craftsman.Plain English, headline first. We explain what we do and show the work; we never hide behind jargon or hype.

02

We sell the work, never a result we can't control.No promised rankings, no promised traffic. Google and the AI engines decide who shows up; what we commit to is the work itself, done right.

03

Truthful, always.Every proof element is real. Nothing is invented to look better, and a claim we can't verify does not ship.

The positioning one-liner

Get your business found: on Google, on the map, and in AI answers.

Supporting lines in active use: "Everything you need to get found, built and run as one system." (what we do) · "We sell the work, never a promised outcome." (the floor line) · "See where you're invisible." (the call to action frame). The free audit is the entry point of every conversion path.

2 · Design tokens

One token source. One namespace. Semantic roles.

Every color, size, space, radius, shadow, and easing on a PreBizSys page comes from one set of CSS custom properties under one namespace: pbs-. Components build against semantic roles, never raw hex values.

Color: the raw palette

--pbs-graphite#1C2126primary ink: headlines, dark sections, footer, body-on-light
--pbs-steel#2D343Csecondary dark panels, footer ground
--pbs-concrete#6B7280captions, placeholder text
--pbs-offwhite#F7F7F5light page ground
--pbs-white#FFFFFFcards, content panels, paper zones
--pbs-orange#F25C05the one decisive accent (reserved)
--pbs-orange-deep#C9430Aaction hover / active
--pbs-green#1F9D55status only: live tags, verified ticks

Color: the semantic roles

Author against these. If a design need has no role, the fix is adding a token to the source, never an inline hex.

RoleValueUse
--pbs-bggraphitedark page ground (hero, statement bands)
--pbs-bg-altoffwhitelight section ground
--pbs-surfacewhitecards and panels
--pbs-text#EDEEF0body text on dark grounds
--pbs-text-on-lightgraphitetext on light grounds
--pbs-text-muted#A6ACB4muted text on dark grounds
--pbs-muted-on-light#586069muted text on light grounds
--pbs-borderrgba(255,255,255,0.10)hairlines on dark
--pbs-border-lightrgba(28,33,38,0.12)hairlines on light
--pbs-actionorangeprimary CTA, key marks, eyebrow tick, link underline
--pbs-action-hoverorange-deepCTA hover and active states
--pbs-on-actionwhitetext on the orange CTA
--pbs-card-shadow0 1px 2px rgba(28,33,38,0.06), 0 14px 34px rgba(28,33,38,0.10)resting card elevation
  • One namespace, forever. Every custom property starts with --pbs- and every class with pbs- (component classes like btn and plan are part of the locked component layer). Never introduce a second namespace. Why: an earlier build ran two token namespaces at once, with three tokens that were never declared anywhere; production rendered different blacks and creams at the same time and nobody could say which was canonical.
  • Never ship var(--x, #fallback) with --x undeclared. The fallback silently becomes the real value and drifts. Every token is declared in the one source AND has a consumer.
  • Orange is reserved. It carries the primary CTA, key marks, the eyebrow tick, and the link underline. It is never a decorative background, never body text, never spread across competing elements. One decisive accent is the brand.
  • Green is a status signal only ("Live" tags, verified ticks), never a second accent.

Type: the system

display: "Space Grotesk" · body: "Inter" · mono: "Space Mono" (full fallback stacks in the token block below)

Mono eyebrow voice · clamp(0.72rem, 0.68rem + 0.2vw, 0.82rem)

Display one

h1 · clamp(2.6rem, 1.7rem + 4.6vw, 5rem) · weight 700 · line-height 1.04 · tracking -0.02em (a height-aware hero variant exists: clamp(2.5rem, 1.4rem + 3.4vw + 0.8vh, 4.35rem))

Display two carries section titles

h2 · clamp(1.9rem, 1.4rem + 2.2vw, 2.85rem) · weight 700 · line-height 1.04

Display three carries card titles

h3 · clamp(1.3rem, 1.1rem + 1vw, 1.75rem) · weight 600 · line-height 1.15

The lead voice introduces a section in two calm lines.

lead · clamp(1.15rem, 1.05rem + 0.55vw, 1.45rem) · line-height 1.25

The body voice does the everyday reading. It holds a comfortable measure (around 60 to 76 characters), a steady 1.6 line height, and never gets decorated. Numbers and labels step into the mono voice; emphasis stays semibold, never shouty.

body · clamp(1rem, 0.96rem + 0.25vw, 1.125rem) · line-height 1.6

  • Weights: 400 regular, 500 medium, 600 semibold, 700 bold. Nothing lighter, nothing heavier.
  • Every text element takes a tokened role. A new role is an addition to the token source, never an inline font-size. Why: on an earlier build one page quietly ran its own fonts and sizes because nothing enforced a single system; it read like a different site.
  • The scale is fluid by clamp (minimum at a 360px viewport, maximum at 1440px). Never author per-breakpoint font-size overrides; the clamp already does the responsive work.
  • Loading strategy (no layout shift): self-host the three families as WOFF2, declare them with font-display: swap, and pair each with a metric-override fallback face (size-adjust, ascent-override, descent-override tuned so the system fallback occupies identical space). The swap then moves nothing. Preload only the two above-the-fold faces (display 700 and body 400), as woff2, with crossorigin. Never load fonts from a third-party host in production. Why: on an earlier build the font swap landed around 2.8 seconds in and shifted the hero about 37px, producing intermittent layout-shift scores of 0.382.

Spacing: one 8px-base scale, three rhythm layers

--pbs-space-1 · 0.25rem · 4px
--pbs-space-2 · 0.5rem · 8px
--pbs-space-3 · 0.75rem · 12px
--pbs-space-4 · 1rem · 16px
--pbs-space-5 · 1.5rem · 24px
--pbs-space-6 · 2rem · 32px
--pbs-space-7 · 3rem · 48px
--pbs-space-8 · 4rem · 64px
--pbs-space-9 · 6rem · 96px
--pbs-space-10 · 8rem · 128px
  • Macro (section rhythm): every content band gets padding-block: var(--pbs-section-pad) where --pbs-section-pad: clamp(3.5rem, 2rem + 6vw, 7rem), roughly 56px on mobile opening to 112px at 1440px. This very page demonstrates it.
  • Meso (one measure): content lives in a container with max-width: 1200px and gutters of clamp(1.25rem, 0.5rem + 3.5vw, 3rem), so content edges align site-wide.
  • Micro (element rhythm): margins and gaps come from the fixed steps above. The micro steps are intentionally NOT fluid; fluidity belongs to the macro rhythm and the type.
  • One sectioning convention. Every content band on every page is <section class="pbs-section pbs-zone--..."><div class="pbs-container">. The only sanctioned exemptions: the site header, the site footer, the full-viewport homepage hero, and the compact page hero. Nothing else, ever. Why: an earlier build let four different sectioning conventions coexist on one site; a single page accumulated five different container widths and heading sizes from 12px to 54px. A closed convention with a named exemption list makes that impossible.
  • Tune spacing only upward. Values may be adjusted to LIFT a thin page to the generous register, never to compress one.

Radius, elevation, motion vocabulary

TokenValueUse
--pbs-radius-sm4pxfocus rings, small chips
--pbs-radius8pxbuttons, inputs, code chips
--pbs-radius-lg14pxcards, panels, image frames
--pbs-radius-pill999pxpills: eyebrow badge, chips, plan tags
--pbs-shadow-sm0 1px 2px rgba(0,0,0,0.20)subtle lift on dark
--pbs-shadow0 10px 30px rgba(0,0,0,0.35)floating panels (menus, drawers)
--pbs-shadow-cta0 8px 24px rgba(242,92,5,0.32)the warm glow under the orange CTA
--pbs-easecubic-bezier(0.22, 1, 0.36, 1)decelerate: content arrives and settles
--pbs-ease-springcubic-bezier(0.34, 1.56, 0.64, 1)gentle overshoot: hover pops
--pbs-dur-fast / --pbs-dur / --pbs-dur-slow140ms / 260ms / 420msmicro-interactions
--pbs-enter-dur / --pbs-enter-move / --pbs-enter-step820ms / 26px / 110msentrance rise: duration, distance, stagger
--pbs-link-underline / --pbs-link-thickness / --pbs-link-offset / --pbs-link-washorange / 2px / 0.15em / rgba(242,92,5,0.10)the prose-link system (section 4)

Copy-paste: the canonical token block (single source, complete)

:root {
  /* COLOR: raw palette */
  --pbs-graphite:      #1C2126;  /* primary ink */
  --pbs-steel:         #2D343C;  /* secondary dark panels, footer */
  --pbs-concrete:      #6B7280;  /* captions, placeholders */
  --pbs-offwhite:      #F7F7F5;  /* light page ground */
  --pbs-white:         #FFFFFF;  /* cards, panels */
  --pbs-orange:        #F25C05;  /* the one decisive accent (reserved) */
  --pbs-orange-deep:   #C9430A;  /* action hover / active */
  --pbs-green:         #1F9D55;  /* status signals only */

  /* COLOR: semantic roles (build against these) */
  --pbs-bg:            var(--pbs-graphite);
  --pbs-bg-alt:        var(--pbs-offwhite);
  --pbs-surface:       var(--pbs-white);
  --pbs-text:          #EDEEF0;               /* body on dark */
  --pbs-text-on-light: var(--pbs-graphite);
  --pbs-text-muted:    #A6ACB4;               /* muted on dark */
  --pbs-muted-on-light: #586069;              /* muted on light */
  --pbs-border:        rgba(255,255,255,0.10);/* hairlines on dark */
  --pbs-border-light:  rgba(28,33,38,0.12);   /* hairlines on light */
  --pbs-action:        var(--pbs-orange);
  --pbs-action-hover:  var(--pbs-orange-deep);
  --pbs-on-action:     var(--pbs-white);
  --pbs-card-shadow:   0 1px 2px rgba(28,33,38,0.06), 0 14px 34px rgba(28,33,38,0.10);

  /* TYPE */
  --pbs-font-display: "Space Grotesk", "Archivo", "Prompt", -apple-system,
                      BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --pbs-font-body:    "Inter", "Karla", -apple-system, BlinkMacSystemFont,
                      "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --pbs-font-mono:    "Space Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --pbs-fs-eyebrow:   clamp(0.72rem, 0.68rem + 0.2vw, 0.82rem);
  --pbs-fs-body:      clamp(1rem,    0.96rem + 0.25vw, 1.125rem);
  --pbs-fs-lead:      clamp(1.15rem, 1.05rem + 0.55vw, 1.45rem);
  --pbs-fs-h3:        clamp(1.3rem,  1.1rem + 1vw,     1.75rem);
  --pbs-fs-h2:        clamp(1.9rem,  1.4rem + 2.2vw,   2.85rem);
  --pbs-fs-h1:        clamp(2.6rem,  1.7rem + 4.6vw,   5rem);
  --pbs-fs-h1-hero:   clamp(2.5rem, 1.4rem + 3.4vw + 0.8vh, 4.35rem);

  --pbs-fw-regular:   400;
  --pbs-fw-medium:    500;
  --pbs-fw-semibold:  600;
  --pbs-fw-bold:      700;
  --pbs-lh-tight:     1.04;   /* big display headlines */
  --pbs-lh-snug:      1.25;   /* subheads */
  --pbs-lh-body:      1.6;    /* running text */
  --pbs-ls-tight:     -0.02em;/* display tracking */
  --pbs-ls-wide:      0.14em; /* eyebrow / overline tracking */

  /* SPACING (8px base scale) */
  --pbs-space-1:  0.25rem;  /*   4px */
  --pbs-space-2:  0.5rem;   /*   8px */
  --pbs-space-3:  0.75rem;  /*  12px */
  --pbs-space-4:  1rem;     /*  16px */
  --pbs-space-5:  1.5rem;   /*  24px */
  --pbs-space-6:  2rem;     /*  32px */
  --pbs-space-7:  3rem;     /*  48px */
  --pbs-space-8:  4rem;     /*  64px */
  --pbs-space-9:  6rem;     /*  96px */
  --pbs-space-10: 8rem;     /* 128px */

  /* LAYOUT */
  --pbs-container:     1200px;
  --pbs-container-pad: clamp(1.25rem, 0.5rem + 3.5vw, 3rem);
  --pbs-section-pad:   clamp(3.5rem, 2rem + 6vw, 7rem);

  /* RADIUS / ELEVATION */
  --pbs-radius-sm: 4px;
  --pbs-radius:    8px;
  --pbs-radius-lg: 14px;
  --pbs-radius-pill: 999px;
  --pbs-shadow-sm: 0 1px 2px rgba(0,0,0,0.20);
  --pbs-shadow:    0 10px 30px rgba(0,0,0,0.35);
  --pbs-shadow-cta:0 8px 24px rgba(242,92,5,0.32);

  /* MOTION */
  --pbs-ease:        cubic-bezier(0.22, 1, 0.36, 1);
  --pbs-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --pbs-dur-fast:    140ms;
  --pbs-dur:         260ms;
  --pbs-dur-slow:    420ms;
  --pbs-enter-dur:   820ms;
  --pbs-enter-move:  26px;
  --pbs-enter-step:  110ms;

  /* LINKS */
  --pbs-link-underline: var(--pbs-action);
  --pbs-link-thickness: 2px;
  --pbs-link-offset:    0.15em;
  --pbs-link-wash:      rgba(242,92,5,0.10);
}
3 · Breakpoints

Three cutoffs, desktop-first, fluid in between.

Most of the responsiveness is continuous: the type scale and macro spacing flex by clamp at every width. The layout itself reflows structurally at exactly three shared cutoffs.

base (desktop, no query)
max-width: 980px
max-width: 860px
max-width: 560px

Resize this window: the live tier readout appears here.

TierQueryWhat reflows
Desktopbase (no query)the full layout; the container caps at 1200px and centers with gutters
Nav complementmin-width: 861pxdesktop nav links shown (the hamburger's complement)
Tablet and belowmax-width: 980pxmulti-column grids drop columns (3-col footer to 2-col; 2-col feature rows to 1-col)
Mobile-landscape and belowmax-width: 860pxnav collapses to the hamburger drawer; 3-up card grids and plan tiles go single-column
Mobile-portrait and belowmax-width: 560pxcapture rows stack; buttons go full-width; the brand sub-label hides; nav bar drops to 64px
  • Cascade direction is desktop-first. The base styles are the widest layout; max-width queries override downward. Author in that order.
  • The cutoffs are exact. Note the strict less-than semantics: 980px wide is INSIDE the 980 tier, 981px is desktop. When you verify a layout, test both sides of each cutoff (981/980, 861/860, 561/560); a reflow defect shows up AT its cutoff, not near it.
  • Never add a fourth breakpoint for a one-off. If a layout breaks between cutoffs, the fix is usually a fluid value (clamp, auto-fit grid, flex-wrap), not a new query.
  • No per-breakpoint font sizes and no per-breakpoint section padding. The clamp tokens already flex those continuously; breakpoints are for STRUCTURE only (column counts, show/hide, stacking direction).
  • Verification widths: 360, 768, 1024, and 1440 span all four tiers and are the headline check; zero horizontal overflow at every width.
4 · Components

The component language, rendered and annotated.

Every component below is live on this page with its production values. Each carries its usage rule and, where one exists, the real failure the rule prevents.

Buttons

  • The primary button is the free-audit pattern and there is exactly ONE orange action per view: the free-audit CTA. It carries the orange fill, the warm glow shadow, a 3px hover lift, and an arrow that nudges 4px right on hover. In a narrow header it swaps its long label ("Get My Free Audit") for a short one ("Free Audit") below 561px.
  • Everything else is a ghost. Secondary actions use the bordered ghost; the accent-ghost (orange border at 45% opacity, orange arrow) is the "second path" treatment, used for the no-website-yet route. Ghosts never fill with orange.
  • Geometry: padding 12px 24px (large: 16px 32px), radius 8px, weight 600, body face, line-height 1. Hover transforms use the decelerate ease at 140ms.
  • Why the reservation rule exists: the one decisive accent is what makes the page read as engineered rather than decorated. Two orange actions on one screen is a spec violation, not a styling choice.

The section head trio

Eyebrow voice

A section title that states the point.

A lead that earns the next thirty seconds of reading in one or two calm sentences.

  • Every section opens with the same trio: mono uppercase eyebrow with the 6px orange tick, display-bold title, calm lead. Head block caps at 60ch with a 48px bottom margin; center it only for symmetric bands.
  • The eyebrow tick is one brand mark repeated everywhere (hero badge dot, section eyebrows, the statement-band rule). Keep it a 6px circle in orange.

Cards

A card that earns trust

White surface, hairline border, 14px radius, the resting card shadow, and a 4px hover lift with a deeper shadow.

Icon tiles are filled

44px tile, graphite fill, white line-art glyph at 1.8 stroke. Pale tinted tiles nearly vanished in testing; filled tiles stay legible.

One card, every context

The same card carries features, work samples, and FAQ-adjacent content. Title in display semibold; body in the muted-on-light voice.

  • Cards are the single most reused unit. Always: --pbs-surface background, --pbs-border-light hairline, --pbs-radius-lg, --pbs-card-shadow, fluid padding clamp(1.5rem, 1rem + 1.4vw, 2.25rem).
  • Hover: translateY(-4px) plus shadow escalation to 0 4px 10px rgba(28,33,38,0.08), 0 22px 48px rgba(28,33,38,0.16), at 260ms decelerate. Transform-only movement, never layout-affecting properties.
  • A "Live" status tag (mono, uppercase, green dot and text) may ride a card ONLY when the thing it points at is genuinely live.

Section bands: the dark/light rhythm

Pages alternate tonal zones so the scroll reads as deliberate beats, and this page does it too. Tone is set ONCE per section by the zone class; components re-skin off the zone, so authors never hand-pick per-element colors.

pbs-ground · hero / statement / closing
pbs-zone--light · off-white
pbs-zone--dark · orange-glow dark
pbs-zone--light
pbs-zone--paper · pure white
pbs-ground · closing capture
  • pbs-ground is the blueprint ground: graphite gradient, warm orange glow, the 64px engineering grid at 6% opacity. Reserved for the hero, the promise/statement moment, and the closing capture. It is the only gridded dark.
  • pbs-zone--dark is the content dark: same gradient and glow, NO grid. Use it for feature bands that need weight.
  • pbs-zone--light / pbs-zone--paper alternate off-white and pure white so stacked light bands read as distinct beats; adjacent light bands get an automatic hairline border between them.
  • Never stack two gridded grounds back to back. The grids will not align and the seam shows.

Pricing tiles: the ascending ladder

Keep watching

Visibility Watch

$79/month

or $790/year (2 months free)

The recurring external scan with a plain-English report of where you stand and what moved.

Fix the biggest gap

Get-Found Starter

$399one-time

$349 if you're already on Visibility Watch

The single-biggest-gap fix. What you pay credits forward.

Most Complete

Run it all for you

Managed

$1,495/month

your $1,495 Deep-Dive Audit becomes month one if you continue

Everything below it, plus the ongoing work that runs the whole program.

  • Tiles are an ASCENDING ladder, never co-equal cards. Order left to right (or top to bottom stacked) from the smallest step up to the destination. The cheaper steps are doors, not alternatives.
  • The anchor tile (highest tier) gets the plan--anchor treatment: a stronger border (rgba(28,33,38,0.20)) and a deeper resting shadow, plus the floating pill tag overlapping its top edge.
  • The tag says "Most Complete": a structural, verifiable claim. Never "Most Popular" or any popularity claim that data does not back (see section 8).
  • Tiles stay graphite/neutral. The free audit remains the page's one orange action; pricing tiles never carry an orange fill. Roles ride in mono uppercase orange text; figures in display bold.
  • The ladder logic in copy: every dollar credits toward the next step ("you never pay twice"). Price figures always show the per-unit (/month, one-time) beside the figure.
  • Below 861px the grid goes single-column in the same ascending order.

Prose links

Body copy opts into the link system with one class: an inline link like this one back to the tokens carries a permanent 2px orange underline offset 0.15em; hover adds a soft orange wash and thickens the line, and the treatment clones cleanly across wrapped lines.

  • Scope is opt-in. The base reset removes underlines globally; prose containers add class="pbs-prose" to get the link treatment. Nav, buttons, cards, and footers keep their own treatments. Why: a build that shipped the reset without a prose rule rendered in-body links INVISIBLE, indistinguishable from plain text.
  • The permanent underline is the non-color cue (accessibility); keyboard focus gets the site-wide 3px orange ring; visited links get no separate state. Contrast holds: link text about 14.9:1 on off-white, underline at or above the 3:1 non-text minimum on every ground.
5 · Motion

Motion that can never strand content.

Two vocabularies and one driver, all governed by one safety rule: if JavaScript is missing, failing, or disabled, everything is visible and the page scrolls natively. Motion is a feel upgrade, never load-bearing.

The reveal-safety rule (binding)

  • Entrance choreography (above the fold, load-time) is CSS-only: the hiding prestate (opacity: 0) and the keyframe animation that releases it live in the SAME declaration block in the SAME render-blocking stylesheet. They cannot be split apart by an optimizer. If CSS loads, the animation runs; if CSS does not load, nothing was ever hidden.
  • Scroll reveals (below the fold) ship FULLY VISIBLE in the CSS: there is no parse-time hidden state at all. JavaScript hides an element only if it sits below the viewport when the engine boots, then releases it on intersection. Elements scrolled fully away re-arm and replay.
  • Nothing in the initial viewport is ever given a hidden state by the scroll system, so motion can never blank a hero, delay the largest paint, or shift layout. Why this rule exists: an earlier build shipped a reveal prestate inline while its release rule rode a deferred file; a page hero sat blank above the fold until the visitor scrolled, and layout-shift metrics read 0 the whole time it was invisible. Verification of any reveal change therefore probes VISIBILITY (computed opacity plus a screenshot), never shift metrics alone.
  • Reveals animate custom properties (--pbs-rv-o, --pbs-rv-y set inline per element), never direct inline opacity or transform, so a stray !important rule elsewhere can pin an element visible without ever trapping it hidden.
  • Any engine error lands on html.pbs-motion-off which clears every armed element and restores native scroll. The failure path is a class, not a hope.

Reduced motion

  • prefers-reduced-motion: reduce kills everything: one global media query zeroes all transitions and animations, forces entrance and reveal elements fully visible, and the motion engine early-returns before it arms anything or starts the smooth-scroll driver. Reduced-motion visitors get a complete, static, natively-scrolling page.

Smooth scroll (Lenis) and anchors

  • The driver is Lenis, configured: duration 1.05, expo-out easing, smoothWheel: true, smoothTouch: false (phones keep native momentum, which keeps interaction latency clean).
  • It loads lazily after the window load event, never on the critical path, and is OFF under reduced motion. When the animation library is also present, both share ONE requestAnimationFrame ticker, never two loops.
  • Anchor links always work: the anchor handler registers unconditionally, so same-page #links jump natively whenever smooth scroll is not active (before it loads, if it fails to load, or when opted out) and route through lenis.scrollTo(target) once it is live.
  • Opt-out: window.PBS_SMOOTH_SCROLL = false keeps native scroll. Smooth scroll is the FIRST thing to cut if the interaction budget is threatened; the page must be fully usable without it.

Copy-paste: the structural reveal CSS (ships render-blocking, never deferred)

/* Scroll reveals: VISIBLE at parse; JS arms below-viewport elements only */
.pbs-reveal {
  --pbs-rv-o: 1;
  --pbs-rv-y: 0px;
  --pbs-rv-delay: 0ms;
  opacity: var(--pbs-rv-o);
  transform: translateY(var(--pbs-rv-y));
}
.pbs-reveal--armed {
  transition: opacity var(--pbs-enter-dur) var(--pbs-ease) var(--pbs-rv-delay),
              transform var(--pbs-enter-dur) var(--pbs-ease) var(--pbs-rv-delay);
  will-change: opacity, transform;
}
html.pbs-motion-off .pbs-reveal { --pbs-rv-o: 1; --pbs-rv-y: 0px; transition: none; }

/* Entrance choreography: prestate + release are ONE atomic block */
@media (prefers-reduced-motion: no-preference) {
  @keyframes pbs-rise {
    from { opacity: 0; transform: translate3d(0, var(--pbs-enter-move), 0); filter: blur(6px); }
    60%  { filter: blur(0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); filter: blur(0); }
  }
  .pbs-enter { opacity: 0; animation: pbs-rise var(--pbs-enter-dur) var(--pbs-ease) both; will-change: transform, opacity, filter; }
  .pbs-enter--1 { animation-delay: calc(var(--pbs-enter-step) * 1); }
  .pbs-enter--2 { animation-delay: calc(var(--pbs-enter-step) * 2); }
  .pbs-enter--3 { animation-delay: calc(var(--pbs-enter-step) * 3); }
  .pbs-enter--4 { animation-delay: calc(var(--pbs-enter-step) * 4); }
  .pbs-enter--5 { animation-delay: calc(var(--pbs-enter-step) * 5); }
}

/* Lenis structural CSS (the engine adds these classes only when active) */
html.lenis, html.lenis body { height: auto; }
html.lenis { scroll-behavior: auto !important; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
html.lenis [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: clip; }
html.lenis iframe { pointer-events: none; }

/* Reduced motion: one global kill, everything visible */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
  .pbs-enter  { opacity: 1 !important; transform: none !important; filter: none !important; }
  .pbs-reveal { opacity: 1 !important; transform: none !important; }
}

Copy-paste: the minimal init pattern (registry + deferred engine)

<!-- All motion is config: adding motion = adding a registry row,
     never a new script block. The engine script implements the rules
     above (arm below-viewport only, custom-property release,
     reduced-motion early-return, fail-safe to html.pbs-motion-off,
     lazy library loading) and ships deferred in the footer. -->
<script>
  window.PBS_MOTION_REGISTRY = [
    { selector: '.pbs-reveal',       effect: 'rise',         options: { replay: true, stagger: 110 } },
    { selector: '.pbs-img--lcp img', effect: 'image-reveal', options: { y: 24, scale: 0.94, duration: 1.0 } }
    /* 'timeline' configs (crafted sequences) are the ONLY thing that
       loads the animation library; a page with none ships zero of it. */
  ];
  /* window.PBS_SMOOTH_SCROLL = false;  // opt out of smooth scroll */
</script>
<script defer src="/systems/js/pbs-motion.js"></script>

The engine and the smooth-scroll and animation libraries are self-hosted, same-origin, in production. Image reveals (the image-reveal effect) are transform-only (rise + slight zoom) and fire only AFTER the image has painted (img.complete && naturalWidth > 0); never animate opacity or clip-path on a hero image.

6 · Images

Geometry reserved before pixels arrive.

Modern formats from day one, explicit dimensions on every image, and one eager hero per page. The page never reflows when an image lands.

Demo: a wide diagram panel inside a 3:2 ratio frame
A below-fold image: lazy, async, inside a 3:2 frame that reserved its box at parse.
  • Formats: AVIF preferred, WebP fallback, original last. Never ship a raw multi-hundred-KB original. (Reference points from a previous build: one un-optimized image set weighed 42.6MB and converted to 7.4MB as WebP; one hero shipped as an 886KB JPEG. AVIF typically saves a further 20 to 30 percent over WebP at equal quality.)
  • Explicit dimensions on EVERY image: width and height attributes, or a ratio frame (.pbs-img-frame with --pbs-ratio). This is the zero-layout-shift mechanism, not a nice-to-have.
  • Exactly one eager image per page (the above-fold hero): loading="eager" fetchpriority="high" decoding="async". Everything else: loading="lazy" decoding="async".
  • Right-size with srcset + honest sizes. The sizes attribute must reflect the real rendered width per viewport.
  • Preload ONLY the hero image, and the preload's imagesizes must EXACTLY equal the rendered sizes; a mismatch double-fetches on high-density phones (a previous fix took one fetch from 145KB to 54KB). Never preload below-fold or never-displayed crops.

Copy-paste: the hero / LCP image (one per page, above the fold)

<picture>
  <source type="image/avif"
          srcset="hero-800.avif 800w, hero-1200.avif 1200w, hero-1600.avif 1600w"
          sizes="(max-width: 860px) 100vw, 62vw" />
  <source type="image/webp"
          srcset="hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"
          sizes="(max-width: 860px) 100vw, 62vw" />
  <img src="hero-1200.webp" width="1200" height="800" alt="..."
       loading="eager" fetchpriority="high" decoding="async" />
</picture>

Copy-paste: every other image (below-fold default)

<figure class="pbs-figure">
  <div class="pbs-img-frame" style="--pbs-ratio: 3 / 2;">
    <img src="photo-800.webp"
         srcset="photo-800.webp 800w, photo-1200.webp 1200w"
         sizes="(max-width: 860px) 100vw, 50vw"
         width="800" height="533" alt="..."
         loading="lazy" decoding="async" />
  </div>
  <figcaption>...</figcaption>
</figure>

The frame defaults to 16:9 and takes any ratio via --pbs-ratio. Frames carry the 14px radius and a faint placeholder tint that paints before the image loads. If the image pipeline negotiates formats on the Accept header, a single src may replace the <picture> form; the dimensions rule is unconditional either way.

7 · Performance budgets

Speed is part of the spec, not an afterthought.

The Core Web Vitals budget is a gate: output that misses it is not done. The budget is measured on mid-tier throttled MOBILE, the honest environment, never a fast laptop.

The Core Web Vitals budget

MetricBudgetWhat holds it
LCP (largest contentful paint)under 2.5seager hero with fetchpriority; AVIF/WebP right-sized; preload only the hero image; critical CSS inline at parse; zero render-blocking fonts (metric-matched fallback paints immediately); all motion JS deferred past paint
CLS (cumulative layout shift)under 0.1, target 0explicit dimensions or ratio frames on every image; metric-override font fallbacks (shift-free swap); no parse-time hidden states (reveal-safety); layout CSS never in a deferred file
INP (interaction to next paint)under 200msone shared animation ticker, never two; native touch scrolling on phones; smooth scroll deferred past load and first to cut; no layout-thrash patterns

The JavaScript weight budget

ScriptApprox. (min + gzip)LoadsRule
Motion engineabout 2KBdeferred, footeralways present; tiny
Smooth-scroll driver (Lenis)about 3KBlazy, AFTER the load eventoff under reduced motion; the first thing to cut
Animation library (GSAP core)about 24KBlazy, ONLY when a timeline config existsa page with no crafted timeline ships ZERO of it
  • Critical-path JavaScript budget = 0. Nothing blocks first paint. Ever.
  • Deferred JavaScript cap: about 30KB gzipped worst case (engine + driver + library together). A typical page using reveals and smooth scroll but no crafted timeline ships about 5KB.
  • Critical-path CSS rules: tokens, base/layout, links, image frames, and ALL reveal/entrance CSS ship inline, render-blocking, at parse, from one source. Layout or reveal rules in a deferred stylesheet apply after first paint and cause visible shift; a previous build measured CLS of 0.08 to 0.15 from exactly that, and a naive flip of all CSS to external-plus-deferred produced CLS of 0.47. Over-include critical CSS generously: extra inline bytes never cause shift, missing rules do.
  • Verify in the honest environment. Layout shift is verified with a real throttled browser run plus an above-fold visibility probe (lab simulators over-report on some stacks, and shift metrics alone miss invisible-content failures). Never claim the budget is met from a fast local render.
8 · Copy rules

Rules for generated text.

Anything that writes copy into a PreBizSys-styled page follows these rules without exception. They are brand floor, not style preference.

  • No em dashes, ever, in output copy. The long dash never appears in generated text. Use a comma, a colon, a period, or parentheses, or restructure the sentence. The en dash (for ranges) and the hyphen are fine. This page itself contains zero em dashes.
  • Plain, confident English. Short sentences. Headline first, detail second. Say what the thing is and what it does; never hide behind jargon, buzzwords, or hype. Write like a craftsman explaining work to a smart non-expert.
  • No fabricated proof elements. Never generate reviews, star ratings, testimonials, client counts, awards, or "as seen in" claims the user did not explicitly supply. No invented popularity labels either: a tag like "Most Popular" requires data; "Most Complete" is the PreBizSys pattern because it is structurally verifiable. A "Live" status tag appears only on something that is genuinely live.
  • No guarantees of rankings or outcomes. Never promise a position on Google, the map pack, or inside an AI answer; never promise traffic, leads, or revenue. Search engines and AI engines decide those, and the brand says so out loud. Sell the WORK (what gets built, set up, and run), never a result outside our control. The canonical floor line: "We sell the work, never a promised outcome."
  • Honest qualifiers stay in. "Free, no obligation", "no call, no credit card", "what you pay credits forward" are brand assets; keep them plain and verifiable.
  • Emphasis discipline: emphasis is semibold text, never ALL CAPS shouting (uppercase is reserved for the mono eyebrow/label voice), never exclamation marks stacked for excitement.
9 · Consuming this system

How an app produces PreBizSys output.

Three contracts: one token source, one load order, and a clear line between what is locked and what is flexible.

Tokens: single source, never forked

  • The token block in section 2 is the single source of truth. Consume it as CSS custom properties exactly as printed. Never copy individual values into component styles, never re-declare a token with a different value, never create a parallel set. If the system evolves, the one block changes and everything inherits.
  • Build against semantic roles (--pbs-action, --pbs-text-on-light), not raw palette names, and never against literal hex values.
  • One namespace: any new property or class the app generates in PreBizSys context uses the pbs- prefix and must not collide with the names in this document.

Load order (the delivery contract)

#LayerDelivery
1Tokens (the :root block)inline, render-blocking, FIRST
2Fonts (@font-face: self-hosted WOFF2 + metric-override fallbacks)inline, render-blocking
3Base (reset, accessibility floor, sectioning convention, tonal zones)inline, render-blocking
4Links (the prose-link system)inline, render-blocking, after base (it must out-cascade the reset)
5Images (ratio frames, figure rhythm)inline, render-blocking
6Reveal (entrance + scroll vocabularies, reduced-motion kill)inline, render-blocking, NEVER deferred
7Components (buttons, cards, plans, nav: the section-4 language)theme/builder layer, consuming layers 1 to 6
8Page-specific (true one-offs only)last; never duplicating a system rule
9Motion JS (engine + lazy driver + lazy library)deferred, footer, after everything paints
  • The order is literal. Layers 1 to 6 ship as inline render-blocking CSS in the head, in exactly this sequence; the cascade depends on it.
  • One source per rule. A rule lives in exactly one layer. Never duplicate a band rule into page CSS "to be safe"; reconcile at the source.
  • If an optimizer or build step defers CSS, layers 1 to 6 stay inline anyway. Layout, spacing, and reveal rules in a deferred file are the single most reliable way to break this system (see section 7).

Locked vs. flexible

Locked (never altered by generated output)Flexible (the app's room to move)
Token values and names · the pbs- namespace · the sectioning convention and its closed exemption list · the breakpoint set (980 / 860+861 / 560) and desktop-first cascade · the orange-reservation rule (one decisive action per view) · the component anatomy in section 4 (buttons, cards, zones, plan tiles, prose links) · the reveal-safety rule and reduced-motion kill · the image dimensions rule · the performance budgets · the copy rules in section 8 · the wordmark treatment Page composition: which bands, in what order, with what zone rhythm · section copy (within the voice and section-8 rules) · which components each band uses · image subjects and ratios (within the format and dimension rules) · how many cards, steps, or tiles a band carries · whether a page uses scroll reveals or ships static · icon line-art glyphs (within the 1.8-stroke style)

The test for any generated page: set it beside the PreBizSys homepage. If a stranger can tell which one the system produced, the output has drifted. When something this spec does not cover comes up, the answer is a question back to PreBizSys, not an improvisation.