298 lines
6.4 KiB
Vue
298 lines
6.4 KiB
Vue
<template>
|
|
<section class="hero-section" aria-labelledby="home-title">
|
|
<div class="hero-copy">
|
|
<p class="eyebrow mono">{{ content.eyebrow }}</p>
|
|
<h1 id="home-title">{{ content.professionalName }}</h1>
|
|
<h2>{{ content.role }}</h2>
|
|
<p class="hero-summary">{{ content.summary }}</p>
|
|
<p class="experience-line">{{ content.experienceLine }}</p>
|
|
<p class="availability-line">{{ content.availability.label }}</p>
|
|
|
|
<div class="cta-row" aria-label="Professional actions">
|
|
<a class="btn primary-action" :href="mailtoHref">Discuss a Project</a>
|
|
<RouterLink class="btn secondary" :to="content.resumeUrl">View Résumé</RouterLink>
|
|
<a class="btn tertiary" href="#platform">Explore the Live Platform</a>
|
|
</div>
|
|
|
|
<div class="tech-list" aria-label="Core technologies">
|
|
<span v-for="tech in content.techStack" :key="tech" class="pill mono">{{ tech }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<aside class="status-card glass" aria-labelledby="status-title">
|
|
<div class="status-head">
|
|
<p class="eyebrow mono">Live platform</p>
|
|
<h3 id="status-title">Titan Lab Status</h3>
|
|
</div>
|
|
<div class="status-list">
|
|
<div v-for="item in statusItems" :key="item.label" class="status-row">
|
|
<span class="status-dot" :class="item.state" aria-hidden="true"></span>
|
|
<div>
|
|
<a v-if="item.labelHref" class="status-label status-title-link" :href="item.labelHref">
|
|
{{ item.label }}
|
|
</a>
|
|
<div v-else class="status-label">{{ item.label }}</div>
|
|
<a
|
|
v-if="item.href && isExternal(item.href)"
|
|
class="status-value mono status-link"
|
|
:href="item.href"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{{ item.value }}
|
|
</a>
|
|
<RouterLink v-else-if="item.href" class="status-value mono status-link" :to="item.href">
|
|
{{ item.value }}
|
|
</RouterLink>
|
|
<div v-else class="status-value mono">{{ item.value }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p class="status-note mono">Last checked: {{ lastCheckedLabel }}</p>
|
|
<p v-if="error || (!loading && !labStatus?.connected)" class="status-note">
|
|
Live status is temporarily unavailable. The platform overview remains available below.
|
|
</p>
|
|
</aside>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { RouterLink } from "vue-router";
|
|
|
|
defineProps({
|
|
content: Object,
|
|
statusItems: Array,
|
|
labStatus: Object,
|
|
loading: Boolean,
|
|
error: String,
|
|
lastCheckedLabel: String,
|
|
mailtoHref: String,
|
|
});
|
|
|
|
function isExternal(href) {
|
|
return typeof href === "string" && /^https?:\/\//.test(href);
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.hero-section {
|
|
position: relative;
|
|
isolation: isolate;
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1.75fr) minmax(320px, 0.85fr);
|
|
gap: 26px;
|
|
align-items: center;
|
|
min-height: calc(100vh - 86px);
|
|
padding: 42px 0 30px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero-section::before,
|
|
.hero-section::after {
|
|
content: "";
|
|
position: absolute;
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
}
|
|
|
|
.hero-section::before {
|
|
inset: 16% -8vw 8% 50%;
|
|
border-radius: 999px;
|
|
background:
|
|
radial-gradient(ellipse at 68% 42%, rgba(0, 229, 197, 0.17), transparent 32%),
|
|
radial-gradient(ellipse at 28% 66%, rgba(120, 180, 255, 0.11), transparent 36%),
|
|
radial-gradient(ellipse at center, rgba(11, 24, 46, 0.58), transparent 72%);
|
|
opacity: 0.8;
|
|
filter: blur(2px);
|
|
}
|
|
|
|
.hero-section::after {
|
|
width: min(300px, 24vw);
|
|
top: 28%;
|
|
right: 6vw;
|
|
bottom: 22%;
|
|
border-radius: 999px;
|
|
background:
|
|
radial-gradient(ellipse at center, rgba(0, 229, 197, 0.16), transparent 64%),
|
|
linear-gradient(90deg, transparent, rgba(120, 180, 255, 0.08), transparent);
|
|
opacity: 0.32;
|
|
filter: blur(18px);
|
|
}
|
|
|
|
.hero-copy,
|
|
.status-card {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.eyebrow {
|
|
color: var(--accent-cyan);
|
|
font-size: 13px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
margin: 0 0 8px;
|
|
}
|
|
|
|
.hero-copy h1 {
|
|
font-size: clamp(44px, 7vw, 72px);
|
|
line-height: 0.98;
|
|
margin: 0;
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
.hero-copy h2 {
|
|
font-size: clamp(26px, 3.2vw, 36px);
|
|
margin: 12px 0 0;
|
|
color: var(--text-primary);
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
.hero-summary {
|
|
max-width: 760px;
|
|
margin: 20px 0 0;
|
|
font-size: clamp(18px, 2.2vw, 22px);
|
|
line-height: 1.55;
|
|
color: var(--text-strong);
|
|
}
|
|
|
|
.experience-line,
|
|
.availability-line {
|
|
max-width: 760px;
|
|
margin: 14px 0 0;
|
|
font-size: 17px;
|
|
line-height: 1.65;
|
|
}
|
|
|
|
.availability-line {
|
|
color: rgba(170, 255, 215, 0.94);
|
|
}
|
|
|
|
.tech-list,
|
|
.cta-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.tech-list {
|
|
margin-top: 22px;
|
|
}
|
|
|
|
.cta-row {
|
|
margin-top: 28px;
|
|
}
|
|
|
|
.primary-action {
|
|
background: linear-gradient(135deg, rgba(0, 229, 197, 0.9), rgba(120, 180, 255, 0.82));
|
|
color: #02141d;
|
|
}
|
|
|
|
.tertiary {
|
|
background: transparent;
|
|
color: var(--text-strong);
|
|
}
|
|
|
|
.status-card {
|
|
padding: 20px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.status-head h3 {
|
|
margin: 0 0 14px;
|
|
font-size: 28px;
|
|
}
|
|
|
|
.status-list {
|
|
display: grid;
|
|
gap: 14px;
|
|
}
|
|
|
|
.status-row {
|
|
display: grid;
|
|
grid-template-columns: 14px 1fr;
|
|
gap: 12px;
|
|
align-items: start;
|
|
padding: 12px;
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
border-radius: var(--radius-sm);
|
|
background: rgba(255, 255, 255, 0.03);
|
|
}
|
|
|
|
.status-dot {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 999px;
|
|
margin-top: 4px;
|
|
background: var(--text-muted);
|
|
}
|
|
|
|
.status-dot.ok {
|
|
background: var(--accent-cyan);
|
|
}
|
|
|
|
.status-dot.bad {
|
|
background: var(--accent-rose);
|
|
}
|
|
|
|
.status-label {
|
|
font-weight: 800;
|
|
color: var(--text-strong);
|
|
}
|
|
|
|
.status-title-link {
|
|
display: inline-flex;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.status-title-link:hover {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.status-value,
|
|
.status-note {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.status-link {
|
|
display: inline-flex;
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.status-note {
|
|
margin: 14px 0 0;
|
|
}
|
|
|
|
@media (min-width: 1180px) {
|
|
.hero-copy h2 {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1080px) {
|
|
.hero-section {
|
|
grid-template-columns: 1fr;
|
|
min-height: auto;
|
|
}
|
|
|
|
.hero-section::before {
|
|
inset: 96px -22vw 24px 18%;
|
|
opacity: 0.34;
|
|
}
|
|
|
|
.hero-section::after {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.hero-section {
|
|
padding-top: 28px;
|
|
}
|
|
|
|
.hero-section::before {
|
|
inset: 84px -44vw 18px 8%;
|
|
opacity: 0.28;
|
|
}
|
|
}
|
|
</style>
|