Full LCARS library-computer interface for a sci-fi podcast/media outfit. Codeless .bnp. Re-skins every mandatory builtin inside LCARS framing and ships the full Wave A surface. - 49 mandatory template overrides (chrome, marketing, primitives, commerce, blog family, auth, page-suggestions), field-faithful re-skins of the frozen builtin defaults; identity carried by the lcars-* class system. - 8 page templates (default+sidebar rail, full-width, full-display, landing, article, blog-index, contact, auth) with elbow-frame chrome. - 4 persona blocks: episode_console, crew_roster, stardate_log, transmission_status. Legacy lcars_header/sidebar/panel removed (now overrides + page chrome). - 5 dual-mode presets (canonical black + daylight-ops cream), all 19 tokens. - Bundled Antonio + Share Tech Mono (OFL); email-safe LCARS wrapper. - Seed "Subspace Signal": home, about, 3 episodes+articles, contact (hail_signals table + row_inserted email-admins workflow), login. - GO BIG motion: dependency-free canvas panel-sweep on the hero (lazy-init, reduced-motion static frame, no-JS fallback). Build + check-safety green. Visual UNVERIFIED until Wave B. Known frozen-cms limits (contract §11 auth/404 non-dispatch, §12 provider-backed empty lists) authored faithfully. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
4.8 KiB
Plaintext
45 lines
4.8 KiB
Plaintext
{# countdown override — oversized mono unit boxes; host countdown JS binds via data-* attrs. #}
|
|
<section class="lcars-section {{ class }}"><div class="lcars-content-well" style="text-align: center;">
|
|
{% if heading %}<h2 class="lcars-heading" style="font-size: clamp(1.75rem, 3vw, 2.25rem); margin: 0 0 0.75rem 0;">{{ heading }}</h2>{% endif %}
|
|
{% if intro %}<p class="lcars-lede" style="max-width: 60ch; margin: 0 auto; color: hsl(var(--muted-foreground));">{{ intro }}</p>{% endif %}
|
|
<div data-countdown style="margin-top: 2.5rem;">
|
|
<div data-countdown-timer data-target="{{ target }}" role="timer" aria-label="Time remaining" style="display: flex; align-items: flex-start; justify-content: center; gap: 1rem; flex-wrap: wrap;">
|
|
<div class="lcars-count-unit {% if variant == "boxes" %}lcars-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="lcars-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="days">--</span><span class="lcars-stat-label" style="margin-top: 0.5rem;">{{ labelDays|default:"Days" }}</span></div>
|
|
<div class="lcars-count-unit {% if variant == "boxes" %}lcars-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="lcars-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="hours">--</span><span class="lcars-stat-label" style="margin-top: 0.5rem;">{{ labelHours|default:"Hours" }}</span></div>
|
|
<div class="lcars-count-unit {% if variant == "boxes" %}lcars-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="lcars-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="minutes">--</span><span class="lcars-stat-label" style="margin-top: 0.5rem;">{{ labelMinutes|default:"Minutes" }}</span></div>
|
|
<div class="lcars-count-unit {% if variant == "boxes" %}lcars-count-box{% endif %}" style="display: flex; flex-direction: column; align-items: center; {% if variant == "boxes" %}min-width: 5rem; border: 1px solid hsl(var(--border)); background: hsl(var(--card)); padding: 1rem 0.75rem;{% endif %}"><span class="lcars-count-figure numeric-tabular" style="font-family: var(--font-mono, ui-monospace, monospace); font-size: clamp(2rem, 5vw, 3rem); font-weight: 500; line-height: 1; color: hsl(var(--foreground));" data-unit="seconds">--</span><span class="lcars-stat-label" style="margin-top: 0.5rem;">{{ labelSeconds|default:"Seconds" }}</span></div>
|
|
</div>
|
|
<div data-countdown-expired style="display: none; margin-top: 2rem;"><p class="lcars-heading" style="font-size: 1.25rem;">{{ expiredMessage|default:"This has started." }}</p>{% if expiredUrl and expiredLabel %}<a class="lcars-btn-primary" href="{{ expiredUrl }}" style="margin-top: 1rem;">{{ expiredLabel }}</a>{% endif %}</div>
|
|
</div>
|
|
<noscript><p class="lcars-text" style="margin-top: 1.5rem; color: hsl(var(--muted-foreground));">Counting down to <time datetime="{{ target }}">{{ target }}</time>.</p></noscript>
|
|
</div></section>
|
|
<script>
|
|
(function(){
|
|
var timers = document.querySelectorAll('[data-countdown-timer]');
|
|
timers.forEach(function(el){
|
|
var target = new Date(el.getAttribute('data-target')).getTime();
|
|
if (isNaN(target)) return;
|
|
var wrap = el.closest('[data-countdown]');
|
|
var expired = wrap ? wrap.querySelector('[data-countdown-expired]') : null;
|
|
function pad(n){ return (n < 10 ? '0' : '') + n; }
|
|
function set(unit, value){ var node = el.querySelector('[data-unit="' + unit + '"]'); if (node) node.textContent = value; }
|
|
var interval;
|
|
function tick(){
|
|
var diff = target - Date.now();
|
|
if (diff <= 0){
|
|
el.style.display = 'none';
|
|
if (expired) expired.style.display = 'block';
|
|
if (interval) clearInterval(interval);
|
|
return;
|
|
}
|
|
set('days', Math.floor(diff / 86400000));
|
|
set('hours', pad(Math.floor(diff % 86400000 / 3600000)));
|
|
set('minutes', pad(Math.floor(diff % 3600000 / 60000)));
|
|
set('seconds', pad(Math.floor(diff % 60000 / 1000)));
|
|
}
|
|
tick();
|
|
interval = setInterval(tick, 1000);
|
|
});
|
|
})();
|
|
</script>
|