new theme

This commit is contained in:
samantha42
2026-03-23 16:06:36 +01:00
parent 90c70be734
commit 13a1d1c1e4
8 changed files with 2188 additions and 472 deletions
BIN
View File
Binary file not shown.
+5 -1
View File
@@ -34,9 +34,11 @@ func Infra(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./s
func Finance(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/finance.html") } func Finance(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/finance.html") }
func Cinema(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cinema.html") } func Cinema(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cinema.html") }
func Cyber(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cyber.html") } func Cyber(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/cyber.html") }
func Engine(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/engine.html") }
func About(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/about.html") } func About(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/about.html") }
func Styles(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/styles.css") } func Styles(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/styles.css") }
func Styles2(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/styles2.css") }
// TradesHandler handles POST /trades // TradesHandler handles POST /trades
// It accepts a CSV file upload (field name: "file") and returns enriched trades as JSON. // It accepts a CSV file upload (field name: "file") and returns enriched trades as JSON.
@@ -94,6 +96,7 @@ func main() {
flag.Parse() flag.Parse()
http.HandleFunc("/styles.css", Styles) http.HandleFunc("/styles.css", Styles)
http.HandleFunc("/styles2.css", Styles2)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" { if r.URL.Path != "/" {
http.Redirect(w, r, "/", http.StatusFound) http.Redirect(w, r, "/", http.StatusFound)
@@ -106,6 +109,7 @@ func main() {
http.HandleFunc("/finance", Finance) http.HandleFunc("/finance", Finance)
http.HandleFunc("/cinema", Cinema) http.HandleFunc("/cinema", Cinema)
http.HandleFunc("/cyber", Cyber) http.HandleFunc("/cyber", Cyber)
http.HandleFunc("/engine", Engine)
http.HandleFunc("/git", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/git", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://git.samantha42.xyz", http.StatusFound) http.Redirect(w, r, "https://git.samantha42.xyz", http.StatusFound)
}) })
BIN
View File
Binary file not shown.
+136 -193
View File
@@ -1,201 +1,144 @@
<!doctype html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>About — Samantha Vero Friis</title> <title>Samantha Vero Friis</title>
<link rel="stylesheet" href="styles.css" /> <link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@300;400;500&family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="stylesheet" href="styles2.css" />
<link </head>
href="https://fonts.googleapis.com/css2?family=Libre+Caslon+Text:ital,wght@0,400;0,700;1,400&display=swap" <body>
rel="stylesheet"
/>
</head>
<body>
<div class="container">
<!-- PAGE NAV — place right before <header> -->
<nav class="page-nav">
<a href="#about" class="active"><span>about</span></a>
<a href="/git" target="_blank"><span>git</span></a>
<br /><span><p class="section-label">//Opinion</p></span>
<a href="/cinema"><span>Cinema</span></a>
<a href="/finance"><span>Finance</span></a>
<a href="/cyber"><span>cyber</span></a>
<a href="/infra"><span>infra</span></a>
</nav>
<header>
<p class="tag">// about me</p>
<h1>Samantha<br /><span>Vero Friis</span></h1>
<p class="header-sub">
Denmark, Aalborg &nbsp;·&nbsp; samantha42.xyz
</p>
</header>
<main> <nav>
<p class="summary"> <a class="nav-logo" href="#">samantha_vero_friis</a>
Bachelor's student in Business Administration seeking an <div class="nav-links">
entry-level analyst or finance-related role while studying. <a href="/engine">engine</a>
Strong numerical skills, advanced Excel, and experience in <a href="/cyber">cyber</a>
company, risk, and financial analysis. Analytical, <a href="/cinema">cinema</a>
detail-oriented, and efficiency-focused. <a href="/cinema">finance</a>
</p> <a href="/infra">infrastructure</a>
<a href="https://samantha42.xyz/git">git</a>
<a class="cta" href="mailto:me@samantha42.xyz">contact</a>
</div>
</nav>
<!-- Skills --> <div class="shell">
<section> <aside>
<p class="section-label">// skills</p> <div>
<div class="skills-list"> <p class="sidebar-label">navigate</p>
<div class="skill-row"> <div class="sidebar-links">
<span>Excel</span> <a href="#about">about</a>
<div class="skill-bar-track"> <a href="#experience">experience</a>
<div <a href="#education">education</a>
class="skill-bar-fill" <a href="#opinion">opinion</a>
style="width: 85%; animation-delay: 0.1s" </div>
></div> </div>
</div> <div>
</div> <p class="sidebar-label">skills</p>
<div class="skill-row"> <div class="skill-list">
<span>Python</span> <div class="skill-item">Excel</div>
<div class="skill-bar-track"> <div class="skill-item">Python</div>
<div <div class="skill-item">SQL</div>
class="skill-bar-fill" <div class="skill-item">Go</div>
style="width: 75%; animation-delay: 0.2s" <div class="skill-item">Git</div>
></div> </div>
</div> </div>
</div> <div>
<div class="skill-row"> <p class="sidebar-label">location</p>
<span>SQL</span> <div style="font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);">Denmark<br>Aalborg</div>
<div class="skill-bar-track"> </div>
<div </aside>
class="skill-bar-fill"
style="width: 60%; animation-delay: 0.3s"
></div>
</div>
</div>
<div class="skill-row">
<span>Go</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 70%; animation-delay: 0.4s"
></div>
</div>
</div>
<div class="skill-row">
<span>Git</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 65%; animation-delay: 0.5s"
></div>
</div>
</div>
</div>
</section>
<!-- Experience --> <main id="about">
<section>
<p class="section-label">// experience</p>
<div class="exp-block"> <div class="hero">
<h2>Independent Equity Research</h2> <p class="hero-eyebrow">portfolio · 2026</p>
<p> <h1>Samantha<br><span>Vero Friis</span></h1>
Perform fundamental analysis using SEC filings <p class="hero-bio">Bachelor's student in Business Administration seeking an entry-level analyst or finance-related role while studying. Strong numerical skills, advanced Excel, and experience in company, risk, and financial analysis. Analytical, detail-oriented, and efficiency-focused.</p>
(10-K, 10-Q) and earnings reports. Assess <div class="hero-meta">
profitability, liquidity, risk factors, and <span><span class="dot"></span>available for work</span>
long-term business sustainability. Handle incomplete <span>samantha42.xyz</span>
or low-transparency information through structured <span>contact@samantha42.xyz</span>
cross-validation. </div>
</p> </div>
</div>
<div class="exp-block"> <section id="experience">
<h2>Technical Tools & Automation</h2> <div class="section-head"><h2>experience</h2></div>
<p> <div class="exp-grid">
Build lightweight backend tools to automate <div class="exp-card">
repetitive analytical and operational tasks. Use <div class="exp-header">
Python for data processing, validation, and workflow <span class="exp-title">Independent Equity Research</span>
automation. Develop lightweight online services in <span class="exp-tag">finance</span>
Go, focusing on speed and reliability. </div>
</p> <p class="exp-body">Fundamental analysis using SEC filings (10-K, 10-Q) and earnings reports. Assess profitability, liquidity, risk factors, and long-term business sustainability. Handle incomplete or low-transparency information through structured cross-validation.</p>
</div>
</section>
<!-- Education -->
<section>
<p class="section-label">// education</p>
<div class="edu-item">
<span class="edu-year">2025 — now</span>
<div>
<p class="edu-title">
Bachelor of Economics & Business Administration
(HA)
</p>
<p class="edu-sub">
Aalborg University &nbsp;·&nbsp; Expected 2028
</p>
</div>
</div>
<div class="edu-item">
<span class="edu-year">2020 — 2023</span>
<div>
<p class="edu-title">
HTX Technical Upper Secondary Education
</p>
<p class="edu-sub">
Viden Gymnasier &nbsp;·&nbsp; Mathematics A,
Biology B<br />Study project: Mathematics &
Technology
</p>
</div>
</div>
</section>
<!-- Languages -->
<section>
<p class="section-label">// languages</p>
<div class="skills-list">
<div class="skill-row">
<span>Danish</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 100%; animation-delay: 0.1s"
></div>
</div>
</div>
<div class="skill-row">
<span>English</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 80%; animation-delay: 0.2s"
></div>
</div>
</div>
</div>
</section>
<!-- Interests -->
<section>
<p class="section-label">// interests</p>
<div class="tags">
<span class="tag-pill">Gaming</span>
<span class="tag-pill">Board games</span>
<span class="tag-pill">Programming</span>
<span class="tag-pill">Financial markets</span>
</div>
</section>
</main>
<footer>
<a href="mailto:contact@samantha42.xyz"
>contact@samantha42.xyz</a
>
<p class="footer-copy">&copy; 2026 — All rights reserved</p>
</footer>
</div> </div>
</body> <div class="exp-card">
<div class="exp-header">
<span class="exp-title">Technical Tools &amp; Automation</span>
<span class="exp-tag">engineering</span>
</div>
<p class="exp-body">Build lightweight backend tools to automate repetitive analytical and operational tasks. Python for data processing, validation, and workflow automation. Lightweight online services in Go, focusing on speed and reliability.</p>
</div>
</div>
</section>
<section id="education">
<div class="section-head"><h2>education</h2></div>
<div class="edu-grid">
<div class="edu-card">
<div class="edu-year">2025 —<br>present</div>
<div>
<div class="edu-degree">Bachelor of Economics &amp; Business Administration (HA)</div>
<div class="edu-inst">Aalborg University · Expected 2028</div>
</div>
</div>
<div class="edu-card">
<div class="edu-year">2020 —<br>2023</div>
<div>
<div class="edu-degree">HTX Technical Upper Secondary Education</div>
<div class="edu-inst">Viden Gymnasier</div>
<div class="edu-detail">Mathematics A, Biology B · Study project: Mathematics &amp; Technology</div>
</div>
</div>
</div>
</section>
<section id="opinion">
<div class="section-head"><h2>opinion</h2></div>
<div class="opinion-grid">
<a class="opinion-link" href="https://samantha42.xyz/cinema">cinema</a>
<a class="opinion-link" href="https://samantha42.xyz/finance">finance</a>
<a class="opinion-link" href="https://samantha42.xyz/cyber">cyber</a>
<a class="opinion-link" href="https://samantha42.xyz/infra">infra</a>
</div>
</section>
<section>
<div class="section-head"><h2>languages</h2></div>
<div class="lang-row">
<span class="lang-badge">Danish</span>
<span class="lang-badge">English</span>
</div>
</section>
<section>
<div class="section-head"><h2>interests</h2></div>
<div class="interests-row">
<span class="interest-chip">gaming</span>
<span class="interest-chip">board-games</span>
<span class="interest-chip">programming</span>
<span class="interest-chip">financial-markets</span>
</div>
</section>
</main>
</div>
<footer>
<div class="footer-contact"><a href="mailto:me@samantha42.xyz">contact@samantha42.xyz</a></div>
<div class="footer-copy">© 2026 — all rights reserved</div>
</footer>
</body>
</html> </html>
+314
View File
@@ -0,0 +1,314 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FP&amp;A Budgeting Engine — Samantha Vero Friis</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Libre+Caslon+Text:wght@400;700;800&family=Syne:wght@400;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="styles.css" />
<style>
/* ── Page-specific layout ── */
header { padding: 2.4rem 0 2rem; }
main { padding: 3rem 0 5rem; }
.prose-block { max-width: 660px; }
pre {
font-family: "DM Mono", monospace;
font-size: 0.78rem;
line-height: 1.8;
background: var(--surface);
border: 1px solid var(--border);
padding: 1.4rem 1.6rem;
overflow-x: auto;
margin: 1.6rem 0;
color: #c8c8c0;
}
/* stat cards at top */
.cards { margin: 2rem 0 2.5rem; }
footer {
border-top: 1px solid var(--border);
padding: 1.6rem 0;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.7rem;
color: var(--muted);
}
footer a { color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--accent); }
@media (max-width: 560px) {
.cards { grid-template-columns: 1fr 1fr; }
}
</style>
</head>
<body>
<!-- ── HEADER ── -->
<header>
<div class="container">
<a class="back" href="https://samantha42.xyz/">← back</a>
<div class="tag">project</div>
<h1>FP&amp;A <span>Budgeting Engine</span></h1>
<div class="byline">
<span>Samantha Vero Friis</span>
<span style="color:var(--muted)">Go · REST API · Finance</span>
<span style="color:var(--muted)">March 2026</span>
</div>
</div>
</header>
<!-- ── STAT CARDS ── -->
<div class="container">
<div class="cards">
<div class="card">
<div class="card-label">Language</div>
<div class="card-value" style="color:var(--accent)">Go</div>
</div>
<div class="card">
<div class="card-label">Database</div>
<div class="card-value" style="color:var(--accent)">SQLite</div>
</div>
<div class="card">
<div class="card-label">Architecture</div>
<div class="card-value" style="font-size:1.1rem;color:var(--accent)">REST API</div>
</div>
<div class="card">
<div class="card-label">Infrastructure</div>
<div class="card-value" style="font-size:1.1rem;color:var(--green)">Zero deps</div>
</div>
</div>
</div>
<!-- ── MAIN ── -->
<main>
<div class="container">
<div class="prose-block">
<p class="lead">
A REST API for corporate budget management, actuals ingestion, and variance
reporting — built to replace the manual Excel workflow that smaller finance
teams still run every month. Single binary, no infrastructure, deploys anywhere.
</p>
<h2>The problem it solves</h2>
<p>
At companies below a certain size — typically below 500 employees, or without
the budget for enterprise planning platforms like Anaplan or Adaptive Planning —
the monthly FP&amp;A close still runs on spreadsheets. Actuals are exported from
the ERP, pasted into a workbook, and variances are computed by hand across
departments and GL accounts. The process is slow, error-prone, and breaks the
moment someone edits the wrong cell.
</p>
<p>
Large enterprises have solved this with dedicated tooling. The gap is the
mid-market: companies that have outgrown Excel but cannot justify a six-figure
implementation project. This engine sits in that space — structured schema,
versioned budgets, on-demand actuals ingestion, and instant variance reports
with no pivot tables and no shared file conflicts.
</p>
<blockquote>
The goal was not to build something clever.<br>
It was to build something that solves a real problem<br>
<span>finance teams in the mid-market actually have.</span>
</blockquote>
<h2>Stack</h2>
<p>
Most finance tooling is Python notebooks or Excel macros — useful for analysis,
fragile in production. Go compiles to a single binary with no runtime
dependencies, makes every error path explicit, and handles concurrency safely.
SQLite was chosen over Postgres deliberately: for a tool running inside a
finance team's environment, zero infrastructure matters. The database is a
single file. Backups are a file copy.
</p>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Layer</th>
<th style="text-align:left">Choice</th>
<th style="text-align:left">Reason</th>
</tr>
</thead>
<tbody>
<tr>
<td class="ticker">Language</td>
<td style="text-align:left">Go</td>
<td style="text-align:left;color:var(--muted)">Single binary, no runtime, explicit errors</td>
</tr>
<tr>
<td class="ticker">Routing</td>
<td style="text-align:left">net/http (stdlib)</td>
<td style="text-align:left;color:var(--muted)">No deps; method+path routing since Go 1.22</td>
</tr>
<tr>
<td class="ticker">Database</td>
<td style="text-align:left">SQLite</td>
<td style="text-align:left;color:var(--muted)">Zero infrastructure, file-based, ships with binary</td>
</tr>
<tr>
<td class="ticker">Schema</td>
<td style="text-align:left">Auto-migrated</td>
<td style="text-align:left;color:var(--muted)"><code>CREATE TABLE IF NOT EXISTS</code> on startup</td>
</tr>
</tbody>
</table>
</div>
<h2>API surface</h2>
<p>
Three resource groups. Budgets support full CRUD by department, GL account,
fiscal period, and version. Actuals are ingested via a single upsert endpoint
— idempotent by period, department, and GL code, designed to accept ERP
JSON exports directly. Variance endpoints return computed reports immediately.
</p>
<pre>POST /api/v1/budgets Create a budget line
PUT /api/v1/budgets/{id} Update amount or notes
DELETE /api/v1/budgets/{id} Remove a budget line
POST /api/v1/actuals/ingest Upsert an actual (idempotent)
GET /api/v1/variance Full variance report
GET /api/v1/variance/alerts Lines exceeding threshold
GET /api/v1/health Returns 200 if DB is reachable</pre>
<p>
Variance endpoints accept <code>year</code>, <code>period</code>,
<code>dept</code>, <code>version</code>, and <code>threshold</code>
as query parameters.
</p>
<h2>Finance concepts implemented correctly</h2>
<p>
<strong style="color:var(--text)">Favourability logic.</strong> Whether a
variance is good or bad depends on account type. Revenue accounts are
favourable when actuals exceed budget; cost accounts when actuals come in
under. The engine handles both via a <code>favour_high</code> flag per GL
account — a distinction generic reporting tools consistently leave to manual
configuration.
</p>
<p>
<strong style="color:var(--text)">Budget versioning.</strong> Original budget
and up to three forecast revisions are stored separately, enabling a full
budget vs. forecast vs. actual three-way comparison without overwriting
history — the standard FP&amp;A reforecast workflow.
</p>
<p>
<strong style="color:var(--text)">GL account typing.</strong> Accounts are
typed as <code>revenue</code>, <code>cogs</code>, <code>opex</code>,
<code>capex</code>, or <code>headcount</code>. This drives the P&amp;L
rollup structure and the favourability logic together, reflecting how an
actual chart of accounts is structured.
</p>
<p>
<strong style="color:var(--text)">Fiscal period decoupling.</strong> Periods
are stored as integers 112, decoupled from calendar months — required for
any company not on a January fiscal year.
</p>
<h2>Example output</h2>
<p>
Engineering department, FY2024 P09. Cloud infrastructure came in under budget.
Consulting exceeded by 51%.
</p>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>GL</th>
<th>Description</th>
<th>Budget</th>
<th>Actual</th>
<th>Var %</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>6100</td>
<td style="text-align:left;color:var(--text)">Salaries &amp; Wages</td>
<td>4,200k</td>
<td>4,380k</td>
<td class="down">4.29%</td>
<td class="down">unfavourable</td>
</tr>
<tr>
<td>6500</td>
<td style="text-align:left;color:var(--text)">Consulting &amp; Contractors</td>
<td>450k</td>
<td>680k</td>
<td class="down">51.11%</td>
<td class="down">unfavourable</td>
</tr>
<tr>
<td>5000</td>
<td style="text-align:left;color:var(--text)">Cloud Infrastructure</td>
<td>850k</td>
<td>791k</td>
<td class="up">+6.94%</td>
<td class="up">favourable</td>
</tr>
</tbody>
</table>
</div>
<h2>Running it</h2>
<pre>git clone https://git.samantha42.xyz/samantha/FPandA-Engine
cd FPandA-Engine
cp .env.example .env
go mod tidy
go run ./cmd/server
# → http://localhost:8080
# → fpa.db created on first run</pre>
<p>
Two environment variables: <code>DB_PATH</code> (use <code>:memory:</code>
for tests) and <code>PORT</code>. Demo seed data for Engineering, Sales,
and Marketing is included in <code>scripts/seed_demo.sql</code>.
</p>
<hr />
<p>
Source at
<a href="https://git.samantha42.xyz/samantha/FPandA-Engine"
style="color:var(--accent);text-decoration:none"
target="_blank">git.samantha42.xyz/samantha/FPandA-Engine</a>
</p>
</div>
</div>
</main>
<footer>
<div class="container" style="display:flex;justify-content:space-between;width:100%">
<a href="mailto:contact@samantha42.xyz">contact@samantha42.xyz</a>
<span>© 2026 — All rights reserved</span>
</div>
</footer>
</body>
</html>
+347 -276
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+231
View File
@@ -0,0 +1,231 @@
:root{
--bg:#f7f6f3;
--surface:#ffffff;
--surface2:#f0eeea;
--border:#e2ddd6;
--border2:#ccc7be;
--text:#1a1917;
--muted:#6b6560;
--dim:#b0aa9f;
--accent:#1a56db;
--accent2:#1447b8;
--green:#0a7c52;
--amber:#92620a;
--ff-mono:'IBM Plex Mono',monospace;
--ff-sans:'IBM Plex Sans',sans-serif;
}
*{box-sizing:border-box;margin:0;padding:0;}
html{font-size:15px;scroll-behavior:smooth;}
body{
background:var(--bg);
color:var(--text);
font-family:var(--ff-sans);
font-weight:300;
line-height:1.7;
min-height:100vh;
}
/* subtle dot grid */
body::before{
content:'';
position:fixed;inset:0;
background-image:radial-gradient(circle, var(--border) 1px, transparent 1px);
background-size:24px 24px;
opacity:0.5;
pointer-events:none;z-index:0;
}
nav{
position:sticky;top:0;z-index:100;
background:rgba(247,246,243,0.94);
backdrop-filter:blur(10px);
border-bottom:1px solid var(--border);
padding:0 clamp(1rem,5vw,3rem);
display:flex;align-items:center;justify-content:space-between;
height:52px;
}
.nav-logo{
font-family:var(--ff-mono);font-size:0.78rem;font-weight:500;
color:var(--accent);text-decoration:none;
display:flex;align-items:center;gap:6px;
}
.nav-logo::before{content:'>';color:var(--green);font-weight:500;}
.nav-links{display:flex;gap:0.2rem;align-items:center;}
.nav-links a{
font-family:var(--ff-mono);font-size:0.72rem;
color:var(--muted);text-decoration:none;
padding:0.3rem 0.7rem;border-radius:4px;
transition:color 0.15s,background 0.15s;
}
.nav-links a:hover{color:var(--text);background:var(--surface2);}
.nav-links .cta{color:var(--accent);border:1px solid var(--accent2);margin-left:0.4rem;}
.nav-links .cta:hover{background:var(--accent2);color:#fff;}
.shell{
position:relative;z-index:1;
display:grid;
grid-template-columns:220px 1fr;
min-height:calc(100vh - 52px);
max-width:1100px;
margin:0 auto;
}
aside{
border-right:1px solid var(--border);
padding:2.5rem 1.5rem;
display:flex;flex-direction:column;gap:2.5rem;
position:sticky;top:52px;height:calc(100vh - 52px);
overflow-y:auto;
background:var(--surface);
}
.sidebar-label{
font-family:var(--ff-mono);font-size:0.65rem;
letter-spacing:0.2em;text-transform:uppercase;
color:var(--dim);margin-bottom:0.8rem;
}
.sidebar-links{display:flex;flex-direction:column;gap:2px;}
.sidebar-links a{
font-family:var(--ff-mono);font-size:0.78rem;color:var(--muted);
text-decoration:none;padding:0.3rem 0.6rem;border-radius:4px;
transition:color 0.15s,background 0.15s;
display:flex;align-items:center;gap:6px;
}
.sidebar-links a::before{content:'//';color:var(--dim);font-size:0.65rem;}
.sidebar-links a:hover{color:var(--text);background:var(--surface2);}
.skill-list{display:flex;flex-direction:column;gap:4px;}
.skill-item{
font-family:var(--ff-mono);font-size:0.78rem;
color:var(--muted);padding:0.3rem 0.6rem;
border-left:2px solid var(--border2);
transition:border-color 0.15s,color 0.15s;cursor:default;
}
.skill-item:hover{border-color:var(--accent);color:var(--text);}
main{
padding:3rem clamp(1.5rem,4vw,3rem);
display:flex;flex-direction:column;gap:4rem;
}
.hero{padding-bottom:3rem;border-bottom:1px solid var(--border);}
.hero-eyebrow{
font-family:var(--ff-mono);font-size:0.72rem;color:var(--green);
letter-spacing:0.18em;margin-bottom:1rem;
display:flex;align-items:center;gap:8px;
}
.hero-eyebrow::before{content:'';display:block;width:20px;height:1px;background:var(--green);}
h1{
font-family:var(--ff-mono);
font-size:clamp(1.8rem,4vw,2.8rem);
font-weight:500;color:var(--text);
letter-spacing:-0.02em;line-height:1.1;
}
h1 span{color:var(--accent);}
.hero-bio{
margin-top:1.2rem;font-size:0.9rem;color:var(--muted);
max-width:560px;line-height:1.8;
}
.hero-meta{
margin-top:1.5rem;display:flex;gap:1.5rem;flex-wrap:wrap;
font-family:var(--ff-mono);font-size:0.72rem;color:var(--dim);
}
.hero-meta span{display:flex;align-items:center;gap:5px;}
.dot{
width:6px;height:6px;border-radius:50%;
background:var(--green);display:inline-block;
animation:pulse 2s infinite;
}
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:0.35;}}
.section-head{display:flex;align-items:center;gap:10px;margin-bottom:1.8rem;}
.section-head h2{
font-family:var(--ff-mono);font-size:0.72rem;font-weight:400;
letter-spacing:0.25em;text-transform:uppercase;color:var(--muted);
white-space:nowrap;
}
.section-head::after{content:'';flex:1;height:1px;background:var(--border);}
.exp-grid{display:flex;flex-direction:column;gap:8px;}
.exp-card{
background:var(--surface);border:1px solid var(--border);
border-radius:6px;padding:1.4rem 1.5rem;
transition:border-color 0.2s,box-shadow 0.2s;
}
.exp-card:hover{border-color:var(--border2);box-shadow:0 2px 12px rgba(0,0,0,0.06);}
.exp-header{
display:flex;align-items:baseline;justify-content:space-between;
margin-bottom:0.6rem;flex-wrap:wrap;gap:0.5rem;
}
.exp-title{font-family:var(--ff-mono);font-size:0.9rem;font-weight:500;color:var(--text);}
.exp-tag{
font-family:var(--ff-mono);font-size:0.65rem;
letter-spacing:0.1em;text-transform:uppercase;
background:rgba(26,86,219,0.07);color:var(--accent);
border:1px solid rgba(26,86,219,0.2);
padding:2px 8px;border-radius:3px;
}
.exp-body{font-size:0.85rem;color:var(--muted);line-height:1.8;}
.edu-grid{display:flex;flex-direction:column;gap:8px;}
.edu-card{
display:grid;grid-template-columns:80px 1fr;gap:1.2rem;
background:var(--surface);border:1px solid var(--border);
border-radius:6px;padding:1.2rem 1.4rem;
transition:border-color 0.2s,box-shadow 0.2s;
}
.edu-card:hover{border-color:var(--border2);box-shadow:0 2px 12px rgba(0,0,0,0.06);}
.edu-year{font-family:var(--ff-mono);font-size:0.7rem;color:var(--amber);padding-top:2px;line-height:1.6;}
.edu-degree{font-size:0.9rem;font-weight:500;color:var(--text);}
.edu-inst{font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);margin-top:2px;}
.edu-detail{font-size:0.82rem;color:var(--dim);margin-top:4px;}
.lang-row{display:flex;gap:0.6rem;flex-wrap:wrap;}
.lang-badge{
font-family:var(--ff-mono);font-size:0.78rem;color:var(--text);
background:var(--surface);border:1px solid var(--border);
padding:0.35rem 0.9rem;border-radius:4px;
}
.interests-row{display:flex;gap:0.6rem;flex-wrap:wrap;}
.interest-chip{
font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);
background:var(--surface);border:1px solid var(--border);
padding:0.3rem 0.75rem;border-radius:4px;
}
.interest-chip::before{content:'# ';color:var(--dim);}
.opinion-grid{display:flex;flex-wrap:wrap;gap:0.6rem;}
.opinion-link{
font-family:var(--ff-mono);font-size:0.78rem;
color:var(--muted);text-decoration:none;
background:var(--surface);border:1px solid var(--border);
padding:0.4rem 0.9rem;border-radius:4px;
display:flex;align-items:center;gap:6px;
transition:border-color 0.2s,color 0.2s,box-shadow 0.2s;
}
.opinion-link::before{content:'→';color:var(--dim);font-size:0.75rem;}
.opinion-link:hover{border-color:var(--accent2);color:var(--text);box-shadow:0 2px 8px rgba(0,0,0,0.06);}
footer{
position:relative;z-index:1;
border-top:1px solid var(--border);
padding:1.4rem clamp(1rem,5vw,3rem);
display:flex;align-items:center;justify-content:space-between;
flex-wrap:wrap;gap:0.5rem;
background:var(--surface);
}
.footer-contact a{font-family:var(--ff-mono);font-size:0.78rem;color:var(--accent);text-decoration:none;}
.footer-contact a:hover{text-decoration:underline;}
.footer-copy{font-family:var(--ff-mono);font-size:0.7rem;color:var(--dim);}
@keyframes fadeUp{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
.hero{animation:fadeUp 0.6s ease both;}
.exp-card:nth-child(1){animation:fadeUp 0.5s 0.05s ease both;}
.exp-card:nth-child(2){animation:fadeUp 0.5s 0.12s ease both;}
.edu-card:nth-child(1){animation:fadeUp 0.5s 0.08s ease both;}
.edu-card:nth-child(2){animation:fadeUp 0.5s 0.15s ease both;}
@media(max-width:700px){
.shell{grid-template-columns:1fr;}
aside{display:none;}
.edu-card{grid-template-columns:1fr;gap:0.3rem;}
}