new articles

This commit is contained in:
samantha42
2026-03-20 11:47:13 +01:00
parent 1f80e7cf9b
commit d66c37677d
6 changed files with 655 additions and 188 deletions
+4
View File
@@ -31,6 +31,8 @@ func Portfolio(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./static/portfolio.html") http.ServeFile(w, r, "./static/portfolio.html")
} }
func Infra(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/infra.html") } func Infra(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/infra.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 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 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 GitPage(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/gitpage.html") } func GitPage(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./static/gitpage.html") }
@@ -99,6 +101,8 @@ func main() {
http.HandleFunc("/", About) http.HandleFunc("/", About)
//http.HandleFunc("/portfolio", Portfolio) //http.HandleFunc("/portfolio", Portfolio)
http.HandleFunc("/infra", Infra) http.HandleFunc("/infra", Infra)
http.HandleFunc("/finance", Finance)
http.HandleFunc("/cinema", Cinema)
http.HandleFunc("/cyber", Cyber) http.HandleFunc("/cyber", Cyber)
//http.HandleFunc("/gitpage", GitPage) //http.HandleFunc("/gitpage", GitPage)
//http.HandleFunc("/gitpage/TicTacToe", GitPageTicTacToe) //http.HandleFunc("/gitpage/TicTacToe", GitPageTicTacToe)
Executable
BIN
View File
Binary file not shown.
+3
View File
@@ -20,8 +20,11 @@
<br /><span><p class="section-label">//Opinion</p></span> <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="/cyber"><span>cyber</span></a>
<a href="/infra"><span>infra</span></a> <a href="/infra"><span>infra</span></a>
</nav> </nav>
<header> <header>
+383
View File
@@ -0,0 +1,383 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Industry That Ate Itself - Samantha Vero Friis</title>
<link
href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container--narrow">
<header>
<a href="/" class="back">← back</a>
<p class="tag">// opinion</p>
<h1>The Industry<br />That Ate<br /><span>Itself</span></h1>
<div class="byline">
<span>Samantha Vero Friis</span>
<span></span>
<span>Film &amp; Media</span>
</div>
</header>
<article>
<p class="lead">
Cinema is not dying because audiences stopped caring about
film. It is dying because the people running it stopped
making decisions that could produce anything worth caring
about. That is a business problem, not a cultural one - and
it has a clear and traceable cause.
</p>
<h2>How You Optimize a Creative Industry to Death</h2>
<p>
Every major studio is ultimately answerable to capital. That
is not unusual - most industries are. What is unusual about
film is that capital and creative quality are in unusually
direct tension. A film that takes a genuine risk might return
ten times its budget or nothing. A sequel to an established
property will almost certainly return two or three times its
budget, reliably, with predictable variance. For a public
company managing quarterly earnings, the choice is not
difficult. You make the sequel.
</p>
<p>
The problem is that this logic, applied consistently across
an entire industry over twenty years, does not produce a
stable business. It produces a slow erosion of the thing
that made the business worth having in the first place.
Audiences do not go to cinemas to see sequels to sequels.
They go because occasionally something extraordinary happens
on a screen and they want to be in the room when it does.
Strip out the extraordinary, and you have stripped out the
reason to show up.
</p>
<h2>The Franchise as a Business Model</h2>
<p>
The Matrix is a precise case study because the original
trilogy was, by design, a complete thing. Three films with
a beginning, a middle, and an ending. The story resolved.
The characters finished where they were going. Whatever
criticisms one might make of Reloaded or Revolutions, the
trilogy had the structural integrity of something that knew
it was going to end. That is increasingly rare, and it
matters - because an ending is what separates a story from
a content pipeline.
</p>
<p>
The correct response to owning the Matrix IP after the
trilogy was either to leave it alone or to build something
genuinely new inside the same universe. The world the
Wachowskis constructed is large enough. New characters, a
different era, a different corner of the machine war, a
different question about the nature of the simulation - any
of these could have been the basis for something that
respected the original while standing on its own. A current
audience does not need Neo. It needs a new conflict with
real stakes and a story it has not already seen.
</p>
<p>
What Warner Bros. produced instead was The Matrix
Resurrections in 2021 - eighteen years after the trilogy
ended - with the same lead actors, the same Agent Smith,
and a plot that literally resurrects the characters the
previous films had concluded. The decision to bring back
the same faces was not a creative one. It was a calculation
that name recognition and nostalgia would lower the
perceived risk of the investment. The studio did not believe
in the universe. It believed in the brand. The audience
noticed. The film failed - not because the Matrix concept
is exhausted, but because the film offered nothing except
proof that the people making it were not willing to find
out whether it could work without a safety net.
</p>
<p>
Star Wars industrialised this dynamic at a scale that was
previously unimaginable. The acquisition of Lucasfilm was
not a creative decision - it was a content library acquisition.
What followed was the systematic conversion of a mythology
that had genuine cultural weight into a production pipeline.
Films, series, spin-offs, prequels, sequels, all released
on a cadence driven by release windows and subscriber targets
rather than by whether anyone had something worth saying.
The audience eventually signalled, clearly, that it was
exhausted. The response was not to slow down - it was to
try different combinations of the same components.
</p>
<blockquote>
A franchise is not a creative decision.<br />
It is a <span>risk management strategy</span> dressed as one.
</blockquote>
<h2>Marvel and the Pipeline Fallacy</h2>
<p>
The Marvel Cinematic Universe is the most important case
study in modern film economics because it succeeded
spectacularly for long enough that the people running it
drew the wrong conclusion from their own success. From Iron
Man in 2008 through to Endgame in 2019, the MCU produced
something genuinely unusual: a connected series of films
that were, individually, mostly good, and collectively
something audiences had never experienced before. The
shared universe worked. Characters crossed over. Threads
built across years. When it paid off, it paid off at a
scale that reshaped the entire industry's assumptions about
what a film could be.
</p>
<p>
The mistake was in identifying why it worked. The shared
universe was not the product. It was the reward for the
product. Audiences invested in the connections between
films because the films themselves had earned that
investment. Iron Man was a good film. The Winter Soldier
was a good film. Guardians of the Galaxy was a good film.
Each one worked as a standalone experience, which meant
that when they intersected, the intersection meant
something. The connective tissue had weight because the
individual pieces had weight.
</p>
<p>
What Marvel - and Disney, which had acquired it - took
from this was a different lesson: that the pipeline itself
was the asset. That releasing four films and multiple
Disney+ series per year, all tagged as MCU content, would
sustain audience engagement indefinitely because the brand
had accumulated enough goodwill to carry anything attached
to it. This is a category error. Goodwill is not a
renewable resource. It is the residue of past quality,
and it depletes every time it is spent on something that
does not deserve it.
</p>
<p>
The post-Endgame MCU inherited an audience that had spent
eleven years being rewarded for paying attention. What it
offered in return was volume. Films that were
competently assembled but existed primarily to move pieces
into position for the next film. Disney+ series that were
mandatory viewing if you wanted to understand the cinema
releases, effectively converting a leisure choice into
homework. Characters introduced and abandoned. Storylines
launched and quietly dropped. The shared universe, which
had once been the payoff for engagement, became the reason
engagement was required just to follow along. The audience
began to disengage - not because superhero films stopped
being possible, but because the implicit contract had been
broken. The pipeline had forgotten that it was dependent
on the films being good, not merely connected.
</p>
<p>
Streaming did not fix this dynamic. It accelerated it and
added a new and more corrosive incentive on top: subscriber
acquisition. A studio making theatrical films needs a film
to be good enough that people will pay to see it. A streaming
platform needs content to exist in sufficient volume and
variety that enough different people will start a subscription
and not cancel it. These are genuinely different problems,
and the second one does not require quality - it requires
surface area.
</p>
<p>
Altered Carbon is a personal example of what this looks
like from the other side. The first season was genuinely
good - ambitious production design, a coherent adaptation
of its source material, the kind of science fiction that
takes its own premise seriously. I liked it. I kept
watching. I was exactly the subscriber Netflix should want
to retain. The show was cancelled after two seasons
regardless, and I eventually cancelled the subscription.
Those two facts are more connected than they might appear.
</p>
<p>
The issue is not that Netflix made a bad decision by its
own logic. The issue is what its logic measures. A
subscriber who genuinely loves three or four ambitious
series and watches them carefully registers identically to
a subscriber who half-watches twenty shows and never
finishes any of them - as long as both keep paying. The
metric that matters is new sign-ups, because that is what
the market rewards. Retention of engaged viewers who care
about specific content is a secondary concern at best.
Cancelling Altered Carbon did not cost Netflix the number
it was optimizing for. It cost them me - eventually - and
I was never the unit being counted.
</p>
<p>
What this produces is a library optimized not for quality
or loyalty but for the minimum threshold of good enough to
keep someone paying while the next acquisition drives new
sign-ups. The service does not need you to love it. It
needs you to not quite hate it enough to cancel. That is a
meaningful distinction. A library built around that
incentive looks very different from one built around making
things worth watching - and over time, the difference
becomes visible.
</p>
<div class="table-wrap">
<table class="attack-table">
<thead>
<tr>
<th>Model</th>
<th>Primary incentive</th>
<th>What it produces</th>
</tr>
</thead>
<tbody>
<tr>
<td>Theatrical studio</td>
<td>
Maximise opening weekend and franchise
extension potential
</td>
<td class="counter">
IP acquisitions, sequels, safe casting,
risk-averse greenlight decisions
</td>
</tr>
<tr>
<td>Streaming platform</td>
<td>
Subscriber acquisition and retention
across the broadest possible audience
</td>
<td class="counter">
High volume, uneven quality, early
cancellations, content churn
</td>
</tr>
<tr>
<td>Franchise extension</td>
<td>
Extract value from an established
property at minimal creative cost
</td>
<td class="counter">
Diminishing quality per instalment,
audience fatigue, brand erosion
</td>
</tr>
<tr>
<td>AI-assisted production</td>
<td>
Reduce per-unit content cost to near
zero while maintaining surface coverage
</td>
<td class="counter">
Structural collapse of the floor —
unlimited slop at no marginal cost
</td>
</tr>
</tbody>
</table>
</div>
<h2>AI Is Where This Was Always Going</h2>
<p>
The question AI puts to the industry is not a technical
one. It is a choice: do we hire a writer, or don't we?
Do we commission a director with a specific vision, or do
we generate the output that vision would have produced?
That choice is now explicit in a way it was not before.
And given everything the industry has already demonstrated
about its priorities - the franchise logic, the subscriber
metrics, the systematic elimination of creative risk —
there is not much reason to expect most studios and
platforms to choose the human when the alternative is
cheaper and more controllable.
</p>
<p>
This is what artists have been worried about, and they are
right - but not quite for the reason usually given. The
concern is often framed as AI producing bad films. The more
accurate concern is that AI produces films optimised for
the same thing the industry already optimises for: volume,
familiarity, minimum acceptable quality. AI does not
introduce a new set of values into the pipeline. It
enforces the existing ones more efficiently. A model
trained on what has performed before will reliably produce
more of what has performed before. For an industry that has
spent two decades treating proven formula as the safest
bet, that is not a warning. It is a feature.
</p>
<p>
What gets lost is not quality in the narrow sense - an
AI can produce something watchable, and watchable is
already the threshold most of the industry is aiming for.
What gets lost is the possibility of something genuinely
unexpected. A writer or director brings a specific
perspective that did not exist before, that cannot be
interpolated from what came before, and that occasionally
produces something the market did not know it wanted until
it arrived. The first Matrix was that. The early MCU was
that. The pipeline, fully enforced by AI, eliminates the
conditions under which that becomes possible. Every film
becomes a variation on the centroid of everything that
already worked. The ceiling does not disappear - it just
gets permanent.
</p>
<h2>What Would Fix It</h2>
<p>
The honest answer is that the incentive structure would
need to change, and there is no obvious mechanism by which
that happens from inside the industry. The studios that
consistently produce the best work are either small enough
to be insulated from franchise logic, backed by individuals
with enough capital and conviction to override short-term
return calculations, or operating in markets where the
economics of Hollywood do not apply in the same way.
</p>
<p>
Audiences have some leverage. Theatrical attendance is a
direct signal. Not watching a streaming series past its
first episode is a direct signal. The platforms and studios
read these signals carefully - they just tend to read them
as evidence that they need better marketing rather than
better films. That misreading is itself a symptom of an
industry that has fully internalised the idea that the
product is secondary to the pipeline.
</p>
<hr />
<p>
Cinema at its best is one of the few experiences that cannot
be fully replicated on a phone screen with half your
attention elsewhere. The industry has spent twenty years
making content that can be. That is not an accident. It is
what you get when every structural incentive points toward
volume, safety, and extraction - and nobody in a position
to change it has a strong enough reason to try.
</p>
</article>
<footer>
<a href="mailto:contact@samantha42.xyz">contact@samantha42.xyz</a>
<p>© 2026 — All rights reserved</p>
</footer>
</div>
</body>
</html>
+265
View File
@@ -0,0 +1,265 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Consensus Is the Problem — Samantha Vero Friis</title>
<link
href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<div class="container--narrow">
<header>
<a href="/" class="back">← back</a>
<p class="tag">// opinion</p>
<h1>Consensus Is<br />the<br /><span>Problem</span></h1>
<div class="byline">
<span>Samantha Vero Friis</span>
<span></span>
<span>Finance &amp; Investing</span>
</div>
</header>
<article>
<p class="lead">
A stock price is not a summary of a company's fundamentals.
It is the market's current best guess about the future.
The price already contains expectations. The question worth
asking is whether those expectations are right.
</p>
<h2>What a Price Is Really Saying</h2>
<p>
Every valuation implies a story. A company trading at forty
times earnings is not just expensive — it is telling you that
the market believes something specific: that growth will remain
high for long enough, that margins will hold, that the
competitive position is durable. Unpack the multiple into its
components and you have a set of forecasts baked into the
price. The investor's job is to decide whether those forecasts
are reasonable.
</p>
<p>
This reframing matters because it shifts the question from
"is this stock cheap?" to "is this story credible?" Cheap
stocks can be cheap for good reason. Expensive stocks can be
undervalued if the embedded assumptions are too conservative.
Value, in this sense, is not a property of price alone — it
is the gap between what the price implies and what reality is
likely to deliver.
</p>
<h2>The Consensus Problem</h2>
<p>
Markets aggregate information quickly and, on average, do it
well. But consensus is not the same as correctness. Consensus
is the average of what a large number of participants currently
believe, weighted by their capital. It reflects what is known,
what is legible, and what is socially acceptable to believe.
It is systematically slow to update on things that are
ambiguous, uncomfortable, or genuinely novel.
</p>
<p>
When an investment thesis is consensus — when every analyst
covers the stock, every major fund holds it, and the positive
narrative is repeated on every earnings call — the upside is
already in the price. There is no one left to convince. A
buyer who enters at that point is not getting paid for a
correct view. They are paying for the privilege of agreeing
with everyone else.
</p>
<blockquote>
The consensus trade is <span>already priced</span>.<br />
Only disagreement can <span>generate alpha</span>.
</blockquote>
<p>
This is the central problem with conventional value investing
as it is commonly practiced today. The stocks that look most
obviously cheap are often the ones where the consensus has
already decided they deserve to be cheap. The screening
criteria that identify "value" are, by construction, widely
known and widely applied. The edge that came from running
those screens in 1975 does not exist in the same form now.
</p>
<h2>Where Mispricing Actually Lives</h2>
<p>
Genuine mispricing tends to occur in situations where the
consensus view is wrong in a specific and identifiable way,
and where that wrongness has not yet been corrected. This
can happen for several structural reasons.
</p>
<p>
Institutional constraints create systematic blind spots. Fund
managers are evaluated on short cycles, penalized for holding
unfashionable names, and often prohibited from owning
securities outside their mandate. These pressures push capital
away from certain situations not because the economics are bad
but because the social and career costs of being wrong there
are high. The result is that some assets are systematically
underowned relative to their intrinsic value.
</p>
<p>
Narrative lag is another source. Companies undergoing genuine
change — a new management team, a shift in cost structure, an
improving competitive position — often continue to be priced
against their old story long after the underlying reality has
shifted. The market is very good at updating on quantitative
signals and very slow at updating on qualitative ones.
</p>
<p>
Complexity discounts are real and often excessive. A business
that is hard to model, that operates across multiple segments,
or that sits at the intersection of industries analysts do not
cover together will often trade at a lower multiple than a
simpler business with identical economics. That discount is not
always irrational — complexity carries risk — but it is often
overdone.
</p>
<div class="table-wrap">
<table class="attack-table">
<thead>
<tr>
<th>Source</th>
<th>Why consensus gets it wrong</th>
<th>What to look for</th>
</tr>
</thead>
<tbody>
<tr>
<td>Institutional constraints</td>
<td>
Capital avoids the name for structural,
not economic, reasons
</td>
<td class="counter">
Forced sellers, mandate mismatches,
index exclusions
</td>
</tr>
<tr>
<td>Narrative lag</td>
<td>
Price reflects the old story; reality
has already changed
</td>
<td class="counter">
Management change, cost restructuring,
improving unit economics
</td>
</tr>
<tr>
<td>Complexity discount</td>
<td>
Analysts underweight what they cannot
easily model
</td>
<td class="counter">
Conglomerates, cross-sector businesses,
opaque but healthy cash flows
</td>
</tr>
<tr>
<td>Sentiment overshoot</td>
<td>
Fear or enthusiasm has moved price far
beyond the rational range
</td>
<td class="counter">
Implied assumptions that require
implausible outcomes to justify current price
</td>
</tr>
</tbody>
</table>
</div>
<h2>The Role of Implied Expectations</h2>
<p>
The most disciplined version of this approach is to work
backwards from price. Rather than building a model and
concluding that a stock is cheap or expensive, start by
asking: what does this price require? What growth rate, what
margin profile, what reinvestment assumption would you need
to believe to justify paying what the market is asking today?
</p>
<p>
If the answer requires a long run of performance with no
credible foundation in the company's history, competitive
position, or industry structure — the price is wrong in one
direction. If the assumptions are so pessimistic that even a
mediocre outcome would beat them, it is wrong in the other.
</p>
<p>
This framing makes the investment case explicit and
falsifiable. It forces a specific disagreement with the
market rather than a vague sense that something is cheap or
expensive. And it clarifies what would need to be true for
the thesis to fail — which is at least as important as
knowing what would need to be true for it to succeed.
</p>
<h2>On Being Wrong in Public</h2>
<p>
A genuinely contrarian position is, by definition, one that
the consensus thinks is mistaken. Holding it requires being
comfortable with the fact that most informed observers
currently disagree with you, and that some period of time
will pass before — if — the price reflects the view you hold.
That discomfort is not incidental to the strategy. It is the
mechanism by which the return is generated. If the position
were comfortable, it would be consensus, and it would already
be priced.
</p>
<p>
This does not mean contrarianism for its own sake is sound.
Being different from the consensus is not the same as being
right. The goal is not to disagree with the market but to
identify specific, well-reasoned cases where the market's
current view is demonstrably wrong — and where the gap
between implied expectations and probable reality is large
enough to be worth the risk of being early, or simply
incorrect.
</p>
<hr />
<p>
Value is not a style. It is a discipline applied wherever
the math supports it. The companies that look most like
"value stocks" by conventional screens are often the ones
where the opportunity has already been arbitraged away. The
real work is in finding the situations where the consensus
has constructed a story the underlying economics cannot
support — or has failed to construct one that they clearly
do. That gap, wherever it appears, is where returns come
from.
</p>
</article>
<footer>
<a href="mailto:contact@samantha42.xyz">contact@samantha42.xyz</a>
<p>© 2026 — All rights reserved</p>
</footer>
</div>
</body>
</html>
-188
View File
@@ -1,188 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Git — Samantha Friis</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;700;800&display=swap" rel="stylesheet"/>
<link rel="stylesheet" href="styles.css"/>
</head>
<body>
<div class="container">
<header>
<a href="/" class="back">← back</a>
<p class="tag">// git</p>
<h1>Code &<br/><span>Repositories</span></h1>
<p class="header-sub">Personal projects &nbsp;·&nbsp; Private & public</p>
</header>
<main class="two-col">
<!-- ── LEFT: Login / Private Dashboard ── -->
<div>
<p class="section-label">// private access</p>
<div class="login-panel" id="login-panel">
<h2>Sign In</h2>
<p class="desc">Access to private repositories and personal workspace. Requires credentials and 2FA.</p>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="samantha" autocomplete="username" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="••••••••••••" autocomplete="current-password" />
</div>
<div class="twofa-group" id="twofa-group">
<label for="twofa">Authenticator Code</label>
<input type="text" id="twofa" placeholder="000 000" maxlength="7" autocomplete="one-time-code" />
<p class="twofa-hint">Enter the 6-digit code from your authenticator app.</p>
</div>
<button class="btn-login" id="login-btn" onclick="handleLogin()">Continue →</button>
<p class="login-error" id="login-error">Invalid credentials. Please try again.</p>
</div>
<!-- Private dashboard (shown after login) -->
<div id="private-dashboard">
<div class="repo-list">
<a href="#" class="repo-item">
<div>
<p class="repo-name">samantha-site</p>
<p class="repo-desc">Personal website and Go backend server</p>
</div>
<span class="repo-badge private">private</span>
</a>
<a href="#" class="repo-item">
<div>
<p class="repo-name">equity-tools</p>
<p class="repo-desc">Python scripts for SEC filing analysis and financial modeling</p>
</div>
<span class="repo-badge active">active</span>
</a>
<a href="#" class="repo-item">
<div>
<p class="repo-name">infra-config</p>
<p class="repo-desc">Dotfiles, server configs, and deployment scripts</p>
</div>
<span class="repo-badge private">private</span>
</a>
<a href="#" class="repo-item">
<div>
<p class="repo-name">typst-templates</p>
<p class="repo-desc">Custom Typst document templates for reports and analysis</p>
</div>
<span class="repo-badge active">active</span>
</a>
</div>
<button class="logout-btn" onclick="handleLogout()">← sign out</button>
</div>
</div>
<!-- ── RIGHT: Public section ── -->
<div>
<div class="repo-list">
<a href="/gitpage/TicTacToe" class="repo-item">
<div>
<p class="repo-name">TicTacToe</p>
<p class="repo-desc">Python learning tic tac toe game in terminal</p>
</div>
<span class="repo-badge private">private</span>
</a>
<a href="/gitpage/infra-config" class="repo-item">
<div>
<p class="repo-name">infra-config</p>
<p class="repo-desc">Dotfiles, server configs, and deployment scripts</p>
</div>
<span class="repo-badge private">private</span>
</a>
<a href="/gitpage/typst-templates" class="repo-item">
<div>
<p class="repo-name">typst-templates</p>
<p class="repo-desc">Custom Typst document templates for reports and analysis</p>
</div>
<span class="repo-badge active">active</span>
</a>
</div>
<button class="logout-btn" onclick="handleLogout()">← sign out</button>
</div>
</div>
</main>
<footer>
<a href="mailto:contact@samantha42.xyz">contact@samantha42.xyz</a>
<p class="footer-copy">&copy; 2026 — All rights reserved</p>
</footer>
</div>
<script>
let step = 1;
function handleLogin() {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value.trim();
const twofa = document.getElementById('twofa').value.trim();
const tfGroup = document.getElementById('twofa-group');
document.getElementById('login-error').classList.remove('visible');
if (step === 1) {
if (!username || !password) { showError('Please enter your username and password.'); return; }
step = 2;
tfGroup.classList.add('visible');
document.getElementById('login-btn').textContent = 'Verify →';
document.getElementById('twofa').focus();
} else {
if (!twofa || twofa.replace(/\s/g, '').length < 6) { showError('Enter the 6-digit code from your authenticator app.'); return; }
if (twofa.replace(/\s/g, '').match(/^\d{6}$/)) {
showDashboard();
} else {
showError('Invalid authenticator code.');
}
}
}
function showError(msg) {
const error = document.getElementById('login-error');
const panel = document.getElementById('login-panel');
error.textContent = msg;
error.classList.add('visible');
panel.classList.add('shake');
setTimeout(() => panel.classList.remove('shake'), 500);
}
function showDashboard() {
document.getElementById('login-panel').style.display = 'none';
document.getElementById('private-dashboard').classList.add('visible');
}
function handleLogout() {
step = 1;
document.getElementById('login-panel').style.display = '';
document.getElementById('private-dashboard').classList.remove('visible');
document.getElementById('username').value = '';
document.getElementById('password').value = '';
document.getElementById('twofa').value = '';
document.getElementById('twofa-group').classList.remove('visible');
document.getElementById('login-btn').textContent = 'Continue →';
document.getElementById('login-error').classList.remove('visible');
}
document.addEventListener('keydown', (e) => { if (e.key === 'Enter') handleLogin(); });
document.getElementById('twofa').addEventListener('input', (e) => {
let val = e.target.value.replace(/\D/g, '').slice(0, 6);
if (val.length > 3) val = val.slice(0, 3) + ' ' + val.slice(3);
e.target.value = val;
});
</script>
</body>
</html>