creating layout
34
src/_data/aitools.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"intro": "Optional. Add to any package — you control what they can say.",
|
||||
"items": [
|
||||
{
|
||||
"key": "visitor-chatbot",
|
||||
"name": "Visitor Chatbot",
|
||||
"price": 4800,
|
||||
"priceType": "annual",
|
||||
"description": "Answers visitor questions using only approved exhibit content and licensed data."
|
||||
},
|
||||
{
|
||||
"key": "document-transcription",
|
||||
"name": "Document Transcription",
|
||||
"price": 3000,
|
||||
"priceType": "annual",
|
||||
"description": "Scanned archival documents into searchable text; 5,000 pages/yr included."
|
||||
},
|
||||
{
|
||||
"key": "crowdsourcing",
|
||||
"name": "Crowdsourcing",
|
||||
"price": 2400,
|
||||
"priceType": "annual",
|
||||
"description": "Moderated community photo & story contributions."
|
||||
}
|
||||
],
|
||||
"bundle": {
|
||||
"key": "complete-ai-package",
|
||||
"name": "Complete AI Package",
|
||||
"price": 8000,
|
||||
"priceType": "annual",
|
||||
"savings": 2200,
|
||||
"description": "All three AI tools together, for less than buying them separately."
|
||||
}
|
||||
}
|
||||
58
src/_data/extras.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"key": "additional-gallery-screen",
|
||||
"name": "Additional gallery screen",
|
||||
"price": 2400,
|
||||
"priceType": "annual"
|
||||
},
|
||||
{
|
||||
"key": "additional-exhibit",
|
||||
"name": "Additional published exhibit",
|
||||
"price": 1000,
|
||||
"priceType": "annual",
|
||||
"description": "Built by your team in Studio."
|
||||
},
|
||||
{
|
||||
"key": "curator-care",
|
||||
"name": "Curator Care",
|
||||
"price": 6000,
|
||||
"priceType": "annual",
|
||||
"description": "We maintain, refresh and update all content."
|
||||
},
|
||||
{
|
||||
"key": "done-for-you-exhibit",
|
||||
"name": "Done-For-You Exhibit",
|
||||
"priceMin": 5000,
|
||||
"priceMax": 15000,
|
||||
"priceType": "one-time-per-exhibit"
|
||||
}
|
||||
],
|
||||
"terms": {
|
||||
"prepay": [
|
||||
{ "years": 3, "multiplier": 2.5 },
|
||||
{ "years": 5, "multiplier": 4 }
|
||||
],
|
||||
"appliesTo": "Every year after fees only."
|
||||
},
|
||||
"hardware": {
|
||||
"priceMin": 3500,
|
||||
"priceMax": 9000,
|
||||
"unit": "per screen installed",
|
||||
"note": "Supplied at cost + 15%, or bring your own.",
|
||||
"confirmed": false
|
||||
},
|
||||
"grantSupport": "We provide statements of work, budgets and letters of support formatted for museum development and heritage funding applications.",
|
||||
"timeline": {
|
||||
"weeksMin": 8,
|
||||
"weeksMax": 16,
|
||||
"note": "Most installations live 8–16 weeks after signing.",
|
||||
"confirmed": false
|
||||
},
|
||||
"workedExample": {
|
||||
"packageKey": "flagship",
|
||||
"collectionKey": "second-world-war-campaign",
|
||||
"aitoolKey": "visitor-chatbot",
|
||||
"comparisonOneOff": 67000
|
||||
}
|
||||
}
|
||||
74
src/_data/historicalCollections.json
Normal file
@@ -0,0 +1,74 @@
|
||||
[
|
||||
{
|
||||
"key": "story-packs",
|
||||
"name": "Story Packs",
|
||||
"subtitle": "Dieppe, 419 Squadron & growing",
|
||||
"price": 1900,
|
||||
"priceType": "annual",
|
||||
"description": "Pre-built, ready-to-publish exhibits from completed research projects.",
|
||||
"whatsInside": "Finished, ready-to-publish exhibits drawn from completed research projects — no commissioning or writing required on your end.",
|
||||
"suits": "Museums that want a proven exhibit fast, without commissioning original research."
|
||||
},
|
||||
{
|
||||
"key": "gulf-war",
|
||||
"name": "Gulf War Collection",
|
||||
"subtitle": "Project Friction",
|
||||
"price": 2500,
|
||||
"priceType": "annual",
|
||||
"description": "Complete and museum-ready.",
|
||||
"whatsInside": "A complete, museum-ready exhibit covering the Gulf War, sourced and mapped by our research partners.",
|
||||
"suits": "Regimental museums and heritage sites with a Gulf War service connection."
|
||||
},
|
||||
{
|
||||
"key": "afghanistan",
|
||||
"name": "Afghanistan Collection",
|
||||
"subtitle": "Project Athena",
|
||||
"price": 2900,
|
||||
"priceType": "annual",
|
||||
"description": "Veteran-contributed, geotagged photos of your regiment's tours.",
|
||||
"whatsInside": "Veteran-contributed, geotagged photographs from your regiment's tours in Afghanistan. Pairs well with the Crowdsourcing AI tool for ongoing contributions.",
|
||||
"suits": "Regimental museums building an Afghanistan-era exhibit with community contributions."
|
||||
},
|
||||
{
|
||||
"key": "second-world-war-campaign",
|
||||
"name": "Second World War — Campaign / Regional",
|
||||
"subtitle": "Project '44",
|
||||
"price": 3900,
|
||||
"priceType": "annual",
|
||||
"description": "Mapped unit positions, frontlines and war diary events for one campaign, operation or regiment.",
|
||||
"whatsInside": "Mapped unit positions, frontlines and war diary events for one campaign, operation or regiment, drawn from Project '44's research.",
|
||||
"suits": "Museums telling one specific Second World War campaign, operation or regimental story."
|
||||
},
|
||||
{
|
||||
"key": "second-world-war-full",
|
||||
"name": "Second World War — Full Collection",
|
||||
"subtitle": "Project '44",
|
||||
"price": 8500,
|
||||
"priceType": "annual",
|
||||
"description": "Includes the staff Research Assistant: plain-English questions, sourced answers from the full archive.",
|
||||
"whatsInside": "The complete Second World War archive, plus the staff Research Assistant — plain-English questions, sourced answers from the full archive.",
|
||||
"suits": "Museums covering the full Second World War story, or that want research help on tap for staff."
|
||||
},
|
||||
{
|
||||
"key": "complete-archive",
|
||||
"name": "Complete Archive",
|
||||
"subtitle": null,
|
||||
"price": 12500,
|
||||
"priceType": "annual",
|
||||
"highlight": "Best value for Flagship & Multi-Site",
|
||||
"description": "Every collection, Research Assistant across all of it.",
|
||||
"whatsInside": "Every historical collection Frame 44 licenses, with the Research Assistant working across the full archive.",
|
||||
"suits": "Flagship and Multi-Site museums that want the whole archive available to draw from."
|
||||
},
|
||||
{
|
||||
"key": "custom-research-build",
|
||||
"name": "Custom Research Build",
|
||||
"subtitle": null,
|
||||
"price": 12000,
|
||||
"priceType": "one-time-per-project",
|
||||
"priceFrom": true,
|
||||
"description": "Commissioned research scoped to the story your museum tells.",
|
||||
"whatsInside": "A commissioned research project scoped to the specific story your museum tells, built by our historian team.",
|
||||
"suits": "Museums with a story not yet covered by an existing collection."
|
||||
}
|
||||
]
|
||||
8
src/_data/i18n.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const en = require("./i18n/en.json");
|
||||
|
||||
// English-only today. Adding /fr/: create ./i18n/fr.json, then switch this
|
||||
// to `(data) => ({ en, fr })[data.lang || "en"]` and set `lang: fr` in front
|
||||
// matter on the src/fr/ page tree.
|
||||
module.exports = function () {
|
||||
return en;
|
||||
};
|
||||
38
src/_data/i18n/en.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"skipLink": "Skip to main content",
|
||||
"nav": {
|
||||
"platform": "Platform",
|
||||
"pricing": "Pricing",
|
||||
"collections": "Collections",
|
||||
"museums": "Museums",
|
||||
"about": "About",
|
||||
"contact": "Contact"
|
||||
},
|
||||
"cta": {
|
||||
"bookConversation": "Book a conversation",
|
||||
"seePricing": "See pricing",
|
||||
"explorePlatform": "Explore the platform",
|
||||
"emailUs": "Email us",
|
||||
"viewCollections": "View all collections",
|
||||
"readMore": "Read more"
|
||||
},
|
||||
"footer": {
|
||||
"attribution": "Historical data licensed from Project '44 (CRMA).",
|
||||
"pricesNote": "All prices in Canadian dollars (CAD), exclusive of taxes.",
|
||||
"copyrightSuffix": "Frame 44 Technology Inc. All rights reserved."
|
||||
},
|
||||
"pricing": {
|
||||
"eyebrow": "How pricing works",
|
||||
"twoNumberExplainer": "Every package has two numbers: what it costs to build, and what it costs to keep running. That's it.",
|
||||
"buildYearLabel": "Build year",
|
||||
"buildYearNote": "One-time. Fits project & development funding.",
|
||||
"everyYearLabel": "Every year after",
|
||||
"everyYearNote": "Annual. Sized for operating budgets.",
|
||||
"currencyNote": "All prices CAD, exclusive of taxes. Hardware quoted separately.",
|
||||
"reassurance": [
|
||||
"Bilingual by design (full EN/FR visitor experience on every package)",
|
||||
"Runs on secure networks (screens run offline, sync when connected)",
|
||||
"Your content stays yours (exports in standard formats)"
|
||||
]
|
||||
}
|
||||
}
|
||||
18
src/_data/museums.json
Normal file
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"key": "grootegast",
|
||||
"name": "Victory Museum",
|
||||
"location": "Grootegast, Netherlands",
|
||||
"type": "Permanent kiosk installation",
|
||||
"summary": "<!-- TODO: confirm case-study summary copy for Victory Museum, Grootegast -->",
|
||||
"image": "/assets/images/placeholders/grootegast.svg"
|
||||
},
|
||||
{
|
||||
"key": "holten",
|
||||
"name": "Holten Canadian War Cemetery Visitor Centre",
|
||||
"location": "Holten, Netherlands",
|
||||
"type": "Permanent kiosk installation",
|
||||
"summary": "<!-- TODO: confirm case-study summary copy for Holten Canadian War Cemetery Visitor Centre -->",
|
||||
"image": "/assets/images/placeholders/holten.svg"
|
||||
}
|
||||
]
|
||||
62
src/_data/packages.json
Normal file
@@ -0,0 +1,62 @@
|
||||
[
|
||||
{
|
||||
"key": "exhibit-one",
|
||||
"name": "Exhibit One",
|
||||
"badge": null,
|
||||
"buildPrice": 7500,
|
||||
"annualPrice": 2900,
|
||||
"summary": "One interactive map exhibit, built with our team.",
|
||||
"features": [
|
||||
"1 interactive map exhibit built with our team",
|
||||
"Web-based & touchscreen-ready",
|
||||
"Full Frame 44 Studio access",
|
||||
"Hosting, support & updates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "gallery",
|
||||
"name": "Gallery",
|
||||
"badge": null,
|
||||
"buildPrice": 19500,
|
||||
"annualPrice": 6500,
|
||||
"summary": "A full gallery screen with room to grow to five exhibits.",
|
||||
"features": [
|
||||
"Up to 5 published exhibits",
|
||||
"First exhibit built by our team",
|
||||
"Touchscreen software for 1 gallery screen",
|
||||
"All themes",
|
||||
"5 staff accounts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "flagship",
|
||||
"name": "Flagship",
|
||||
"badge": "Most installed",
|
||||
"buildPrice": 45000,
|
||||
"annualPrice": 9500,
|
||||
"summary": "A bespoke build from our historian team, with something new every year.",
|
||||
"features": [
|
||||
"Bespoke build by our historian team",
|
||||
"Unlimited published exhibits",
|
||||
"Touchscreen software for 2 gallery screens",
|
||||
"One new or refreshed exhibit every year",
|
||||
"Custom branding & priority support"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "multi-site",
|
||||
"name": "Multi-Site",
|
||||
"badge": null,
|
||||
"buildPrice": 75000,
|
||||
"buildPriceFrom": true,
|
||||
"annualPrice": null,
|
||||
"annualPriceLabel": "Custom",
|
||||
"summary": "Multiple locations on one synced platform, scoped to your institution.",
|
||||
"features": [
|
||||
"Multiple locations, synced content",
|
||||
"Shared collections across sites",
|
||||
"Dedicated account historian",
|
||||
"Scoped & quoted per institution"
|
||||
]
|
||||
}
|
||||
]
|
||||
24
src/_data/site.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Frame 44 Technology Inc.",
|
||||
"shortName": "Frame 44",
|
||||
"url": "https://www.frame44.com",
|
||||
"lang": "en",
|
||||
"locales": ["en"],
|
||||
"description": "Map-native interactive exhibit software for museums, historical societies and heritage sites.",
|
||||
"contactEmail": "contact@frame44.example",
|
||||
"contactEmailConfirmed": false,
|
||||
"logo": "/assets/images/logo-footer.png",
|
||||
"logoWidth": 457,
|
||||
"logoHeight": 160,
|
||||
"logoHeader": "/assets/images/logo-header.png",
|
||||
"logoFooter": "/assets/images/logo-footer.png",
|
||||
"ctaUrl": "/contact/",
|
||||
"nav": [
|
||||
{ "key": "platform", "url": "/platform/" },
|
||||
{ "key": "pricing", "url": "/pricing/" },
|
||||
{ "key": "collections", "url": "/collections/" },
|
||||
{ "key": "museums", "url": "/museums/" },
|
||||
{ "key": "about", "url": "/about/" },
|
||||
{ "key": "contact", "url": "/contact/" }
|
||||
]
|
||||
}
|
||||
23
src/_data/team.json
Normal file
@@ -0,0 +1,23 @@
|
||||
[
|
||||
{
|
||||
"key": "placeholder-1",
|
||||
"name": "<!-- TODO: name -->",
|
||||
"role": "<!-- TODO: role -->",
|
||||
"bio": "<!-- TODO: bio -->",
|
||||
"photo": "/assets/images/placeholders/team-1.svg"
|
||||
},
|
||||
{
|
||||
"key": "placeholder-2",
|
||||
"name": "<!-- TODO: name -->",
|
||||
"role": "<!-- TODO: role -->",
|
||||
"bio": "<!-- TODO: bio -->",
|
||||
"photo": "/assets/images/placeholders/team-2.svg"
|
||||
},
|
||||
{
|
||||
"key": "placeholder-3",
|
||||
"name": "<!-- TODO: name -->",
|
||||
"role": "<!-- TODO: role -->",
|
||||
"bio": "<!-- TODO: bio -->",
|
||||
"photo": "/assets/images/placeholders/team-3.svg"
|
||||
}
|
||||
]
|
||||
43
src/_includes/layouts/base.njk
Normal file
@@ -0,0 +1,43 @@
|
||||
<!doctype html>
|
||||
<html lang="{{ site.lang }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ title }} | {{ site.shortName }}</title>
|
||||
<meta name="description" content="{{ description }}">
|
||||
<link rel="canonical" href="{{ site.url }}{{ page.url }}">
|
||||
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:site_name" content="{{ site.shortName }}">
|
||||
<meta property="og:title" content="{{ title }}">
|
||||
<meta property="og:description" content="{{ description }}">
|
||||
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
|
||||
<meta property="og:locale" content="en_CA">
|
||||
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" href="/assets/images/apple-touch-icon.png">
|
||||
|
||||
<link rel="stylesheet" href="/assets/css/style.css">
|
||||
|
||||
{% set orgSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
"name": site.name,
|
||||
"url": site.url,
|
||||
"logo": site.url + site.logo,
|
||||
"description": site.description
|
||||
} %}
|
||||
<script type="application/ld+json">{{ orgSchema | dump | safe }}</script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">{{ i18n.skipLink }}</a>
|
||||
{% include "partials/header.njk" %}
|
||||
<main id="main">
|
||||
{{ content | safe }}
|
||||
</main>
|
||||
{% include "partials/footer.njk" %}
|
||||
<script src="/assets/js/main.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
50
src/_includes/macros.njk
Normal file
@@ -0,0 +1,50 @@
|
||||
{% macro ctaBand(heading, body, ctaLabel, ctaUrl) %}
|
||||
<section class="section cta-band">
|
||||
<div class="container">
|
||||
<h2>{{ heading }}</h2>
|
||||
<p>{{ body }}</p>
|
||||
<a class="button button--primary" href="{{ ctaUrl }}">{{ ctaLabel }}</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro pricingCard(pkg) %}
|
||||
<article class="card pricing-card{% if pkg.badge %} pricing-card--featured{% endif %}">
|
||||
{% if pkg.badge %}<span class="pricing-card__badge">{{ pkg.badge }}</span>{% endif %}
|
||||
<h3>{{ pkg.name }}</h3>
|
||||
<p>{{ pkg.summary }}</p>
|
||||
<div class="pricing-card__prices">
|
||||
<div>
|
||||
<span class="price-figure">{% if pkg.buildPriceFrom %}from {% endif %}{{ pkg.buildPrice | cad }}</span>
|
||||
<span class="price-figure__label">Build year</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="price-figure">{% if pkg.annualPrice %}{{ pkg.annualPrice | cad }}{% else %}{{ pkg.annualPriceLabel }}{% endif %}</span>
|
||||
<span class="price-figure__label">Every year after</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="pricing-card__features">
|
||||
{% for feature in pkg.features %}
|
||||
<li>{{ feature }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro collectionCard(item) %}
|
||||
<article class="card collection-card">
|
||||
<div class="collection-card__meta">
|
||||
<h3>{{ item.name }}{% if item.subtitle %} <span class="tag">{{ item.subtitle }}</span>{% endif %}</h3>
|
||||
</div>
|
||||
{% if item.highlight %}<p class="tag">{{ item.highlight }}</p>{% endif %}
|
||||
<p>{{ item.description }}</p>
|
||||
<p><strong>What's inside:</strong> {{ item.whatsInside }}</p>
|
||||
<p><strong>Suits:</strong> {{ item.suits }}</p>
|
||||
<p class="price-figure">
|
||||
{% if item.priceFrom %}from {% endif %}{{ item.price | cad }}
|
||||
<span class="price-figure__label">
|
||||
{% if item.priceType == "annual" %}per year{% else %}one-time, per project{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
</article>
|
||||
{% endmacro %}
|
||||
15
src/_includes/partials/footer.njk
Normal file
@@ -0,0 +1,15 @@
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<div class="site-footer__row">
|
||||
<img class="site-footer__logo" src="{{ site.logoFooter }}" alt="{{ site.name }}" width="{{ site.logoWidth }}" height="{{ site.logoHeight }}">
|
||||
<p>© {% year %} {{ i18n.footer.copyrightSuffix }}</p>
|
||||
</div>
|
||||
<p>{{ i18n.footer.attribution }}</p>
|
||||
<p>{{ i18n.footer.pricesNote }}</p>
|
||||
<ul class="site-footer__nav">
|
||||
{% for item in site.nav %}
|
||||
<li><a href="{{ item.url }}">{{ i18n.nav[item.key] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
19
src/_includes/partials/header.njk
Normal file
@@ -0,0 +1,19 @@
|
||||
<header class="site-header">
|
||||
<div class="container site-header__bar">
|
||||
<a class="site-header__logo" href="/" aria-label="{{ site.name }} — home">
|
||||
<img src="{{ site.logoHeader }}" alt="{{ site.name }}" width="{{ site.logoWidth }}" height="{{ site.logoHeight }}">
|
||||
</a>
|
||||
<nav class="site-nav" aria-label="Primary">
|
||||
<ul class="site-nav__list" id="primary-nav-list">
|
||||
{% for item in site.nav %}
|
||||
<li><a href="{{ item.url }}"{% if page.url == item.url %} aria-current="page"{% endif %}>{{ i18n.nav[item.key] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<a class="button button--primary header-cta" href="{{ site.ctaUrl }}">{{ i18n.cta.bookConversation }}</a>
|
||||
<button class="site-nav__toggle" type="button" aria-expanded="false" aria-controls="primary-nav-list">
|
||||
<span class="visually-hidden">Menu</span>
|
||||
<span aria-hidden="true">☰</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
40
src/about/index.njk
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: About
|
||||
description: Frame 44 is a Canadian, veteran-founded software company building map-native interactive exhibits for museums and heritage sites.
|
||||
permalink: /about/
|
||||
---
|
||||
{% from "macros.njk" import ctaBand %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">About</p>
|
||||
<h1>A Canadian company, built by people who've served</h1>
|
||||
<p class="hero__sub">Frame 44 Technology is a Canadian software company. We build map-native interactive exhibit software for museums, historical societies and heritage sites — veteran-founded, and built to make real, sourced history easy for visitors to explore.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Our relationship with Project '44</h2>
|
||||
<p>Project '44, run by the nonprofit CRMA, is Frame 44's first and biggest customer. Frame 44 licenses historical data from Project '44 and its sister archives — we don't own that research. Think of it this way: the platform behind Project '44, available for your story.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted">
|
||||
<div class="container">
|
||||
<h2>Our team</h2>
|
||||
<div class="grid grid--3">
|
||||
{% for person in team %}
|
||||
<article class="card team-card">
|
||||
<img src="{{ person.photo }}" alt="Team headshot placeholder" width="400" height="400" loading="lazy">
|
||||
<h3>{{ person.name | safe }}</h3>
|
||||
<p class="tag">{{ person.role | safe }}</p>
|
||||
<p>{{ person.bio | safe }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("Let's talk about your museum.", "Every installation starts with a conversation.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
665
src/assets/css/style.css
Normal file
@@ -0,0 +1,665 @@
|
||||
/* ---- Fonts ---- */
|
||||
|
||||
@font-face {
|
||||
font-family: "Schibsted Grotesk";
|
||||
src: url("/assets/fonts/schibsted-grotesk/schibsted-grotesk-600.woff2") format("woff2");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Schibsted Grotesk";
|
||||
src: url("/assets/fonts/schibsted-grotesk/schibsted-grotesk-700.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("/assets/fonts/inter/inter-400.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
src: url("/assets/fonts/inter/inter-600.woff2") format("woff2");
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
/* ---- Tokens ---- */
|
||||
|
||||
:root {
|
||||
--color-red: #df272f;
|
||||
--color-ink: #1b1d21;
|
||||
--color-body: #24262b;
|
||||
--color-muted: #6a6e76;
|
||||
--color-line: #e3e5e8;
|
||||
--color-bg-light: #f6f7f8;
|
||||
--color-bg: #ffffff;
|
||||
|
||||
--font-heading: "Schibsted Grotesk", ui-sans-serif, system-ui, "Segoe UI", Arial, sans-serif;
|
||||
--font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
|
||||
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 1rem;
|
||||
--space-4: 1.5rem;
|
||||
--space-5: 2rem;
|
||||
--space-6: 3rem;
|
||||
--space-7: 4rem;
|
||||
--space-8: 6rem;
|
||||
|
||||
--radius: 10px;
|
||||
--radius-lg: 16px;
|
||||
--container-width: 1200px;
|
||||
--shadow-card: 0 1px 2px rgba(27, 29, 33, 0.06), 0 8px 24px rgba(27, 29, 33, 0.06);
|
||||
}
|
||||
|
||||
/* ---- Reset ---- */
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font-body);
|
||||
color: var(--color-body);
|
||||
background: var(--color-bg);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
font-family: var(--font-heading);
|
||||
color: var(--color-ink);
|
||||
line-height: 1.15;
|
||||
margin: 0 0 var(--space-3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(2.25rem, 1.7rem + 2.2vw, 3.5rem);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.6rem, 1.35rem + 1vw, 2.25rem);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: clamp(1.2rem, 1.1rem + 0.4vw, 1.4rem);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 var(--space-3);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-red);
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 1.25em;
|
||||
}
|
||||
|
||||
/* ---- Accessibility ---- */
|
||||
|
||||
:focus-visible {
|
||||
outline: 3px solid var(--color-ink);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
top: -100%;
|
||||
left: var(--space-3);
|
||||
background: var(--color-ink);
|
||||
color: #fff;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
z-index: 100;
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---- Layout ---- */
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: var(--container-width);
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--space-4);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding-block: var(--space-7);
|
||||
}
|
||||
|
||||
.section--tight {
|
||||
padding-block: var(--space-6);
|
||||
}
|
||||
|
||||
.section--muted {
|
||||
background: var(--color-bg-light);
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.grid--2 {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.grid--3 {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.grid--4 {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Site header ---- */
|
||||
|
||||
.site-header {
|
||||
background: var(--color-ink);
|
||||
border-bottom: 3px solid var(--color-red);
|
||||
}
|
||||
|
||||
.site-header__bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
padding-block: var(--space-3);
|
||||
}
|
||||
|
||||
.site-header__logo img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.site-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
.site-nav__list {
|
||||
display: none;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
.site-nav__list a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.site-nav__list a:hover,
|
||||
.site-nav__list a:focus-visible {
|
||||
color: var(--color-red);
|
||||
}
|
||||
|
||||
.site-nav__toggle {
|
||||
display: inline-flex;
|
||||
background: none;
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
color: #fff;
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.site-nav__list {
|
||||
display: flex;
|
||||
}
|
||||
.site-nav__toggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.site-nav__list--open {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
top: 100%;
|
||||
background: var(--color-ink);
|
||||
padding: var(--space-4);
|
||||
gap: var(--space-3);
|
||||
border-bottom: 3px solid var(--color-red);
|
||||
}
|
||||
|
||||
.header-cta {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---- Buttons ---- */
|
||||
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--space-2);
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius);
|
||||
padding: 0.75rem 1.375rem;
|
||||
border: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button--primary {
|
||||
background: var(--color-red);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button--primary:hover,
|
||||
.button--primary:focus-visible {
|
||||
background: #c11f26;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button--ghost {
|
||||
background: transparent;
|
||||
border-color: currentColor;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button--ghost:hover,
|
||||
.button--ghost:focus-visible {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button--outline {
|
||||
background: transparent;
|
||||
border-color: var(--color-ink);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.button--outline:hover,
|
||||
.button--outline:focus-visible {
|
||||
background: var(--color-ink);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ---- Hero ---- */
|
||||
|
||||
.hero {
|
||||
background: var(--color-bg-light);
|
||||
background-image: radial-gradient(circle at 15% 20%, rgba(223, 39, 47, 0.06), transparent 40%),
|
||||
repeating-linear-gradient(120deg, rgba(27, 29, 33, 0.035) 0 1px, transparent 1px 90px);
|
||||
padding-block: var(--space-8);
|
||||
}
|
||||
|
||||
.hero__inner {
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.hero__eyebrow {
|
||||
color: var(--color-red);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.hero__sub {
|
||||
font-size: 1.15rem;
|
||||
color: var(--color-muted);
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
.hero__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
margin-top: var(--space-5);
|
||||
}
|
||||
|
||||
/* ---- Cards ---- */
|
||||
|
||||
.card {
|
||||
background: var(--color-bg);
|
||||
border: 1px solid var(--color-line);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--space-5);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
/* ---- Steps ---- */
|
||||
|
||||
.steps {
|
||||
counter-reset: step;
|
||||
}
|
||||
|
||||
.step {
|
||||
position: relative;
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
.step::before {
|
||||
counter-increment: step;
|
||||
content: counter(step);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: 50%;
|
||||
background: var(--color-ink);
|
||||
color: #fff;
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ---- Pricing ---- */
|
||||
|
||||
.price-figure {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
color: var(--color-red);
|
||||
font-size: 2rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.price-figure__label {
|
||||
display: block;
|
||||
font-family: var(--font-body);
|
||||
font-weight: 500;
|
||||
color: var(--color-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
|
||||
.pricing-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.pricing-card--featured {
|
||||
border-color: var(--color-red);
|
||||
box-shadow: 0 0 0 2px var(--color-red);
|
||||
}
|
||||
|
||||
.pricing-card__badge {
|
||||
position: absolute;
|
||||
top: -0.75rem;
|
||||
left: var(--space-4);
|
||||
background: var(--color-red);
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.pricing-card__prices {
|
||||
display: flex;
|
||||
gap: var(--space-5);
|
||||
padding-block: var(--space-3);
|
||||
border-block: 1px solid var(--color-line);
|
||||
}
|
||||
|
||||
.pricing-card__features {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.pricing-card__features li {
|
||||
padding-left: 1.5em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pricing-card__features li::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0.5em;
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
border-radius: 50%;
|
||||
background: var(--color-red);
|
||||
}
|
||||
|
||||
.price-table-note {
|
||||
color: var(--color-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.worked-example {
|
||||
border-left: 4px solid var(--color-red);
|
||||
padding-left: var(--space-4);
|
||||
}
|
||||
|
||||
.worked-example dl {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: var(--space-1) var(--space-3);
|
||||
margin: var(--space-3) 0;
|
||||
}
|
||||
|
||||
.worked-example dt {
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.worked-example dd {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.worked-example__total {
|
||||
border-top: 1px solid var(--color-line);
|
||||
padding-top: var(--space-2);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.reassurance-band {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
@media (min-width: 700px) {
|
||||
.reassurance-band {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.reassurance-band li {
|
||||
padding-left: 1.75em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.reassurance-band li::before {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: var(--color-red);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ---- Collections & museums ---- */
|
||||
|
||||
.collection-card,
|
||||
.museum-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.collection-card__meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
background: var(--color-bg-light);
|
||||
border: 1px solid var(--color-line);
|
||||
border-radius: 999px;
|
||||
padding: 0.15rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.museum-card img,
|
||||
.team-card img {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
/* ---- CTA band ---- */
|
||||
|
||||
.cta-band {
|
||||
background: var(--color-ink);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cta-band h2 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cta-band p {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
max-width: 55ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
/* ---- Contact ---- */
|
||||
|
||||
.contact-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* ---- Footer ---- */
|
||||
|
||||
.site-footer {
|
||||
background: var(--color-bg-light);
|
||||
border-top: 1px solid var(--color-line);
|
||||
padding-block: var(--space-6);
|
||||
color: var(--color-muted);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.site-footer a {
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.site-footer__logo {
|
||||
height: 24px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.site-footer__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2) var(--space-4);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.site-footer__nav {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-3);
|
||||
padding: 0;
|
||||
margin: var(--space-4) 0 0;
|
||||
}
|
||||
|
||||
.site-footer__nav a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ---- Utilities ---- */
|
||||
|
||||
.mt-4 {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: var(--space-5);
|
||||
}
|
||||
|
||||
.card--accent {
|
||||
border-color: var(--color-red);
|
||||
}
|
||||
|
||||
/* ---- TODO markers ---- */
|
||||
|
||||
.todo-flag {
|
||||
display: inline-block;
|
||||
background: #fff4e5;
|
||||
border: 1px dashed #c77700;
|
||||
color: #8a5600;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
BIN
src/assets/images/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
src/assets/images/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 582 B |
BIN
src/assets/images/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/images/logo-footer.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
src/assets/images/logo-header.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
7
src/assets/images/placeholders/grootegast.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" role="img" aria-label="Placeholder photo — Victory Museum, Grootegast">
|
||||
<rect width="800" height="450" fill="#F6F7F8"/>
|
||||
<path d="M0 375 L800 325 M0 300 L800 250 M0 225 L800 175" stroke="#E3E5E8" stroke-width="2" fill="none"/>
|
||||
<rect x="0" y="0" width="6" height="450" fill="#DF272F"/>
|
||||
<text x="40" y="225" font-family="Arial, sans-serif" font-size="24" fill="#6A6E76">Photo placeholder</text>
|
||||
<text x="40" y="255" font-family="Arial, sans-serif" font-size="18" fill="#6A6E76">Victory Museum, Grootegast</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 590 B |
7
src/assets/images/placeholders/holten.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" role="img" aria-label="Placeholder photo — Holten Canadian War Cemetery Visitor Centre">
|
||||
<rect width="800" height="450" fill="#F6F7F8"/>
|
||||
<path d="M0 375 L800 325 M0 300 L800 250 M0 225 L800 175" stroke="#E3E5E8" stroke-width="2" fill="none"/>
|
||||
<rect x="0" y="0" width="6" height="450" fill="#DF272F"/>
|
||||
<text x="40" y="225" font-family="Arial, sans-serif" font-size="24" fill="#6A6E76">Photo placeholder</text>
|
||||
<text x="40" y="255" font-family="Arial, sans-serif" font-size="18" fill="#6A6E76">Holten Canadian War Cemetery</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 609 B |
9
src/assets/images/placeholders/studio-screenshot.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" role="img" aria-label="Placeholder screenshot of Frame 44 Studio">
|
||||
<rect width="800" height="600" fill="#F6F7F8"/>
|
||||
<path d="M0 500 L800 440 M0 400 L800 340 M0 300 L800 240 M0 200 L800 140" stroke="#E3E5E8" stroke-width="2" fill="none"/>
|
||||
<circle cx="260" cy="260" r="10" fill="#DF272F"/>
|
||||
<circle cx="480" cy="360" r="10" fill="#DF272F"/>
|
||||
<circle cx="380" cy="180" r="10" fill="#DF272F"/>
|
||||
<rect x="0" y="0" width="800" height="6" fill="#1B1D21"/>
|
||||
<text x="40" y="560" font-family="Arial, sans-serif" font-size="22" fill="#6A6E76">Studio screenshot placeholder</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 648 B |
6
src/assets/images/placeholders/team-1.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" role="img" aria-label="Placeholder headshot">
|
||||
<rect width="400" height="400" fill="#F6F7F8"/>
|
||||
<circle cx="200" cy="160" r="70" fill="#E3E5E8"/>
|
||||
<path d="M70 380 C70 280 330 280 330 380 Z" fill="#E3E5E8"/>
|
||||
<text x="200" y="360" font-family="Arial, sans-serif" font-size="16" fill="#6A6E76" text-anchor="middle">Photo placeholder</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
6
src/assets/images/placeholders/team-2.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" role="img" aria-label="Placeholder headshot">
|
||||
<rect width="400" height="400" fill="#F6F7F8"/>
|
||||
<circle cx="200" cy="160" r="70" fill="#E3E5E8"/>
|
||||
<path d="M70 380 C70 280 330 280 330 380 Z" fill="#E3E5E8"/>
|
||||
<text x="200" y="360" font-family="Arial, sans-serif" font-size="16" fill="#6A6E76" text-anchor="middle">Photo placeholder</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
6
src/assets/images/placeholders/team-3.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" role="img" aria-label="Placeholder headshot">
|
||||
<rect width="400" height="400" fill="#F6F7F8"/>
|
||||
<circle cx="200" cy="160" r="70" fill="#E3E5E8"/>
|
||||
<path d="M70 380 C70 280 330 280 330 380 Z" fill="#E3E5E8"/>
|
||||
<text x="200" y="360" font-family="Arial, sans-serif" font-size="16" fill="#6A6E76" text-anchor="middle">Photo placeholder</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 412 B |
10
src/assets/js/main.js
Normal file
@@ -0,0 +1,10 @@
|
||||
(function () {
|
||||
const toggle = document.querySelector(".site-nav__toggle");
|
||||
const list = document.querySelector(".site-nav__list");
|
||||
if (!toggle || !list) return;
|
||||
|
||||
toggle.addEventListener("click", () => {
|
||||
const isOpen = list.classList.toggle("site-nav__list--open");
|
||||
toggle.setAttribute("aria-expanded", String(isOpen));
|
||||
});
|
||||
})();
|
||||
BIN
src/assets/logos/Favicon.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
src/assets/logos/Favicon2.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-02.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-03.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-04.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-05 - Copy (2).png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-05.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-06.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-07.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-08.png
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/logos/Frame 44 Logo PNG-09.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
40
src/collections/index.njk
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Historical Collections
|
||||
description: Historical data collections licensed from Project '44 and its sister archives — scoped to the story your museum tells.
|
||||
permalink: /collections/
|
||||
---
|
||||
{% from "macros.njk" import ctaBand %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">Historical Collections</p>
|
||||
<h1>Research you don't have to commission yourself</h1>
|
||||
<p class="hero__sub">The platform behind Project '44, available for your story. Frame 44 licenses historical data from Project '44 and its sister archives (CRMA) so your exhibits launch with real, sourced content.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="grid">
|
||||
{% for item in historicalCollections %}
|
||||
<article class="card collection-card">
|
||||
<div class="collection-card__meta">
|
||||
<h2>{{ item.name }}{% if item.subtitle %} <span class="tag">{{ item.subtitle }}</span>{% endif %}</h2>
|
||||
<p class="price-figure">
|
||||
{% if item.priceFrom %}from {% endif %}{{ item.price | cad }}
|
||||
<span class="price-figure__label">{% if item.priceType == "annual" %}per year{% else %}one-time, per project{% endif %}</span>
|
||||
</p>
|
||||
</div>
|
||||
{% if item.highlight %}<p class="tag">{{ item.highlight }}</p>{% endif %}
|
||||
<p>{{ item.description }}</p>
|
||||
<p><strong>What's inside:</strong> {{ item.whatsInside }}</p>
|
||||
<p><strong>Suits:</strong> {{ item.suits }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="mt-4"><a href="/pricing/#collections">See collection pricing alongside packages and AI tools →</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("Not sure which collection fits your museum?", "Tell us the story you're telling and we'll point you to the right collection — or scope a custom research build.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
31
src/contact/index.njk
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Contact
|
||||
description: Every installation starts with a conversation. Get in touch with Frame 44 to scope your exhibit and price it to your funding.
|
||||
permalink: /contact/
|
||||
---
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">Contact</p>
|
||||
<h1>Every installation starts with a conversation.</h1>
|
||||
<p class="hero__sub">We'll scope your exhibit, prepare a grant-ready proposal, and price it to your funding — no obligation.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container contact-panel">
|
||||
<p>
|
||||
<a class="button button--primary" href="mailto:{{ site.contactEmail }}?subject=Exhibit%20inquiry">
|
||||
{{ i18n.cta.emailUs }}: {{ site.contactEmail }}
|
||||
</a>
|
||||
{% if not site.contactEmailConfirmed %}<span class="todo-flag">TODO: confirm real contact email</span>{% endif %}
|
||||
</p>
|
||||
<p>Prefer to write ahead of a call? Tell us:</p>
|
||||
<ul>
|
||||
<li>Your museum or heritage site</li>
|
||||
<li>The story or collection you have in mind</li>
|
||||
<li>Any funding deadline we should be scoping around</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
BIN
src/favicon.ico
Normal file
|
After Width: | Height: | Size: 604 B |
76
src/index.njk
Normal file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Map-native interactive exhibits for museums & heritage sites
|
||||
description: Frame 44 builds map-native interactive exhibits for museums and heritage sites — touchscreen kiosks and web exhibits your own staff can edit, forever.
|
||||
---
|
||||
{% from "macros.njk" import ctaBand %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">Frame 44 Technology</p>
|
||||
<h1>Map-native interactive exhibits for museums & heritage sites</h1>
|
||||
<p class="hero__sub">Give visitors a map they can touch and explore — timelines, story panels and imagery, on your museum floor and on your website. Built and supported by our team; edited by yours.</p>
|
||||
<div class="hero__actions">
|
||||
<a class="button button--primary" href="{{ site.ctaUrl }}">{{ i18n.cta.bookConversation }}</a>
|
||||
<a class="button button--outline" href="/platform/">{{ i18n.cta.explorePlatform }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Three things to understand</h2>
|
||||
<div class="grid grid--3 steps">
|
||||
<div class="step card">
|
||||
<h3>An exhibit</h3>
|
||||
<p>One interactive story — a map your visitors can touch and explore, with timelines, photos and text panels.</p>
|
||||
</div>
|
||||
<div class="step card">
|
||||
<h3>A gallery screen</h3>
|
||||
<p>The physical touchscreen on your museum floor. The same exhibits also run on your website.</p>
|
||||
</div>
|
||||
<div class="step card">
|
||||
<h3>Frame 44 Studio</h3>
|
||||
<p>The tool your own staff use to edit text, photos and maps — no technical skills needed.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted">
|
||||
<div class="container">
|
||||
<h2>Permanent installations</h2>
|
||||
<p>Frame 44 exhibits are running in museums and heritage sites today.</p>
|
||||
<div class="grid grid--2">
|
||||
{% for museum in museums %}
|
||||
<article class="card museum-card">
|
||||
<img src="{{ museum.image }}" alt="{{ museum.name }}" width="800" height="450" loading="lazy">
|
||||
<h3>{{ museum.name }}</h3>
|
||||
<p class="tag">{{ museum.location }} · {{ museum.type }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p><a href="/museums/">See the full case studies →</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>{{ i18n.pricing.eyebrow }}</h2>
|
||||
<p>{{ i18n.pricing.twoNumberExplainer }}</p>
|
||||
<div class="grid grid--4">
|
||||
{% for pkg in packages %}
|
||||
<div class="card">
|
||||
<h3>{{ pkg.name }}</h3>
|
||||
<p class="price-figure">{% if pkg.buildPriceFrom %}from {% endif %}{{ pkg.buildPrice | cad }}</p>
|
||||
<p class="price-figure__label">{{ i18n.pricing.buildYearLabel }}</p>
|
||||
<p class="price-figure">{% if pkg.annualPrice %}{{ pkg.annualPrice | cad }}{% else %}{{ pkg.annualPriceLabel }}{% endif %}</p>
|
||||
<p class="price-figure__label">{{ i18n.pricing.everyYearLabel }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p><a class="button button--outline" href="/pricing/">{{ i18n.cta.seePricing }}</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("Every installation starts with a conversation.", "We'll scope your exhibit, prepare a grant-ready proposal, and price it to your funding — no obligation.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
32
src/museums/index.njk
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Museums
|
||||
description: Case studies from Frame 44 installations — the Victory Museum in Grootegast and the Holten Canadian War Cemetery Visitor Centre.
|
||||
permalink: /museums/
|
||||
---
|
||||
{% from "macros.njk" import ctaBand %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">Museums</p>
|
||||
<h1>Where Frame 44 is installed today</h1>
|
||||
<p class="hero__sub">Two permanent kiosk installations, in the Netherlands.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="grid grid--2">
|
||||
{% for museum in museums %}
|
||||
<article class="card museum-card">
|
||||
<img src="{{ museum.image }}" alt="{{ museum.name }}" width="800" height="450" loading="lazy">
|
||||
<h2>{{ museum.name }}</h2>
|
||||
<p class="tag">{{ museum.location }} · {{ museum.type }}</p>
|
||||
<p>{{ museum.summary | safe }}</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("Want your museum here next?", "We'll scope your exhibit, prepare a grant-ready proposal, and price it to your funding — no obligation.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
69
src/platform/index.njk
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: The Platform
|
||||
description: Frame 44 Atlas, the map platform, and Frame 44 Studio, the exhibit builder — the two pieces behind every Frame 44 exhibit.
|
||||
permalink: /platform/
|
||||
---
|
||||
{% from "macros.njk" import ctaBand %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">Platform</p>
|
||||
<h1>Your team builds in Studio. Everything runs on Atlas.</h1>
|
||||
<p class="hero__sub">Frame 44 Atlas is the map platform behind every exhibit — the map engine, your historical data, hosting and sync. Frame 44 Studio is where your staff build and edit exhibits — no developer required.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Frame 44 Atlas</h2>
|
||||
<p>The platform underneath every Frame 44 exhibit — the map engine, your historical data, and the infrastructure that keeps it all running.</p>
|
||||
<ul>
|
||||
<li>Live MapLibre map integration</li>
|
||||
<li>Historical map layers and projections</li>
|
||||
<li>Hosting and content sync</li>
|
||||
<li>Offline kiosk runtime for gallery screens</li>
|
||||
<li>Monitoring and support</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted">
|
||||
<div class="container grid grid--2">
|
||||
<div>
|
||||
<h2>Frame 44 Studio</h2>
|
||||
<p>The block-based builder your own staff use to assemble each exhibit — no coding, no developer on standby.</p>
|
||||
<ul>
|
||||
<li>Headings, fact panels and timeline cards</li>
|
||||
<li>Photo and document galleries</li>
|
||||
<li>Map buttons, overlay toggles and scenario buttons</li>
|
||||
<li>Themes to match your institution's look</li>
|
||||
<li>One-click publish and sync workflow</li>
|
||||
</ul>
|
||||
</div>
|
||||
<img src="/assets/images/placeholders/studio-screenshot.svg" alt="Screenshot placeholder of Frame 44 Studio" width="800" height="600" loading="lazy">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container grid grid--2">
|
||||
<div class="card">
|
||||
<h3>Gallery screens</h3>
|
||||
<p>Kiosk software built to run in a museum, not an office. It works offline on secure networks and syncs new content automatically when it's connected. We monitor and support every screen we install.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Web exhibits</h3>
|
||||
<p>The same exhibits your visitors touch in the gallery also run on your museum's website — built once, published everywhere, at no extra authoring effort.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted">
|
||||
<div class="container">
|
||||
<h2>Your team can edit everything, forever</h2>
|
||||
<p>Once an exhibit is built, it's yours to maintain. Your staff can update text, swap photos, adjust map overlays and publish changes themselves — no ticket, no developer, no waiting.</p>
|
||||
<p>Your content stays yours — exports in standard formats if you ever leave.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("See Studio in a live walkthrough.", "We'll show you Frame 44 Studio and answer questions about your specific exhibit.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
151
src/pricing/index.njk
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
layout: layouts/base.njk
|
||||
title: Pricing
|
||||
description: Frame 44 pricing — packages, historical collections and AI tools. Every price shown here is CAD, exclusive of taxes.
|
||||
permalink: /pricing/
|
||||
---
|
||||
{% from "macros.njk" import ctaBand, pricingCard, collectionCard %}
|
||||
{% set flagship = packages | findByKey(extras.workedExample.packageKey) %}
|
||||
{% set campaign = historicalCollections | findByKey(extras.workedExample.collectionKey) %}
|
||||
{% set chatbot = aitools.items | findByKey(extras.workedExample.aitoolKey) %}
|
||||
{% set buildYearTotal = flagship.buildPrice + campaign.price + chatbot.price %}
|
||||
{% set everyYearTotal = flagship.annualPrice + campaign.price + chatbot.price %}
|
||||
|
||||
<section class="hero">
|
||||
<div class="container hero__inner">
|
||||
<p class="hero__eyebrow">{{ i18n.pricing.eyebrow }}</p>
|
||||
<h1>Pricing</h1>
|
||||
<p class="hero__sub">{{ i18n.pricing.twoNumberExplainer }}</p>
|
||||
<div class="grid grid--2 mt-5">
|
||||
<div class="card">
|
||||
<h3>{{ i18n.pricing.buildYearLabel }}</h3>
|
||||
<p>{{ i18n.pricing.buildYearNote }}</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>{{ i18n.pricing.everyYearLabel }}</h3>
|
||||
<p>{{ i18n.pricing.everyYearNote }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="packages">
|
||||
<div class="container">
|
||||
<h2>Packages</h2>
|
||||
<div class="grid grid--2">
|
||||
{% for pkg in packages %}
|
||||
{{ pricingCard(pkg) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted">
|
||||
<div class="container">
|
||||
<h2>A worked example</h2>
|
||||
<div class="card worked-example">
|
||||
<dl>
|
||||
<dt>{{ flagship.name }}</dt>
|
||||
<dd>{{ flagship.buildPrice | cad }} build · {{ flagship.annualPrice | cad }}/yr</dd>
|
||||
<dt>{{ campaign.name }} data licence</dt>
|
||||
<dd>{{ campaign.price | cad }}/yr</dd>
|
||||
<dt>{{ chatbot.name }}</dt>
|
||||
<dd>{{ chatbot.price | cad }}/yr</dd>
|
||||
<dt class="worked-example__total">Total</dt>
|
||||
<dd class="worked-example__total">{{ buildYearTotal | cad }} build year · {{ everyYearTotal | cad }} every year after</dd>
|
||||
</dl>
|
||||
<p class="price-table-note">Compare that with comparable one-off custom installs, typically starting near {{ extras.workedExample.comparisonOneOff | cad }} — with no updates, no builder, and nothing new next year.</p>
|
||||
<p class="price-table-note">Most installations live <span class="todo-flag">TODO: confirm timeline</span> {{ extras.timeline.note }} after signing.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="collections">
|
||||
<div class="container">
|
||||
<h2>Historical Collections</h2>
|
||||
<p>Licensed from Project '44 and its sister archives (CRMA) — scoped to the story your museum tells.</p>
|
||||
<div class="grid grid--3">
|
||||
{% for item in historicalCollections %}
|
||||
{{ collectionCard(item) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted" id="ai-tools">
|
||||
<div class="container">
|
||||
<h2>AI Tools</h2>
|
||||
<p>{{ aitools.intro }}</p>
|
||||
<div class="grid grid--3">
|
||||
{% for tool in aitools.items %}
|
||||
<article class="card">
|
||||
<h3>{{ tool.name }}</h3>
|
||||
<p>{{ tool.description }}</p>
|
||||
<p class="price-figure">{{ tool.price | cad }}<span class="price-figure__label">per year</span></p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="card card--accent mt-4">
|
||||
<h3>{{ aitools.bundle.name }}</h3>
|
||||
<p>{{ aitools.bundle.description }}</p>
|
||||
<p class="price-figure">{{ aitools.bundle.price | cad }}<span class="price-figure__label">per year — save {{ aitools.bundle.savings | cad }}/yr</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section" id="extras">
|
||||
<div class="container">
|
||||
<h2>Extras</h2>
|
||||
<div class="grid grid--2">
|
||||
{% for item in extras.items %}
|
||||
<article class="card">
|
||||
<h3>{{ item.name }}</h3>
|
||||
{% if item.description %}<p>{{ item.description }}</p>{% endif %}
|
||||
<p class="price-figure">
|
||||
{% if item.priceMin %}{{ item.priceMin | cad }}–{{ item.priceMax | cad }}{% else %}{{ item.price | cad }}{% endif %}
|
||||
<span class="price-figure__label">{% if item.priceType == "annual" %}per year{% else %}one-time, per exhibit{% endif %}</span>
|
||||
</p>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section section--muted" id="terms">
|
||||
<div class="container">
|
||||
<h2>Terms</h2>
|
||||
<div class="grid grid--2">
|
||||
<div class="card">
|
||||
<h3>Multi-year prepay</h3>
|
||||
<p>Prepay your "every year after" fees and save:</p>
|
||||
<ul>
|
||||
{% for term in extras.terms.prepay %}
|
||||
<li>{{ term.years }}-year prepaid = {{ term.multiplier }}× annual</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p class="price-table-note">Worked example: {{ flagship.name }} 3-year prepaid = {{ flagship.annualPrice | prepay(3) | cad }}, vs {{ (flagship.annualPrice * 3) | cad }} paid annually.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Hardware</h3>
|
||||
<p>Touchscreens typically <span class="todo-flag">TODO: confirm range</span> {{ extras.hardware.priceMin | cad }}–{{ extras.hardware.priceMax | cad }} {{ extras.hardware.unit }}. {{ extras.hardware.note }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-4">
|
||||
<h3>Grant support</h3>
|
||||
<p>{{ extras.grantSupport }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<ul class="reassurance-band">
|
||||
{% for line in i18n.pricing.reassurance %}
|
||||
<li>{{ line }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p class="price-table-note mt-4">{{ i18n.pricing.currencyNote }}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ ctaBand("Ready to see this priced for your museum?", "Tell us what you're building and we'll put together a grant-ready proposal.", i18n.cta.bookConversation, site.ctaUrl) }}
|
||||
4
src/robots.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://www.frame44.com/sitemap.xml
|
||||
12
src/sitemap.njk
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
permalink: /sitemap.xml
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
{%- for entry in collections.all -%}
|
||||
<url>
|
||||
<loc>{{ site.url }}{{ entry.url }}</loc>
|
||||
</url>
|
||||
{%- endfor -%}
|
||||
</urlset>
|
||||