Files
hammerfest/layouts/partials/sections/hero.html
T
2026-09-30 16:19:00 -06:00

110 lines
5.5 KiB
HTML

{{/*
The opening of every page: an optional note (the eyebrow), the statement,
the dek, the actions — then either the layer panel (when `fit:` is set)
or the field, a blue plate with the platform's four layers drawn as bars.
`bars: false` drops the field.
*/}}
{{ $s := . }}
<header class="hero{{ if $s.compact }} hero-compact{{ end }}">
{{ with $s.eyebrow }}<p class="note" data-reveal><span class="note-live" aria-hidden="true"></span><span class="note-text">{{ . }}</span></p>{{ end }}
<h1 class="statement" data-reveal style="--d:80ms">{{ $s.title | markdownify }}</h1>
{{ with $s.subhead }}<p class="dek" data-reveal style="--d:140ms">{{ . | markdownify }}</p>{{ end }}
{{ if $s.ctas }}<div class="actions" data-reveal style="--d:200ms">{{ partial "sections/ctas.html" $s.ctas }}</div>{{ end }}
{{ with $s.log }}
{{/*
An agent's activity log, typed out line by line (see the script below).
Lines take `tag` + `text`; `think: true` marks a reasoning step (greyed,
with a pause); `done: true` closes the run. The window keeps a fixed
height and scrolls as lines arrive. `logImage` swaps the blue field for
a photo; `logCaption` credits it.
*/}}
<figure class="hero-field hero-log{{ if $s.logImage }} has-image{{ end }}" data-reveal style="--d:260ms">
{{ with $s.logImage }}<img class="log-bg" src="{{ . | relURL }}" alt="" fetchpriority="high">{{ end }}
<div class="log-window">
<div class="log-bar">
<span class="log-title">{{ $s.logTitle | default "agent · your-company.federated.computer" }}</span>
<span class="log-live"><span class="note-live" aria-hidden="true"></span>Live</span>
</div>
<div class="log-view">
<ol class="log-lines" aria-label="An AI agent at work on a Federated Core">
{{ range . }}
<li class="log-line{{ if .done }} is-done{{ end }}{{ if .think }} is-think{{ end }}">
<span class="log-tag">{{ .tag | default (cond .done "Done" (cond (.think | default false) "Thinking" "")) }}</span>
<span class="log-text">{{ .text }}</span>
</li>
{{ end }}
</ol>
</div>
</div>
{{ with $s.logCaption }}<figcaption class="log-caption">{{ . }}</figcaption>{{ end }}
</figure>
<script>
(function () {
var fig = document.currentScript.previousElementSibling;
if (!fig || matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var view = fig.querySelector('.log-view'), list = fig.querySelector('.log-lines');
var lines = [].slice.call(fig.querySelectorAll('.log-line'));
var onScreen = true, i = 0;
function visible() { return onScreen && !document.hidden; }
fig.classList.add('is-animated');
lines.forEach(function (li) {
li.style.setProperty('--n', li.querySelector('.log-text').textContent.trim().length);
});
// Pause while the log is off screen or the tab is hidden.
function schedule(fn, ms) { setTimeout(function () { if (visible()) fn(); else schedule(fn, 400); }, ms); }
function next() {
if (i === lines.length) { schedule(reset, 4200); return; }
var li = lines[i++], n = +li.style.getPropertyValue('--n');
// Finished lines drop their typing animation, so a repaint can't replay it.
lines.forEach(function (l) { if (l.classList.contains('is-current')) l.classList.add('is-typed'); l.classList.remove('is-current'); });
li.classList.add('is-on', 'is-current');
// Keep the newest line in view, like a terminal: slide the list up.
var shift = Math.max(0, li.offsetTop + li.offsetHeight + 18 - view.clientHeight);
list.style.transform = 'translateY(' + -shift + 'px)';
var pause = li.classList.contains('is-think') ? 1300 : li.classList.contains('is-done') ? 300 : 450;
schedule(next, n * 16 + pause);
}
function reset() {
fig.classList.add('is-clearing');
schedule(function () {
lines.forEach(function (l) { l.classList.remove('is-on', 'is-current', 'is-typed'); });
list.style.transition = 'none';
list.style.transform = '';
void list.offsetHeight;
list.style.transition = '';
fig.classList.remove('is-clearing');
i = 0;
schedule(next, 500);
}, 450);
}
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (e) { onScreen = e[0].isIntersecting; }).observe(fig);
}
schedule(next, 700);
})();
</script>
{{ else }}
{{ with $s.fit }}
<aside class="hero-fit" aria-label="Platform layers this path uses" data-reveal style="--d:260ms">
<div class="hero-fit-head">
<p class="hero-fit-title">What this path uses</p>
<a class="more" href="{{ "/solutions/#compare" | relURL }}">Compare all four<span class="arrow" aria-hidden="true">→</span></a>
</div>
{{ partial "fit.html" (dict "fit" .) }}
</aside>
{{ else }}
{{ if ne $s.bars false }}
<figure class="hero-field" aria-hidden="true" data-reveal style="--d:260ms">
{{/* Listed in stack order; drawn bottom-up so Core is the foundation. */}}
<div class="field-stack">
{{ range $i, $l := hugo.Data.layers }}
<span class="field-layer" style="--i:{{ $i }}"><i></i><em>{{ printf "%02d" (add $i 1) }} · {{ $l.name }}</em></span>
{{ end }}
</div>
</figure>
{{ end }}
{{ end }}
{{ end }}
</header>