Compare commits
15 changed files with 367 additions and 885 deletions
|
|
@ -1,16 +0,0 @@
|
||||||
<svg width="114" height="114" viewBox="0 0 114 114" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M111.78 51.8875L62.0349 2.1486C59.1727 -0.7162 54.5267 -0.7162 51.6609 2.1486L41.3319 12.4786L54.4339 25.5806C57.4798 24.5522 60.971 25.2417 63.3978 27.669C65.8372 30.1114 66.5218 33.6324 65.4676 36.6885L78.0956 49.3165C81.1507 48.2637 84.6756 48.9439 87.1151 51.3877C90.5257 54.7973 90.5257 60.3222 87.1151 63.7327C83.704 67.1443 78.1791 67.1443 74.7661 63.7327C72.2016 61.1662 71.5673 57.3982 72.8662 54.2385L61.0892 42.4615L61.0882 73.4525C61.9197 73.8641 62.7044 74.4135 63.3973 75.1034C66.8069 78.5126 66.8069 84.0365 63.3973 87.4514C59.9867 90.8605 54.4593 90.8605 51.0523 87.4514C47.6422 84.0368 47.6422 78.5129 51.0523 75.1034C51.895 74.2622 52.8702 73.6254 53.9107 73.1986V41.9196C52.8697 41.4946 51.8957 40.8626 51.0517 40.0146C48.4687 37.4336 47.8466 33.6426 49.1713 30.4707L36.2553 17.5527L2.14928 51.6577C-0.716425 54.5247 -0.716425 59.1707 2.14928 62.0357L51.8913 111.775C54.7551 114.639 59.3995 114.639 62.2673 111.775L111.779 62.2707C114.644 59.4045 114.644 54.7571 111.779 51.8917" fill="url(#paint0_linear_372_6)"/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear_372_6" x1="0" y1="0" x2="114" y2="114" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#FFB5C9"/>
|
|
||||||
<stop offset="0.125" stop-color="#FFB8C7"/>
|
|
||||||
<stop offset="0.25" stop-color="#FFBCC4"/>
|
|
||||||
<stop offset="0.375" stop-color="#FFBFC2"/>
|
|
||||||
<stop offset="0.5" stop-color="#FFC3BF"/>
|
|
||||||
<stop offset="0.625" stop-color="#FFC6BD"/>
|
|
||||||
<stop offset="0.75" stop-color="#FFC9BA"/>
|
|
||||||
<stop offset="0.875" stop-color="#FFCDB8"/>
|
|
||||||
<stop offset="1" stop-color="#FFD0B5"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Steam</title><path d="M11.979 0C5.678 0 .511 4.86.022 11.037l6.432 2.658c.545-.371 1.203-.59 1.912-.59.063 0 .125.004.188.006l2.861-4.142V8.91c0-2.495 2.028-4.524 4.524-4.524 2.494 0 4.524 2.031 4.524 4.527s-2.03 4.525-4.524 4.525h-.105l-4.076 2.911c0 .052.004.105.004.159 0 1.875-1.515 3.396-3.39 3.396-1.635 0-3.016-1.173-3.331-2.727L.436 15.27C1.862 20.307 6.486 24 11.979 24c6.627 0 11.999-5.373 11.999-12S18.605 0 11.979 0zM7.54 18.21l-1.473-.61c.262.543.714.999 1.314 1.25 1.297.539 2.793-.076 3.332-1.375.263-.63.264-1.319.005-1.949s-.75-1.121-1.377-1.383c-.624-.26-1.29-.249-1.878-.03l1.523.63c.956.4 1.409 1.5 1.009 2.455-.397.957-1.497 1.41-2.454 1.012H7.54zm11.415-9.303c0-1.662-1.353-3.015-3.015-3.015-1.665 0-3.015 1.353-3.015 3.015 0 1.665 1.35 3.015 3.015 3.015 1.663 0 3.015-1.35 3.015-3.015zm-5.273-.005c0-1.252 1.013-2.266 2.265-2.266 1.249 0 2.266 1.014 2.266 2.266 0 1.251-1.017 2.265-2.266 2.265-1.253 0-2.265-1.014-2.265-2.265z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg fill="#000000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>VRChat</title><path d="M22.732 6.767H1.268A1.27 1.27 0 0 0 0 8.035v5.296c0 .7.57 1.268 1.268 1.268h18.594l1.725 2.22c.215.275.443.415.68.415.153 0 .296-.06.403-.167.128-.129.193-.308.193-.536l-.002-1.939A1.27 1.27 0 0 0 24 13.331V8.035c0-.7-.569-1.269-1.268-1.269Zm.8 6.564a.8.8 0 0 1-.8.801h-.34v.031l.004 2.371c0 .155-.05.233-.129.233s-.19-.079-.31-.235l-1.866-2.4H1.268a.8.8 0 0 1-.8-.8V8.064a.8.8 0 0 1 .8-.8h21.464a.8.8 0 0 1 .8.8v5.266ZM4.444 8.573c-.127 0-.225.041-.254.15l-.877 3.129-.883-3.128c-.03-.11-.127-.15-.254-.15-.202 0-.473.126-.473.311 0 .012.005.035.011.058l1.114 3.63c.058.173.265.254.485.254s.433-.08.484-.254l1.109-3.63c.005-.023.011-.04.011-.058 0-.179-.27-.312-.473-.312Zm2.925 2.36c.433-.132.757-.49.757-1.153 0-.918-.612-1.207-1.368-1.207H5.614a.234.234 0 0 0-.242.231v3.752c0 .156.184.237.374.237s.376-.081.376-.237V11.05h.484l.82 1.593c.058.115.156.179.26.179.219 0 .467-.203.467-.393a.155.155 0 0 0-.028-.092l-.756-1.403Zm-.61-.473h-.636V9.231h.635c.375 0 .618.162.618.618s-.242.612-.618.612Zm10.056.826h1.004l-.502-1.772-.502 1.772Zm4.684-3.095H9.366a.8.8 0 0 0-.8.8v3.383a.8.8 0 0 0 .8.8h12.132a.8.8 0 0 0 .8-.8V8.992a.8.8 0 0 0-.8-.801Zm-10.946 3.977c.525 0 .571-.374.589-.617.011-.179.173-.236.369-.236.26 0 .38.075.38.369 0 .698-.57 1.142-1.379 1.142-.727 0-1.327-.357-1.327-1.322v-1.61c0-.963.606-1.322 1.333-1.322.802 0 1.374.427 1.374 1.097 0 .3-.121.37-.375.37-.214 0-.37-.064-.375-.238-.012-.178-.052-.57-.6-.57-.387 0-.606.213-.606.663v1.61c0 .45.219.664.617.664Zm4.703.388c0 .156-.19.237-.375.237s-.375-.081-.375-.237V10.9h-1.299v1.656c0 .156-.19.237-.375.237s-.375-.081-.375-.237V8.804c0-.161.185-.23.375-.23s.375.069.375.23v1.507h1.299V8.804c0-.161.185-.23.375-.23s.375.069.375.23v3.752Zm3.198.236c-.127 0-.225-.04-.254-.15l-.22-.768h-1.322l-.219.768c-.029.11-.127.15-.254.15-.202 0-.473-.127-.473-.311 0-.012.006-.035.012-.058l1.114-3.63c.051-.173.265-.254.478-.254s.433.08.485.254l1.114 3.63c.006.023.012.04.012.058 0 .179-.272.311-.473.311Zm2.989-3.543h-.843v3.306c0 .156-.19.237-.375.237s-.375-.081-.375-.237V9.25h-.848c-.15 0-.237-.157-.237-.34 0-.162.075-.336.237-.336h2.44c.162 0 .238.173.238.335 0 .18-.087.34-.237.34Z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
|
|
@ -1,27 +1,25 @@
|
||||||
---
|
---
|
||||||
import { events } from "@/lib/events";
|
import { events } from "@/lib/events";
|
||||||
import AboutRow from "@/components/AboutRow.astro";
|
|
||||||
import DDay from "./DDay.astro";
|
|
||||||
|
|
||||||
const sortedEvents = [...events].sort((a, b) => b.date.localeCompare(a.date));
|
const sortedEvents = [...events].sort((a, b) => b.date.localeCompare(a.date));
|
||||||
const years = [...new Set(sortedEvents.map((event) => event.date.slice(0, 4)))];
|
const years = [...new Set(sortedEvents.map((event) => event.date.slice(0, 4)))];
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
||||||
const eventsByYear = years.map((year) => ({
|
const eventsByYear = years.map((year) => ({
|
||||||
year,
|
year,
|
||||||
events: sortedEvents.filter((event) => event.date.startsWith(year)),
|
events: sortedEvents.filter((event) => event.date.startsWith(year)),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
||||||
const getShortDate = (date: string) => {
|
const getShortDate = (date: string) => {
|
||||||
const [, month, day] = date.split("-");
|
const [, month, day] = date.split("-");
|
||||||
return `${month}.${day}`;
|
return `${month}.${day}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
||||||
const daysUntilYearEnd = Math.ceil(
|
const daysUntilYearEnd = Math.ceil(
|
||||||
(new Date(currentYear + 1, 0, 1).getTime() - Date.now()) /
|
(new Date(currentYear + 1, 0, 1).getTime() - Date.now()) /
|
||||||
(1000 * 60 * 60 * 24),
|
(1000 * 60 * 60 * 24),
|
||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -33,101 +31,103 @@ const daysUntilYearEnd = Math.ceil(
|
||||||
>
|
>
|
||||||
<div class="grid min-h-full grid-cols-1 lg:grid-cols-2">
|
<div class="grid min-h-full grid-cols-1 lg:grid-cols-2">
|
||||||
<div
|
<div
|
||||||
class="relative z-10 px-6 py-12 sm:px-10 lg:px-[clamp(52px,5.208vw,100px)] lg:py-[clamp(58px,9.26vh,100px)] text-base"
|
class="relative z-10 px-6 py-12 sm:px-10 lg:px-[clamp(52px,5.208vw,100px)] lg:py-[clamp(58px,9.26vh,100px)]"
|
||||||
>
|
>
|
||||||
<p>
|
<div
|
||||||
안녕하세요, 정보보안과 소프트웨어 엔지니어링을 주로하고 있는
|
id="experience"
|
||||||
암냥입니다.
|
class="max-w-[654px] break-keep text-[24px] leading-[1.2] tracking-[-0.02em] lg:leading-none"
|
||||||
</p>
|
>
|
||||||
<div class="flex flex-row mt-4 gap-1">
|
<p id="about-title">
|
||||||
<b>생일 : </b><DDay targetDate="2010-11-08T03:00:00+09:00" />
|
저는 <b>{years.length}년간 {events.length}개</b>의
|
||||||
</div>
|
개성있는 조각들을 모아왔어요.
|
||||||
<a href="#contact" class="flex flex-row items-center underline">
|
</p>
|
||||||
<span>연락하기</span>
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-4 text-foreground/40"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M7 17 17 7"></path>
|
|
||||||
<path d="M7 7h10v10"></path>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
<p>저는...</p>
|
|
||||||
<section class="mt-[0.75em]">
|
|
||||||
<div class="mt-[0.5em] flex flex-col gap-[0.34em]">
|
|
||||||
<AboutRow year="2026" connector="wave">
|
|
||||||
<img
|
|
||||||
class="h-[1em] w-[1em] shrink-0"
|
|
||||||
src="/about/layer7-mark.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span>선린인터넷고등학교 정보보안 동아리</span>
|
|
||||||
<img
|
|
||||||
class="h-[1em] w-[1em] shrink-0 [filter:var(--mono-filter)]"
|
|
||||||
src="/about/layer7-wordmark.svg"
|
|
||||||
alt=""
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span>Layer7</span>
|
|
||||||
</AboutRow>
|
|
||||||
|
|
||||||
<AboutRow year="2026" connector="wave">
|
<section class="mt-[0.75em]" aria-label="Experience">
|
||||||
<img
|
{
|
||||||
class="h-[1em] w-[1em] shrink-0"
|
eventsByYear.map(({ year, events: yearEvents }) => (
|
||||||
src="/about/sunrin.svg"
|
<section
|
||||||
alt=""
|
class="py-[0.55em]"
|
||||||
aria-hidden="true"
|
aria-labelledby={`experience-${year}`}
|
||||||
/>
|
>
|
||||||
<span>선린인터넷고등학교 정보보호과</span>
|
<div class="flex items-center gap-3">
|
||||||
</AboutRow>
|
<h3
|
||||||
<AboutRow year="2026" connector="line">
|
id={`experience-${year}`}
|
||||||
<img
|
class="text-[0.62em] font-bold leading-none"
|
||||||
class="h-[1em] w-[1em] shrink-0 object-contain"
|
>
|
||||||
src="/about/adofai.svg"
|
{year}
|
||||||
alt=""
|
</h3>
|
||||||
aria-hidden="true"
|
<span class="text-[0.43em] font-medium tracking-[0.04em] text-foreground/45">
|
||||||
/>
|
{yearEvents.length}{" "}
|
||||||
<span>ADOFAI.gg Development Team</span>
|
{yearEvents.length === 1
|
||||||
</AboutRow>
|
? "EVENT"
|
||||||
<AboutRow year="2025" connector="line">
|
: "EVENTS"}
|
||||||
<img
|
</span>
|
||||||
class="h-[1em] w-[1em] shrink-0 object-contain"
|
<div class="h-px flex-1 bg-foreground/10" />
|
||||||
src="/about/whitehat.avif"
|
</div>
|
||||||
alt=""
|
|
||||||
aria-hidden="true"
|
<ol class="mt-[0.35em]">
|
||||||
/>
|
{yearEvents.map((event) => (
|
||||||
<span>화이트햇스쿨 3기 이수</span>
|
<li class="grid min-w-0 grid-cols-[3.5em_minmax(0,1fr)] gap-x-[0.6em] border-b border-foreground/[0.07] py-[0.48em] last:border-b-0">
|
||||||
</AboutRow>
|
<time
|
||||||
</div>
|
class="pt-[0.08em] text-[0.55em] font-medium tabular-nums tracking-[0.02em] text-foreground/50"
|
||||||
</section>
|
datetime={event.date}
|
||||||
<div class="flex flex-row">
|
>
|
||||||
<a
|
{getShortDate(event.date)}
|
||||||
href="#experience"
|
</time>
|
||||||
class="flex flex-row items-center underline"
|
<div class="min-w-0">
|
||||||
><span>등등...</span>
|
<div class="mb-[0.12em] flex items-center gap-[0.45em]">
|
||||||
<svg
|
<span class="text-[0.43em] font-semibold tracking-[0.04em] text-foreground/50">
|
||||||
aria-hidden="true"
|
{event.category}
|
||||||
class="h-4 text-foreground/40"
|
</span>
|
||||||
viewBox="0 0 24 24"
|
{event.link && (
|
||||||
fill="none"
|
<svg
|
||||||
stroke="currentColor"
|
aria-hidden="true"
|
||||||
stroke-width="2"
|
class="h-[0.52em] w-[0.52em] text-foreground/40"
|
||||||
stroke-linecap="round"
|
viewBox="0 0 24 24"
|
||||||
stroke-linejoin="round"
|
fill="none"
|
||||||
>
|
stroke="currentColor"
|
||||||
<path d="M7 17 17 7"></path>
|
stroke-width="2"
|
||||||
<path d="M7 7h10v10"></path>
|
stroke-linecap="round"
|
||||||
</svg></a
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<p>을 이루어냈어요.</p>
|
<path d="M7 17 17 7" />
|
||||||
|
<path d="M7 7h10v10" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{event.link ? (
|
||||||
|
<a
|
||||||
|
href={event.link}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-[0.62em] leading-[1.35] underline decoration-foreground/20 underline-offset-2 transition-colors hover:decoration-foreground focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground"
|
||||||
|
>
|
||||||
|
{event.description}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<p class="text-[0.62em] leading-[1.35]">
|
||||||
|
{event.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ol>
|
||||||
|
{year === currentYear.toString() && (
|
||||||
|
<div class="my-8 flex items-center gap-4">
|
||||||
|
<div class="flex-1 h-px bg-foreground/10" />
|
||||||
|
<span class="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
|
✨ | D-{daysUntilYearEnd}동안
|
||||||
|
만들어나갈 멋진 것들을
|
||||||
|
기대해주세요.
|
||||||
|
</span>
|
||||||
|
<div class="flex-1 h-px bg-foreground/10" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -135,13 +135,13 @@ const daysUntilYearEnd = Math.ceil(
|
||||||
class="relative order-first h-[min(150vw,720px)] min-h-[600px] overflow-hidden lg:sticky lg:top-0 lg:order-none lg:h-[calc(100svh-64px)] lg:min-h-0 lg:self-start"
|
class="relative order-first h-[min(150vw,720px)] min-h-[600px] overflow-hidden lg:sticky lg:top-0 lg:order-none lg:h-[calc(100svh-64px)] lg:min-h-0 lg:self-start"
|
||||||
aria-label="활동 사진"
|
aria-label="활동 사진"
|
||||||
>
|
>
|
||||||
<!-- <img
|
<img
|
||||||
class="absolute inset-0 h-full w-full select-none pointer-events-none object-contain"
|
class="absolute inset-0 h-full w-full select-none pointer-events-none object-contain"
|
||||||
src="/side.avif"
|
src="/side.avif"
|
||||||
alt="활동 사진 콜라주"
|
alt="활동 사진 콜라주"
|
||||||
width="3840"
|
width="3840"
|
||||||
height="4320"
|
height="4320"
|
||||||
/> -->
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,583 +0,0 @@
|
||||||
---
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const groups = [
|
|
||||||
{
|
|
||||||
id: "direct",
|
|
||||||
title: "연락처",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: "개인 이메일",
|
|
||||||
value: "me@imnya.ng",
|
|
||||||
href: "mailto:me@imnya.ng",
|
|
||||||
icon: "mail",
|
|
||||||
tone: "pink",
|
|
||||||
email: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "dazzle.st 이메일",
|
|
||||||
value: "imnyang@dazzle.st",
|
|
||||||
href: "mailto:imnyang@dazzle.st",
|
|
||||||
icon: "mail",
|
|
||||||
tone: "green",
|
|
||||||
email: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Discord",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://discord.com/users/909353223901569035",
|
|
||||||
icon: "discord",
|
|
||||||
tone: "purple",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "social",
|
|
||||||
title: "소셜",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: "X (Twitter)",
|
|
||||||
value: "@imnya_ng",
|
|
||||||
href: "https://twitter.com/imnya_ng",
|
|
||||||
icon: "x",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Instagram",
|
|
||||||
value: "@imnya.ng",
|
|
||||||
href: "https://instagram.com/imnya.ng",
|
|
||||||
icon: "instagram",
|
|
||||||
tone: "pink",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "YouTube",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://youtube.com/@imnyang",
|
|
||||||
icon: "youtube",
|
|
||||||
tone: "red",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "work",
|
|
||||||
title: "코드와 글",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: "GitHub",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://github.com/imnyang",
|
|
||||||
icon: "github",
|
|
||||||
tone: "neutral",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "git.mizuki.guru",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://git.mizuki.guru/imnyang",
|
|
||||||
icon: "mizuki",
|
|
||||||
tone: "orange",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Blog",
|
|
||||||
value: "blog.imnya.ng",
|
|
||||||
href: "https://blog.imnya.ng",
|
|
||||||
icon: "blog",
|
|
||||||
tone: "green",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "game",
|
|
||||||
title: "Game",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: "Steam",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://steamcommunity.com/id/imnyang/",
|
|
||||||
icon: "steam",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "maimai",
|
|
||||||
value: "@imnya.ng",
|
|
||||||
href: "https://mai.sft.sh/imnyang",
|
|
||||||
icon: "maimai",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Project Sekai KR",
|
|
||||||
value: "7354623251148069648",
|
|
||||||
href: "#",
|
|
||||||
icon: "project-sekai",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Project Sekai JP",
|
|
||||||
value: "729310364018688008",
|
|
||||||
href: "#",
|
|
||||||
icon: "project-sekai",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "VRChat",
|
|
||||||
value: "@imnyang",
|
|
||||||
href: "https://vrchat.com/home/user/usr_d53ac697-182d-40d0-9fd8-1a7442b0ebfd",
|
|
||||||
icon: "vrchat",
|
|
||||||
tone: "blue",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const imageIcons: Record<string, { src: string; monochrome: boolean }> = {
|
|
||||||
github: { src: "/icon/github.svg", monochrome: true },
|
|
||||||
discord: { src: "/icon/discord.svg", monochrome: true },
|
|
||||||
mizuki: { src: "/icon/mizuki.svg", monochrome: false },
|
|
||||||
// Source: https://github.com/simple-icons/simple-icons/blob/develop/icons/steam.svg
|
|
||||||
steam: { src: "/icon/steam.svg", monochrome: true },
|
|
||||||
maimai: { src: "/icon/maimai.webp", monochrome: false },
|
|
||||||
vrchat: { src: "/icon/vrchat.svg", monochrome: true },
|
|
||||||
// Source: https://pjsekai.sega.jp/assets/data/webp/icon/icon_app.png.webp
|
|
||||||
"project-sekai": { src: "/icon/project-sekai.webp", monochrome: false },
|
|
||||||
};
|
|
||||||
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const paths: Record<string, string> = {
|
|
||||||
mail: "M22 12a10 10 0 1 0-4 8 M16 8v5a3 3 0 0 0 6 0v-1 M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0",
|
|
||||||
x: "M4 3h4l12 18h-4L4 3Z M20 3l-7 8 M4 21l7-8",
|
|
||||||
instagram:
|
|
||||||
"M7 3h10a4 4 0 0 1 4 4v10a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V7a4 4 0 0 1 4-4Z M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0 M17.5 6.5h.01",
|
|
||||||
youtube:
|
|
||||||
"M7 5h10a5 5 0 0 1 5 5v4a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5v-4a5 5 0 0 1 5-5Z M10 9l5 3-5 3V9Z",
|
|
||||||
blog: "M5 3h11l3 3v15H5V3Z M15 3v4h4 M9 11h6 M9 15h6",
|
|
||||||
};
|
|
||||||
---
|
|
||||||
|
|
||||||
<section id="contact" class="contact-section" aria-labelledby="contact-title">
|
|
||||||
<div class="contact-content">
|
|
||||||
<header class="contact-heading">
|
|
||||||
<h2 id="contact-title">Contact<span aria-hidden="true">.</span></h2>
|
|
||||||
<p class="contact-description">
|
|
||||||
새로운 이야기와 재미있는 만남은 언제나 환영해요.
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="contact-groups">
|
|
||||||
{
|
|
||||||
groups.map((group) => (
|
|
||||||
<section aria-labelledby={`contact-${group.id}`}>
|
|
||||||
<h3
|
|
||||||
id={`contact-${group.id}`}
|
|
||||||
class="contact-group-title"
|
|
||||||
>
|
|
||||||
{group.title}
|
|
||||||
</h3>
|
|
||||||
<ul class="contact-list">
|
|
||||||
{group.items.map((item) => (
|
|
||||||
<li class="contact-row">
|
|
||||||
<a
|
|
||||||
class="contact-link"
|
|
||||||
href={item.href}
|
|
||||||
target={
|
|
||||||
"email" in item
|
|
||||||
? undefined
|
|
||||||
: "_blank"
|
|
||||||
}
|
|
||||||
rel={
|
|
||||||
"email" in item
|
|
||||||
? undefined
|
|
||||||
: "noopener noreferrer"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class={`contact-icon tone-${item.tone}`}
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
{imageIcons[item.icon] ? (
|
|
||||||
<img
|
|
||||||
src={
|
|
||||||
imageIcons[item.icon]
|
|
||||||
.src
|
|
||||||
}
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
alt=""
|
|
||||||
class:list={{
|
|
||||||
"mono-icon":
|
|
||||||
imageIcons[
|
|
||||||
item.icon
|
|
||||||
].monochrome,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.7"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d={paths[item.icon]}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
<span class="contact-name">
|
|
||||||
{item.name}
|
|
||||||
</span>
|
|
||||||
<span class="contact-value">
|
|
||||||
{item.value}
|
|
||||||
</span>
|
|
||||||
{!("email" in item) && (
|
|
||||||
<svg
|
|
||||||
class="contact-arrow"
|
|
||||||
aria-hidden="true"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M7 17 17 7M7 7h10v10" />
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
{"email" in item && (
|
|
||||||
<button
|
|
||||||
class="contact-copy"
|
|
||||||
type="button"
|
|
||||||
data-contact-email={item.value}
|
|
||||||
aria-label={`${item.value} 복사`}
|
|
||||||
title="이메일 주소 복사"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="copy-symbol"
|
|
||||||
aria-hidden="true"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.5"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<rect
|
|
||||||
x="8"
|
|
||||||
y="8"
|
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
rx="2"
|
|
||||||
/>
|
|
||||||
<path d="M16 8V4a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h4" />
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
class="copied-symbol"
|
|
||||||
aria-hidden="true"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="1.8"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="m5 12 4 4L19 6" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="contact-footer">
|
|
||||||
<p>여기까지 와주셔서 고마워요.</p>
|
|
||||||
<a href="#home">맨 위로 <span aria-hidden="true">↑</span></a>
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
class="contact-status"
|
|
||||||
role="status"
|
|
||||||
aria-live="polite"
|
|
||||||
aria-atomic="true"
|
|
||||||
>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.contact-section {
|
|
||||||
--contact-top-spacing: clamp(48px, 7vw, 88px);
|
|
||||||
min-height: 100%;
|
|
||||||
/* A viewport-spanning snap area permits free scrolling through the links. */
|
|
||||||
scroll-snap-align: start;
|
|
||||||
padding: var(--contact-top-spacing) clamp(24px, 5.208vw, 100px) 32px;
|
|
||||||
}
|
|
||||||
.contact-content {
|
|
||||||
max-width: 1080px;
|
|
||||||
margin-inline: auto;
|
|
||||||
}
|
|
||||||
.contact-heading {
|
|
||||||
display: flow-root;
|
|
||||||
/* Keep the snap target smaller than the viewport so entry stops at the title. */
|
|
||||||
scroll-snap-align: start;
|
|
||||||
scroll-snap-stop: always;
|
|
||||||
scroll-margin-top: var(--contact-top-spacing);
|
|
||||||
padding-bottom: 32px;
|
|
||||||
border-bottom: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
.contact-eyebrow {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.16em;
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
margin-top: 8px;
|
|
||||||
font-size: clamp(36px, 5vw, 52px);
|
|
||||||
font-weight: 750;
|
|
||||||
letter-spacing: -0.045em;
|
|
||||||
line-height: 1.15;
|
|
||||||
}
|
|
||||||
h2 span {
|
|
||||||
color: #dc6291;
|
|
||||||
}
|
|
||||||
.contact-description {
|
|
||||||
margin-top: 14px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: color-mix(in srgb, var(--foreground) 65%, var(--background));
|
|
||||||
word-break: keep-all;
|
|
||||||
}
|
|
||||||
.contact-groups {
|
|
||||||
display: grid;
|
|
||||||
gap: 28px;
|
|
||||||
margin-top: 28px;
|
|
||||||
}
|
|
||||||
.contact-group-title {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.contact-list {
|
|
||||||
display: grid;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.contact-row {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 9px;
|
|
||||||
transition:
|
|
||||||
background-color 160ms,
|
|
||||||
border-color 160ms;
|
|
||||||
}
|
|
||||||
.contact-row:hover,
|
|
||||||
.contact-row:focus-within {
|
|
||||||
background: var(--muted);
|
|
||||||
border-color: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--foreground) 22%,
|
|
||||||
var(--background)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
.contact-link {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 22px auto minmax(0, 1fr) 18px;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 58px;
|
|
||||||
padding: 12px 18px;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.contact-row:has(.contact-copy) .contact-link {
|
|
||||||
grid-template-columns: 22px auto minmax(0, 1fr);
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
.contact-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.contact-icon svg,
|
|
||||||
.contact-icon img {
|
|
||||||
width: 21px;
|
|
||||||
height: 21px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
.mono-icon {
|
|
||||||
filter: var(--mono-filter);
|
|
||||||
}
|
|
||||||
.tone-pink {
|
|
||||||
color: #ce5484;
|
|
||||||
}
|
|
||||||
.tone-green {
|
|
||||||
color: #46916c;
|
|
||||||
}
|
|
||||||
.tone-purple {
|
|
||||||
color: #8279e2;
|
|
||||||
}
|
|
||||||
.tone-blue {
|
|
||||||
color: #569fc9;
|
|
||||||
}
|
|
||||||
.tone-red {
|
|
||||||
color: #d96570;
|
|
||||||
}
|
|
||||||
.tone-orange {
|
|
||||||
color: #c58055;
|
|
||||||
}
|
|
||||||
.contact-name {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 650;
|
|
||||||
}
|
|
||||||
.contact-value {
|
|
||||||
text-align: right;
|
|
||||||
font-size: 14px;
|
|
||||||
color: color-mix(in srgb, var(--foreground) 65%, var(--background));
|
|
||||||
overflow-wrap: anywhere;
|
|
||||||
}
|
|
||||||
.contact-arrow {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
transition:
|
|
||||||
transform 160ms,
|
|
||||||
color 160ms;
|
|
||||||
}
|
|
||||||
.contact-link:hover .contact-arrow {
|
|
||||||
transform: translate(2px, -2px);
|
|
||||||
color: var(--foreground);
|
|
||||||
}
|
|
||||||
.contact-copy {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
align-self: center;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
margin: 0 5px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.contact-copy:hover {
|
|
||||||
color: var(--foreground);
|
|
||||||
background: color-mix(in srgb, var(--foreground) 7%, transparent);
|
|
||||||
}
|
|
||||||
.contact-copy svg {
|
|
||||||
width: 17px;
|
|
||||||
height: 17px;
|
|
||||||
}
|
|
||||||
.copied-symbol,
|
|
||||||
.contact-copy[data-copied] .copy-symbol {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.contact-copy[data-copied] .copied-symbol {
|
|
||||||
display: block;
|
|
||||||
color: #46916c;
|
|
||||||
}
|
|
||||||
.contact-link:focus-visible,
|
|
||||||
.contact-copy:focus-visible,
|
|
||||||
.contact-footer a:focus-visible {
|
|
||||||
outline: 2px solid var(--ring);
|
|
||||||
outline-offset: 3px;
|
|
||||||
}
|
|
||||||
.contact-footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 16px;
|
|
||||||
margin-top: 40px;
|
|
||||||
padding-top: 20px;
|
|
||||||
border-top: 1px solid var(--border);
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted-foreground);
|
|
||||||
}
|
|
||||||
.contact-footer a {
|
|
||||||
flex-shrink: 0;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
.contact-footer a:hover {
|
|
||||||
color: var(--foreground);
|
|
||||||
}
|
|
||||||
.contact-footer span {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||
.contact-status {
|
|
||||||
min-height: 20px;
|
|
||||||
margin-top: 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--foreground);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
@media (max-width: 540px) {
|
|
||||||
.contact-link,
|
|
||||||
.contact-row:has(.contact-copy) .contact-link {
|
|
||||||
grid-template-columns: 22px minmax(0, 1fr) 18px;
|
|
||||||
gap: 4px 12px;
|
|
||||||
padding: 14px;
|
|
||||||
}
|
|
||||||
.contact-row:has(.contact-copy) .contact-link {
|
|
||||||
grid-template-columns: 22px minmax(0, 1fr);
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
.contact-icon {
|
|
||||||
grid-row: 1 / 3;
|
|
||||||
}
|
|
||||||
.contact-name {
|
|
||||||
grid-column: 2;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.contact-value {
|
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 2;
|
|
||||||
text-align: left;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
.contact-arrow {
|
|
||||||
grid-column: 3;
|
|
||||||
grid-row: 1 / 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.contact-row,
|
|
||||||
.contact-arrow {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
.contact-link:hover .contact-arrow {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
const contactStatus =
|
|
||||||
document.querySelector<HTMLElement>(".contact-status");
|
|
||||||
document
|
|
||||||
.querySelectorAll<HTMLButtonElement>("[data-contact-email]")
|
|
||||||
.forEach((button) => {
|
|
||||||
let resetTimer: ReturnType<typeof setTimeout>;
|
|
||||||
button.addEventListener("click", async () => {
|
|
||||||
const email = button.dataset.contactEmail;
|
|
||||||
if (!email) return;
|
|
||||||
try {
|
|
||||||
await navigator.clipboard.writeText(email);
|
|
||||||
clearTimeout(resetTimer);
|
|
||||||
button.dataset.copied = "true";
|
|
||||||
button.setAttribute("aria-label", `${email} 복사 완료`);
|
|
||||||
if (contactStatus)
|
|
||||||
contactStatus.textContent = `${email} 주소를 복사했어요.`;
|
|
||||||
resetTimer = setTimeout(() => {
|
|
||||||
delete button.dataset.copied;
|
|
||||||
button.setAttribute("aria-label", `${email} 복사`);
|
|
||||||
}, 2000);
|
|
||||||
} catch {
|
|
||||||
if (contactStatus)
|
|
||||||
contactStatus.textContent =
|
|
||||||
"복사하지 못했어요. 이메일 주소를 직접 선택해 복사해주세요.";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,31 +1,27 @@
|
||||||
---
|
---
|
||||||
export interface Props {
|
export interface Props {
|
||||||
targetDate: string;
|
targetDate: string;
|
||||||
label?: string;
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { targetDate, label } = Astro.props;
|
const { targetDate, label } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div class="flex flex-row items-start text-left text-base gap-2">
|
<div class="flex flex-col items-end text-right text-sm">
|
||||||
<span class="text-foreground"
|
<span id="dday-label">D-Day</span>
|
||||||
>{label && `${label} | `}{
|
<span class="text-muted-foreground">{label} | {new Date(targetDate).toDateString()}</span>
|
||||||
new Date(targetDate).toLocaleDateString()
|
|
||||||
}</span
|
|
||||||
>
|
|
||||||
<span id="dday-label">D-Day</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script define:vars={{ targetDate }}>
|
<script define:vars={{ targetDate }}>
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const target = new Date(targetDate);
|
const target = new Date(targetDate);
|
||||||
const diffTime = target.getTime() - today.getTime();
|
const diffTime = target.getTime() - today.getTime();
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
let label = "D-Day";
|
let label = "D-Day";
|
||||||
if (diffDays > 0) label = `D-${diffDays}`;
|
if (diffDays > 0) label = `D-${diffDays}`;
|
||||||
else if (diffDays < 0) label = `D+${Math.abs(diffDays)}`;
|
else if (diffDays < 0) label = `D+${Math.abs(diffDays)}`;
|
||||||
|
|
||||||
const el = document.getElementById("dday-label");
|
const el = document.getElementById('dday-label');
|
||||||
if (el) el.textContent = label;
|
if (el) el.textContent = label;
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"];
|
||||||
class="mt-6 max-w-[32rem] text-base leading-relaxed text-foreground/65"
|
class="mt-6 max-w-[32rem] text-base leading-relaxed text-foreground/65"
|
||||||
>
|
>
|
||||||
불과 얼음의 춤(A Dance of Fire and Ice)의 이펙트 제작과
|
불과 얼음의 춤(A Dance of Fire and Ice)의 이펙트 제작과
|
||||||
플레이를<br /> 함께 다루는 대회, Effect Playing Contest. 2025와
|
플레이를 함께 다루는 대회, Effect Playing Contest. 2025와
|
||||||
Finale 시즌에 참여하며 방송부터 ARG, 개발까지 함께했어요.
|
Finale 시즌에 참여하며 방송부터 ARG, 개발까지 함께했어요.
|
||||||
</p>
|
</p>
|
||||||
<ul
|
<ul
|
||||||
|
|
@ -125,12 +125,12 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"];
|
||||||
<button
|
<button
|
||||||
class="group relative block h-full w-full cursor-pointer border-0 bg-transparent p-0 text-white focus-visible:outline-2 focus-visible:outline-offset-[-5px] focus-visible:outline-[#ff92ad]"
|
class="group relative block h-full w-full cursor-pointer border-0 bg-transparent p-0 text-white focus-visible:outline-2 focus-visible:outline-offset-[-5px] focus-visible:outline-[#ff92ad]"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="EPC Finale 후기 영상 재생"
|
aria-label="EPC Finale 결승 방송을 2시간 19분 26초부터 재생"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="block h-full w-full object-cover"
|
class="block h-full w-full object-cover"
|
||||||
src="/projects/epc-finale.avif"
|
src="/projects/epc-finale-broadcast.avif"
|
||||||
alt="썸네일"
|
alt="EPC Finale의 두 선수 경기 화면과 실시간 판정을 보여주는 방송 화면"
|
||||||
width="655"
|
width="655"
|
||||||
height="387"
|
height="387"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
|
@ -142,8 +142,11 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"];
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="absolute inset-x-0 bottom-0 flex justify-between gap-2 bg-gradient-to-b from-transparent to-[#000c] px-4 pt-[30px] pb-3 text-left text-[clamp(10px,1vw,14px)] font-semibold"
|
class="absolute inset-x-0 bottom-0 flex justify-between gap-2 bg-gradient-to-b from-transparent to-[#000c] px-4 pt-[30px] pb-3 text-left text-[clamp(10px,1vw,14px)] font-semibold"
|
||||||
>후기 영상
|
>Finale 결승 방송 <span
|
||||||
</span>
|
class="font-normal opacity-80"
|
||||||
|
>02:19:26부터 재생</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
|
|
@ -175,7 +178,7 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"];
|
||||||
decoding="async"
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
class="absolute bottom-[-10%] left-[-6%] z-[3] pointer-events-none h-auto w-[13%] aspect-[94/131] rotate-90"
|
class="absolute bottom-0 left-[-6%] z-[3] pointer-events-none h-auto w-[13%] aspect-[94/131] rotate-90"
|
||||||
src="/projects/tape.svg"
|
src="/projects/tape.svg"
|
||||||
alt=""
|
alt=""
|
||||||
width="94"
|
width="94"
|
||||||
|
|
@ -203,8 +206,9 @@ const roles = ["Broadcast Manager", "ARG Creator", "Developer"];
|
||||||
if (!videoFrame) return;
|
if (!videoFrame) return;
|
||||||
const player = document.createElement("iframe");
|
const player = document.createElement("iframe");
|
||||||
player.src =
|
player.src =
|
||||||
"https://www.youtube-nocookie.com/embed/3NY8vgxvKaU?autoplay=1&playsinline=1";
|
"https://www.youtube-nocookie.com/embed/5HDW7u3xt6Y?start=8366&autoplay=1&playsinline=1";
|
||||||
player.title = "EPC : Finale 방송화면에 이런 비밀이???";
|
player.title =
|
||||||
|
"EPC Finale Playing Section Tournament Finals — 2:19:26부터 재생";
|
||||||
player.allow =
|
player.allow =
|
||||||
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
|
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share";
|
||||||
player.allowFullscreen = true;
|
player.allowFullscreen = true;
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
---
|
|
||||||
import { events } from "@/lib/events";
|
|
||||||
|
|
||||||
const sortedEvents = [...events].sort((a, b) => b.date.localeCompare(a.date));
|
|
||||||
const years = [...new Set(sortedEvents.map((event) => event.date.slice(0, 4)))];
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const eventsByYear = years.map((year) => ({
|
|
||||||
year,
|
|
||||||
events: sortedEvents.filter((event) => event.date.startsWith(year)),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const getShortDate = (date: string) => {
|
|
||||||
const [, month, day] = date.split("-");
|
|
||||||
return `${month}.${day}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentYear = new Date().getFullYear();
|
|
||||||
// biome-ignore lint/correctness/noUnusedVariables: Used in the Astro template below.
|
|
||||||
const daysUntilYearEnd = Math.ceil(
|
|
||||||
(new Date(currentYear + 1, 0, 1).getTime() - Date.now()) /
|
|
||||||
(1000 * 60 * 60 * 24),
|
|
||||||
);
|
|
||||||
---
|
|
||||||
|
|
||||||
<section
|
|
||||||
id="experience"
|
|
||||||
data-node-id="4:104"
|
|
||||||
class="min-h-full snap-start snap-always bg-background text-foreground"
|
|
||||||
aria-labelledby="experience-title"
|
|
||||||
>
|
|
||||||
<div class="grid min-h-full grid-cols-1 lg:grid-cols-2">
|
|
||||||
<div
|
|
||||||
class="relative z-10 px-6 py-12 sm:px-10 lg:px-[clamp(52px,5.208vw,100px)] lg:py-[clamp(58px,9.26vh,100px)]"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="max-w-[654px] break-keep text-[24px] leading-[1.2] tracking-[-0.02em] lg:leading-none"
|
|
||||||
>
|
|
||||||
<p id="experience-title">
|
|
||||||
저는 <b>{years.length}년간 {events.length}개</b>의 개성있는
|
|
||||||
조각들을 모아왔어요.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<section class="mt-[0.75em]" aria-label="Experience">
|
|
||||||
{
|
|
||||||
eventsByYear.map(({ year, events: yearEvents }) => (
|
|
||||||
<section
|
|
||||||
class="py-[0.55em]"
|
|
||||||
aria-labelledby={`experience-${year}`}
|
|
||||||
>
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<h3
|
|
||||||
id={`experience-${year}`}
|
|
||||||
class="text-[0.62em] font-bold leading-none"
|
|
||||||
>
|
|
||||||
{year}
|
|
||||||
</h3>
|
|
||||||
<span class="text-[0.43em] font-medium tracking-[0.04em] text-foreground/45">
|
|
||||||
{yearEvents.length}{" "}
|
|
||||||
{yearEvents.length === 1
|
|
||||||
? "EVENT"
|
|
||||||
: "EVENTS"}
|
|
||||||
</span>
|
|
||||||
<div class="h-px flex-1 bg-foreground/10" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ol class="mt-[0.35em]">
|
|
||||||
{yearEvents.map((event) => (
|
|
||||||
<li class="grid min-w-0 grid-cols-[3.5em_minmax(0,1fr)] gap-x-[0.6em] border-b border-foreground/[0.07] py-[0.48em] last:border-b-0">
|
|
||||||
<time
|
|
||||||
class="pt-[0.08em] text-[0.55em] font-medium tabular-nums tracking-[0.02em] text-foreground/50"
|
|
||||||
datetime={event.date}
|
|
||||||
>
|
|
||||||
{getShortDate(event.date)}
|
|
||||||
</time>
|
|
||||||
<div class="min-w-0">
|
|
||||||
<div class="mb-[0.12em] flex items-center gap-[0.45em]">
|
|
||||||
<span class="text-[0.43em] font-semibold tracking-[0.04em] text-foreground/50">
|
|
||||||
{event.category}
|
|
||||||
</span>
|
|
||||||
{event.link && (
|
|
||||||
<svg
|
|
||||||
aria-hidden="true"
|
|
||||||
class="h-[0.52em] w-[0.52em] text-foreground/40"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
>
|
|
||||||
<path d="M7 17 17 7" />
|
|
||||||
<path d="M7 7h10v10" />
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{event.link ? (
|
|
||||||
<a
|
|
||||||
href={event.link}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="text-[0.62em] leading-[1.35] underline decoration-foreground/20 underline-offset-2 transition-colors hover:decoration-foreground focus-visible:rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-foreground"
|
|
||||||
>
|
|
||||||
{event.description}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<p class="text-[0.62em] leading-[1.35]">
|
|
||||||
{event.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ol>
|
|
||||||
{year === currentYear.toString() && (
|
|
||||||
<div class="my-8 flex items-center gap-4">
|
|
||||||
<div class="flex-1 h-px bg-foreground/10" />
|
|
||||||
<span class="text-xs text-muted-foreground whitespace-nowrap">
|
|
||||||
✨ | D-{daysUntilYearEnd}동안
|
|
||||||
만들어나갈 멋진 것들을 기대해주세요.
|
|
||||||
</span>
|
|
||||||
<div class="flex-1 h-px bg-foreground/10" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="relative order-first h-[min(150vw,720px)] min-h-[600px] overflow-hidden lg:sticky lg:top-0 lg:order-none lg:h-[calc(100svh-64px)] lg:min-h-0 lg:self-start"
|
|
||||||
aria-label="활동 사진"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
class="absolute inset-0 h-full w-full select-none pointer-events-none object-contain"
|
|
||||||
src="/side.avif"
|
|
||||||
alt="활동 사진 콜라주"
|
|
||||||
width="3840"
|
|
||||||
height="4320"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
@ -22,11 +22,11 @@
|
||||||
<span class="block" data-node-id="43:16">
|
<span class="block" data-node-id="43:16">
|
||||||
<span class="font-bold">현실</span><span
|
<span class="font-bold">현실</span><span
|
||||||
class="font-normal text-foreground/50"
|
class="font-normal text-foreground/50"
|
||||||
>{"로 만들어나가는 "}
|
>로 만들어나가는</span
|
||||||
</span><br class="hidden max-md:block md:hidden" /><span
|
><br class="hidden max-md:block md:hidden" /><span
|
||||||
class="font-bold"
|
class="font-bold"
|
||||||
>
|
>
|
||||||
암냥</span
|
남현석</span
|
||||||
><span class="font-normal text-foreground/50">입니다.</span>
|
><span class="font-normal text-foreground/50">입니다.</span>
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
|
||||||
97
src/components/Projects.astro
Normal file
97
src/components/Projects.astro
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
---
|
||||||
|
const projects = [
|
||||||
|
{
|
||||||
|
name: 'EPC : Finale Broadcast Manager & Developer',
|
||||||
|
url: 'https://www.youtube.com/playlist?list=PLZeYZotn5_IPFvDUWdEUu9xOBwOkdZ0F0',
|
||||||
|
desc: 'ADOFAI is web-scale',
|
||||||
|
detail: '달성이 주관하고 ADOFAI.gg가 공동 주최하는 Effect Playing Contest : Finale의 방송 화면 및 ARG를 담당하였습니다.',
|
||||||
|
tags: ['React', 'ElysiaJS'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'EPC 2025 Broadcast Manager',
|
||||||
|
url: 'https://www.youtube.com/playlist?list=PLZeYZotn5_IOJDek6e35NKzUtJm09yxZD',
|
||||||
|
desc: 'ADOFAI is web-scale',
|
||||||
|
detail: '달성이 주관하고 ADOFAI.gg가 공동 주최하는 Effect Playing Contest 2025 방송 화면의 대부분의 기능을 개발하였습니다.',
|
||||||
|
tags: ['React', 'ElysiaJS'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'newsletter',
|
||||||
|
url: 'https://github.com/imnyang/newsletter',
|
||||||
|
desc: 'For Memos',
|
||||||
|
detail: '그저 이메일이 오면 Discord 웹훅으로 포워딩합니다.',
|
||||||
|
tags: ['Rust', 'IMAP']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'memos-rss',
|
||||||
|
url: 'https://github.com/imnyang/memos-rss',
|
||||||
|
desc: 'For Memos',
|
||||||
|
detail: 'Discord 포럼 채널에 RSS 피드에 올라온 내용을 포워딩합니다.',
|
||||||
|
tags: ['Rust', 'Discord Bot', 'RSS']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'today.isangjeong',
|
||||||
|
url: 'https://instagram.com/today.isangjeong',
|
||||||
|
desc: 'Instagram Bot',
|
||||||
|
detail: '매일 학교의 급식 메뉴를 자동으로 업로드하는 인스타그램 봇입니다.',
|
||||||
|
tags: ['TypeScript', 'Instagram', '@napi-rs/canvas']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class="break-keep break-words w-full">
|
||||||
|
<div class="space-y-8" id="projects-list">
|
||||||
|
{projects.map((project, idx) => (
|
||||||
|
<div class={`space-y-2 project-item ${idx >= 3 ? 'hidden' : ''}`}>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="flex justify-between items-center gap-y-2 gap-x-4">
|
||||||
|
{project.url ? (
|
||||||
|
<a href={project.url} target="_blank" rel="noopener noreferrer" class="text-lg text-nowrap items-center gap-x-2 flex flex-row">
|
||||||
|
{project.name}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-arrow-out-up-right"><path d="M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6"/><path d="m21 3-9 9"/><path d="M15 3h6v6"/></svg>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span class="text-lg">{project.name}</span>
|
||||||
|
)}
|
||||||
|
<span class="text-sm text-muted-foreground text-nowrap">{project.desc}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-muted-foreground">{project.detail}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 *:bg-muted *:px-2 *:py-0.5 *:rounded *:text-xs *:font-medium *:text-muted-foreground">
|
||||||
|
{project.tags.map((tag) => (
|
||||||
|
<span>{tag}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{projects.length > 3 && (
|
||||||
|
<button
|
||||||
|
id="load-more-btn"
|
||||||
|
class="mt-6 bg-muted text-muted-foreground rounded font-medium text-sm hover:bg-muted/80 px-4 py-2 transition-colors cursor-pointer"
|
||||||
|
>
|
||||||
|
더 불러오기
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let visibleCount = 3;
|
||||||
|
const btn = document.getElementById('load-more-btn');
|
||||||
|
const items = document.querySelectorAll('.project-item');
|
||||||
|
|
||||||
|
if (btn) {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
visibleCount += 3;
|
||||||
|
items.forEach((item, idx) => {
|
||||||
|
if (idx < visibleCount) {
|
||||||
|
item.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (visibleCount >= items.length) {
|
||||||
|
btn.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
135
src/components/Timeline.astro
Normal file
135
src/components/Timeline.astro
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
---
|
||||||
|
import { events } from "@/lib/events";
|
||||||
|
|
||||||
|
const years = [
|
||||||
|
...new Set(events.map((e) => new Date(e.date).getFullYear())),
|
||||||
|
].sort((a, b) => b - a);
|
||||||
|
|
||||||
|
const eventsByYear: Record<number, typeof events> = {};
|
||||||
|
for (const year of years) {
|
||||||
|
eventsByYear[year] = events.filter(
|
||||||
|
(e) => new Date(e.date).getFullYear() === year,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const daysUntilYearEnd = Math.ceil(
|
||||||
|
(new Date(currentYear + 1, 0, 1).getTime() - new Date().getTime()) /
|
||||||
|
(1000 * 60 * 60 * 24),
|
||||||
|
);
|
||||||
|
---
|
||||||
|
|
||||||
|
<div
|
||||||
|
id="timeline"
|
||||||
|
class="w-full flex flex-col items-center justify-center mt-8"
|
||||||
|
>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-col md:flex-row gap-4 h-full">
|
||||||
|
<!-- Left: Year buttons -->
|
||||||
|
<div
|
||||||
|
class="w-full md:w-24 flex flex-row md:flex-col gap-2 overflow-y-auto pr-2"
|
||||||
|
>
|
||||||
|
{
|
||||||
|
years.map((year, i) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-year={year}
|
||||||
|
class:list={[
|
||||||
|
"year-btn font-semibold px-4 py-2 rounded-lg text-sm transition-colors cursor-pointer",
|
||||||
|
i === 0
|
||||||
|
? "bg-foreground text-background"
|
||||||
|
: "border border-border bg-background text-foreground hover:bg-muted",
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{year}
|
||||||
|
</button>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right: Events -->
|
||||||
|
<div class="flex-1 overflow-y-auto pr-2">
|
||||||
|
{
|
||||||
|
years.map((year, i) => (
|
||||||
|
<div
|
||||||
|
data-events-year={year}
|
||||||
|
class:list={["year-events space-y-2", i !== 0 && "hidden"]}
|
||||||
|
>
|
||||||
|
{eventsByYear[year].map((event) => (
|
||||||
|
<div class="rounded-lg border bg-background px-4 py-3">
|
||||||
|
<div class="flex flex-row gap-2 mb-1">
|
||||||
|
<span class="text-md font-semibold">
|
||||||
|
{new Date(event.date).toLocaleDateString("en-US", {
|
||||||
|
month: "short",
|
||||||
|
day: "2-digit",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<span class="text-md font-semibold text-muted-foreground">
|
||||||
|
ㆍ{event.category}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{event.link ? (
|
||||||
|
<a href={event.link} class="">
|
||||||
|
{event.description}{" "}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="inline-block w-4 h-4 mb-1 ml-1"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||||
|
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<span>{event.description}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{year === currentYear && (
|
||||||
|
<div class="my-8 flex items-center gap-4">
|
||||||
|
<div class="flex-1 h-px bg-gradient-to-r from-transparent via-muted-foreground to-transparent" />
|
||||||
|
<span class="text-xs text-muted-foreground whitespace-nowrap">
|
||||||
|
✨ | D-{daysUntilYearEnd}동안 만들어나갈 멋진 것들을
|
||||||
|
기대해주세요.
|
||||||
|
</span>
|
||||||
|
<div class="flex-1 h-px bg-gradient-to-r from-transparent via-muted-foreground to-transparent" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const btns = document.querySelectorAll<HTMLButtonElement>(".year-btn");
|
||||||
|
const panels = document.querySelectorAll<HTMLElement>(".year-events");
|
||||||
|
|
||||||
|
btns.forEach((btn) => {
|
||||||
|
btn.addEventListener("click", () => {
|
||||||
|
const year = btn.dataset.year;
|
||||||
|
|
||||||
|
btns.forEach((b) => {
|
||||||
|
b.className =
|
||||||
|
"year-btn font-semibold px-4 py-2 rounded text-sm transition-colors cursor-pointer border border-border bg-background text-foreground hover:bg-muted";
|
||||||
|
});
|
||||||
|
btn.className =
|
||||||
|
"year-btn font-semibold px-4 py-2 rounded text-sm transition-colors cursor-pointer bg-foreground text-background";
|
||||||
|
|
||||||
|
panels.forEach((panel) => {
|
||||||
|
if (panel.dataset.eventsYear === year) {
|
||||||
|
panel.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
panel.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
---
|
---
|
||||||
import About from "@/components/About.astro";
|
import About from "@/components/About.astro";
|
||||||
import ContactSection from "@/components/ContactSection.astro";
|
|
||||||
import EPCProjects from "@/components/EPCProjects.astro";
|
import EPCProjects from "@/components/EPCProjects.astro";
|
||||||
import Experience from "@/components/Experience.astro";
|
|
||||||
import Header from "@/components/Header.astro";
|
import Header from "@/components/Header.astro";
|
||||||
import Hero from "@/components/Hero.astro";
|
import Hero from "@/components/Hero.astro";
|
||||||
import "@/globals.css";
|
import "@/globals.css";
|
||||||
|
|
@ -41,8 +39,6 @@ import "@/globals.css";
|
||||||
<Hero />
|
<Hero />
|
||||||
<About />
|
<About />
|
||||||
<EPCProjects />
|
<EPCProjects />
|
||||||
<Experience />
|
|
||||||
<ContactSection />
|
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue