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 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 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 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
// It accepts a CSV file upload (field name: "file") and returns enriched trades as JSON.
@@ -94,6 +96,7 @@ func main() {
flag.Parse()
http.HandleFunc("/styles.css", Styles)
http.HandleFunc("/styles2.css", Styles2)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Redirect(w, r, "/", http.StatusFound)
@@ -106,6 +109,7 @@ func main() {
http.HandleFunc("/finance", Finance)
http.HandleFunc("/cinema", Cinema)
http.HandleFunc("/cyber", Cyber)
http.HandleFunc("/engine", Engine)
http.HandleFunc("/git", func(w http.ResponseWriter, r *http.Request) {
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">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About — Samantha Vero Friis</title>
<link rel="stylesheet" href="styles.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Libre+Caslon+Text:ital,wght@0,400;0,700;1,400&display=swap"
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>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Samantha Vero Friis</title>
<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="stylesheet" href="styles2.css" />
</head>
<body>
<main>
<p class="summary">
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>
<nav>
<a class="nav-logo" href="#">samantha_vero_friis</a>
<div class="nav-links">
<a href="/engine">engine</a>
<a href="/cyber">cyber</a>
<a href="/cinema">cinema</a>
<a href="/cinema">finance</a>
<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 -->
<section>
<p class="section-label">// skills</p>
<div class="skills-list">
<div class="skill-row">
<span>Excel</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 85%; animation-delay: 0.1s"
></div>
</div>
</div>
<div class="skill-row">
<span>Python</span>
<div class="skill-bar-track">
<div
class="skill-bar-fill"
style="width: 75%; animation-delay: 0.2s"
></div>
</div>
</div>
<div class="skill-row">
<span>SQL</span>
<div class="skill-bar-track">
<div
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>
<div class="shell">
<aside>
<div>
<p class="sidebar-label">navigate</p>
<div class="sidebar-links">
<a href="#about">about</a>
<a href="#experience">experience</a>
<a href="#education">education</a>
<a href="#opinion">opinion</a>
</div>
</div>
<div>
<p class="sidebar-label">skills</p>
<div class="skill-list">
<div class="skill-item">Excel</div>
<div class="skill-item">Python</div>
<div class="skill-item">SQL</div>
<div class="skill-item">Go</div>
<div class="skill-item">Git</div>
</div>
</div>
<div>
<p class="sidebar-label">location</p>
<div style="font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);">Denmark<br>Aalborg</div>
</div>
</aside>
<!-- Experience -->
<section>
<p class="section-label">// experience</p>
<main id="about">
<div class="exp-block">
<h2>Independent Equity Research</h2>
<p>
Perform 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>
<div class="hero">
<p class="hero-eyebrow">portfolio · 2026</p>
<h1>Samantha<br><span>Vero Friis</span></h1>
<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>
<div class="hero-meta">
<span><span class="dot"></span>available for work</span>
<span>samantha42.xyz</span>
<span>contact@samantha42.xyz</span>
</div>
</div>
<div class="exp-block">
<h2>Technical Tools & Automation</h2>
<p>
Build lightweight backend tools to automate
repetitive analytical and operational tasks. Use
Python for data processing, validation, and workflow
automation. Develop lightweight online services in
Go, focusing on speed and reliability.
</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>
<section id="experience">
<div class="section-head"><h2>experience</h2></div>
<div class="exp-grid">
<div class="exp-card">
<div class="exp-header">
<span class="exp-title">Independent Equity Research</span>
<span class="exp-tag">finance</span>
</div>
<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>
</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>
+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;}
}