- hero rain canvas: seed drops across full height so the digital-rain reads dense from the first painted frame (was starting all drops above the viewport -> near-blank within a short capture settle). - seed: expand the About story + give the pull-quote a real attribution (was the two-word 'The studio' stub); expand all three devlog articles, making-it-rain to ~10 paragraphs, so no article strands a void above the footer. - manifest CSS: inset the HUD corner brackets 5px so they clear rounded / overflow-hidden panels (fixes the clipped cyan tick on the code_neon block). - SVG geometry guard: explicit width/height on all 37 icon <use>/inline SVGs (px = tailwind unit x4) so a purged utility can't balloon them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
105 lines
13 KiB
Plaintext
105 lines
13 KiB
Plaintext
{# navbar (WO-TF-005): variants standard|centered-logo|split|transparent-over-hero; sticky none|sticky|shrink-on-scroll; dependency-free off-canvas drawer (pure-CSS baseline + progressive-JS focus-trap/Escape/scroll-lock); menu data from the `menus` provider. Cyberpunk override: mono nav links, neon dropdown panels, clip-cut CTA — drawer ids/style/script/data-bn-* preserved byte-for-byte. #}
|
|
{% set nv = variant %}
|
|
{% if not nv %}{% if template == "centered" %}{% set nv = "centered-logo" %}{% elif template == "transparent" %}{% set nv = "transparent-over-hero" %}{% else %}{% set nv = "standard" %}{% endif %}{% endif %}
|
|
{% set sb = stickyBehavior %}
|
|
{% if not sb %}{% if sticky %}{% set sb = "sticky" %}{% else %}{% set sb = "none" %}{% endif %}{% endif %}
|
|
{% set use_mega = megaMenu or template == "mega" %}
|
|
{% set is_transparent = nv == "transparent-over-hero" %}
|
|
{% set cta_text = cta.text|default:ctaLabel %}
|
|
{% set cta_url = cta.url|default:ctaUrl %}
|
|
{% set bg = backgroundColor|default:"bg-background" %}
|
|
{% macro navlink(item) %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %}{% if item.title %} title="{{ item.title }}"{% endif %} class="cyberpunk-mono uppercase tracking-wider px-3 py-2 text-xs font-medium text-foreground/80 hover:text-primary transition-colors {{ item.css_class }}">{{ item.label }}</a>{% endmacro %}
|
|
{% macro dropdown(item) %}<div class="relative group">
|
|
<button type="button" class="cyberpunk-mono uppercase tracking-wider px-3 py-2 text-xs font-medium text-foreground/80 hover:text-primary transition-colors inline-flex items-center gap-1">{{ item.label }}<svg width="16" height="16" class="h-4 w-4 transition-transform group-hover:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></button>
|
|
{% if use_mega %}<div class="absolute left-1/2 -translate-x-1/2 mt-1 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="p-6 bg-card border border-primary/40 neon-edge-magenta rounded-md shadow-xl grid gap-8" style="grid-template-columns:repeat({{ item.children|length|default:1 }},minmax(10rem,1fr));min-width:min(90vw,42rem)">{% for child in item.children %}<div class="space-y-1">{% if child.item_type == "header" %}<div class="cyberpunk-mono px-2 py-1 text-xs font-semibold text-accent uppercase tracking-widest border-b border-primary/40 mb-2">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 rounded hover:bg-accent transition-colors {{ child.css_class }}">{% if child.icon %}<span class="mr-1.5 text-primary">::{{ child.icon }}::</span>{% endif %}<span class="text-sm font-medium text-foreground">{{ child.label }}</span>{% if child.description %}<span class="block text-xs text-muted-foreground mt-0.5">{{ child.description }}</span>{% endif %}</a>{% endif %}</div>{% endfor %}</div></div>
|
|
{% else %}<div class="absolute left-0 mt-1 w-56 opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"><div class="py-2 bg-card border border-primary/40 neon-edge-cyan rounded-md shadow-lg">{% for child in item.children %}{% if child.item_type == "header" %}<div class="cyberpunk-mono px-4 py-2 text-xs font-semibold text-accent uppercase tracking-widest">{{ child.label }}</div>{% else %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-4 py-2 text-sm text-foreground hover:bg-accent transition-colors {{ child.css_class }}">{{ child.label }}</a>{% endif %}{% endfor %}</div></div>{% endif %}
|
|
</div>{% endmacro %}
|
|
{% macro brand(size) %}<a href="/" class="flex-shrink-0 flex items-center">{% if logoType == "text" and logoText.text %}<span class="cyberpunk-display text-xl font-bold text-foreground {{ logoText.color }}">{{ logoText.text }}</span>{% elif logo %}<img src="{{ logo }}" alt="{{ alt|default:companyName }}" class="{{ size }} w-auto"{% if logoMaxWidth or logoMaxHeight %} style="{% if logoMaxWidth %}max-width:{{ logoMaxWidth }};{% endif %}{% if logoMaxHeight %}max-height:{{ logoMaxHeight }};{% endif %}"{% endif %}/>{% elif companyName %}<span class="cyberpunk-display text-xl font-bold text-foreground">{{ companyName }}</span>{% endif %}</a>{% endmacro %}
|
|
{% macro ctabtn(extra) %}{% if cta_text %}<a href="{{ cta_url|default:'#' }}" class="cyberpunk-mono uppercase tracking-wider clip-cut rgb-split inline-flex items-center rounded-none bg-primary px-4 py-2 text-xs font-semibold text-primary-foreground hover:bg-primary/90 transition-colors {{ extra }}">{{ cta_text }}</a>{% endif %}{% endmacro %}
|
|
<nav data-bn-navbar data-bn-nav-variant="{{ nv }}" data-bn-sticky="{{ sb }}"{% if is_transparent %} data-bn-transparent="1"{% endif %} class="bn-navbar w-full {% if sb == "sticky" or sb == "shrink-on-scroll" %}sticky top-0 z-40 {% endif %}{% if is_transparent %}absolute top-0 left-0 z-40 text-white{% else %}{{ bg }}{% if borderBottom %} border-b border-primary/30{% endif %}{% endif %} {{ class }}">
|
|
{% if showUtilityBar %}<div class="cyberpunk-mono w-full text-xs {% if not showOnMobile %}hidden sm:block{% endif %} bg-muted/60 text-muted-foreground border-b border-primary/20"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex items-center justify-between h-8"><div>{{ utilityBarText|safe }}</div><div class="flex items-center gap-4">{% if utilityBarPhone %}<a href="tel:{{ utilityBarPhone }}" class="hover:text-primary transition-colors">{{ utilityBarPhone }}</a>{% endif %}{% if utilityBarEmail %}<a href="mailto:{{ utilityBarEmail }}" class="hover:text-primary transition-colors">{{ utilityBarEmail }}</a>{% endif %}</div></div></div>{% endif %}
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="bn-navbar-row flex items-center justify-between h-16">
|
|
{% if nv == "centered-logo" %}
|
|
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}</div>
|
|
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
|
|
<div class="hidden md:flex items-center gap-3 flex-1 justify-end">{{ ctabtn("") }}</div>
|
|
{% elif nv == "split" %}
|
|
<div class="hidden md:flex items-center gap-1 flex-1">{% for item in menu.items %}{% if forloop.Counter0 * 2 < menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}</div>
|
|
<div class="flex items-center justify-center">{{ brand("h-9") }}</div>
|
|
<div class="hidden md:flex items-center gap-1 flex-1 justify-end">{% for item in menu.items %}{% if forloop.Counter0 * 2 >= menu.items|length %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
|
|
{% else %}
|
|
<div class="flex items-center">{{ brand("h-8") }}</div>
|
|
<div class="hidden md:flex items-center gap-1">{% for item in menu.items %}{% if item.children %}{{ dropdown(item) }}{% else %}{{ navlink(item) }}{% endif %}{% endfor %}{{ ctabtn("ml-3") }}</div>
|
|
{% endif %}
|
|
<div class="flex items-center md:hidden">
|
|
<label for="bn-navbar-toggle" data-bn-nav-open aria-controls="bn-navbar-drawer" aria-expanded="false" class="inline-flex items-center justify-center p-2 rounded-md cursor-pointer hover:bg-accent/50 transition-colors"><span class="sr-only">Open menu</span><svg width="24" height="24" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg></label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{# --- Mobile off-canvas drawer: pure-CSS checkbox baseline (works with no JS) --- #}
|
|
<input type="checkbox" id="bn-navbar-toggle" class="bn-navbar-toggle" aria-hidden="true"/>
|
|
<label for="bn-navbar-toggle" class="bn-navbar-overlay fixed inset-0 bg-black/50 z-40 md:hidden" aria-hidden="true"></label>
|
|
<div id="bn-navbar-drawer" class="bn-navbar-drawer fixed top-0 right-0 h-full w-80 max-w-[85vw] bg-background text-foreground z-50 shadow-xl md:hidden flex flex-col" role="dialog" aria-modal="true" aria-label="Site menu">
|
|
<div class="flex items-center justify-between p-4 border-b border-primary/30">{{ brand("h-8") }}<label for="bn-navbar-toggle" data-bn-nav-close class="p-2 rounded-md cursor-pointer text-muted-foreground hover:text-primary hover:bg-accent"><span class="sr-only">Close menu</span><svg width="24" height="24" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg></label></div>
|
|
<nav class="flex-1 overflow-y-auto py-4" aria-label="Mobile">{% for item in menu.items %}{% if item.children %}<details class="group/acc"><summary class="cyberpunk-mono uppercase tracking-wide flex items-center justify-between px-6 py-3 text-sm font-medium cursor-pointer hover:bg-accent transition-colors list-none">{{ item.label }}<svg width="16" height="16" class="h-4 w-4 transition-transform group-open/acc:rotate-180" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/></svg></summary><div class="pl-8 pb-2">{% for child in item.children %}{% if child.item_type != "header" %}<a href="{{ child.url|default:'#' }}"{% if child.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="block px-2 py-2 text-sm text-muted-foreground hover:text-primary transition-colors">{{ child.label }}</a>{% endif %}{% endfor %}</div></details>{% else %}<a href="{{ item.url|default:'#' }}"{% if item.open_in_new_tab %} target="_blank" rel="noopener"{% endif %} class="cyberpunk-mono uppercase tracking-wide block px-6 py-3 text-sm font-medium hover:bg-accent transition-colors">{{ item.label }}</a>{% endif %}{% endfor %}</nav>
|
|
{% if cta_text %}<div class="p-4 border-t border-primary/30"><a href="{{ cta_url|default:'#' }}" class="cyberpunk-mono uppercase tracking-wider clip-cut block w-full text-center rounded-none bg-primary px-4 py-3 text-base font-semibold text-primary-foreground hover:bg-primary/90 transition-colors">{{ cta_text }}</a></div>{% endif %}
|
|
</div>
|
|
</nav>
|
|
<style>
|
|
.bn-navbar-toggle{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none;}
|
|
.bn-navbar-drawer{transform:translateX(100%);transition:transform .3s ease;}
|
|
.bn-navbar-overlay{opacity:0;pointer-events:none;transition:opacity .3s ease;}
|
|
.bn-navbar-toggle:checked ~ .bn-navbar-drawer{transform:translateX(0);}
|
|
.bn-navbar-toggle:checked ~ .bn-navbar-overlay{opacity:1;pointer-events:auto;}
|
|
.bn-navbar-drawer details > summary::-webkit-details-marker{display:none;}
|
|
[data-bn-sticky="shrink-on-scroll"].bn-navbar-shrunk .bn-navbar-row{height:3rem;}
|
|
[data-bn-sticky="shrink-on-scroll"] .bn-navbar-row{transition:height .2s ease;}
|
|
[data-bn-transparent].bn-navbar-solid{position:sticky;color:inherit;background:hsl(var(--background));border-bottom:1px solid hsl(var(--border));}
|
|
</style>
|
|
<script>
|
|
(function(){
|
|
if(window.__bnNavInit)return;window.__bnNavInit=1;
|
|
function ready(fn){if(document.readyState!='loading')fn();else document.addEventListener('DOMContentLoaded',fn);}
|
|
ready(function(){
|
|
document.querySelectorAll('[data-bn-navbar]').forEach(function(nav){
|
|
var toggle=nav.querySelector('.bn-navbar-toggle');
|
|
var drawer=nav.querySelector('.bn-navbar-drawer');
|
|
var opener=nav.querySelector('[data-bn-nav-open]');
|
|
if(toggle&&drawer){
|
|
function focusables(){return drawer.querySelectorAll('a[href],button,label[for],input,summary,[tabindex]:not([tabindex="-1"])');}
|
|
toggle.addEventListener('change',function(){
|
|
var open=toggle.checked;
|
|
document.documentElement.style.overflow=open?'hidden':'';
|
|
document.body.style.overflow=open?'hidden':'';
|
|
if(opener)opener.setAttribute('aria-expanded',open?'true':'false');
|
|
if(open){var f=focusables();if(f.length)f[0].focus();}
|
|
else if(opener&&opener.focus)opener.focus();
|
|
});
|
|
drawer.addEventListener('keydown',function(e){
|
|
if(e.key==='Escape'){toggle.checked=false;toggle.dispatchEvent(new Event('change'));return;}
|
|
if(e.key!=='Tab')return;
|
|
var f=focusables();if(!f.length)return;
|
|
var first=f[0],last=f[f.length-1];
|
|
if(e.shiftKey&&document.activeElement===first){e.preventDefault();last.focus();}
|
|
else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();first.focus();}
|
|
});
|
|
drawer.querySelectorAll('a[href]').forEach(function(a){a.addEventListener('click',function(){toggle.checked=false;toggle.dispatchEvent(new Event('change'));});});
|
|
}
|
|
var sb=nav.getAttribute('data-bn-sticky');
|
|
var transparent=nav.getAttribute('data-bn-transparent')==='1';
|
|
if(sb==='shrink-on-scroll'||transparent){
|
|
var overHero=false;
|
|
if(transparent){var next=nav.nextElementSibling;overHero=!!next&&next.getBoundingClientRect().height>=240;if(!overHero)nav.classList.add('bn-navbar-solid');}
|
|
var onScroll=function(){
|
|
var y=window.scrollY||window.pageYOffset;
|
|
if(sb==='shrink-on-scroll')nav.classList.toggle('bn-navbar-shrunk',y>40);
|
|
if(transparent&&overHero)nav.classList.toggle('bn-navbar-solid',y>40);
|
|
};
|
|
window.addEventListener('scroll',onScroll,{passive:true});onScroll();
|
|
}
|
|
});
|
|
});
|
|
})();
|
|
</script>
|