temporary push, with testing changes for nav and Juhatus

This commit is contained in:
Henri
2026-04-06 15:12:14 +03:00
parent 78f70458e2
commit 99ad293289
4 changed files with 778 additions and 174 deletions

View File

@@ -22,6 +22,45 @@
let isClosing = $state(false);
let currentPath = $state(getPath());
const aboutRoutes = [
"/lapikutest",
"/aboutus",
"/tudengile",
"/student",
"/mentorid",
"/mentors",
"/juhatus",
"/management",
];
const eventsRoutes = ["/kalender", "/striim", "/koolitused"];
function isCurrent(path) {
return currentPath === path;
}
function isInGroup(paths) {
return paths.includes(currentPath);
}
function navButtonClass(active = false) {
return active
? "border-transparent text-orange-500 bg-white/5"
: "border-transparent text-white/75 hover:text-white hover:bg-white/5";
}
function dropdownButtonClass(active = false) {
return active
? "border-transparent text-orange-500 bg-white/5"
: "border-transparent text-white/75 hover:text-white hover:bg-white/5";
}
function dropdownItemClass(active = false) {
return active
? "bg-orange-500/10 text-orange-500"
: "bg-transparent text-black hover:text-orange-500";
}
// Update currentPath when navigation occurs
$effect(() => {
const handleNavigation = () => {
@@ -125,75 +164,101 @@
</style>
<header class="fixed inset-x-0 top-0 z-50 text-white">
<Container maxWidth={true} center={false} class="py-0">
<div class="border-b-2 border-orange-500 bg-gray-900/95 backdrop-blur-md">
<Container maxWidth={true} center={true} class="py-0">
{#if $text && Object.keys($text).length > 0}
<div class="flex items-center w-full relative min-h-10">
<!-- Logo -->
{#if currentPath !== "/"}
<div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 md:relative md:left-auto md:translate-x-0 md:top-auto md:translate-y-0">
<Button onClick={() => handleNavigation("/")} class="border-transparent">
<img src="assets/LapLogo_black_white.png" alt="Lapikud Logo" class="h-14" />
<div class="flex min-h-16 items-center gap-4">
<Button
onClick={() => handleNavigation("/")}
class="nav-logo-button border-transparent bg-transparent px-0 py-0 text-white hover:text-white"
>
<img src="/assets/LapLogo_white_orange.png" alt="Lapikud Logo" class="h-12 md:h-14 w-auto" />
</Button>
</div>
{/if}
<div class="flex items-center justify-end ml-auto w-full md:w-auto md:border-b-3 md:border-white backdrop-blur-lg bg-black/35">
<!-- Desktop Navigation -->
<nav class="z-10 hidden md:flex gap-1 justify-end text-lg">
<div class="flex items-center justify-end ml-auto w-full md:w-auto">
<nav class="z-10 hidden md:flex items-center gap-1 justify-end text-lg">
<Dropdown
name={$text.about}
buttonClass="hover:text-orange-500 border-transparent"
panelClass="bg-white text-black"
buttonClass={`${dropdownButtonClass(isInGroup(aboutRoutes))} ${isInGroup(aboutRoutes) ? "nav-active-parent" : ""}`}
panelClass="nav-dropdown-panel"
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("/lapikutest")}
>{$text.aboutPages.info}</Button
<Button
class={dropdownItemClass(isCurrent("/lapikutest") || isCurrent("/aboutus"))}
onClick={() => handleNavigation("/lapikutest")}
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("/tudengile")}
><Bot />{$text.aboutPages.join}</Button
{$text.aboutPages.info}
</Button>
<Button
class={dropdownItemClass(isCurrent("/tudengile") || isCurrent("/student"))}
onClick={() => handleNavigation("/tudengile")}
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("/mentorid")}
><HandHeart />{$text.aboutPages.mentors}</Button
<Bot />{$text.aboutPages.join}
</Button>
<Button
class={dropdownItemClass(isCurrent("/mentorid") || isCurrent("/mentors"))}
onClick={() => handleNavigation("/mentorid")}
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("/juhatus")}
><Lectern />{$text.aboutPages.board}</Button
<HandHeart />{$text.aboutPages.mentors}
</Button>
<Button
class={dropdownItemClass(isCurrent("/juhatus") || isCurrent("/management"))}
onClick={() => handleNavigation("/juhatus")}
>
<Lectern />{$text.aboutPages.board}
</Button>
</Dropdown>
<Dropdown
name={$text.events}
buttonClass="hover:text-orange-500 border-transparent"
panelClass="bg-white text-black"
buttonClass={`${dropdownButtonClass(isInGroup(eventsRoutes))} ${isInGroup(eventsRoutes) ? "nav-active-parent" : ""}`}
panelClass="nav-dropdown-panel"
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("/koolitused")}
><Presentation />{$text.eventsPages.workshops}</Button
<Button
class={dropdownItemClass(isCurrent("/koolitused"))}
onClick={() => handleNavigation("/koolitused")}
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("https://asikarikas.ee/", { external: true })}
><Trophy />ASI Karikas</Button
>
<Button class="bg-transparent hover:text-orange-500 border-transparent" onClick={() => navigate("https://remondikohvik.lapikud.ee/", { external: true })}
><Coffee />{$text.eventsPages.repair}</Button
>
</Dropdown>
<Button class="hover:text-orange-500 border-transparent" onClick={() => navigate("/helpdesk")}
>{$text.helpdesk}
<Presentation />{$text.eventsPages.workshops}
</Button>
<Button class="hover:text-orange-500 border-transparent" onClick={() => navigate("/kontakt")}
>{$text.contact}
<Button
class="bg-transparent text-black hover:text-orange-500"
onClick={() => handleNavigation("https://asikarikas.ee/", { external: true })}
>
<Trophy />ASI Karikas
</Button>
<Button
class="bg-transparent text-black hover:text-orange-500"
onClick={() => handleNavigation("https://remondikohvik.lapikud.ee/", { external: true })}
>
<Coffee />{$text.eventsPages.repair}
</Button>
</Dropdown>
<Button
class={navButtonClass(isCurrent("/helpdesk"))}
onClick={() => handleNavigation("/helpdesk")}
>
{$text.helpdesk}
</Button>
<!-- Language Switcher -->
<Button
class="px-3 py-1 text-sm ml-4 hover:text-orange-500 border-transparent"
onClick={() => switchLanguageRoute($currentLang === 'est' ? 'en' : 'est')}
class={navButtonClass(isCurrent("/kontakt") || isCurrent("/contact"))}
onClick={() => handleNavigation("/kontakt")}
>
{$currentLang === 'est' ? 'EN' : 'EST'}
{$text.contact}
</Button>
<Button
class="ml-3 border-transparent bg-transparent px-3 py-1 text-sm text-white/75 hover:text-orange-500 hover:bg-white/5"
onClick={() => switchLanguageRoute($currentLang === "est" ? "en" : "est")}
>
{$currentLang === "est" ? "EN" : "EST"}
</Button>
</nav>
<!-- Mobile Hamburger Button - Fixed position -->
{#if !mobileMenuOpen}
<Button
onClick={toggleMobileMenu}
class="mobile-only p-2 text-white transition-colors bg-transparent absolute right-0 hover:text-white border-transparent"
class="mobile-only ml-auto border-transparent bg-transparent p-2 text-white transition-colors hover:text-orange-500"
aria-label="Toggle navigation menu"
>
<Menu size={32} />
@@ -203,6 +268,7 @@
</div>
{/if}
</Container>
</div>
</header>
<!-- Full Screen Mobile Menu -->
@@ -226,33 +292,40 @@
<!-- Menu Content -->
<Stack gap="gap-5" class="flex-1 px-8 pt-24 pb-12 text-white text-xl">
<Button
onClick={() => handleNavigation("/")}
class="mb-3 border-transparent bg-transparent px-0 py-0 text-left text-white hover:text-white"
>
<img src="/assets/LapLogo_white_orange.png" alt="Lapikud Logo" class="h-12 w-auto" />
</Button>
<!-- About Section -->
<Stack gap="gap-3">
<h3 class="text-2xl font-bold text-orange-500">{$text.about}</h3>
<Stack gap="gap-2" class="pl-4">
<Button
onClick={() => handleNavigation("/lapikutest")}
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/lapikutest") || isCurrent("/aboutus") ? "text-orange-500" : "hover:text-orange-500"}`}
>
{$text.aboutPages.info}
</Button>
<Button
onClick={() => handleNavigation("/tudengile")}
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/tudengile") || isCurrent("/student") ? "text-orange-500" : "hover:text-orange-500"}`}
>
<Bot size={20} />
{$text.aboutPages.join}
</Button>
<Button
onClick={() => handleNavigation("/mentorid")}
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/mentorid") || isCurrent("/mentors") ? "text-orange-500" : "hover:text-orange-500"}`}
>
<HandHeart size={20} />
{$text.aboutPages.mentors}
</Button>
<Button
onClick={() => handleNavigation("/juhatus")}
class="text-left transition-colors flex items-center gap-2 bg-transparent hover:text-orange-500 border-transparent"
class={`text-left transition-colors flex items-center gap-2 bg-transparent border-transparent ${isCurrent("/juhatus") || isCurrent("/management") ? "text-orange-500" : "hover:text-orange-500"}`}
>
<Lectern size={20} />
{$text.aboutPages.board}
@@ -292,13 +365,13 @@
<Stack gap="gap-3">
<Button
onClick={() => handleNavigation("/helpdesk")}
class="text-left text-2xl font-semibold transition-colors bg-transparent hover:text-orange-500 border-transparent"
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/helpdesk") ? "text-orange-500" : "hover:text-orange-500"}`}
>
{$text.helpdesk}
</Button>
<Button
onClick={() => handleNavigation("/kontakt")}
class="text-left text-2xl font-semibold transition-colors bg-transparent hover:text-orange-500 border-transparent"
class={`text-left text-2xl font-semibold transition-colors bg-transparent border-transparent ${isCurrent("/kontakt") || isCurrent("/contact") ? "text-orange-500" : "hover:text-orange-500"}`}
>
{$text.contact}
</Button>

View File

@@ -0,0 +1,17 @@
{
"hero": {
"eyebrow": "MTÜ Lapikud",
"title": "Management",
"intro": "People who keep the organisation moving forward."
},
"current": {
"label": "Current board",
"hint": "Contacts and responsibilities"
},
"history": {
"label": "People who have led the organisation over time",
"hint": "Thanks to everyone who has helped guide Lapikud"
},
"loading": "Loading data…",
"error": "Failed to load the management data."
}

View File

@@ -0,0 +1,17 @@
{
"hero": {
"eyebrow": "MTÜ Lapikud",
"title": "Juhtkond",
"intro": "Inimesed, kes viivad organisatsiooni edasi."
},
"current": {
"label": "Praegune juhtkond",
"hint": "Kontaktid ja vastutusvaldkonnad"
},
"history": {
"label": "Organisatsiooni juhtinud inimesed läbi aegade",
"hint": "Tänu kõigile, kes on Lapikuid juhtinud"
},
"loading": "Laadin andmeid…",
"error": "Juhtkonna andmete laadimine ebaõnnestus."
}

View File

@@ -1,113 +1,610 @@
<script>
import {
Section,
Grid,
Card,
Center,
Container,
Stack,
} from "$components";
import { Section, Grid, Container, Image } from "$components";
import { text } from "$lib";
import { onMount } from "svelte";
import yaml from 'js-yaml';
import { currentLang, getLangText } from "$lib";
import Mailbox from "lucide-svelte/icons/mailbox";
import yaml from "js-yaml";
import Mail from "lucide-svelte/icons/mail";
import Phone from "lucide-svelte/icons/phone";
let currentManagement = [];
let pastManagement = [];
let lang = 'est';
let loading = true;
let loadError = false;
currentLang.subscribe(value => {
lang = value;
});
const fallbackText = {
est: {
hero: {
eyebrow: "MTÜ Lapikud",
title: "Juhtkond",
intro: "Inimesed, kes viivad organisatsiooni edasi.",
},
current: {
label: "Praegune juhtkond",
hint: "Kontaktid ja vastutusvaldkonnad",
},
history: {
label: "Organisatsiooni juhtinud inimesed läbi aegade",
hint: "Tänu kõigile, kes on Lapikuid juhtinud",
},
loading: "Laadin andmeid…",
error: "Juhtkonna andmete laadimine ebaõnnestus.",
},
en: {
hero: {
eyebrow: "MTÜ Lapikud",
title: "Management",
intro: "People who keep the organisation moving forward.",
},
current: {
label: "Current board",
hint: "Contacts and responsibilities",
},
history: {
label: "People who have led the organisation over time",
hint: "Thanks to everyone who has helped guide Lapikud",
},
loading: "Loading data…",
error: "Failed to load the management data.",
},
};
function getText() {
return $text.management ?? fallbackText.est;
}
function parseYearLabel(label) {
const text = String(label ?? "");
const match = text.match(/^(\d{4})(?:\s*\((.+)\))?$/);
if (!match) {
return { year: text, tag: "" };
}
return {
year: match[1],
tag: match[2] ?? "",
};
}
function getBoardLabel(member) {
if (!member) return "";
if (member.role && member.role !== "Juhatuse liige") {
return member.role;
}
return member.subrole || member.role || "";
}
function getPhoneHref(phone) {
if (!phone) return "";
return `tel:${String(phone).replace(/[\s()-]/g, "")}`;
}
function getInitials(name) {
return String(name ?? "")
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]?.toUpperCase() ?? "")
.join("") || "?";
}
onMount(async () => {
try {
const currentRes = await fetch('/_data/management.yml');
const currentYaml = await currentRes.text();
currentManagement = yaml.load(currentYaml) || [];
const [currentRes, pastRes] = await Promise.all([
fetch("/_data/management.yml"),
fetch("/_data/past_management.yml"),
]);
const pastRes = await fetch('/_data/past_management.yml');
const pastYaml = await pastRes.text();
const [currentYaml, pastYaml] = await Promise.all([
currentRes.text(),
pastRes.text(),
]);
currentManagement = yaml.load(currentYaml) || [];
pastManagement = yaml.load(pastYaml) || [];
} catch (error) {
console.error('Error loading management data:', error);
console.error("Error loading management data:", error);
loadError = true;
} finally {
loading = false;
}
});
</script>
<!-- Current Management Section -->
<Section padding="large">
<Center>
<h1 class="text-4xl font-light pb-8 text-center">Praegune juhtkond</h1>
</Center>
<Grid min="500px" gap="3rem">
{#each currentManagement as member (member.name)}
<div class="flex flex-col sm:flex-row gap-6 items-start">
<div class="w-full sm:w-auto sm:max-w-[200px] aspect-square shrink-0">
<img
src={`/assets/management-images/${member.photo}`}
alt={member.name}
class="w-full h-full object-cover rounded-md"
/>
</div>
<div class="flex flex-col gap-2 grow">
<h3 class="text-2xl font-medium">{member.name}</h3>
<p class="text-base text-gray-700">
{getLangText(member, 'role', lang)} - {getLangText(member, 'subrole', lang)}
</p>
<a
href="mailto:{member.email}"
class="text-base flex items-center gap-2 text-[#f0941d] hover:opacity-80 transition"
<Section
padding="none"
fullWidth={true}
contentClass="!px-0 !py-0"
class="management-page overflow-hidden text-white"
background="#0d0d0d"
>
<Mailbox size={18} />{member.email}
</a>
<a
href="tel:{member.phone}"
class="text-base flex items-center gap-2 text-[#f0941d] hover:opacity-80 transition"
>
<Phone size={18} />{member.phone}
</a>
<div class="hero-shell">
<div class="hero-orb hero-orb-1" aria-hidden="true"></div>
<div class="hero-orb hero-orb-2" aria-hidden="true"></div>
<Container class="relative z-10 py-[clamp(3rem,8vw,6rem)]">
<div class="max-w-4xl">
<p class="eyebrow">{getText().hero.eyebrow}</p>
<h1 class="hero-title">{getText().hero.title}</h1>
<p class="hero-intro">{getText().hero.intro}</p>
</div>
</Container>
</div>
{/each}
</Grid>
</Section>
<!-- Past Management Section -->
<Section padding="large" background="rgba(240, 148, 29, 0.05)">
<Container center={false}>
<Center>
<h1 class="text-4xl font-light pb-8 text-center">
Organisatsiooni juhtinud inimesed läbi aegade
</h1>
</Center>
<Stack gap="3rem">
{#each pastManagement as yearData (yearData.year)}
<div>
<div class="flex items-center gap-4 pb-6 mb-6 border-b-2 border-[#f0941d]">
<h2 class="text-3xl font-light text-[#f0941d] whitespace-nowrap">
{yearData.year}
</h2>
<Section padding="large" class="current-section bg-white text-gray-900">
<Container>
<div class="section-label">
<span>{getText().current.label}</span>
<span class="section-label-line"></span>
</div>
<Grid min="120px" gap="1rem">
<div class="section-heading-row">
<div>
<h2 class="section-title">{getText().current.label}</h2>
<p class="section-subtitle">{getText().current.hint}</p>
</div>
</div>
{#if loading}
<p class="section-message">{getText().loading}</p>
{:else if loadError}
<p class="section-message section-message-error">{getText().error}</p>
{:else}
<Grid min="280px" gap="gap-6" class="board-grid">
{#each currentManagement as member (member.name)}
<article class="board-card">
<div class="board-card-media">
{#if member.photo}
<Image
src={`/assets/management-images/${member.photo}`}
alt={member.name}
objectFit="cover"
class="board-card-image"
pictureClass="board-card-picture"
/>
{:else}
<div class="board-card-placeholder">{getInitials(member.name)}</div>
{/if}
</div>
<div class="board-card-body">
<div class="board-card-domain">{getBoardLabel(member)}</div>
<div class="board-card-name">{member.name}</div>
<div class="board-card-contacts">
{#if member.email}
<a href={`mailto:${member.email}`}>
<Mail size={14} strokeWidth={2} />
<span>{member.email}</span>
</a>
{/if}
{#if member.phone}
<a href={getPhoneHref(member.phone)}>
<Phone size={14} strokeWidth={2} />
<span>{member.phone}</span>
</a>
{/if}
</div>
</div>
</article>
{/each}
</Grid>
{/if}
</Container>
</Section>
<div class="divider"></div>
<Section padding="large" class="history-section" background="#fffdfa">
<Container>
<div class="section-label">
<span>{getText().history.label}</span>
<span class="section-label-line"></span>
</div>
<div class="section-heading-row">
<div>
<h2 class="section-title">{getText().history.label}</h2>
<p class="section-subtitle">{getText().history.hint}</p>
</div>
</div>
{#if loading}
<p class="section-message">{getText().loading}</p>
{:else if loadError}
<p class="section-message section-message-error">{getText().error}</p>
{:else}
<div class="history-list">
{#each pastManagement as yearData (yearData.year)}
<section class="year-block">
<div class="year-header">
<div class="year-heading-group">
<span class="year-number">{parseYearLabel(yearData.year).year}</span>
{#if parseYearLabel(yearData.year).tag}
<span class="year-tag">{parseYearLabel(yearData.year).tag}</span>
{/if}
</div>
<span class="year-line"></span>
</div>
<Grid min="150px" gap="gap-5" class="year-people">
{#each yearData.members as member (member.name)}
<div class="flex flex-col items-center">
<div class="w-full h-auto max-w-[150px] mx-auto aspect-square mb-3">
<img
<div class="person-item">
<div class="person-photo">
{#if member.image}
<Image
src={`/assets/past-management-images/optimised/${member.image}`}
alt={member.name}
class="w-full h-full object-cover rounded-md"
objectFit="cover"
class="person-image"
pictureClass="person-picture"
/>
{:else}
<div class="person-placeholder">{getInitials(member.name)}</div>
{/if}
</div>
<h3 class="text-base font-medium text-center">
{member.name}
</h3>
<span class="person-name">{member.name}</span>
</div>
{/each}
</Grid>
</div>
</section>
{/each}
</Stack>
</div>
{/if}
</Container>
</Section>
<style>
.hero-shell {
position: relative;
overflow: hidden;
background: linear-gradient(180deg, #0d0d0d 0%, #111111 100%);
}
.hero-shell::after {
content: "";
position: absolute;
inset: auto 0 0 0;
height: 1px;
background: rgba(240, 148, 29, 0.16);
}
.hero-orb {
position: absolute;
border: 1px solid rgba(240, 148, 29, 0.12);
border-radius: 999px;
pointer-events: none;
}
.hero-orb-1 {
top: -4rem;
right: -4rem;
width: 20rem;
height: 20rem;
}
.hero-orb-2 {
bottom: -5rem;
right: 4rem;
width: 13rem;
height: 13rem;
border-color: rgba(240, 148, 29, 0.08);
}
.eyebrow {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.75rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: #f0941d;
margin: 0 0 1rem;
}
.hero-title {
margin: 0;
font-size: clamp(2.4rem, 6vw, 4.2rem);
line-height: 1.05;
font-weight: 700;
letter-spacing: -0.04em;
}
.hero-intro {
margin: 1rem 0 0;
max-width: 52ch;
font-size: clamp(1.05rem, 1.5vw, 1.25rem);
line-height: 1.7;
color: rgba(255, 255, 255, 0.76);
}
.section-label {
display: flex;
align-items: center;
gap: 0.8rem;
margin-bottom: 2rem;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.72rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: #f0941d;
}
.section-label-line {
flex: 1;
height: 1px;
background: rgba(13, 13, 13, 0.12);
}
.section-heading-row {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 1.5rem;
margin-bottom: 1.75rem;
}
.section-title {
margin: 0;
font-size: clamp(1.8rem, 3vw, 2.6rem);
line-height: 1.12;
letter-spacing: -0.03em;
}
.section-subtitle {
margin: 0.45rem 0 0;
color: rgba(13, 13, 13, 0.62);
font-size: 0.98rem;
}
.section-message {
margin: 0;
padding: 1rem 0;
color: rgba(13, 13, 13, 0.65);
}
.section-message-error {
color: #b42318;
}
:global(.board-grid) {
align-items: stretch;
}
.board-card {
overflow: hidden;
border-radius: 14px;
border: 1px solid rgba(13, 13, 13, 0.08);
background: #ffffff;
transition: transform 0.2s ease, border-color 0.2s ease,
box-shadow 0.2s ease;
box-shadow: 0 1px 0 rgba(13, 13, 13, 0.02);
}
.board-card:hover {
transform: translateY(-4px);
border-color: rgba(240, 148, 29, 0.35);
box-shadow: 0 12px 30px rgba(13, 13, 13, 0.08);
}
.board-card-media {
aspect-ratio: 4 / 3;
background: linear-gradient(135deg, #f7f1e6, #efe5d3);
overflow: hidden;
}
:global(.board-card-picture),
:global(.board-card-image) {
width: 100%;
height: 100%;
}
:global(.board-card-image) {
display: block;
}
.board-card-placeholder {
width: 100%;
height: 100%;
display: grid;
place-items: center;
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.04em;
color: #d67d0a;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
}
.board-card-body {
padding: 1.2rem 1.2rem 1.25rem;
border-top: 1px solid rgba(13, 13, 13, 0.06);
}
.board-card-domain {
display: inline-flex;
align-items: center;
margin-bottom: 0.7rem;
padding: 0.2rem 0.55rem;
border-radius: 999px;
background: rgba(240, 148, 29, 0.1);
color: #d67d0a;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.65rem;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.board-card-name {
margin: 0 0 0.8rem;
font-size: 1.1rem;
font-weight: 650;
line-height: 1.25;
}
.board-card-contacts {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.board-card-contacts a {
display: inline-flex;
align-items: center;
gap: 0.45rem;
color: rgba(13, 13, 13, 0.54);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.77rem;
font-weight: 500;
text-decoration: none;
transition: color 0.2s ease;
}
.board-card-contacts a:hover {
color: #f0941d;
text-decoration: none;
}
.divider {
height: 1px;
background: rgba(13, 13, 13, 0.08);
}
:global(.history-section) {
background: #fffdfa;
}
.history-list {
display: flex;
flex-direction: column;
gap: 2.5rem;
}
.year-block {
margin: 0;
}
.year-header {
display: flex;
align-items: baseline;
gap: 1rem;
margin-bottom: 1.1rem;
}
.year-heading-group {
display: flex;
align-items: baseline;
gap: 0.75rem;
white-space: nowrap;
}
.year-number {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 1.6rem;
font-weight: 700;
letter-spacing: -0.04em;
line-height: 1;
}
.year-tag {
padding: 0.2rem 0.55rem;
border-radius: 999px;
background: rgba(240, 148, 29, 0.12);
color: #d67d0a;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.64rem;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.year-line {
flex: 1;
height: 1px;
background: rgba(13, 13, 13, 0.1);
}
:global(.year-people) {
align-items: start;
}
.person-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.55rem;
text-align: center;
}
.person-photo {
width: 112px;
height: 112px;
overflow: hidden;
border-radius: 10px;
background: linear-gradient(135deg, #efe6d7, #e7dbc7);
outline: 2px solid transparent;
outline-offset: 2px;
transition: outline-color 0.2s ease, transform 0.2s ease;
}
.person-item:hover .person-photo {
outline-color: #f0941d;
transform: translateY(-1px);
}
:global(.person-picture),
:global(.person-image) {
width: 100%;
height: 100%;
}
:global(.person-image) {
display: block;
}
.person-placeholder {
width: 100%;
height: 100%;
display: grid;
place-items: center;
color: #d67d0a;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 0.9rem;
font-weight: 700;
}
.person-name {
max-width: 14ch;
font-size: 0.86rem;
line-height: 1.35;
font-weight: 600;
color: #0d0d0d;
}
@media (max-width: 768px) {
.section-heading-row {
align-items: flex-start;
flex-direction: column;
}
.year-header {
align-items: flex-start;
flex-direction: column;
}
.year-line {
width: 100%;
}
.person-photo {
width: 96px;
height: 96px;
}
}
</style>