Compare commits

..
3 Commits
Author SHA1 Message Date
Godopu f3972cac27 feat(animation): implement subtle and high-performance scroll reveal animations across public pages
- Enhance Reveal component with HTMLAttributes passthrough and inline style merging
- Integrate Reveal into Home, Members, Publications, Lectures, and Standardization sections
- Apply lightweight stagger delay (max 360ms cap) without breaking grid stretch (h-full)
- Preserve ProjectPageView CSS Scroll Snap direct-child contract via as='article' in-place replacement
- Integrate Counter component for metrics and category badges
- Support prefers-reduced-motion media query and noscript fallback
- All 12 quality gates passed clean with unanimous PASS from all reviewers
2026-08-26 00:11:24 +09:00
Godopu b0bce3be8e style(members): set professor profile image container background to transparent with border only
- Remove bg-ivory from prof image wrapper container
- Apply bg-transparent and border border-line for clean transparent silhouette
- Passed all 12 E2E test gates (Exit Code 0)
2026-08-25 23:43:04 +09:00
Godopu ca6e85ce9b feat(members): replace Affiliation & Office box with Prof. Seok-Joo Koh profile image
- Copy prof_profile.png to refer_landing_page/public/images/
- Configure images.unoptimized in next.config.js for static export compatibility
- Render high-resolution portrait profile image in professor section
- All 12 quality gates passed clean (Exit Code 0)
2026-08-25 23:40:12 +09:00
14 changed files with 615 additions and 488 deletions
Vendored
BIN
View File
Binary file not shown.
@@ -1,4 +1,5 @@
import React from "react";
import Reveal from "@/components/Reveal";
import type { ResearchProject } from "../../content/types";
import ProjectPageViewControls from "./ProjectPageViewControls";
@@ -13,22 +14,24 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
return (
<section className="space-y-6">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Key Initiatives
</span>
<Reveal variant="up">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Key Initiatives
</span>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Research Projects
</h2>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Research Projects
</h2>
<p className="text-xs text-ink-mute font-mono">
{projects.length} core international standardization research projects
</p>
</div>
<p className="text-xs text-ink-mute font-mono">
{projects.length} core international standardization research projects
</p>
</div>
</Reveal>
{/* Scroll-Snap Track: Mobile 1up, Tablet 2up, Desktop Static 3-col Grid */}
<div
@@ -40,13 +43,16 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
className="flex gap-6 overflow-x-auto snap-x snap-mandatory scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden desktop:grid desktop:grid-cols-3 desktop:overflow-visible focus:outline-none focus:ring-1 focus:ring-cobalt/50 rounded"
>
{projects.map((proj, idx) => (
<article
<Reveal
key={proj.slug}
as="article"
id={`project-${proj.slug}`}
role="group"
aria-roledescription="slide"
aria-label={`${projects.length}개 중 ${idx + 1}번째 프로젝트`}
className="snap-start shrink-0 min-w-0 w-full md:w-[calc(50%-12px)] desktop:w-full flex flex-col justify-between bg-paper p-6 rounded-lg border border-line hover:border-cobalt/60 transition-colors space-y-5 break-words [word-break:keep-all]"
variant="up"
delay={Math.min(idx, 6) * 60}
className="snap-start shrink-0 min-w-0 w-full md:w-[calc(50%-12px)] desktop:w-full flex flex-col justify-between bg-paper p-6 rounded-lg border border-line hover:border-cobalt/60 transition-colors space-y-5 break-words [word-break:keep-all] h-full"
>
<div className="space-y-4">
{/* Header Info */}
@@ -124,7 +130,7 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
</div>
)}
</div>
</article>
</Reveal>
))}
</div>
+96 -81
View File
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import Reveal from "@/components/Reveal";
import { getSemesters } from "../../lib/api";
import type { Semester } from "../../content/types";
@@ -23,110 +24,124 @@ export default function LecturesPage() {
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LECTURES
</h1>
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LECTURES
</h1>
</Reveal>
</section>
{/* Recent 3 Semesters */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Recent Courses
</h2>
<span className="font-mono text-xs text-ink-mute hidden sm:inline">
Latest 3 Semesters
</span>
</div>
<Reveal variant="up">
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Recent Courses
</h2>
<span className="font-mono text-xs text-ink-mute hidden sm:inline">
Latest 3 Semesters
</span>
</div>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 sm:gap-6">
{recentSemesters.map((sem, idx) => (
<div
<Reveal
key={idx}
className="bg-paper p-5 sm:p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between shadow-sm"
variant="up"
delay={idx * 80}
className="h-full"
>
<div className="space-y-3">
<div className="flex justify-between items-center border-b border-line pb-2.5">
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
{sem.term} {sem.year}
</span>
<span className="font-mono text-[0.7rem] uppercase tracking-wider text-vermillion-dark font-bold px-2 py-0.5 rounded bg-vermillion-dark/10">
Latest
</span>
<div
className="bg-paper p-5 sm:p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between shadow-sm h-full"
>
<div className="space-y-3">
<div className="flex justify-between items-center border-b border-line pb-2.5">
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
{sem.term} {sem.year}
</span>
<span className="font-mono text-[0.7rem] uppercase tracking-wider text-vermillion-dark font-bold px-2 py-0.5 rounded bg-vermillion-dark/10">
Latest
</span>
</div>
<ul className="space-y-2.5 pt-1">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-sm sm:text-base text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-xs text-ink-mute">
Code: {c.code} {c.note && `(${c.note})`}
</div>
</li>
))}
</ul>
</div>
<ul className="space-y-2.5 pt-1">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-sm sm:text-base text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-xs text-ink-mute">
Code: {c.code} {c.note && `(${c.note})`}
</div>
</li>
))}
</ul>
<div className="pt-3 border-t border-line/60 font-mono text-xs text-ink-mute flex justify-between items-center">
<span>Total Offerings</span>
<span className="font-semibold text-ink">{sem.courses.length} Courses</span>
</div>
</div>
<div className="pt-3 border-t border-line/60 font-mono text-xs text-ink-mute flex justify-between items-center">
<span>Total Offerings</span>
<span className="font-semibold text-ink">{sem.courses.length} Courses</span>
</div>
</div>
</Reveal>
))}
</div>
</section>
{/* Older Semesters (Native <details>/<summary> progressive disclosure) */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<details className="group border border-line rounded-lg bg-paper p-5 sm:p-6 transition-all duration-200">
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
<div className="space-y-1">
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
Browse all semesters
</span>
<p className="text-xs text-ink-mute font-mono">
Course history from {oldestSem ? `${oldestSem.year} ${oldestSem.term}` : "2004 Spring"} to {newestOlderSem ? `${newestOlderSem.year} ${newestOlderSem.term}` : "2024 Fall"} ({olderSemesters.length} Semesters)
</p>
</div>
<span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3.5 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden">Open </span>
<span className="hidden group-open:inline">Close </span>
</span>
</summary>
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 sm:gap-6">
{olderSemesters.map((sem, idx) => (
<div
key={idx}
className="bg-ivory p-5 rounded-lg border border-line space-y-3 shadow-sm hover:border-cobalt-dark/40 transition-colors"
>
<div className="font-serif text-base sm:text-lg font-normal text-ink border-b border-line pb-2 flex justify-between items-center">
<span>
{sem.term} {sem.year}
</span>
<span className="font-mono text-xs text-ink-mute">
{sem.courses.length} Courses
<Reveal variant="up">
<div className="border border-line rounded-lg bg-paper p-5 sm:p-6 transition-all duration-200">
<details className="group">
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
<div className="space-y-1">
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
Browse all semesters
</span>
<p className="text-xs text-ink-mute font-mono">
Course history from {oldestSem ? `${oldestSem.year} ${oldestSem.term}` : "2004 Spring"} to {newestOlderSem ? `${newestOlderSem.year} ${newestOlderSem.term}` : "2024 Fall"} ({olderSemesters.length} Semesters)
</p>
</div>
<span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3.5 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden">Open </span>
<span className="hidden group-open:inline">Close </span>
</span>
</summary>
<ul className="space-y-2">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-sm text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-[0.7rem] text-ink-mute">
Code: {c.code} {c.note && `(${c.note})`}
</div>
</li>
))}
</ul>
<div className="mt-6 pt-6 border-t border-line grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 sm:gap-6">
{olderSemesters.map((sem, idx) => (
<div
key={idx}
className="bg-ivory p-5 rounded-lg border border-line space-y-3 shadow-sm hover:border-cobalt-dark/40 transition-colors"
>
<div className="font-serif text-base sm:text-lg font-normal text-ink border-b border-line pb-2 flex justify-between items-center">
<span>
{sem.term} {sem.year}
</span>
<span className="font-mono text-xs text-ink-mute">
{sem.courses.length} Courses
</span>
</div>
<ul className="space-y-2">
{sem.courses.map((c, cIdx) => (
<li key={cIdx} className="space-y-0.5">
<div className="font-serif text-sm text-ink leading-snug">
{c.en}
</div>
<div className="font-mono text-[0.7rem] text-ink-mute">
Code: {c.code} {c.note && `(${c.note})`}
</div>
</li>
))}
</ul>
</div>
))}
</div>
))}
</details>
</div>
</details>
</Reveal>
</section>
</div>
);
+119 -89
View File
@@ -1,6 +1,8 @@
"use client";
import React, { useEffect, useState } from "react";
import Image from "next/image";
import Reveal from "@/components/Reveal";
import { getMembers, getAlumni } from "../../lib/api";
import { ProfessorDetailsDialog } from "./_components/ProfessorDetailsDialog";
import type { Member, Alumnus } from "../../content/types";
@@ -42,121 +44,147 @@ export default function MembersPage() {
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LAB MEMBERS
</h1>
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
LAB MEMBERS
</h1>
</Reveal>
</section>
{/* Professor Section */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
<div className="lg:col-span-8 space-y-4">
<h2 className="font-serif text-3xl font-normal text-ink">
Prof. Seok-Joo Koh
</h2>
<div className="text-sm text-ink-mute leading-relaxed space-y-2">
<p>
Professor at the School of Computer Science and Engineering, Kyungpook National University, and Dean of the College of IT Engineering. He directs the AI Agent Networking Laboratory (ANL), leading research on QUIC/gRPC transport, A2A/ACP protocols, MCP &amp; SKILL architectures, and multi-agent orchestration, and serves as Project Editor in ISO/IEC JTC1, ITU-T, and IEC TC100.
</p>
<div>
<ProfessorDetailsDialog />
<Reveal variant="up">
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
<div className="lg:col-span-8 space-y-4">
<h2 className="font-serif text-3xl font-normal text-ink">
Prof. Seok-Joo Koh
</h2>
<div className="text-sm text-ink-mute leading-relaxed space-y-2">
<p>
Professor at the School of Computer Science and Engineering, Kyungpook National University, and Dean of the College of IT Engineering. He directs the AI Agent Networking Laboratory (ANL), leading research on QUIC/gRPC transport, A2A/ACP protocols, MCP &amp; SKILL architectures, and multi-agent orchestration, and serves as Project Editor in ISO/IEC JTC1, ITU-T, and IEC TC100.
</p>
<div>
<ProfessorDetailsDialog />
</div>
</div>
<div className="pt-4 border-t border-line/60 flex flex-wrap gap-4 text-xs font-mono">
<a
href="mailto:sjkoh@knu.ac.kr"
className="text-cobalt-dark hover:underline font-bold"
>
sjkoh@knu.ac.kr
</a>
<span className="text-ink-mute">|</span>
<a
href="https://github.com/sjkoh12"
target="_blank"
rel="noopener noreferrer"
className="text-ink hover:text-cobalt-dark hover:underline"
>
GitHub Profile
</a>
</div>
</div>
<div className="pt-4 border-t border-line/60 flex flex-wrap gap-4 text-xs font-mono">
<a
href="mailto:sjkoh@knu.ac.kr"
className="text-cobalt-dark hover:underline font-bold"
>
sjkoh@knu.ac.kr
</a>
<span className="text-ink-mute">|</span>
<a
href="https://github.com/sjkoh12"
target="_blank"
rel="noopener noreferrer"
className="text-ink hover:text-cobalt-dark hover:underline"
>
GitHub Profile
</a>
<div className="lg:col-span-4 flex justify-center items-center">
<div className="relative w-48 h-48 sm:w-56 sm:h-56 md:w-60 md:h-60 rounded-xl overflow-hidden border border-line bg-transparent">
<Image
src="/images/prof_profile.png"
alt="Prof. Seok-Joo Koh"
fill
sizes="(max-width: 1024px) 224px, 240px"
className="object-cover object-top"
priority
/>
</div>
</div>
</div>
<div className="lg:col-span-4 bg-ivory p-6 rounded border border-line text-xs space-y-2 font-mono">
<div className="text-ink font-bold border-b border-line pb-1">
Affiliation &amp; Office
</div>
<p className="text-ink-mute">Kyungpook National University</p>
<p className="text-ink-mute">IT Building #5 Room 527</p>
<p className="text-ink-mute">Daegu, Republic of Korea</p>
</div>
</div>
</Reveal>
</section>
{/* Active Researchers Section */}
<section className="space-y-8 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Active Researchers
</h2>
<Reveal variant="up">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Active Researchers
</h2>
</div>
</div>
</div>
</Reveal>
{/* Ph.D. Candidates / Students */}
<div className="space-y-4">
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
Ph.D. Course ({phdStudents.length})
</h3>
<Reveal variant="up">
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
Ph.D. Course ({phdStudents.length})
</h3>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{phdStudents.map((mem, idx) => (
<div
<Reveal
key={idx}
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
variant="up"
delay={Math.min(idx, 6) * 60}
className="h-full"
>
<div className="flex justify-between items-baseline">
<span className="font-serif text-lg font-normal text-ink">
{mem.ko}
</span>
<span className="font-mono text-xs text-cobalt-dark">
{DEGREE_LABEL[mem.degree] || mem.degree}
</span>
</div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && (
<div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation}
<div
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors h-full"
>
<div className="flex justify-between items-baseline">
<span className="font-serif text-lg font-normal text-ink">
{mem.ko}
</span>
<span className="font-mono text-xs text-cobalt-dark">
{DEGREE_LABEL[mem.degree] || mem.degree}
</span>
</div>
)}
</div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && (
<div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation}
</div>
)}
</div>
</Reveal>
))}
</div>
</div>
{/* M.S. Candidates / Students */}
<div className="space-y-4 pt-4">
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
M.S. Course ({msStudents.length})
</h3>
<Reveal variant="up">
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
M.S. Course ({msStudents.length})
</h3>
</Reveal>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{msStudents.map((mem, idx) => (
<div
<Reveal
key={idx}
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
variant="up"
delay={Math.min(idx, 6) * 60}
className="h-full"
>
<div className="flex justify-between items-baseline">
<span className="font-serif text-lg font-normal text-ink">
{mem.ko}
</span>
<span className="font-mono text-xs text-cobalt-dark">
{DEGREE_LABEL[mem.degree] || mem.degree}
</span>
</div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && (
<div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation}
<div
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors h-full"
>
<div className="flex justify-between items-baseline">
<span className="font-serif text-lg font-normal text-ink">
{mem.ko}
</span>
<span className="font-mono text-xs text-cobalt-dark">
{DEGREE_LABEL[mem.degree] || mem.degree}
</span>
</div>
)}
</div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && (
<div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation}
</div>
)}
</div>
</Reveal>
))}
</div>
</div>
@@ -164,13 +192,15 @@ export default function MembersPage() {
{/* Alumni Section (Native <details>/<summary> progressive disclosure) */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Graduates
</h2>
<Reveal variant="up">
<div className="flex justify-between items-end">
<div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Graduates
</h2>
</div>
</div>
</div>
</Reveal>
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
+125 -101
View File
@@ -2,6 +2,8 @@
import React, { useEffect, useState } from "react";
import Link from "next/link";
import Reveal from "@/components/Reveal";
import Counter from "@/components/Counter";
import HeroComposition from "./_components/HeroComposition";
import { ProjectPageView } from "./_components/ProjectPageView";
import { getStatsSummary, getResearchAreaNames, getResearchProjects } from "../lib/api";
@@ -40,57 +42,67 @@ export default function HomePage() {
<div className="container-content space-y-16 md:space-y-24 pt-10 md:pt-16 pb-16 md:pb-24">
{/* Hero Section */}
<section className="relative">
<div className="mb-4">
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Kyungpook National University · AI Agent Networking Lab (ANL)
</span>
</div>
<Reveal variant="up">
<div className="mb-4">
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Kyungpook National University · AI Agent Networking Lab (ANL)
</span>
</div>
</Reveal>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:items-center">
<div className="lg:col-span-7 space-y-6 lg:pt-4">
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
Multi-Agent <br />
<span className="italic font-serif text-cobalt-dark">Orchestration</span>
</h1>
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl">
The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration.
</p>
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
Multi-Agent <br />
<span className="italic font-serif text-cobalt-dark">Orchestration</span>
</h1>
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl mt-4">
The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration.
</p>
</Reveal>
{/* Metrics Row (Derived from real content data) */}
<div className="pt-6 border-t border-line grid grid-cols-3 gap-4">
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
{stats.intlPubsCount}
<Reveal variant="up" delay={80}>
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
<Counter value={stats.intlPubsCount} />
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
International Journals &amp; Conference
</div>
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
International Journals &amp; Conference
</Reveal>
<Reveal variant="up" delay={160}>
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
<Counter value={stats.stdDocsCount} />
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
Standardization Contributions
</div>
</div>
</div>
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
{stats.stdDocsCount}
</Reveal>
<Reveal variant="up" delay={240}>
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
<Counter value={stats.patentCount} />
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
Patent<br />
<span className="text-[0.68rem] tracking-normal font-sans normal-case text-ink-mute">
(IoT Architecture &amp; Protocols)
</span>
</div>
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
Standardization Contributions
</div>
</div>
<div>
<div className="font-serif text-3xl text-cobalt-dark font-normal">
{stats.patentCount}
</div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
Patent<br />
<span className="text-[0.68rem] tracking-normal font-sans normal-case text-ink-mute">
(IoT Architecture &amp; Protocols)
</span>
</div>
</div>
</Reveal>
</div>
</div>
<div className="w-full max-w-[500px] mx-auto my-[30px] lg:my-0 lg:max-w-none lg:mx-0 lg:col-span-5 border border-line bg-paper p-4 rounded-lg">
<Reveal variant="scale" delay={120} className="w-full max-w-[500px] mx-auto my-[30px] lg:my-0 lg:max-w-none lg:mx-0 lg:col-span-5 border border-line bg-paper p-4 rounded-lg">
<HeroComposition />
</div>
</Reveal>
</div>
</section>
@@ -99,22 +111,24 @@ export default function HomePage() {
{/* 14 Research Areas Grid */}
<section className="space-y-8 pt-16 md:pt-20 border-t border-line">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Core Domains
</span>
<Reveal variant="up">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Core Domains
</span>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Research Areas
</h2>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
Research Areas
</h2>
<p className="text-xs text-ink-mute font-mono">
{researchAreas.length} fundamental networking protocols and AI system domains
</p>
</div>
<p className="text-xs text-ink-mute font-mono">
{researchAreas.length} fundamental networking protocols and AI system domains
</p>
</div>
</Reveal>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{researchAreas.map((area, idx) => {
@@ -122,28 +136,34 @@ export default function HomePage() {
const isOdd = (idx + 1) % 2 !== 0;
return (
<div
<Reveal
key={no}
className="bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-ink transition-colors duration-200 h-full"
variant="up"
delay={Math.min(idx, 6) * 60}
className="h-full"
>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span
className={`font-mono text-xs font-bold ${
isOdd ? "text-vermillion-dark" : "text-cobalt-dark"
}`}
>
{no}
</span>
<span className="font-mono text-[0.68rem] text-ink-mute uppercase tracking-wider">
Topic #{no}
</span>
<div
className="bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-ink transition-colors duration-200 h-full"
>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span
className={`font-mono text-xs font-bold ${
isOdd ? "text-vermillion-dark" : "text-cobalt-dark"
}`}
>
{no}
</span>
<span className="font-mono text-[0.68rem] text-ink-mute uppercase tracking-wider">
Topic #{no}
</span>
</div>
<h3 className="font-serif text-base font-normal text-ink leading-snug">
{area}
</h3>
</div>
<h3 className="font-serif text-base font-normal text-ink leading-snug">
{area}
</h3>
</div>
</div>
</Reveal>
);
})}
</div>
@@ -151,43 +171,47 @@ export default function HomePage() {
{/* International Standardizations Section */}
<section className="space-y-6 pt-16 md:pt-20 border-t border-line">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Global Engagement
</span>
<Reveal variant="up">
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Global Engagement
</span>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
International Standardizations
</h2>
</div>
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
International Standardizations
</h2>
</div>
</div>
</Reveal>
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6">
<p className="text-ink/80 text-sm sm:text-base leading-relaxed max-w-3xl">
Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations):
</p>
<Reveal variant="up">
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6">
<p className="text-ink/80 text-sm sm:text-base leading-relaxed max-w-3xl">
Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations):
</p>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3 pt-2">
{INTERNATIONAL_SDOS.map((sdo, idx) => (
<li key={idx} className="flex items-start gap-2.5 text-sm text-ink font-sans">
<span className="inline-block w-1.5 h-1.5 rounded-full bg-cobalt-dark mt-2 shrink-0"></span>
<span>{sdo}</span>
</li>
))}
</ul>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3 pt-2">
{INTERNATIONAL_SDOS.map((sdo, idx) => (
<li key={idx} className="flex items-start gap-2.5 text-sm text-ink font-sans">
<span className="inline-block w-1.5 h-1.5 rounded-full bg-cobalt-dark mt-2 shrink-0"></span>
<span>{sdo}</span>
</li>
))}
</ul>
<div className="pt-4 border-t border-line/60">
<Link
href="/standardization"
className="inline-flex items-center gap-2 font-mono text-xs font-semibold uppercase tracking-wider text-cobalt-dark hover:text-ink transition-colors draw-underline"
>
<span>Please refer to here for details </span>
</Link>
<div className="pt-4 border-t border-line/60">
<Link
href="/standardization"
className="inline-flex items-center gap-2 font-mono text-xs font-semibold uppercase tracking-wider text-cobalt-dark hover:text-ink transition-colors draw-underline"
>
<span>Please refer to here for details </span>
</Link>
</div>
</div>
</div>
</Reveal>
</section>
</div>
);
@@ -2,6 +2,7 @@
import React, { useEffect, useState } from "react";
import { notFound } from "next/navigation";
import Reveal from "@/components/Reveal";
import { CategoryNav } from "../../_components/CategoryNav";
import { getPublications, getPatents } from "../../../../lib/api";
import { PUB_CATEGORIES } from "../../../../content/categories";
@@ -61,32 +62,41 @@ export function PublicationCategoryClient({ category }: { category: string }) {
}));
}
// Extract unique sorted years
// Extract distinct available years from date strings (YYYY-MM or YYYY)
const availableYears = Array.from(
new Set(
allRecords
.map((r) => (r.publishedAt || "").match(/\b(19\d\d|20\d\d)\b/)?.[1])
.map((r) => {
const dateStr = r.publishedAt || "";
const match = dateStr.match(/\b(19\d\d|20\d\d)\b/);
return match ? match[1] : null;
})
.filter((y): y is string => Boolean(y))
)
).sort((a, b) => b.localeCompare(a));
const records =
selectedYear === "all"
? allRecords
: allRecords.filter((r) => (r.publishedAt || "").includes(selectedYear));
// Filter records by selected year
const records = allRecords.filter((r) => {
if (selectedYear === "all") return true;
const dateStr = r.publishedAt || "";
const yr = dateStr.match(/\b(19\d\d|20\d\d)\b/)?.[1] || "";
return yr === selectedYear;
});
return (
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section className="space-y-1">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
{currentCat.label}
</h1>
{currentCat.subtitle && (
<p className="font-mono text-sm text-ink-mute">
{currentCat.subtitle}
</p>
)}
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
{currentCat.label}
</h1>
{currentCat.subtitle && (
<p className="font-mono text-sm text-ink-mute mt-1">
{currentCat.subtitle}
</p>
)}
</Reveal>
</section>
{/* Category Nav Cards */}
@@ -96,38 +106,40 @@ export function PublicationCategoryClient({ category }: { category: string }) {
{/* Record List Section */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<div className="flex flex-wrap justify-between items-center gap-4 border-b border-line pb-3">
<div className="flex items-center gap-2">
<span className="font-mono text-xs text-ink-mute uppercase">Filter Year:</span>
<select
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
className="px-2.5 py-1 text-xs border border-line rounded bg-paper text-ink focus:outline-none focus:ring-1 focus:ring-cobalt font-mono"
>
<option value="all">All Years ({allRecords.length})</option>
{availableYears.map((yr) => {
const yrCount = allRecords.filter((r) => (r.publishedAt || "").includes(yr)).length;
return (
<option key={yr} value={yr}>
{yr} ({yrCount})
</option>
);
})}
</select>
{selectedYear !== "all" && (
<button
onClick={() => setSelectedYear("all")}
className="text-xs text-cobalt-dark hover:underline font-mono"
<Reveal variant="up">
<div className="flex flex-wrap justify-between items-center gap-4 border-b border-line pb-3">
<div className="flex items-center gap-2">
<span className="font-mono text-xs text-ink-mute uppercase">Filter Year:</span>
<select
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
className="px-2.5 py-1 text-xs border border-line rounded bg-paper text-ink focus:outline-none focus:ring-1 focus:ring-cobalt font-mono"
>
Reset
</button>
)}
</div>
<option value="all">All Years ({allRecords.length})</option>
{availableYears.map((yr) => {
const yrCount = allRecords.filter((r) => (r.publishedAt || "").includes(yr)).length;
return (
<option key={yr} value={yr}>
{yr} ({yrCount})
</option>
);
})}
</select>
{selectedYear !== "all" && (
<button
onClick={() => setSelectedYear("all")}
className="text-xs text-cobalt-dark hover:underline font-mono"
>
Reset
</button>
)}
</div>
<span className="font-mono text-xs text-ink-mute uppercase">
Showing {records.length} of {allRecords.length} Records
</span>
</div>
<span className="font-mono text-xs text-ink-mute uppercase">
Showing {records.length} of {allRecords.length} Records
</span>
</div>
</Reveal>
{records.length === 0 ? (
<div className="bg-paper p-8 rounded-lg border border-line text-center space-y-3">
@@ -1,5 +1,7 @@
import React from "react";
import Link from "next/link";
import Reveal from "@/components/Reveal";
import Counter from "@/components/Counter";
import { PUB_CATEGORIES } from "../../../content/categories";
import { PubCategory } from "../../../content/types";
@@ -24,87 +26,95 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) {
return (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Categories
</h2>
{currentCategory && (
<Link
href="/publications"
className="font-mono text-xs text-ink-mute hover:text-ink hover:underline transition-colors"
>
&larr; Overview
</Link>
)}
</div>
<Reveal variant="up">
<div className="flex justify-between items-center">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Categories
</h2>
{currentCategory && (
<Link
href="/publications"
className="font-mono text-xs text-ink-mute hover:text-ink hover:underline transition-colors"
>
&larr; Overview
</Link>
)}
</div>
</Reveal>
<div className="grid gap-5 sm:gap-6 md:grid-cols-3">
{PUB_CATEGORIES.map((cat) => {
{PUB_CATEGORIES.map((cat, idx) => {
const count = getCount(cat.slug);
const isActive = currentCategory === cat.slug;
return (
<div
<Reveal
key={cat.slug}
className={`flex flex-col justify-between bg-paper p-5 sm:p-6 rounded-lg transition-all ${
isActive
? "border-2 border-cobalt-dark shadow-md"
: "border border-line shadow-sm hover:border-cobalt-dark/40"
}`}
variant="up"
delay={idx * 80}
className="h-full"
>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span
className={`font-mono text-xs font-bold uppercase tracking-wider ${
isActive ? "text-cobalt-dark" : "text-ink-mute"
<div
className={`flex flex-col justify-between bg-paper p-5 sm:p-6 rounded-lg transition-all h-full ${
isActive
? "border-2 border-cobalt-dark shadow-md"
: "border border-line shadow-sm hover:border-cobalt-dark/40"
}`}
>
<div className="space-y-3">
<div className="flex justify-between items-center">
<span
className={`font-mono text-xs font-bold uppercase tracking-wider ${
isActive ? "text-cobalt-dark" : "text-ink-mute"
}`}
>
Category
</span>
{isActive && (
<span className="font-mono text-[0.65rem] font-bold px-2 py-0.5 rounded bg-cobalt-dark/10 text-cobalt-dark uppercase">
Current
</span>
)}
</div>
<h3
className={`font-serif text-xl ${
isActive ? "text-cobalt-dark font-normal" : "text-ink font-normal"
}`}
>
Category
</span>
{isActive && (
<span className="font-mono text-[0.65rem] font-bold px-2 py-0.5 rounded bg-cobalt-dark/10 text-cobalt-dark uppercase">
Current
</span>
)}
{cat.label}
{cat.subtitle && (
<span className="block text-xs font-mono text-ink-mute mt-1 font-normal">
{cat.subtitle}
</span>
)}
</h3>
</div>
<h3
className={`font-serif text-xl ${
isActive ? "text-cobalt-dark font-normal" : "text-ink font-normal"
}`}
>
{cat.label}
{cat.subtitle && (
<span className="block text-xs font-mono text-ink-mute mt-1 font-normal">
{cat.subtitle}
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
<span className="font-display text-3xl font-bold text-ink">
<Counter value={count} />
<span className="text-xs font-mono text-ink-mute ml-1 font-normal">
{cat.slug === "patent" ? "Patents" : "Papers"}
</span>
</span>
{isActive ? (
<span className="inline-flex items-center text-xs font-bold text-cobalt-dark font-mono">
Active View
</span>
) : (
<Link
href={`/publications/${cat.slug}`}
aria-label={`View all ${cat.label} records`}
className="inline-flex items-center text-xs font-bold text-vermillion-dark hover:underline focus:outline-none focus:ring-1 focus:ring-vermillion-dark rounded"
>
View All &rarr;
</Link>
)}
</h3>
</div>
</div>
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
<span className="font-display text-3xl font-bold text-ink">
{count}
<span className="text-xs font-mono text-ink-mute ml-1 font-normal">
{cat.slug === "patent" ? "Patents" : "Papers"}
</span>
</span>
{isActive ? (
<span className="inline-flex items-center text-xs font-bold text-cobalt-dark font-mono">
Active View
</span>
) : (
<Link
href={`/publications/${cat.slug}`}
aria-label={`View all ${cat.label} records`}
className="inline-flex items-center text-xs font-bold text-vermillion-dark hover:underline focus:outline-none focus:ring-1 focus:ring-vermillion-dark rounded"
>
View All &rarr;
</Link>
)}
</div>
</div>
</Reveal>
);
})}
</div>
+19 -8
View File
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import Reveal from "@/components/Reveal";
import { CategoryNav } from "./_components/CategoryNav";
import { getPublications, getPatents } from "../../lib/api";
import type { Publication, Patent } from "../../content/types";
@@ -31,9 +32,11 @@ export default function PublicationsIndexPage() {
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
PUBLICATIONS
</h1>
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
PUBLICATIONS
</h1>
</Reveal>
</section>
{/* Category Summary Cards */}
@@ -43,13 +46,21 @@ export default function PublicationsIndexPage() {
{/* Highlights Section */}
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Highlights
</h2>
<Reveal variant="up">
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
Highlights
</h2>
</Reveal>
<div className="divide-y divide-line border border-line bg-paper rounded-lg overflow-hidden shadow-sm">
{highlights.map((pub, idx) => (
<article key={`${pub.category}-${idx}`} className="p-5 hover:bg-ivory/60 transition-colors space-y-2">
<Reveal
key={`${pub.category}-${idx}`}
as="article"
variant="up"
delay={Math.min(idx, 6) * 60}
className="p-5 hover:bg-ivory/60 transition-colors space-y-2"
>
<div className="flex flex-wrap items-center gap-2 text-xs font-mono text-ink-mute">
<span className="px-2 py-0.5 rounded bg-ivory border border-line text-ink font-bold">
{pub.publishedAt?.substring(0, 7) || "Recent"}
@@ -76,7 +87,7 @@ export default function PublicationsIndexPage() {
{pub.venue}{pub.volume ? `, ${pub.volume}` : ""}
</p>
)}
</article>
</Reveal>
))}
</div>
</section>
+88 -79
View File
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import Reveal from "@/components/Reveal";
import { getStandardsBodies } from "../../lib/api";
import type { StandardsBody } from "../../content/types";
@@ -16,95 +17,103 @@ export default function StandardizationPage() {
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
{/* Page Header */}
<section>
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
International Standardization
</h1>
<Reveal variant="up">
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
International Standardization
</h1>
</Reveal>
</section>
{/* Standards Bodies Section */}
<section className="space-y-8 sm:space-y-10 pt-6 md:pt-8 border-t border-line">
{standardsBodies.map((body, idx) => (
<div key={idx} className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6 shadow-sm">
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-2">
<span className="font-mono text-xs text-cobalt-dark font-bold uppercase">
[{body.scope}]
</span>
{body.role && (
<span className="bg-vermillion-dark text-ivory font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold">
{body.role}
<Reveal
key={idx}
variant="up"
delay={idx * 120}
>
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6 shadow-sm">
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
<div>
<div className="flex items-center gap-2">
<span className="font-mono text-xs text-cobalt-dark font-bold uppercase">
[{body.scope}]
</span>
)}
</div>
<h2 className="font-serif text-2xl font-normal text-ink mt-1">
{body.org}
</h2>
<p className="text-xs text-ink-mute font-mono mt-0.5">
{body.full} ({body.period})
</p>
</div>
<div className="font-mono text-xs text-ink-mute bg-ivory border border-line px-3 py-1.5 rounded">
Projects: {body.projects.length}
</div>
</div>
{/* 3-Tier Hierarchy: Projects & Documents */}
<div className="space-y-6">
{body.projects.map((proj, pIdx) => (
<div key={pIdx} className="bg-ivory p-6 rounded border border-line space-y-3">
<div className="font-serif text-lg font-normal text-ink border-b border-line/60 pb-2 flex justify-between items-center">
<div>
<span className="font-mono text-xs text-cobalt-dark font-bold mr-2">
{proj.wg}
{body.role && (
<span className="bg-vermillion-dark text-ivory font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold">
{body.role}
</span>
<span>{proj.name}</span>
</div>
<span className="font-mono text-xs text-ink-mute">
{proj.documents.length} Docs
</span>
)}
</div>
{proj.documents.length > 0 ? (
<div className="space-y-3 pt-1">
{proj.documents.map((doc, dIdx) => (
<div
key={dIdx}
className="bg-paper p-4 rounded border border-line/60 space-y-1.5"
>
<div className="flex justify-between items-start gap-4">
<h3 className="font-serif text-base font-normal text-ink leading-snug">
&ldquo;{doc.title}&rdquo;
</h3>
<span
className={`font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold shrink-0 ${
doc.status === "InProgress"
? "bg-vermillion-dark text-ivory"
: "bg-cobalt-dark text-ivory"
}`}
>
{doc.status}
</span>
</div>
<div className="font-mono text-xs text-ink-mute flex justify-between">
<span>{doc.ref}</span>
{doc.publishedAt && (
<span className="text-ink">{doc.publishedAt}</span>
)}
</div>
</div>
))}
</div>
) : (
<div className="text-xs text-ink-mute font-mono italic">
Project specification in progress
</div>
)}
<h2 className="font-serif text-2xl font-normal text-ink mt-1">
{body.org}
</h2>
<p className="text-xs text-ink-mute font-mono mt-0.5">
{body.full} ({body.period})
</p>
</div>
))}
<div className="font-mono text-xs text-ink-mute bg-ivory border border-line px-3 py-1.5 rounded">
Projects: {body.projects.length}
</div>
</div>
{/* 3-Tier Hierarchy: Projects & Documents */}
<div className="space-y-6">
{body.projects.map((proj, pIdx) => (
<div key={pIdx} className="bg-ivory p-6 rounded border border-line space-y-3">
<div className="font-serif text-lg font-normal text-ink border-b border-line/60 pb-2 flex justify-between items-center">
<div>
<span className="font-mono text-xs text-cobalt-dark font-bold mr-2">
{proj.wg}
</span>
<span>{proj.name}</span>
</div>
<span className="font-mono text-xs text-ink-mute">
{proj.documents.length} Docs
</span>
</div>
{proj.documents.length > 0 ? (
<div className="space-y-3 pt-1">
{proj.documents.map((doc, dIdx) => (
<div
key={dIdx}
className="bg-paper p-4 rounded border border-line/60 space-y-1.5"
>
<div className="flex justify-between items-start gap-4">
<h3 className="font-serif text-base font-normal text-ink leading-snug">
&ldquo;{doc.title}&rdquo;
</h3>
<span
className={`font-mono text-[0.65rem] px-2 py-0.5 rounded font-bold shrink-0 ${
doc.status === "InProgress"
? "bg-vermillion-dark text-ivory"
: "bg-cobalt-dark text-ivory"
}`}
>
{doc.status}
</span>
</div>
<div className="font-mono text-xs text-ink-mute flex justify-between">
<span>{doc.ref}</span>
{doc.publishedAt && (
<span className="text-ink">{doc.publishedAt}</span>
)}
</div>
</div>
))}
</div>
) : (
<div className="text-xs text-ink-mute font-mono italic">
Project specification in progress
</div>
)}
</div>
))}
</div>
</div>
</div>
</Reveal>
))}
</section>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

+10 -3
View File
@@ -1,6 +1,6 @@
"use client";
import { useEffect, useRef, type ElementType, type ReactNode } from "react";
import { useEffect, useRef, type ElementType, type ReactNode, type HTMLAttributes } from "react";
type Variant = "up" | "left" | "right" | "scale";
@@ -18,7 +18,9 @@ export default function Reveal({
delay = 0,
as: Tag = "div",
className = "",
}: {
style,
...rest
}: HTMLAttributes<HTMLElement> & {
children: ReactNode;
variant?: Variant;
delay?: number;
@@ -47,12 +49,17 @@ export default function Reveal({
return () => io.disconnect();
}, []);
const combinedStyle = delay
? { ...style, transitionDelay: `${delay}ms` }
: style;
return (
<Tag
ref={ref}
data-variant={variant}
style={delay ? { transitionDelay: `${delay}ms` } : undefined}
style={combinedStyle}
className={`reveal ${className}`}
{...rest}
>
{children}
</Tag>
+3
View File
@@ -3,6 +3,9 @@ const nextConfig = {
output: "export",
trailingSlash: true,
reactStrictMode: true,
images: {
unoptimized: true,
},
};
module.exports = nextConfig;
Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB