Freeframe

Documentation

Compass

v1.0.1Updated Oct 17, 2026A warm, editorial site for a leadership coach or consultant, with a booking flow, a self-assessment quiz and client results.

Overview#

Compass is a website template for coaches and consultants. The demo content is for Bearing, the fictional leadership coaching practice of Nadia Whitcombe in Pittsburgh, who works with new managers, founders and senior leaders through 1:1 coaching, a group cohort and team workshops. The design is warm and editorial: a serif display face with italic accents, ivory and linen backgrounds, a dusk-indigo brand colour, marigold highlights and cut-paper SVG illustrations. This is the Astro version: Astro 7, TypeScript and Tailwind CSS v4, with no UI framework, built to plain static files. It has the same pages, design, copy and interactions as the HTML version. This guide covers version 1.0.0 (see template.json). Live demo: freeframe.dev/templates/compass.

Quick start#

Requirements: Node.js 24 or newer and pnpm (npm works too).

sh
pnpm install
pnpm dev          # http://localhost:4321

With npm: npm install, then npm run dev.

Command What it does
pnpm dev Development server at http://localhost:4321 with hot reload
pnpm build Static site in dist/
pnpm preview Serve dist/ locally after a build
pnpm check astro check: type-checks .astro files and TypeScript
pnpm typecheck Generate Astro's types, then type-check with tsc (TypeScript 7)
pnpm lint ESLint (TypeScript and Astro rules)
pnpm build:demo Build for /demos/astro/compass into demo/ (only used for the demo on freeframe.dev)

Pages#

Route File Purpose Notable sections and features
/ src/pages/index.astro Home Hero with portrait and intro-call CTA, press wordmarks, "who it's for" cards, count-up outcomes, programs preview, the four method points, testimonial carousel, free guide sign-up, final CTA
/programs src/pages/programs.astro Programs and pricing Foundations and Executive 1:1 packages, strategy session, First 90 Days cohort, team workshops, comparison table, FAQ accordion, "Recommended by your self-check" badges
/method src/pages/method.astro The Bearing Method Interactive compass diagram with tabs, 60-minute session structure, six-month engagement timeline
/results src/pages/results.astro Client results Summary stats, eight client stories with before/after metrics, filters by client type, pull quote
/about src/pages/about.astro About the coach Story, studio illustration, career path, credentials, values, personal notes
/resources src/pages/resources.astro Resources Leadership self-check quiz, filterable library of articles, worksheets and episodes, podcast, guide sign-up
/article src/pages/article.astro Sample article Long-form layout with drop cap, illustration, pull quote, "try this week" box, author card, related links
/book src/pages/book.astro Book a call Four-step booking flow with a live summary sidebar
/contact src/pages/contact.astro Contact SVG map, studio address, contact cards, validated contact form, other ways to work together
/404 src/pages/404.astro Not found Wandering compass needle and links back into the site

Project structure#

compass/
  astro.config.mjs          site URL, base path, static output (pages built as about.html)
  package.json              scripts and dependencies
  pnpm-workspace.yaml       marks the folder as a standalone pnpm project
  tsconfig.json             extends Astro's strict preset
  eslint.config.mjs         ESLint flat config (typescript-eslint + eslint-plugin-astro)
  public/img/
    favicon.svg             browser tab icon
    og-image.svg            social image source (1200 x 630)
    og-image.png            social image used in og:image
    nadia-portrait.svg      arch-framed portrait illustration (480 x 600)
    studio.svg              studio interior illustration (800 x 560)
    guide-cover.svg         cover of the free guide (360 x 460)
  src/
    pages/*.astro           one file per page (see Pages)
    layouts/Layout.astro    <html>, <head> metadata, skip link, header, footer and the script imports
    components/
      SiteHeader.astro      sticky header, desktop and mobile menus
      SiteFooter.astro      footer columns, social links, studio details, wordmark and "Back to top" link
    data/
      nav.ts                header, mobile menu and footer links
      home.ts               "who it's for" cards, outcome stats, method points, testimonials
      programs.ts           the four programs shown by the quiz result (name, price, link, summary)
      program-details.ts    package inclusions, cohort weeks and facts, team formats, workshop topics, FAQ
      method.ts             the four compass points, session steps, engagement timeline, "your part"
      stories.ts            client stories and the results filter chips
      resources.ts          the resource library and its filter chips
      about.ts              career path, credentials, values
      quiz.ts               self-check questions, result types and the storage key
      booking.ts            call types, availability rules and the call-type cards
    lib/
      site.ts               site name, email, phone, theme colour and the url() helper
      icons.ts              line icons as SVG strings, and the logo mark
    scripts/
      main.ts               all interactive behaviour (menu, reveals, carousel, tabs, filters, forms, quiz, booking)
      back-to-top.ts        the back-to-top button
    styles/global.css       Tailwind CSS v4: brand tokens and component classes
  template.json             gallery metadata for freeframe.dev
  README.md, DOCS.md, CHANGELOG.md

Customising#

Brand and colours#

All design tokens live in the @theme block at the top of src/styles/global.css. Each token becomes Tailwind utilities (bg-dusk-800, text-marigold-700, border-linen-200 and so on).

Token group Used for
--color-ivory, --color-linen-50 to --color-linen-300 Page background, soft section backgrounds, borders
--color-stone-500 to --color-stone-800 Body copy and muted text
--color-dusk-50 to --color-dusk-950 The brand indigo: buttons, links, dark sections, the footer
--color-marigold-100 to --color-marigold-700 The warm accent: highlights, stars, accent buttons on dark backgrounds
--color-clay-100 to --color-clay-700 Secondary accent for tags and illustrations

To change the brand colour, replace the dusk scale; Astro rebuilds the CSS on the next pnpm dev change or pnpm build:

css
@theme {
  --color-dusk-700: #2f5d50;
  --color-dusk-800: #244a40;
  --color-dusk-900: #1a3730;
  --color-dusk-950: #12261f;
}

Keep the contrast rules noted in the comments: use marigold-700 (not 400/500) for marigold text on light backgrounds, and stone-600 or darker for small text. Reusable pieces such as .btn, .btn-primary, .card, .chip, .input, .eyebrow and .display are defined in the @layer components block of the same file. The theme-color meta tag comes from site.themeColor in src/lib/site.ts.

The illustrations (public/img/*.svg), the logo mark in src/lib/icons.ts and the inline SVGs in the pages use hex colours directly. If you change the palette, search for the old hex values (for example #2a3259 and #f0c464) and replace them.

Typography#

Two Google Fonts are loaded by one <link> in src/layouts/Layout.astro:

  • Source Serif 4 (300–600, roman and italic, optical sizes) for headings, numbers and quotes: --font-serif, used through the .display class and font-serif.
  • Instrument Sans (400–700) for body text and the interface: --font-sans.

Each token has a system fallback stack, so the site still reads well if the fonts don't load. To swap a font, change the Google Fonts URL in the layout and the matching token in @theme:

css
--font-serif: "Fraunces", "Iowan Old Style", Georgia, ui-serif, serif;

Inline SVGs that contain text (press wordmarks, the method compass, the podcast cover, the map and the footer wordmark) name the fonts in font-family attributes; quote family names that contain digits, as in font-family="'Source Serif 4', Georgia, serif".

Content#

Page copy that appears once (headings, intros, the article) is written in the page's .astro file. Anything that repeats is typed data in src/data/ and rendered with .map(), so adding an item is one entry.

Site details. The site name, email and phone number used in the header, footer, contact page and meta tags come from site in src/lib/site.ts:

ts
export const site = {
  name: "Bearing Leadership Coaching",
  themeColor: "#fbf8f3",
  email: "hello@bearing.example",
  phone: "(412) 555-0143",
  phoneHref: "tel:+14125550143",
};

Client stories. STORIES in src/data/stories.ts. Each story has filter tags, tag pills, the program, a title, a paragraph, two or three before/after metrics and the client. To add one:

ts
{
  id: "maya",
  tags: "founders",
  labels: [{ tone: "tag-marigold", text: "Founder" }],
  program: "Executive 1:1 · 6 months",
  title: "Your headline",
  text: "What changed, in two or three sentences.",
  cols: "sm:grid-cols-2",
  metrics: [
    { label: "Meetings per week", before: "30", after: "18", bars: [100, 60] },
    { label: "Team engagement score", before: "58", after: "81", bars: [72, 100], up: true },
  ],
  person: { initials: "MS", tone: "bg-dusk-100 text-dusk-800", name: "Maya Singh", role: "CEO, Example Co" },
},

id becomes the card's id="story-maya". bars are the two bar widths in percent (the larger value is 100); up colours the "after" bar dusk for numbers that should rise, otherwise it is marigold. Valid tags are the chip values in STORY_FILTERS: managers, founders, executives, teams and career; a story can have several, separated by spaces ("executives teams"). Chip counts are computed from the data at build time and recounted by the script.

Resources. RESOURCES in src/data/resources.ts. Each entry has a kind ("Article", "Worksheet" or "Podcast"), topics (the chip values in RESOURCE_FILTERS: delegation, feedback, confidence, strategy, career), a header tone, an eyebrow, title, text and meta (reading time or format). The kind sets the icon and the link in RESOURCE_KINDS: articles go to /article, worksheets to the guide sign-up (#guide) and episodes to the podcast section (#podcast).

ts
{ kind: "Article", topics: "feedback", tone: "bg-dusk-50 text-dusk-700", eyebrow: "Feedback", title: "Your title", text: "One-line summary.", meta: "6 min read" },

Home page. AUDIENCES, STATS, METHOD_POINTS and TESTIMONIALS in src/data/home.ts. The first four testimonials also supply the hero's initials avatars. Each stat's value is both the count-up target and the number shown without JavaScript.

Programs. The programs page reads FOUNDATIONS_INCLUDES, EXECUTIVE_INCLUDES, COHORT_WEEKS, COHORT_DETAILS, TEAM_FORMATS, WORKSHOP_TOPICS and FAQ from src/data/program-details.ts; prices, the comparison table and the strategy-session panel are written in src/pages/programs.astro. PROGRAMS in src/data/programs.ts holds the name, price, link and summary that the quiz result shows. Prices also appear in the home page preview; keep all of them in step when you change a price.

Cohort dates. First 90 Days cohorts start on the second Wednesday of January, April, July and October. COHORT_DATES in src/data/programs.ts is null in the demo, which shows rolling dates: the first start at least COHORT_MIN_WEEKS (6) weeks away and the one after it, so the demo never advertises a cohort that has started. Set { next: "2027-01-13", after: "2027-04-14" } for your real dates. The script (src/lib/cohort.ts) fills in every element with data-cohort="next" or "after" in the format of data-cohort-format: full ("Wednesday, January 13, 2027"), long ("January 13, 2027"), month ("April 2027") or season ("Winter 2027"). The dates written in the pages are shown without JavaScript; keep them in step once you set real dates.

Method and about. COMPASS_POINTS, SESSION_STEPS, TIMELINE and BRING in src/data/method.ts; PATH, CREDENTIALS and VALUES in src/data/about.ts.

Quiz and booking. See Leadership self-check and Booking flow.

Images and artwork#

All artwork is SVG, so there are no photos to license. Files in public/img/ are copied to the build as they are and referenced with url("/img/..."). To use real photos instead:

  • Portrait (nadia-portrait.svg, used on home, about, article and book pages): replace with a 4:5 photo. To keep the arch shape, add class="arch object-cover" to the <img>; .arch rounds the top into a full arch.
  • Studio (studio.svg on about): any 10:7 landscape image.
  • Guide cover (guide-cover.svg, home and resources): a 360 x 460 (roughly 4:5) cover of your own lead magnet.
  • The method compass, press wordmarks, map, podcast cover, article illustration and 404 compass are inline SVG in their pages. Replace the press wordmarks in index.astro with your own logos, and only list publications that have really featured you.

The social image is public/img/og-image.png (1200 x 630), rendered from og-image.svg. Edit the SVG, then export a new PNG with any vector editor or a headless browser. The favicon is public/img/favicon.svg. Icons are SVG strings in src/lib/icons.ts, rendered with <Fragment set:html={icon("arrow", "size-5")} />; add a path to paths to add an icon.

The header and footer are components, so there is one place to change them:

  • Links: src/data/nav.ts. mainNav is the desktop menu, mobileNav the mobile menu (Home, the main links and "Book a free intro call"), and footerNav the footer's Coaching and Practice columns. Write paths without .html ("/programs#faq"); the components pass them through url().
  • Current page: each page passes current to <Layout> (current="programs"), which sets aria-current="page" on the matching links; the underline and mobile highlight come from the aria-[current=page] styles on .nav-link and .mobile-link. The article passes current="resources". The "Book a free call" button in the header is shown from the sm breakpoint up and is marked current on the booking page. The desktop menu appears from the lg breakpoint (1024 px); below that the menu button shows the mobile menu.
  • Footer: social links, the studio address and the large "Bearing" wordmark (an aria-hidden inline SVG) are in SiteFooter.astro.

To add a page, create src/pages/<name>.astro with <Layout title=... description=... current="<name>">, put the content in <main id="main" tabindex="-1">, and add it to nav.ts.

Features#

All behaviour is in src/scripts/main.ts (and back-to-top.ts), imported once from the layout. Each feature looks for its markup and does nothing on pages without it.

Booking flow#

/book contains a four-step flow inside the [data-booking] element, driven by initBooking() in main.ts:

  1. Call type: radio cards named call-type, rendered from CALL_TYPE_CARDS with the length and price from CALL_TYPES (both in src/data/booking.ts). Link to /book?type=strategy (or team) to preselect a type.

  2. Date and time: a month calendar (Monday first) and a list of time slots. Slots are generated from AVAILABILITY:

    ts
    export const AVAILABILITY = {
      timeZone: 'America/New_York',
      zoneName: 'Eastern Time',
      hours: { 1: [['09:00', '12:00'], ['13:30', '17:00']], /* ... */ 5: [['09:00', '12:30']] },
      slotStep: 30,
      minNoticeHours: 24,
      daysAhead: 45,
      closed: ['2026-11-26', '2026-12-25' /* ... */],
      demoBusyShare: 0.35,
    };

    hours is keyed by weekday (0 is Sunday) in studio time; a slot fits when its whole length (minutes of the call type) ends before the window closes. demoBusyShare hides a stable share of slots so the demo looks realistic; set it to 0 once you connect real availability. Times are shown in the visitor's time zone (detected with Intl), with the studio time underneath and a "Show times in" selector to switch to studio time. Slots that cross midnight are labelled (+1 day).

  3. Details: a validated form ([data-booking-form]) with name, email, role, optional company, a message of at least 20 characters and a consent checkbox.

  4. Confirm: a summary of the request, a clear "this is a demo" notice, a Save a calendar hold (.ics) button that downloads an event file built in the browser, and Book another call.

The sidebar ([data-booking-aside]) updates as choices are made. To add a call type, add a key to CallTypeKey, CALL_TYPES and CALL_TYPE_CARDS.

Accessibility: each step has a focusable heading that receives focus when the step changes; the step list marks the current step with aria-current="step". Calendar days are buttons with full-date aria-labels that include the number of open times; only one day is in the tab order, and the arrow keys move by day and week, Home/End go to the start/end of the week and Page Up/Page Down move four weeks. Unavailable days use aria-disabled="true". Time slots are native radio buttons inside a fieldset, and a missing date or time shows an inline role="alert" message.

Leadership self-check#

The quiz on /resources (#quiz, element [data-quiz]) has three views: intro, questions and result. Questions are rendered from QUIZ_QUESTIONS in src/data/quiz.ts as a fieldset of radio buttons, one question at a time with a progress bar and Back/Next buttons. Each answer is tagged with a result type; the type with the most answers wins, and ties go to the type listed first in QUIZ_RESULTS. The result shows the type, three steps to try, a bar for each type and the recommended program from PROGRAMS.

To change the quiz, edit the arrays: every answer is [text, resultType], and each result's program must be a key of PROGRAMS (TypeScript checks both). Link to /resources#quiz from anywhere to send people to it. The legend of each new question receives focus so screen reader users hear it.

On /programs, elements with data-recommended (hidden by default) are shown inside the element whose data-program matches the saved quiz result: foundations, executive, cohort or teams.

Method compass#

On /method, the four tabs ([data-compass-tabs], standard role="tablist" with arrow-key, Home and End support) show one panel each; both are rendered from COMPASS_POINTS. The compass SVG ([data-compass]) turns its .needle group to the selected point by the shortest route and highlights the matching [data-point] circle; clicking a point in the drawing selects its tab. The drawing is decorative for assistive technology, which uses the tabs.

Filters#

Any element with data-filter-root gets a filter: chip buttons with data-filter (with all meaning everything), cards with data-tags, an optional [data-filter-count] live region and an optional [data-filter-empty] message. The selected filter is written to the URL (?type= on results, ?topic= on resources, set with data-filter-param), so filtered views can be shared and linked, for example /results?type=founders. Chips use aria-pressed. To add a filter value, add a chip to STORY_FILTERS or RESOURCE_FILTERS and use the value in some items' tags.

The home page carousel ([data-carousel]) is a horizontal scroll-snap list, so swiping, trackpads and the arrow keys (once the list is focused) all work. The Previous/Next buttons scroll by one card and disable at the ends; the "1 / 6" counter is visual only. There is no autoplay. Slides and their "n of 6" labels come from TESTIMONIALS; the counter's starting text is in index.astro.

FAQ accordion#

Each question on /programs is a .acc-btn button with aria-expanded and aria-controls pointing to its panel, rendered from FAQ. The first item starts open; change the i === 0 checks in programs.astro to open another. Answers are HTML strings, so they can contain links (build the link with url(), as the last answer does).

Count-up stats, scroll reveals and motion#

Numbers with data-count (and optional data-decimals) count up once when they scroll into view; the final value is already in the HTML. Elements with .reveal fade in on scroll (add .reveal-delay-1 to -3 to stagger), and everything above a revealed element is revealed too. The 404 needle wanders with a CSS animation. All motion is switched off when the visitor prefers reduced motion.

Header, mobile menu and back to top#

The header gains a border and shadow after scrolling. Below 1024 px the menu button toggles #mobile-menu, updates aria-expanded and its label, and closes on Escape or when a link is chosen. A back-to-top button appears after about one screen of scrolling and moves focus to the main content; to remove it, delete src/scripts/back-to-top.ts, its import in Layout.astro and the .back-to-top styles in global.css.

Forms#

There are four forms. None of them sends anything until you connect it, and each success state says so.

Form Where Validates
Guide sign-up ([data-guide-form]) /, /resources First name required, email format
Contact ([data-contact-form]) /contact Name, email, optional phone (7+ digits if filled), topic, message of 20+ characters
Booking details ([data-booking-form]) /book Name, email, role, message of 20+ characters, consent checkbox
Quiz ([data-quiz-form]) /resources An answer is chosen before moving on

Validation is driven by data-validate attributes (required, email, phone-optional, message, consent) and the messages in fieldMessage() in main.ts. Errors appear under the field, are linked with aria-describedby, set aria-invalid="true" and clear as the visitor fixes them; the first invalid field receives focus.

Connecting to Formspree#

Create a form at Formspree and copy its endpoint. Then, in main.ts, find the comment Connect a real endpoint here in the guide/contact form handler and send the data before showing the success panel:

ts
form.addEventListener('submit', async (e) => {
  e.preventDefault();
  if (!validateForm(form)) return;
  try {
    const res = await fetch('https://formspree.io/f/your-id', {
      method: 'POST',
      body: new FormData(form),
      headers: { Accept: 'application/json' },
    });
    if (!res.ok) throw new Error('Send failed');
  } catch {
    alert('Sorry, something went wrong. Please email hello@bearing.example.');
    return;
  }
  form.hidden = true;
  if (success) { success.classList.remove('hidden'); success.focus(); }
});

Then update the success text in the page (remove the "This is a demo" notice). For the guide sign-up you can instead post to your email tool (Kit, Mailchimp, Buttondown) using the form action it gives you.

The site is static, so there is no server code. If you need one (to email a request or save it to a CRM), add an Astro adapter and an API endpoint, or use your host's functions (Netlify Functions, Vercel Functions, Cloudflare Workers) and post to that URL instead of Formspree.

Booking flow#

You have three options:

  • Replace it with an embed. Remove the [data-booking] section in book.astro and paste the inline embed code from Cal.com or Calendly. Keep the page header; the booking code in main.ts only runs when [data-booking] exists.
  • Keep the flow and send a request. In the [data-booking-form] submit handler in initBooking(), post the details plus the chosen slot (state.type, state.slot.at.toISOString()) to a form service before calling goTo(4), then confirm each request by hand. Change the step 4 notice to say a confirmation will follow.
  • Use a scheduling API. Load real free slots from the Cal.com or Calendly API on your own server and replace slotsFor() with them; never put API keys in client-side code.

Saved data (localStorage)#

Key Shape Notes
compass-quiz-v1 { "answers": [0, 2, 1, 0, 3, 2], "date": "2026-10-12" } One answer index per question

readQuiz() in main.ts ignores the value unless answers is an array with exactly one whole-number index per question, each in range. The result type is always recalculated from the answers, so editing QUIZ_RESULTS never leaves a stale result. Retake the self-check removes the key. To clear it manually, run localStorage.removeItem('compass-quiz-v1') in the browser console. If storage is blocked, the quiz still works; it just isn't remembered. If you change the questions, bump QUIZ_KEY in src/data/quiz.ts (for example to compass-quiz-v2).

SEO and sharing#

Each page passes title and description to <Layout>; the layout writes the <title>, meta description, canonical URL, og:* tags and twitter:card, plus theme-color and the SVG favicon. Canonical and og:url use the clean URL (/programs, not /programs.html), and og:image is the absolute URL of /img/og-image.png. Both are built from site in astro.config.mjs, which reads SITE_URL:

sh
SITE_URL=https://www.your-domain.com pnpm build

or change the fallback (https://compass.example.com) in astro.config.mjs. The 404 page passes noindex, which outputs <meta name="robots" content="noindex"> and no canonical.

For a sitemap, add the official @astrojs/sitemap integration, and add a public/robots.txt. Consider ProfessionalService or Person structured data (JSON-LD) with your name, address and services; add it as a <script type="application/ld+json"> in the layout's <head>.

Accessibility#

Built in:

  • Skip link, header/nav/main/footer landmarks, one <h1> per page and a logical heading order.
  • Visible focus rings (:focus-visible in global.css, marigold on dark sections), including on radio cards, quiz answers and time slots.
  • Mobile menu with aria-expanded, aria-controls and Escape to close.
  • Filter chips with aria-pressed and a live result count; accessible tabs and accordion; a keyboard-operable calendar; a carousel that never moves on its own.
  • Labelled form fields, inline errors linked with aria-describedby, and focus moved to the first error.
  • alt text or aria-hidden on every image and SVG.
  • Reduced motion turns off reveals, count-ups, the needle animations and smooth scrolling.
  • Text colours meet WCAG AA contrast.

When editing, keep one <h1> per page, give every new input a <label>, keep the aria-controls ids of tabs and accordion buttons in step with their panels, keep alt text meaningful when you swap illustrations for photos, and check new colour pairs for contrast (small text on ivory should be stone-600 or darker).

Building and deploying#

sh
pnpm build        # static site in dist/
pnpm preview      # preview dist/ locally

dist/ is plain HTML, CSS and JavaScript. Pages are written as programs.html rather than programs/index.html (build.format: "file" in astro.config.mjs), and links point to /programs without a trailing slash. Netlify, Cloudflare Pages and GitHub Pages serve /programs from programs.html automatically; Vercel does with cleanUrls (below). 404.html is served for unknown URLs by most hosts.

  • Netlify / Cloudflare Pages: build command pnpm build, publish directory dist.
  • Vercel: import the repository; it detects Astro and static output needs no adapter. Add a vercel.json with { "cleanUrls": true } so /programs serves programs.html.
  • GitHub Pages: use the official Astro GitHub Action. For a project site (username.github.io/repo), build with DEMO_BASE_PATH=/repo.
  • Any static host: upload the contents of dist/. If the host doesn't map /programs to programs.html, change build.format to "directory" in astro.config.mjs.

To serve the site from a sub-folder, set DEMO_BASE_PATH when building, for example DEMO_BASE_PATH=/compass pnpm build. It becomes Astro's base, and every link and image written with url() includes it (including the program links in the quiz result). astro.config.mjs also sets compressHTML: false, so the spaces between words and inline tags that start on a new line are kept.

demo/ (if present) is the build used for the freeframe.dev demo; you can delete it.

TypeScript 7 setup#

package.json installs two TypeScript packages on purpose. @typescript/native is TypeScript 7 and provides the tsc that pnpm typecheck runs. typescript is an alias for @typescript/typescript6, the TypeScript 6 API that astro check and typescript-eslint load. When those tools support TypeScript 7, replace both with a single typescript@7. tsconfig.json extends Astro's strict preset.

Troubleshooting#

Problem Fix
A link or image 404s after deploying to a sub-folder Write it with url("/path") rather than a hard-coded "/path", and build with DEMO_BASE_PATH set to the folder.
/programs 404s but /programs.html works Your host doesn't map clean URLs. On Vercel add { "cleanUrls": true }; elsewhere set build.format: "directory".
No time slots on any day Check AVAILABILITY.hours in src/data/booking.ts uses weekday numbers 0 to 6 and "HH:MM" strings, the call length fits inside each window, and daysAhead is larger than minNoticeHours / 24.
Times look an hour off around daylight saving changes Make sure AVAILABILITY.timeZone is an IANA name such as America/New_York, not a fixed offset.
The quiz always shows an old result It is saved in localStorage; click Retake the self-check or remove compass-quiz-v1, and bump QUIZ_KEY when you change the questions.
A filter chip shows 0 or the wrong cards The chip's value must appear exactly (lowercase, no spaces) in the items' tags or topics.
SVG text renders in the wrong font Quote font names that contain digits in font-family attributes, for example 'Source Serif 4'.
Content stays invisible after editing Elements with .reveal are hidden until main.ts runs and they scroll into view; check the browser console for errors.

License#

MIT. Free for personal and commercial use, including client projects. The license text is in LICENSE.txt in the download. All artwork (SVG illustrations, icons, favicon and social image) is included under the same license. Bearing, Nadia Whitcombe and every client, company and publication named in the template are fictional.

Search documentation

Search the guides and every template's documentation