115 lines
5.7 KiB
HTML
115 lines
5.7 KiB
HTML
{{/*
|
|
The opening of every page: a full-bleed dark band. On the left, an
|
|
optional note (the eyebrow), the statement, the dek, the actions; on the
|
|
right, the agent log (when `log:` is set), the layer panel (when `fit:`
|
|
is set), or the field — the platform's four layers drawn as bars.
|
|
`bars: false` drops the field. `logImage` sets a photo behind the whole band.
|
|
*/}}
|
|
{{ $s := . }}
|
|
{{ $visual := or $s.log $s.fit (ne $s.bars false) }}
|
|
<header class="hero{{ if $s.compact }} hero-compact{{ end }}{{ if $visual }} has-visual{{ end }}{{ if $s.logImage }} has-image{{ end }}">
|
|
{{ with $s.logImage }}<img class="hero-bg" src="{{ . | relURL }}" alt="" fetchpriority="high">{{ end }}
|
|
<div class="wrap hero-grid">
|
|
<div class="hero-copy">
|
|
{{ 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 }}
|
|
</div>
|
|
|
|
{{ 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. `logCaption` credits the photo.
|
|
*/}}
|
|
<figure class="hero-log" data-reveal style="--d:260ms">
|
|
<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>
|
|
</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 }}
|
|
</div>
|
|
{{ with $s.logCaption }}<p class="hero-caption">{{ . }}</p>{{ end }}
|
|
</header>
|