Compare commits
3 commits
b479b8a946
...
bbe9605843
| Author | SHA1 | Date | |
|---|---|---|---|
| bbe9605843 | |||
| 1c763ba246 | |||
| 388c2b2e84 |
|
|
@ -14,3 +14,5 @@ pages:
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
rules:
|
||||||
|
- if: $CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "main"
|
||||||
|
|
|
||||||
36
config.yml
|
|
@ -71,12 +71,12 @@ languages:
|
||||||
image: "/img/screenshots/domain-logs.webp"
|
image: "/img/screenshots/domain-logs.webp"
|
||||||
|
|
||||||
- title: "domain-abstract-list"
|
- title: "domain-abstract-list"
|
||||||
description: "Une zone simplifiée, sous forme de liste, plus rapide à modifier"
|
description: "Les enregistrements bruts de la zone"
|
||||||
image: "/img/screenshots/domain-abstract-list.webp"
|
image: "/img/screenshots/domain-abstract-records.webp"
|
||||||
|
|
||||||
- title: "sources"
|
- title: "sources"
|
||||||
description: "Liste des hébergeurs existantes où récupérer les domaines"
|
description: "Liste des hébergeurs existants où récupérer les domaines"
|
||||||
image: "/img/screenshots/sources.png"
|
image: "/img/screenshots/providers-list.webp"
|
||||||
|
|
||||||
params:
|
params:
|
||||||
author:
|
author:
|
||||||
|
|
@ -87,6 +87,28 @@ params:
|
||||||
survey: "https://feedback.happydomain.org/"
|
survey: "https://feedback.happydomain.org/"
|
||||||
tryit: "https://try.happydomain.org/"
|
tryit: "https://try.happydomain.org/"
|
||||||
|
|
||||||
|
# Screenshots cycled in the hero browser mockup (jumbo partial).
|
||||||
|
jumboscreen:
|
||||||
|
- image: "/img/screenshots/domains-list.webp"
|
||||||
|
path: "/domains"
|
||||||
|
alt: "List of your domains in happyDomain"
|
||||||
|
weight: 10
|
||||||
|
|
||||||
|
- image: "/img/screenshots/checks-dashboard.webp"
|
||||||
|
path: "/domains/happydomain.org"
|
||||||
|
alt: "Each domain has extensive checks"
|
||||||
|
weight: 20
|
||||||
|
|
||||||
|
- image: "/img/screenshots/domain-abstract.webp"
|
||||||
|
path: "/domains/happydomain.org/editor"
|
||||||
|
alt: "A zone organized by service"
|
||||||
|
weight: 30
|
||||||
|
|
||||||
|
- image: "/img/screenshots/zone-diff.webp"
|
||||||
|
path: "/zone/happydomain.org/records"
|
||||||
|
alt: "Reviewing changes before applying them"
|
||||||
|
weight: 40
|
||||||
|
|
||||||
others_links:
|
others_links:
|
||||||
- text: "Sign in"
|
- text: "Sign in"
|
||||||
href: "/en/beta/"
|
href: "/en/beta/"
|
||||||
|
|
@ -125,12 +147,12 @@ params:
|
||||||
image: "/img/screenshots/domain-logs.webp"
|
image: "/img/screenshots/domain-logs.webp"
|
||||||
|
|
||||||
- title: "domain-abstract-list"
|
- title: "domain-abstract-list"
|
||||||
description: "Simplified zone in list, for quicker editing"
|
description: "Raw records of the zone"
|
||||||
image: "/img/screenshots/domain-abstract-list.webp"
|
image: "/img/screenshots/domain-abstract-records.webp"
|
||||||
|
|
||||||
- title: "sources"
|
- title: "sources"
|
||||||
description: "Existing name service providers where pull domains"
|
description: "Existing name service providers where pull domains"
|
||||||
image: "/img/screenshots/sources.png"
|
image: "/img/screenshots/providers-list.webp"
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
main:
|
main:
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,14 @@
|
||||||
|
|
||||||
{{ partial "jumbo.html" . }}
|
{{ partial "jumbo.html" . }}
|
||||||
|
|
||||||
{{ partial "carousel.html" . }}
|
{{ partial "work-with.html" . }}
|
||||||
|
|
||||||
|
{{ partial "big-idea.html" . }}
|
||||||
|
|
||||||
{{ partial "features.html" . }}
|
{{ partial "features.html" . }}
|
||||||
|
|
||||||
|
{{ partial "carousel.html" . }}
|
||||||
|
|
||||||
{{ partial "discover.html" . }}
|
{{ partial "discover.html" . }}
|
||||||
|
|
||||||
{{/* partial "testimonials.html" . */}}
|
{{/* partial "testimonials.html" . */}}
|
||||||
|
|
|
||||||
72
layouts/partials/big-idea.html
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
<!-- ── The big idea: services not records ─────────────── -->
|
||||||
|
<section class="section section--paper" id="idea">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-head">
|
||||||
|
<span class="eyebrow"><span class="dot"></span>The big idea</span>
|
||||||
|
<h2 class="h2">Stop reading zone files.<br>Start managing services.</h2>
|
||||||
|
<p class="lede">DNS records are an implementation detail. happyDomain groups them by what they actually <em>do</em> — your email, your website, your delegation — so you can edit with intent instead of decoding RFCs.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="idea-grid">
|
||||||
|
<div class="idea-col">
|
||||||
|
<div class="idea-tag"><span class="num">A</span>Without happyDomain</div>
|
||||||
|
<h3><span class="strike">happydomain.org</span> · zone file</h3>
|
||||||
|
<pre class="zone-pre"><span class="c">; Zone: happydomain.org.</span>
|
||||||
|
<span class="c">; TTL = 3600</span>
|
||||||
|
|
||||||
|
@ IN <span class="k">SOA</span> ns1 admin 2024010101 3600 900 604800 300
|
||||||
|
@ IN <span class="k">NS</span> ns1.happydomain.org.
|
||||||
|
@ IN <span class="k">NS</span> ns2.happydomain.org.
|
||||||
|
@ IN <span class="k">A</span> <span class="v">93.184.216.34</span>
|
||||||
|
@ IN <span class="k">AAAA</span> <span class="v">2606:2800:220:1:248:1893:25c8:1946</span>
|
||||||
|
@ IN <span class="k">MX</span> 10 mail.happydomain.org.
|
||||||
|
@ IN <span class="k">TXT</span> <span class="v">"v=spf1 include:_spf.eu ~all"</span>
|
||||||
|
_dmarc IN <span class="k">TXT</span> <span class="v">"v=DMARC1; p=none; rua=…"</span>
|
||||||
|
mail IN <span class="k">A</span> <span class="v">93.184.216.40</span>
|
||||||
|
www IN <span class="k">CNAME</span> @
|
||||||
|
@ IN <span class="k">CAA</span> <span class="v">0 issue "letsencrypt.org"</span></pre>
|
||||||
|
</div>
|
||||||
|
<div class="idea-col">
|
||||||
|
<div class="idea-tag"><span class="num">B</span>With happyDomain</div>
|
||||||
|
<h3>happydomain.org · services</h3>
|
||||||
|
<div class="svc">
|
||||||
|
<div class="svc-row">
|
||||||
|
<div class="svc-l">
|
||||||
|
<div class="svc-ico"><i class="bi bi-globe2"></i></div>
|
||||||
|
<div><div class="svc-name">Website</div><div class="svc-desc">A, AAAA on @ · CNAME on www</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="svc-r"><span class="svc-cnt">3</span><span class="svc-ok"><i class="bi bi-check-circle-fill"></i> OK</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="svc">
|
||||||
|
<div class="svc-row">
|
||||||
|
<div class="svc-l">
|
||||||
|
<div class="svc-ico"><i class="bi bi-envelope"></i></div>
|
||||||
|
<div><div class="svc-name">Email</div><div class="svc-desc">MX, SPF and DMARC policy</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="svc-r"><span class="svc-cnt">3</span><span class="svc-ok"><i class="bi bi-check-circle-fill"></i> OK</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="svc">
|
||||||
|
<div class="svc-row">
|
||||||
|
<div class="svc-l">
|
||||||
|
<div class="svc-ico"><i class="bi bi-diagram-3"></i></div>
|
||||||
|
<div><div class="svc-name">Delegation</div><div class="svc-desc">2 nameservers</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="svc-r"><span class="svc-cnt">2</span><span class="svc-ok"><i class="bi bi-check-circle-fill"></i> OK</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="svc">
|
||||||
|
<div class="svc-row">
|
||||||
|
<div class="svc-l">
|
||||||
|
<div class="svc-ico"><i class="bi bi-shield-check"></i></div>
|
||||||
|
<div><div class="svc-name">TLS / Security</div><div class="svc-desc">CAA · Let's Encrypt only</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="svc-r"><span class="svc-cnt">1</span><span class="svc-ok"><i class="bi bi-check-circle-fill"></i> OK</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p style="font-size:12.5px; color: var(--fg-3); margin: 16px 0 0;">Same zone. Edit by intent, not by record type.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
@ -1,88 +1,254 @@
|
||||||
<style>
|
<style>
|
||||||
.jumbo-hero {
|
.jumbo-hero {
|
||||||
padding: 8rem 0 5rem;
|
padding: 8rem 0 5rem;
|
||||||
background: linear-gradient(135deg, var(--hd-accent-subtle, #f0fdf4) 0%, var(--hd-bg-canvas, white) 60%);
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--hd-accent-subtle, #f0fdf4) 0%,
|
||||||
|
var(--hd-bg-canvas, white) 60%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
.hero-eyebrow {
|
.hero-eyebrow {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: .75rem;
|
gap: 0.75rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
font-size: .875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--hd-fg-3, #6b7280);
|
color: var(--hd-fg-3, #6b7280);
|
||||||
}
|
}
|
||||||
.hero-eyebrow .badge-os {
|
.hero-eyebrow .badge-os {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .35rem;
|
gap: 0.35rem;
|
||||||
padding: .25em .75em;
|
padding: 0.25em 0.75em;
|
||||||
background: var(--hd-accent-subtle, #f0fdf4);
|
background: var(--hd-accent-subtle, #f0fdf4);
|
||||||
border: 1px solid var(--hd-accent, #22c55e);
|
border: 1px solid var(--hd-accent, #22c55e);
|
||||||
border-radius: 2em;
|
border-radius: 2em;
|
||||||
color: var(--hd-accent, #22c55e);
|
color: var(--hd-accent, #22c55e);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: .8rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
.hero-meta {
|
.hero-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
font-size: .875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--hd-fg-3, #6b7280);
|
color: var(--hd-fg-3, #6b7280);
|
||||||
}
|
}
|
||||||
.hero-meta .check { color: var(--hd-accent, #22c55e); margin-right: .25rem; }
|
.hero-meta .check {
|
||||||
|
color: var(--hd-accent, #22c55e);
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
/* Browser mockup */
|
/* Browser mockup */
|
||||||
.hero-stack { position: relative; }
|
.hero-stack {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
.hero-stack .browser:last-child {
|
.hero-stack .browser:last-child {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0; right: 0;
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
width: 86%;
|
width: 86%;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
opacity: .45;
|
opacity: 0.45;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.hero-stack .browser:first-child { position: relative; z-index: 1; }
|
.hero-stack .browser:first-child {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
.browser {
|
.browser {
|
||||||
background: var(--hd-bg-canvas, #fff);
|
background: var(--hd-bg-canvas, #fff);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 16px 48px rgba(0,0,0,.12);
|
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.12);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--hd-border, #e5e7eb);
|
border: 1px solid var(--hd-border, #e5e7eb);
|
||||||
}
|
}
|
||||||
.browser-bar {
|
.browser-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .75rem;
|
gap: 0.75rem;
|
||||||
padding: .55rem 1rem;
|
padding: 0.55rem 1rem;
|
||||||
background: var(--hd-bg-subtle, #f9fafb);
|
background: var(--hd-bg-subtle, #f9fafb);
|
||||||
border-bottom: 1px solid var(--hd-border, #e5e7eb);
|
border-bottom: 1px solid var(--hd-border, #e5e7eb);
|
||||||
}
|
}
|
||||||
.browser-dots { display: flex; gap: 5px; }
|
.browser-dots {
|
||||||
.browser-dots span { width: 10px; height: 10px; border-radius: 50%; background: var(--hd-border, #e5e7eb); }
|
display: flex;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.browser-dots span {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--hd-border, #e5e7eb);
|
||||||
|
}
|
||||||
.browser-url {
|
.browser-url {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: .01rem;
|
gap: 0.01rem;
|
||||||
background: var(--hd-bg-canvas, #fff);
|
background: var(--hd-bg-canvas, #fff);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: .22rem .6rem;
|
padding: 0.22rem 0.6rem;
|
||||||
font-size: .75rem;
|
font-size: 0.75rem;
|
||||||
color: var(--hd-fg-3, #6b7280);
|
color: var(--hd-fg-3, #6b7280);
|
||||||
}
|
}
|
||||||
.browser-url .lock { color: var(--hd-accent, #22c55e); font-size: .7rem; }
|
.browser-url .lock {
|
||||||
|
color: var(--hd-accent, #22c55e);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Screenshot carousel ── */
|
||||||
|
.browser {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
/* window dots double as carousel indicators */
|
||||||
|
.browser-dots .bdot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--hd-border, #e5e7eb);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
background 0.35s ease,
|
||||||
|
transform 0.35s ease,
|
||||||
|
box-shadow 0.35s ease;
|
||||||
|
}
|
||||||
|
.browser-dots .bdot:hover {
|
||||||
|
transform: scale(1.18);
|
||||||
|
}
|
||||||
|
.browser-dots .bdot.active {
|
||||||
|
background: var(--hd-accent, #22c55e);
|
||||||
|
transform: scale(1.25);
|
||||||
|
box-shadow: 0 0 0 3px var(--hd-accent-subtle, #f0fdf4);
|
||||||
|
}
|
||||||
|
/* URL path swaps with the slide */
|
||||||
|
.browser-url .url-path {
|
||||||
|
transition: opacity 0.25s ease;
|
||||||
|
}
|
||||||
|
.browser.loading .url-path {
|
||||||
|
opacity: 0.35;
|
||||||
|
}
|
||||||
|
/* loading sweep, like a browser navigating */
|
||||||
|
.browser-load {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 2px;
|
||||||
|
width: 0;
|
||||||
|
background: var(--hd-accent, #22c55e);
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 6;
|
||||||
|
pointer-events: none;
|
||||||
|
border-radius: 0 2px 2px 0;
|
||||||
|
}
|
||||||
|
.browser.loading .browser-load {
|
||||||
|
animation: browserLoad 0.7s ease-out;
|
||||||
|
}
|
||||||
|
@keyframes browserLoad {
|
||||||
|
0% {
|
||||||
|
width: 0;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
width: 85%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* the carousel viewport */
|
||||||
|
.carousel-window {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
aspect-ratio: 1920 / 980;
|
||||||
|
background: var(--hd-bg-subtle, #f9fafb);
|
||||||
|
}
|
||||||
|
.carousel-track {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
.carousel-track .slide {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
margin: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.8s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.carousel-track .slide.active {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.carousel-track .slide img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: top left;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* 7s progress indicator */
|
||||||
|
.carousel-progress {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 3px;
|
||||||
|
width: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.06);
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
.carousel-progress span {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
width: 0;
|
||||||
|
background: var(--hd-accent, #22c55e);
|
||||||
|
}
|
||||||
|
.carousel-progress span.run {
|
||||||
|
animation: carProg 7s linear forwards;
|
||||||
|
}
|
||||||
|
@keyframes carProg {
|
||||||
|
from {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* pause the progress timer on hover */
|
||||||
|
.browser.paused .carousel-progress span {
|
||||||
|
animation-play-state: paused;
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.carousel-track .slide {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
.carousel-progress span.run {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
.browser.loading .browser-load {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<section class="jumbo-hero">
|
<section class="jumbo-hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row align-items-center g-5">
|
<div class="row align-items-center g-5">
|
||||||
|
|
||||||
<!-- Copy column -->
|
<!-- Copy column -->
|
||||||
<div class="col-lg-5">
|
<div class="col-lg-5">
|
||||||
<a class="hero-eyebrow" href="https://git.happydomain.org/" target="_blank">
|
<a
|
||||||
<span class="badge-os"><i class="bi bi-git"></i> Open source</span>
|
class="hero-eyebrow"
|
||||||
|
href="https://git.happydomain.org/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span class="badge-os"
|
||||||
|
><i class="bi bi-git"></i> Open source</span
|
||||||
|
>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1 class="display-4 fw-bold mb-5" style="text-wrap: balance">
|
<h1 class="display-4 fw-bold mb-5" style="text-wrap: balance">
|
||||||
|
|
@ -111,41 +277,93 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hero-meta">
|
<div class="hero-meta">
|
||||||
<span><i class="bi bi-check2 check"></i><strong>No account</strong> needed for the demo</span>
|
<span
|
||||||
<span><i class="bi bi-check2 check"></i><strong>~30s</strong> to first zone</span>
|
><i class="bi bi-check2 check"></i
|
||||||
<span><i class="bi bi-check2 check"></i><strong>55+</strong> providers</span>
|
><strong>No account</strong> needed for the demo</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
><i class="bi bi-check2 check"></i
|
||||||
|
><strong>~30s</strong> to first zone</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
><i class="bi bi-check2 check"></i
|
||||||
|
><strong>55+</strong> providers</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Visual column -->
|
<!-- Visual column -->
|
||||||
<div class="col-lg-7 d-none d-lg-block">
|
<div class="col-lg-7 d-none d-lg-block">
|
||||||
<div class="hero-stack">
|
<div class="hero-stack">
|
||||||
<!-- Front browser frame: zone editor -->
|
<!-- Front browser frame: screenshot carousel -->
|
||||||
<div class="browser">
|
<div class="browser" id="hero-carousel">
|
||||||
|
<span class="browser-load"></span>
|
||||||
<div class="browser-bar">
|
<div class="browser-bar">
|
||||||
<div class="browser-dots"><span></span><span></span><span></span></div>
|
<div
|
||||||
|
class="browser-dots"
|
||||||
|
role="tablist"
|
||||||
|
aria-label="Screenshots"
|
||||||
|
>
|
||||||
|
{{ range $i, $s := sort .Site.Params.jumboscreen "weight" }}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="bdot{{ if eq $i 0 }} active{{ end }}"
|
||||||
|
data-slide="{{ $i }}"
|
||||||
|
role="tab"
|
||||||
|
aria-selected="{{ if eq $i 0 }}true{{ else }}false{{ end }}"
|
||||||
|
aria-label="{{ $s.alt }}"
|
||||||
|
></button>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
<div class="browser-url">
|
<div class="browser-url">
|
||||||
<i class="bi bi-lock-fill lock me-1"></i>
|
<i class="bi bi-lock-fill lock me-1"></i>
|
||||||
<span>app.</span><strong>happydomain.org</strong><span>/zone/happydomain.org</span>
|
<span>app.</span><strong>happydomain.org</strong
|
||||||
|
><span class="url-path"
|
||||||
|
>{{ with index (sort .Site.Params.jumboscreen "weight") 0 }}{{ .path }}{{ end }}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="carousel-window">
|
||||||
|
<div class="carousel-track">
|
||||||
|
{{ range $i, $s := sort .Site.Params.jumboscreen "weight" }}
|
||||||
|
<figure
|
||||||
|
class="slide{{ if eq $i 0 }} active{{ end }}"
|
||||||
|
data-path="{{ $s.path }}"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src="/img/screenshots/domain-abstract.webp"
|
src="{{ $s.image }}"
|
||||||
alt="happyDomain zone editor"
|
alt="{{ $s.alt }}"
|
||||||
style="width:100%;display:block"
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
|
</figure>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="carousel-progress"><span></span></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Back browser frame, peeking -->
|
<!-- Back browser frame, peeking -->
|
||||||
<div class="browser" aria-hidden="true">
|
<div class="browser" aria-hidden="true">
|
||||||
<div class="browser-bar">
|
<div class="browser-bar">
|
||||||
<div class="browser-dots"><span></span><span></span><span></span></div>
|
<div class="browser-dots">
|
||||||
<div class="browser-url"><i class="bi bi-lock-fill lock"></i><span>try.</span><strong>happydomain.org</strong></div>
|
<span></span><span></span><span></span>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding:14px 16px;height:120px;background:var(--hd-bg-subtle,#f9fafb)"></div>
|
<div class="browser-url">
|
||||||
|
<i class="bi bi-lock-fill lock"></i
|
||||||
|
><span>try.</span
|
||||||
|
><strong>happydomain.org</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
padding: 14px 16px;
|
||||||
|
height: 120px;
|
||||||
|
background: var(--hd-bg-subtle, #f9fafb);
|
||||||
|
"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert alert-warning mt-5 mb-0" role="alert">
|
<div class="alert alert-warning mt-5 mb-0" role="alert">
|
||||||
|
|
@ -188,3 +406,86 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var root = document.getElementById("hero-carousel");
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
var slides = Array.prototype.slice.call(
|
||||||
|
root.querySelectorAll(".slide"),
|
||||||
|
);
|
||||||
|
var dots = Array.prototype.slice.call(root.querySelectorAll(".bdot"));
|
||||||
|
var pathEl = root.querySelector(".url-path");
|
||||||
|
var prog = root.querySelector(".carousel-progress span");
|
||||||
|
var paths = slides.map(function (slide) {
|
||||||
|
return slide.getAttribute("data-path");
|
||||||
|
});
|
||||||
|
|
||||||
|
var DURATION = 7000;
|
||||||
|
var i = 0,
|
||||||
|
timer = null;
|
||||||
|
var reduce =
|
||||||
|
window.matchMedia &&
|
||||||
|
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
|
||||||
|
function restartProgress() {
|
||||||
|
if (!prog || reduce) return;
|
||||||
|
prog.classList.remove("run");
|
||||||
|
void prog.offsetWidth; // force reflow so the animation restarts
|
||||||
|
prog.classList.add("run");
|
||||||
|
}
|
||||||
|
|
||||||
|
function go(n) {
|
||||||
|
n = ((n % slides.length) + slides.length) % slides.length;
|
||||||
|
if (n === i) return;
|
||||||
|
|
||||||
|
// mimic a real page navigation: brief loading sweep + dimmed URL
|
||||||
|
root.classList.add("loading");
|
||||||
|
window.setTimeout(function () {
|
||||||
|
root.classList.remove("loading");
|
||||||
|
}, 700);
|
||||||
|
|
||||||
|
slides[i].classList.remove("active");
|
||||||
|
dots[i].classList.remove("active");
|
||||||
|
dots[i].setAttribute("aria-selected", "false");
|
||||||
|
|
||||||
|
i = n;
|
||||||
|
|
||||||
|
slides[i].classList.add("active");
|
||||||
|
dots[i].classList.add("active");
|
||||||
|
dots[i].setAttribute("aria-selected", "true");
|
||||||
|
if (pathEl) pathEl.textContent = paths[i];
|
||||||
|
|
||||||
|
restartProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedule() {
|
||||||
|
window.clearInterval(timer);
|
||||||
|
if (reduce) return;
|
||||||
|
timer = window.setInterval(function () {
|
||||||
|
go(i + 1);
|
||||||
|
}, DURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
dots.forEach(function (dot, idx) {
|
||||||
|
dot.addEventListener("click", function () {
|
||||||
|
go(idx);
|
||||||
|
schedule(); // reset the timer so the chosen slide gets its full 7s
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pause while the visitor is inspecting a screenshot.
|
||||||
|
root.addEventListener("mouseenter", function () {
|
||||||
|
root.classList.add("paused");
|
||||||
|
window.clearInterval(timer);
|
||||||
|
});
|
||||||
|
root.addEventListener("mouseleave", function () {
|
||||||
|
root.classList.remove("paused");
|
||||||
|
schedule();
|
||||||
|
});
|
||||||
|
|
||||||
|
restartProgress();
|
||||||
|
schedule();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
|
||||||
18
layouts/partials/work-with.html
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!-- ── Trust strip ──────────────────────────────────────── -->
|
||||||
|
<section class="trust">
|
||||||
|
<div class="container trust-grid">
|
||||||
|
<div class="trust-tag"><span class="dot"></span>Works with</div>
|
||||||
|
<div class="trust-list">
|
||||||
|
<span><i class="bi bi-globe2"></i>OVH</span>
|
||||||
|
<span><i class="bi bi-leaf"></i>Gandi</span>
|
||||||
|
<span><i class="bi bi-droplet"></i>DigitalOcean</span>
|
||||||
|
<span><i class="bi bi-cloud"></i>Cloudflare</span>
|
||||||
|
<span><i class="bi bi-server"></i>Hetzner</span>
|
||||||
|
<span><i class="bi bi-tag"></i>Namecheap</span>
|
||||||
|
<span><i class="bi bi-terminal"></i>BIND</span>
|
||||||
|
<span><i class="bi bi-hdd-network"></i>Knot DNS</span>
|
||||||
|
<span><i class="bi bi-cpu"></i>PowerDNS</span>
|
||||||
|
</div>
|
||||||
|
<a class="trust-more" href="https://app.happydomain.org/providers/features" target="_blank" rel="noopener">+ 46 more →</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
@ -316,6 +316,104 @@ footer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Trust strip ── */
|
||||||
|
.trust {
|
||||||
|
padding: 20px 0;
|
||||||
|
border-top: 1px solid var(--bs-border-color);
|
||||||
|
border-bottom: 1px solid var(--bs-border-color);
|
||||||
|
background: var(--hd-bg-canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-grid {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-tag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--hd-accent);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-tag .dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--hd-accent);
|
||||||
|
animation: pulse-dot 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-dot {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-list {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-list span {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--hd-fg-3);
|
||||||
|
background: transparent;
|
||||||
|
transition:
|
||||||
|
border-color 0.2s,
|
||||||
|
color 0.2s,
|
||||||
|
background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-list span:hover {
|
||||||
|
border-color: var(--hd-accent);
|
||||||
|
color: var(--hd-accent);
|
||||||
|
background: color-mix(in srgb, var(--hd-accent) 8%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-list span i {
|
||||||
|
font-size: 0.9em;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-more {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--hd-accent);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trust-more:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
#community > div > div {
|
#community > div > div {
|
||||||
width: 50% !important;
|
width: 50% !important;
|
||||||
|
|
|
||||||
BIN
static/img/screenshots/checks-dashboard.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/img/screenshots/domain-abstract-records.webp
Normal file
|
After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 250 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 25 KiB |
BIN
static/img/screenshots/home.webp
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/img/screenshots/providers-list.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 61 KiB |