Files
Brain/tekdek-employees-api/public/employees.html

245 lines
9.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Architects of TekDek</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#0d0d0f;--bg-card:#16161a;--bg-card-hover:#1a1a1f;
--text:#e0ddd5;--text-muted:#8a8780;--text-dim:#5a5955;
--font-serif:'Cinzel',serif;--font-sans:'Inter',sans-serif;--font-mono:'JetBrains Mono',monospace;
}
html{scroll-behavior:smooth}
body{background:var(--bg);color:var(--text);font-family:var(--font-sans);line-height:1.7;overflow-x:hidden}
/* Hero */
.hero{text-align:center;padding:6rem 1.5rem 4rem;max-width:800px;margin:0 auto}
.hero h1{font-family:var(--font-serif);font-size:clamp(2rem,5vw,3.2rem);color:#f0ece2;letter-spacing:.04em;margin-bottom:1rem}
.hero .subtitle{font-size:1.05rem;color:var(--text-muted);max-width:600px;margin:0 auto;line-height:1.8}
.hero .divider{width:80px;height:2px;background:linear-gradient(90deg,transparent,#C4A24E,transparent);margin:2rem auto}
/* Employee list */
.employees{max-width:900px;margin:0 auto;padding:0 1.5rem 4rem}
/* Card */
.employee-card{
background:var(--bg-card);border-radius:12px;padding:2.5rem;margin-bottom:3rem;
border-left:4px solid var(--accent,#C4A24E);
opacity:0;transform:translateY(30px);transition:opacity .6s ease,transform .6s ease;
position:relative;
}
.employee-card.visible{opacity:1;transform:translateY(0)}
.employee-card.stagger-left{margin-right:auto;margin-left:0}
.employee-card.stagger-right{margin-left:auto;margin-right:0}
@media(min-width:769px){
.employee-card.stagger-left{max-width:820px;margin-right:80px}
.employee-card.stagger-right{max-width:820px;margin-left:80px}
}
.card-header{display:flex;gap:1.5rem;align-items:center;margin-bottom:1.5rem;flex-wrap:wrap}
.avatar{
width:120px;height:120px;border-radius:50%;flex-shrink:0;
display:flex;align-items:center;justify-content:center;
font-size:2.5rem;color:#0d0d0f;font-weight:700;
background:var(--accent,#C4A24E);
box-shadow:0 0 30px color-mix(in srgb,var(--accent) 30%,transparent);
}
.avatar img{width:100%;height:100%;border-radius:50%;object-fit:cover}
.card-meta{flex:1;min-width:200px}
.card-meta h2{font-family:var(--font-serif);font-size:1.6rem;color:#f0ece2;margin-bottom:.2rem}
.card-meta .title{font-family:var(--font-mono);font-size:.85rem;color:var(--accent,#C4A24E);margin-bottom:.3rem}
.card-meta .tagline{font-family:var(--font-sans);font-size:.95rem;color:var(--text-muted);font-style:italic}
.bio{margin-bottom:1.25rem;color:var(--text);font-size:.95rem}
/* Mythology expand */
.mythology-toggle{
background:none;border:none;color:var(--accent,#C4A24E);cursor:pointer;
font-family:var(--font-sans);font-size:.9rem;display:inline-flex;align-items:center;gap:.4rem;
padding:.3rem 0;margin-bottom:1rem;transition:opacity .2s;
}
.mythology-toggle:hover{opacity:.8}
.mythology-toggle .arrow{display:inline-block;transition:transform .3s ease;font-size:.7rem}
.mythology-toggle.open .arrow{transform:rotate(90deg)}
.mythology-content{
max-height:0;overflow:hidden;transition:max-height .5s ease,opacity .4s ease;opacity:0;
color:var(--text-muted);font-size:.9rem;line-height:1.8;padding-left:1rem;
border-left:2px solid color-mix(in srgb,var(--accent) 30%,transparent);margin-bottom:1rem;
}
.mythology-content.open{opacity:1;margin-bottom:1rem}
/* Skills */
.skills{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:1.25rem}
.skill-pill{
font-size:.75rem;padding:.3rem .7rem;border-radius:20px;font-weight:500;
background:color-mix(in srgb,var(--accent) 15%,transparent);
color:var(--accent,#C4A24E);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);
}
/* Quote */
.quote{font-style:italic;color:var(--text-muted);font-size:.9rem;padding-top:.75rem;border-top:1px solid #222}
.quote::before{content:'"'}
.quote::after{content:'"'}
/* Closing */
.closing{text-align:center;padding:2rem 1.5rem 6rem;max-width:700px;margin:0 auto}
.closing .divider{width:80px;height:2px;background:linear-gradient(90deg,transparent,#C4A24E,transparent);margin:0 auto 2rem}
.closing p{color:var(--text-muted);font-size:1rem;line-height:1.8}
/* Loading */
.loading{text-align:center;padding:4rem;color:var(--text-dim)}
.loading .spinner{display:inline-block;width:24px;height:24px;border:2px solid var(--text-dim);border-top-color:transparent;border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
/* Error */
.error{text-align:center;padding:4rem;color:#e07a2f}
/* Responsive */
@media(max-width:768px){
.hero{padding:4rem 1rem 2.5rem}
.employees{padding:0 1rem 3rem}
.employee-card{padding:1.5rem}
.employee-card.stagger-left,.employee-card.stagger-right{margin-left:0;margin-right:0;max-width:100%}
.card-header{flex-direction:column;text-align:center}
.avatar{width:100px;height:100px;font-size:2rem}
.card-meta{text-align:center}
}
/* Focus styles */
.mythology-toggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
/* Reduced motion */
@media(prefers-reduced-motion:reduce){
.employee-card{opacity:1;transform:none;transition:none}
.mythology-content{transition:none}
}
</style>
</head>
<body>
<main>
<section class="hero" role="banner">
<h1>The Architects of TekDek</h1>
<div class="divider" aria-hidden="true"></div>
<p class="subtitle">They didn't just build software. They forged something that remembers where it came from — mythology woven into every line of code, every pixel, every system. These are the minds behind TekDek.</p>
</section>
<section class="employees" id="employees" aria-label="TekDek team members">
<div class="loading" id="loading" role="status">
<div class="spinner" aria-hidden="true"></div>
<p>Summoning the architects…</p>
</div>
</section>
<section class="closing" id="closing" style="display:none">
<div class="divider" aria-hidden="true"></div>
<p>Separately, they are forces. Together, they are TekDek — where myth meets machine, and every build is a step closer to legend.</p>
</section>
</main>
<script>
(function(){
const SYMBOLS = {labyrinth:'◎',gear:'⚙',wings:'𓅃',compass:'⊕',forge:'⚒',eye:'◉'};
const container = document.getElementById('employees');
const loading = document.getElementById('loading');
const closing = document.getElementById('closing');
async function init(){
try{
const res = await fetch('/api/employees');
if(!res.ok) throw new Error(`API returned ${res.status}`);
const data = await res.json();
const employees = data.data || data;
loading.remove();
render(employees);
closing.style.display='';
observeCards();
}catch(e){
loading.innerHTML=`<div class="error"><p>Could not summon the architects.</p><p style="font-size:.85rem;margin-top:.5rem">${e.message}</p></div>`;
}
}
function render(employees){
employees.forEach((emp,i)=>{
const stagger = i%2===0?'stagger-left':'stagger-right';
const card = document.createElement('article');
card.className=`employee-card ${stagger}`;
card.style.setProperty('--accent',emp.accent_color||'#C4A24E');
card.setAttribute('aria-label',`${emp.name}, ${emp.title}`);
const symbol = SYMBOLS[emp.symbol]||emp.symbol||'◆';
const avatarInner = emp.avatar_url
? `<img src="${esc(emp.avatar_url)}" alt="${esc(emp.name)} avatar" loading="lazy">`
: symbol;
const skillsHtml = (emp.skills||[]).map(s=>`<span class="skill-pill">${esc(s)}</span>`).join('');
const mythId = `myth-${emp.slug||i}`;
card.innerHTML=`
<div class="card-header">
<div class="avatar" aria-hidden="true">${avatarInner}</div>
<div class="card-meta">
<h2>${esc(emp.name)}</h2>
<div class="title">${esc(emp.title)}</div>
${emp.tagline?`<div class="tagline">${esc(emp.tagline)}</div>`:''}
</div>
</div>
${emp.bio?`<p class="bio">${esc(emp.bio)}</p>`:''}
${emp.mythology?`
<button class="mythology-toggle" aria-expanded="false" aria-controls="${mythId}">
<span class="arrow">▶</span> Read the mythology
</button>
<div class="mythology-content" id="${mythId}" role="region" aria-label="Mythology of ${esc(emp.name)}">
<p>${esc(emp.mythology)}</p>
</div>
`:''}
${skillsHtml?`<div class="skills" aria-label="Skills">${skillsHtml}</div>`:''}
${emp.quote?`<p class="quote">${esc(emp.quote)}</p>`:''}
`;
container.appendChild(card);
// Mythology toggle
const btn = card.querySelector('.mythology-toggle');
if(btn){
const content = card.querySelector('.mythology-content');
btn.addEventListener('click',()=>{
const open = btn.classList.toggle('open');
content.classList.toggle('open');
btn.setAttribute('aria-expanded',open);
if(open){content.style.maxHeight=content.scrollHeight+'px'}
else{content.style.maxHeight='0'}
});
}
});
}
function observeCards(){
if(!('IntersectionObserver' in window)){
document.querySelectorAll('.employee-card').forEach(c=>c.classList.add('visible'));
return;
}
const obs = new IntersectionObserver((entries)=>{
entries.forEach(e=>{if(e.isIntersecting){e.target.classList.add('visible');obs.unobserve(e.target)}});
},{threshold:0.15,rootMargin:'0px 0px -50px 0px'});
document.querySelectorAll('.employee-card').forEach(c=>obs.observe(c));
}
function esc(s){
if(!s)return'';
const d=document.createElement('div');d.textContent=s;return d.innerHTML;
}
init();
})();
</script>
</body>
</html>