diff --git a/Todo.txt b/Todo.txt deleted file mode 100644 index adc1aed..0000000 --- a/Todo.txt +++ /dev/null @@ -1,23 +0,0 @@ -1. Current work/job if - --. Game list -- tech tools and eq research sections. -- phone support - - -- add a call to action section, tto reduce friction to contact me -- collapse interest sections, as not all people want to see that. in a pro enviroemnet -- make the the sun moon an more minimal svg icon. -- more curte ico -- 2049 on copy right, so i dont forget -- more unique desig(fun) -- always some thing happening - -- parallax scrolling on background. --- background may include, --- items are free falling --- computers(macentosch), black board, calculators. fun numbers(rainbow), books. coffe mugs - -- better portifolio intergration with link to repo. - -- company history. \ No newline at end of file diff --git a/source/42.html b/source/42.html deleted file mode 100644 index c5c4186..0000000 --- a/source/42.html +++ /dev/null @@ -1,153 +0,0 @@ - - - - - -42 - - - - - - -
-
42
-
-

So long, and thanks for all the fish.

-

Douglas Adams  ·  The Hitchhiker's Guide to the Galaxy

- -
-

shhh. i have a secret to share — follow me

-
-
- - - \ No newline at end of file diff --git a/source/admin.html b/source/admin.html deleted file mode 100644 index dc2b4a7..0000000 --- a/source/admin.html +++ /dev/null @@ -1,516 +0,0 @@ - - - - - - admin · samantha42.xyz - - - - - - - - - -
- - -
- - - -
-
-
subscribers
-
-
newsletter list
-
-
-
emails sent
-
-
all time
-
-
-
analyses
-
2
-
published
-
-
-
jobs ok
-
-
last 24h
-
-
- -
-
-

// job status

- -
- -
- -
- portfolio-engine - checking... - loading - -
- -
-
- -
-
-

// send newsletter

- sending... -
- -
-
- equity research · newsletter - to all subscribers -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
- -
-
-

// equity research

- send as newsletter ↑ -
- -
- MSTR - sell - Strategy · BTC proxy via debt issuance - pdf ↗ -
-
- NOVO.B / NVO - buy - Novo Nordisk · pipeline undervalued - pdf ↗ -
- -
-
-

// add new analysis

-
-
-
- - -
-
- - -
-
-
- - -
-
- - -
- -
-
-
- -
-
- - - - \ No newline at end of file diff --git a/source/cinema.html b/source/cinema.html deleted file mode 100644 index c1729ed..0000000 --- a/source/cinema.html +++ /dev/null @@ -1,383 +0,0 @@ - - - - - - The Industry That Ate Itself - Samantha Vero Friis - - - - -
-
- ← back -

// opinion

-

The Industry
That Ate
Itself

- -
- -
-

- 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. -

- -

How You Optimize a Creative Industry to Death

- -

- 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. -

- -

- 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. -

- -

The Franchise as a Business Model

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -
- A franchise is not a creative decision.
- It is a risk management strategy dressed as one. -
- -

Marvel and the Pipeline Fallacy

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ModelPrimary incentiveWhat it produces
Theatrical studio - Maximise opening weekend and franchise - extension potential - - IP acquisitions, sequels, safe casting, - risk-averse greenlight decisions -
Streaming platform - Subscriber acquisition and retention - across the broadest possible audience - - High volume, uneven quality, early - cancellations, content churn -
Franchise extension - Extract value from an established - property at minimal creative cost - - Diminishing quality per instalment, - audience fatigue, brand erosion -
AI-assisted production - Reduce per-unit content cost to near - zero while maintaining surface coverage - - Structural collapse of the floor — - unlimited slop at no marginal cost -
-
- -

AI Is Where This Was Always Going

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

What Would Fix It

- -

- 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. -

- -

- 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. -

- -
- -

- 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. -

-
- -
- - \ No newline at end of file diff --git a/source/cyber.html b/source/cyber.html deleted file mode 100644 index ea52f0b..0000000 --- a/source/cyber.html +++ /dev/null @@ -1,318 +0,0 @@ - - - - - - Cybersecurity — Samantha Vero Friis - - - - -
-
- ← back -

// opinion

-

The Weakest
Link Is Always
Human

- -
- -
-

- Most breaches are not the result of a sophisticated exploit - or a zero-day vulnerability. They are the result of a person - clicking the wrong link, reusing a password, or trusting a - phone call they should not have answered. Security is not - primarily a technical problem. It is an organizational one. -

- -

Know What You Are Protecting

- -

- Before you can build a security posture, you need to - understand your data. Not all information carries the same - weight. Customer financial records, internal credentials, - and strategic planning documents are not the same as a - public press release or a shared meeting agenda. Treating - all data as equally sensitive wastes resources and creates - compliance fatigue. Treating it all as equally safe is how - companies end up in the news. -

- -

- The first step is classification. Every organization should - have a clear and enforced taxonomy for what they hold — what - is critical, what is sensitive, and what is routine. Once - data is classified, access decisions, storage choices, and - transmission policies all become considerably more - straightforward. -

- -
-
-

● Critical

-

Restricted

-

- Credentials, financial records, legal documents, - private keys. Encrypted at rest and in transit. - Need-to-know access only. Audited on access. -

-
-
-

● Sensitive

-

Internal

-

- Strategy documents, personnel files, client data. - Role-based access. Not to leave internal systems - without explicit sign-off. -

-
-
-

● Standard

-

General

-

- Shared documentation, public-facing content, - operational guides. Normal access controls. No - special handling required. -

-
-
- -

Passwords Are Not a Personal Choice

- -

- The idea that employees should be trusted to choose and - manage their own passwords is one of the most persistent and - destructive myths in corporate security. People are not good - at passwords. They reuse them across services, they write - them down, they choose predictable patterns, and they resist - changing them when asked. This is not a character flaw — it - is human nature operating against a task humans were never - designed to do well. -

- -

- The answer is a mandatory, company-wide password manager. - Not recommended. Not optional. Mandatory. Every account - created for company use lives in the vault. Credentials are - generated by the manager — long, random, unique per service. - No employee ever needs to know or remember most of them. - Access to the vault itself is protected by a strong master - passphrase and hardware-backed two-factor authentication. -

- -
- A password an employee cannot remember is a - password an attacker cannot guess. -
- -

Two-Factor Is Not Optional

- -

- Two-factor authentication is the single highest-return - security investment any organization can make. A stolen - password without the second factor is nearly useless. Yet an - astonishing number of companies treat 2FA as something to - enable "if employees want to." That framing gets it exactly - backwards. -

- -

- Every account with access to company systems requires 2FA, - enforced at the policy level — not left to the individual. - Preference should go to hardware security keys like - YubiKey or authenticator apps generating - time-based codes. SMS-based 2FA is better than nothing, but - SIM-swap attacks make it the weakest option and it should be - phased out wherever possible. -

- -

- Enrollment should happen on day one, before access is - granted to any system. Not after. The moment an employee has - a credential without a second factor is a window of exposure - that does not need to exist. -

- -

Make Access Deliberately Hard

- -

- This sounds counterintuitive. Friction is the enemy of - productivity — until it is the thing standing between your - database and someone who found a badge in the parking lot. - The principle of least privilege is not just a compliance - checkbox. It is an architecture decision. -

- -

- Employees should have access to exactly what they need to do - their job, and nothing more. Access should be reviewed on a - schedule and revoked immediately upon role change or - departure. Broad access to critical systems should require - explicit justification, a second approver, and a time limit. - The default answer to an access request is not yes — it is: - why do you need this, for how long, and who else knows? -

- -

- This friction is not about distrust. It is about reducing - the blast radius of any single compromised account. If an - attacker gains access to a junior analyst's credentials, - they should find themselves in a narrow corridor, not a wide - open floor. -

- -

Social Engineering Is the Real Attack Surface

- -

- Technical controls can be near-perfect and still fail the - moment someone calls the help desk pretending to be a - manager who has been locked out while traveling. Social - engineering works because it exploits social instincts that - are otherwise useful — the tendency to help, to defer to - authority, to avoid confrontation. -

- -

- Mitigating this requires two things: process and culture. - Process means that no credential reset, no access grant, and - no sensitive disclosure ever happens over a phone call or an - email alone, regardless of how urgent it sounds or who the - request appears to come from. Verification must follow a - separate, pre-established channel. The louder someone - insists the situation is urgent, the more carefully the - process should be followed. -

- -

- Culture means that employees feel safe slowing down and - saying no. An employee who challenges an unusual request - should be recognized for it, not made to feel obstructive. - The organization needs to make it socially acceptable — even - admirable — to be the person who held the line. -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Attack VectorMethodCountermeasure
Phishing - Fraudulent email mimicking a trusted sender - to harvest credentials or deploy malware - - Email filtering, DMARC/DKIM, regular drill - simulations, no credential entry from email - links -
Vishing - Voice call impersonating IT, management, or - a vendor to extract access or trigger an - action - - Zero verbal credential resets. All requests - verified via a separate known channel, no - exceptions -
Credential Stuffing - Automated use of leaked passwords from other - breaches against company systems - - Mandatory password manager with unique - credentials per service. 2FA blocks the rest -
Insider Threat - Disgruntled or compromised employee - exfiltrating data or granting unauthorized - access - - Least-privilege access, access logging and - anomaly alerts, prompt offboarding - procedures -
Pretexting - Fabricated scenario (new hire, auditor, - vendor) to gain physical or digital access - - Visitor protocols, escort requirements, no - tailgating culture, ID verification for all - physical access -
-
- -

Security Is a Continuous Practice

- -

- A one-time audit followed by a certificate on the wall is - not a security posture. It is a snapshot. Threat landscapes - change, personnel changes, and systems change. Security - needs to be a living practice — regular access reviews, - periodic credential audits, tabletop exercises for breach - scenarios, and honest post-mortems when something goes - wrong. -

- -

- The companies that handle breaches best are not necessarily - the ones that prevented them — they are the ones that - detected them fast, contained them well, and communicated - clearly. That kind of response capability does not appear on - the day of the incident. It is built over years of treating - security as infrastructure, not an afterthought. -

- -
- -

- You cannot patch people. But you can build systems and - cultures that make it very difficult for human error to - become organizational catastrophe. Mandatory 2FA. Mandatory - password managers. Minimal access by default. Verification - processes that do not bend under social pressure. These are - not sophisticated controls. They are disciplined ones — and - discipline, consistently applied, is the closest thing to - security that actually exists. -

-
- - -
- - diff --git a/source/engine.html b/source/engine.html deleted file mode 100644 index 80c2771..0000000 --- a/source/engine.html +++ /dev/null @@ -1,314 +0,0 @@ - - - - - - FP&A Budgeting Engine — Samantha Vero Friis - - - - - - - - -
-
- ← back - -
project
- -

FP&A Budgeting Engine

- - -
-
- - -
-
-
-
Language
-
Go
-
-
-
Database
-
SQLite
-
-
-
Architecture
-
REST API
-
-
-
Infrastructure
-
Zero deps
-
-
-
- - -
-
-
- -

- 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. -

- -

The problem it solves

- -

- 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&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. -

- -

- 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. -

- -
- The goal was not to build something clever.
- It was to build something that solves a real problem
- finance teams in the mid-market actually have. -
- -

Stack

- -

- 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. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LayerChoiceReason
LanguageGoSingle binary, no runtime, explicit errors
Routingnet/http (stdlib)No deps; method+path routing since Go 1.22
DatabaseSQLiteZero infrastructure, file-based, ships with binary
SchemaAuto-migratedCREATE TABLE IF NOT EXISTS on startup
-
- -

API surface

- -

- 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. -

- -
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
- -

- Variance endpoints accept year, period, - dept, version, and threshold - as query parameters. -

- -

Finance concepts implemented correctly

- -

- Favourability logic. 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 favour_high flag per GL - account — a distinction generic reporting tools consistently leave to manual - configuration. -

- -

- Budget versioning. 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&A reforecast workflow. -

- -

- GL account typing. Accounts are - typed as revenue, cogs, opex, - capex, or headcount. This drives the P&L - rollup structure and the favourability logic together, reflecting how an - actual chart of accounts is structured. -

- -

- Fiscal period decoupling. Periods - are stored as integers 1–12, decoupled from calendar months — required for - any company not on a January fiscal year. -

- -

Example output

- -

- Engineering department, FY2024 P09. Cloud infrastructure came in under budget. - Consulting exceeded by 51%. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GLDescriptionBudgetActualVar %Status
6100Salaries & Wages4,200k4,380k−4.29%unfavourable
6500Consulting & Contractors450k680k−51.11%unfavourable
5000Cloud Infrastructure850k791k+6.94%favourable
-
- -

Running it

- -
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
- -

- Two environment variables: DB_PATH (use :memory: - for tests) and PORT. Demo seed data for Engineering, Sales, - and Marketing is included in scripts/seed_demo.sql. -

- -
- -

- Source at - git.samantha42.xyz/samantha/FPandA-Engine -

- -
-
-
- - - - - \ No newline at end of file diff --git a/source/finance.html b/source/finance.html deleted file mode 100644 index 6c6dc64..0000000 --- a/source/finance.html +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - Consensus Is the Problem — Samantha Vero Friis - - - - -
-
- ← back -

// opinion

-

Consensus Is
the
Problem

- -
- -
-

- 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. -

- -

What a Price Is Really Saying

- -

- 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. -

- -

- 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. -

- -

The Consensus Problem

- -

- 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. -

- -

- 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. -

- -
- The consensus trade is already priced.
- Only disagreement can generate alpha. -
- -

- 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. -

- -

Where Mispricing Actually Lives

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -

- 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. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SourceWhy consensus gets it wrongWhat to look for
Institutional constraints - Capital avoids the name for structural, - not economic, reasons - - Forced sellers, mandate mismatches, - index exclusions -
Narrative lag - Price reflects the old story; reality - has already changed - - Management change, cost restructuring, - improving unit economics -
Complexity discount - Analysts underweight what they cannot - easily model - - Conglomerates, cross-sector businesses, - opaque but healthy cash flows -
Sentiment overshoot - Fear or enthusiasm has moved price far - beyond the rational range - - Implied assumptions that require - implausible outcomes to justify current price -
-
- -

The Role of Implied Expectations

- -

- 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? -

- -

- 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. -

- -

- 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. -

- -

On Being Wrong in Public

- -

- 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. -

- -

- 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. -

- -
- -

- 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. -

-
- - -
- - \ No newline at end of file diff --git a/source/icon.png b/source/icon.png deleted file mode 100644 index 89ae401..0000000 Binary files a/source/icon.png and /dev/null differ diff --git a/source/infra.html b/source/infra.html deleted file mode 100644 index 4f11ab4..0000000 --- a/source/infra.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - Infrastructure — Samantha Vero Friis - - - - -
-
- ← back -

// opinion

-

Convenience
Is Making Us
Worse

- -
- -
-

- The tools a company chooses to build on top of says a lot - about how serious it is about the craft. The industry-wide - drift toward Word, Teams, and Microsoft 365 is not a - productivity upgrade — it is a slow erosion of competence - dressed up as convenience. -

- -

The Convenience Trap

- -

- Every tool that hides complexity from you takes something - away. When a new hire can open a laptop running Windows, - install Office in ten minutes, and be "operational" by noon, - that feels like a win. But operational is not the same as - capable. What that person has actually learned is how to - navigate a UI. They have not learned how their machine - works, how their data flows, or how to adapt when the - abstraction breaks. -

- -

- Large vendor ecosystems like Microsoft 365 are engineered - for the lowest common denominator. They are excellent at - onboarding someone with no background — and they stop there. - The ceiling is built in by design. You cannot meaningfully - extend Word. You cannot script Teams in any serious way. You - are a tenant inside someone else's system, paying monthly - for the privilege of not understanding what is happening - underneath you. -

- -
- Convenience optimizes for the speed of the - newcomer.
- It does not care about the ceiling of the - expert. -
- -

What We Trade Away

- -

- The deeper issue is what gets lost over time. A team that - has never had to configure anything has never had to - understand anything. When your document pipeline is Word - plus SharePoint, you never learn what a proper typesetting - system looks like. When your servers are Azure with a - click-through UI, you never develop intuition for - networking, permissions, or failure modes. The knowledge - simply does not accumulate. -

- -

- The companies most dependent on big vendor stacks also tend - to be the most fragile. A license change, a price increase, - a deprecated feature — and suddenly nobody knows what to do. - There is no internal knowledge to fall back on. The stack - was always a rented abstraction. -

- -
-
-

Vendor-Locked Stack

-
    -
  • Fast to start, hard to master
  • -
  • Knowledge lives in the UI, not the team
  • -
  • Expensive at scale, locked by contracts
  • -
  • No meaningful customization
  • -
  • Fragile when the vendor changes terms
  • -
  • Homogeneous — everyone works the same way
  • -
-
-
-

Custom Infrastructure

-
    -
  • Slower to start, no ceiling
  • -
  • Knowledge compounds inside the team
  • -
  • Cheap at scale, you own what you build
  • -
  • Configured precisely for your workflow
  • -
  • Resilient — you understand every layer
  • -
  • Personal — the environment fits the person
  • -
-
-
- -

Linux as a Philosophy

- -

- A Linux desktop is not just a different operating system. It - is a different relationship with your machine. You configure - your window manager, your keybindings, your terminal, your - dotfiles. The environment becomes an extension of how you - think. Two engineers on the same team can have entirely - different setups that are both optimized for their own - cognitive style — and that is a feature, not a problem. -

- -

- This is not about being difficult or contrarian. It is about - the fact that a person who has spent time configuring - neovim, managing a - systemd service, or writing their own shell - scripts has fundamentally more understanding of computation - than someone who has spent the same time clicking through - GUIs. That understanding compounds. It transfers across - every problem they touch. -

- -

Documents Should Be Code

- -

- The same logic applies to document production. Word - processing is a GUI wrapped around formatting decisions you - did not make. The output is inconsistent, the versioning is - painful, and the result is never quite right. A typesetting - system like Typst treats a document as source - code — deterministic, version-controlled, composable. You - write a template once and it is correct everywhere, forever. - The document becomes a reproducible artifact. -

- -

- Custom pipelines like this require an upfront investment. - You have to learn the tool, build the template, establish - the workflow. But once built, the leverage is enormous. - Reports, invoices, analysis documents — all generated - cleanly from structured data, with zero manual formatting. - That is not possible in Word. It is not even imaginable in - Word. -

- -

Own Your Servers

- -

- Self-hosted infrastructure demands that someone in your - organization understands it. That is not a burden — it is - the point. When you run your own services, you know exactly - where your data lives, what it costs to run, and what - happens when something fails. You build operational - knowledge that no managed cloud service can give you. -

- -

- This does not mean rejecting cloud entirely. It means being - intentional. Use the cloud where the economics clearly favor - it. Build and operate everything else yourself. The goal is - a team that could reconstruct its own stack from scratch if - it had to — because a team capable of that is a team that - actually understands what it has built. -

- -
- -

- The best infrastructure is not the most convenient. It is - the most understood. Tools that demand something from you — - configuration, learning, patience — are the tools that make - you better over time. Convenience is a ceiling. Complexity, - embraced deliberately, is a floor that keeps getting higher. -

-
- - -
- - diff --git a/source/login.html b/source/login.html deleted file mode 100644 index f5a9a15..0000000 --- a/source/login.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - Samantha Vero Friis - - - - - - - - - - - -
-
-
- ← back -

samantha42.xyz

-

Secure Portal

-
-
- -
- -
-
- - -
- -
- - -
- - -

// verifying...

-
- -
-
-
- - - - - \ No newline at end of file diff --git a/source/secret.html b/source/secret.html deleted file mode 100644 index 90c113b..0000000 --- a/source/secret.html +++ /dev/null @@ -1,236 +0,0 @@ - - - -
- -
-
-
-
- samantha42@secret:~$ - -
-
-
-
-
- - diff --git a/source/static/gamesbanners/Tombraider.png b/source/static/gamesbanners/Tombraider.png deleted file mode 100644 index 46daae7..0000000 Binary files a/source/static/gamesbanners/Tombraider.png and /dev/null differ diff --git a/source/static/gamesbanners/baldursgate3.jpg b/source/static/gamesbanners/baldursgate3.jpg deleted file mode 100644 index fc60c6c..0000000 Binary files a/source/static/gamesbanners/baldursgate3.jpg and /dev/null differ diff --git a/source/static/gamesbanners/detroitbecomehuman.jpg b/source/static/gamesbanners/detroitbecomehuman.jpg deleted file mode 100644 index 046c76c..0000000 Binary files a/source/static/gamesbanners/detroitbecomehuman.jpg and /dev/null differ diff --git a/source/static/gamesbanners/factorio.jpg b/source/static/gamesbanners/factorio.jpg deleted file mode 100644 index 1928e80..0000000 Binary files a/source/static/gamesbanners/factorio.jpg and /dev/null differ diff --git a/source/static/gamesbanners/gtav.webp b/source/static/gamesbanners/gtav.webp deleted file mode 100644 index a766141..0000000 Binary files a/source/static/gamesbanners/gtav.webp and /dev/null differ diff --git a/source/static/gamesbanners/minecraft.webp b/source/static/gamesbanners/minecraft.webp deleted file mode 100644 index 85cca1d..0000000 Binary files a/source/static/gamesbanners/minecraft.webp and /dev/null differ diff --git a/source/static/gamesbanners/planetzoo.webp b/source/static/gamesbanners/planetzoo.webp deleted file mode 100644 index b6b1f5b..0000000 Binary files a/source/static/gamesbanners/planetzoo.webp and /dev/null differ diff --git a/source/static/gamesbanners/riskofrain2.jpg b/source/static/gamesbanners/riskofrain2.jpg deleted file mode 100644 index 8e42c74..0000000 Binary files a/source/static/gamesbanners/riskofrain2.jpg and /dev/null differ diff --git a/source/static/gamesbanners/slaythespire2.png b/source/static/gamesbanners/slaythespire2.png deleted file mode 100644 index 4f4ab09..0000000 Binary files a/source/static/gamesbanners/slaythespire2.png and /dev/null differ diff --git a/source/static/icons/Invoice-OKZP3LVL-0011.pdf b/source/static/icons/Invoice-OKZP3LVL-0011.pdf deleted file mode 100644 index 8a8dc24..0000000 Binary files a/source/static/icons/Invoice-OKZP3LVL-0011.pdf and /dev/null differ diff --git a/source/static/icons/half-moon.svg b/source/static/icons/half-moon.svg deleted file mode 100644 index d3bf344..0000000 --- a/source/static/icons/half-moon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/source/static/icons/leafeon-the-eevees-and-umbreon-l3kahsutrpv30hko.jpg b/source/static/icons/leafeon-the-eevees-and-umbreon-l3kahsutrpv30hko.jpg deleted file mode 100644 index 81648d5..0000000 Binary files a/source/static/icons/leafeon-the-eevees-and-umbreon-l3kahsutrpv30hko.jpg and /dev/null differ diff --git a/source/static/icons/magic-moon.png b/source/static/icons/magic-moon.png deleted file mode 100644 index 9b17b6f..0000000 Binary files a/source/static/icons/magic-moon.png and /dev/null differ diff --git a/source/static/icons/mooncloud.png b/source/static/icons/mooncloud.png deleted file mode 100644 index 0972b73..0000000 Binary files a/source/static/icons/mooncloud.png and /dev/null differ diff --git a/source/static/icons/sleepmoon.png b/source/static/icons/sleepmoon.png deleted file mode 100644 index 2209b5a..0000000 Binary files a/source/static/icons/sleepmoon.png and /dev/null differ diff --git a/source/static/icons/sun-light.svg b/source/static/icons/sun-light.svg deleted file mode 100644 index e1bdbdb..0000000 --- a/source/static/icons/sun-light.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/source/static/icons/sun.png b/source/static/icons/sun.png deleted file mode 100644 index e980f8a..0000000 Binary files a/source/static/icons/sun.png and /dev/null differ diff --git a/source/static/images/97143-200.png b/source/static/images/97143-200.png deleted file mode 100644 index 9826374..0000000 Binary files a/source/static/images/97143-200.png and /dev/null differ diff --git a/source/static/images/book.png b/source/static/images/book.png deleted file mode 100644 index d0da635..0000000 Binary files a/source/static/images/book.png and /dev/null differ diff --git a/source/static/images/books.png b/source/static/images/books.png deleted file mode 100644 index 772e241..0000000 Binary files a/source/static/images/books.png and /dev/null differ diff --git a/source/static/images/calc.png b/source/static/images/calc.png deleted file mode 100644 index 96e07f1..0000000 Binary files a/source/static/images/calc.png and /dev/null differ diff --git a/source/static/images/coffe.png b/source/static/images/coffe.png deleted file mode 100644 index 4fd1fcd..0000000 Binary files a/source/static/images/coffe.png and /dev/null differ diff --git a/source/static/images/files.png b/source/static/images/files.png deleted file mode 100644 index c132375..0000000 Binary files a/source/static/images/files.png and /dev/null differ diff --git a/source/static/images/games.png b/source/static/images/games.png deleted file mode 100644 index cd35ed1..0000000 Binary files a/source/static/images/games.png and /dev/null differ diff --git a/source/static/images/mac.png b/source/static/images/mac.png deleted file mode 100644 index 13bf902..0000000 Binary files a/source/static/images/mac.png and /dev/null differ diff --git a/source/static/images/map.png b/source/static/images/map.png deleted file mode 100644 index 3c3e4c6..0000000 Binary files a/source/static/images/map.png and /dev/null differ diff --git a/source/static/images/rabbit.svg b/source/static/images/rabbit.svg deleted file mode 100644 index 5c1852d..0000000 --- a/source/static/images/rabbit.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/source/static/movies.md b/source/static/movies.md deleted file mode 100644 index c8f7435..0000000 --- a/source/static/movies.md +++ /dev/null @@ -1,69 +0,0 @@ -= Top 5 Movies -1. Blade Runner 2049 -2. A cure of Wellness -3. Fight Club -4. Dracula: A Love Tale -5. Girl with a Dragon Tattoo - - -= Movies -- Tenet -- The Outrun -- Hail Mary -- Day Break -- l, Tonya (2018) -- Caddo lake (2024) -- The Shawshank Redemption -- Fight Club -- Perl -- A Cure of Wellness, (2016) -- green book -- spirited away -- Saltburn -- Dracula: A Love Tale (2025) -- Frankenstein (2015) -- The Fall Guy -- The Notebook -- Primal Fear -- blade runner 2049 -- girl with a dragon tatto -- Wicked -- Matrix -- Book of Eli -- Pulp Fiction -- Midsommer -- Living -- Moneyball -- Legally Blonde (2001) -- Dune -- Internal sunshine - - -== Danish -- A royal affiar -- Adams æbler - -= Top 5 Shows -1. Chernobyl -2. Succesion -3. Breaking Bad -4. Avatar, the last air bender -5. Adolescence - -= Shows -- succesion -- Breaking bad -- Adolescence (2025) -- The penguin -- Community -- Avatar, the last air bender -- Foundation -- Lessons in Chemistry -- Hanna -- You -- killing eve -- ozark -- Chernobyl -- The wire - - diff --git a/source/static/posters/AcureForWellness.jpg b/source/static/posters/AcureForWellness.jpg deleted file mode 100644 index 9afda5e..0000000 Binary files a/source/static/posters/AcureForWellness.jpg and /dev/null differ diff --git a/source/static/posters/Adolescence.webp b/source/static/posters/Adolescence.webp deleted file mode 100644 index 578a922..0000000 Binary files a/source/static/posters/Adolescence.webp and /dev/null differ diff --git a/source/static/posters/Avatar.webp b/source/static/posters/Avatar.webp deleted file mode 100644 index 025d73d..0000000 Binary files a/source/static/posters/Avatar.webp and /dev/null differ diff --git a/source/static/posters/Breaking Bad.webp b/source/static/posters/Breaking Bad.webp deleted file mode 100644 index 18414e3..0000000 Binary files a/source/static/posters/Breaking Bad.webp and /dev/null differ diff --git a/source/static/posters/Chernobyl.webp b/source/static/posters/Chernobyl.webp deleted file mode 100644 index 7a39b96..0000000 Binary files a/source/static/posters/Chernobyl.webp and /dev/null differ diff --git a/source/static/posters/Dracula.webp b/source/static/posters/Dracula.webp deleted file mode 100644 index b123e2d..0000000 Binary files a/source/static/posters/Dracula.webp and /dev/null differ diff --git a/source/static/posters/Fightclub.jpg b/source/static/posters/Fightclub.jpg deleted file mode 100644 index 7662808..0000000 Binary files a/source/static/posters/Fightclub.jpg and /dev/null differ diff --git a/source/static/posters/Succession.webp b/source/static/posters/Succession.webp deleted file mode 100644 index 92e72be..0000000 Binary files a/source/static/posters/Succession.webp and /dev/null differ diff --git a/source/static/posters/TheGirlwiththeDragonTattoo.webp b/source/static/posters/TheGirlwiththeDragonTattoo.webp deleted file mode 100644 index 377fa67..0000000 Binary files a/source/static/posters/TheGirlwiththeDragonTattoo.webp and /dev/null differ diff --git a/source/static/posters/bladerunner2049.jpg b/source/static/posters/bladerunner2049.jpg deleted file mode 100644 index 28879f9..0000000 Binary files a/source/static/posters/bladerunner2049.jpg and /dev/null differ diff --git a/source/static/posters/foundation.jpg b/source/static/posters/foundation.jpg deleted file mode 100644 index 7edf378..0000000 Binary files a/source/static/posters/foundation.jpg and /dev/null differ diff --git a/source/static/posters/matrix.jpg b/source/static/posters/matrix.jpg deleted file mode 100644 index 1f43c1d..0000000 Binary files a/source/static/posters/matrix.jpg and /dev/null differ diff --git a/source/static/posters/risetr-banner.png b/source/static/posters/risetr-banner.png deleted file mode 100644 index 28a69fe..0000000 Binary files a/source/static/posters/risetr-banner.png and /dev/null differ diff --git a/source/static/scripts/posteranimation.js b/source/static/scripts/posteranimation.js deleted file mode 100644 index a7c9534..0000000 --- a/source/static/scripts/posteranimation.js +++ /dev/null @@ -1,742 +0,0 @@ -function cureClick() { - if (document.getElementById('cure-canvas')) return; - if (document.getElementById('lightning-canvas')) stopLightning(); - if (document.getElementById('typewriter-canvas')) stopTypewriter(); - if (document.getElementById('matrix-canvas')) stopMatrix(); - if (document.getElementById('rain-canvas')) stopRain(); - - const el = document.getElementById('interest'); - el.textContent = "Actually... I'm feeling much better now!" - startWater(); - isDay = false; - makenight(); - setTimeout(() => { - el.textContent = 'interest'; - stopWater(); - }, 10000); -} - -let cureCanvas, cureCtx, cureAnimFrame; - -function startWater() { - cureCanvas = document.createElement('canvas'); - cureCanvas.id = 'cure-canvas'; - cureCanvas.width = window.innerWidth; - cureCanvas.height = window.innerHeight; - Object.assign(cureCanvas.style, { - position: 'fixed', - top: 0, - left: 0, - pointerEvents: 'none', - zIndex: -1, // behind everything - opacity: '0', - transition: 'opacity 1.4s ease' - }); - document.body.appendChild(cureCanvas); - cureCtx = cureCanvas.getContext('2d'); - requestAnimationFrame(() => { cureCanvas.style.opacity = '1'; }); - - const W = cureCanvas.width; - const H = cureCanvas.height; - - // waterline sits at 38% from the top - const waterlineY = H * 0.38; - - // floating particles below the surface - const particles = Array.from({ length: 55 }, () => ({ - x: Math.random() * W, - y: waterlineY + 20 + Math.random() * (H - waterlineY - 20), - r: 1 + Math.random() * 2.5, - speedX: (Math.random() - 0.5) * 0.3, - speedY: -0.08 - Math.random() * 0.18, // slow upward drift - opacity: 0.08 + Math.random() * 0.18 - })); - - function drawFrame(ts) { - cureCtx.clearRect(0, 0, W, H); - - // ── sky above waterline (very subtle, mostly transparent) ────────── - const sky = cureCtx.createLinearGradient(0, 0, 0, waterlineY); - sky.addColorStop(0, 'rgba(190, 230, 255, 0.06)'); - sky.addColorStop(1, 'rgba(190, 230, 255, 0.01)'); - cureCtx.fillStyle = sky; - cureCtx.fillRect(0, 0, W, waterlineY); - - // ── water body below waterline ───────────────────────────────────── - const water = cureCtx.createLinearGradient(0, waterlineY, 0, H); - water.addColorStop(0, 'rgba(80, 190, 220, 0.28)'); - water.addColorStop(0.35, 'rgba(40, 140, 190, 0.22)'); - water.addColorStop(0.7, 'rgba(20, 90, 150, 0.20)'); - water.addColorStop(1, 'rgba(10, 50, 110, 0.22)'); - cureCtx.fillStyle = water; - cureCtx.fillRect(0, waterlineY, W, H - waterlineY); - - // ── caustic shimmer streaks ──────────────────────────────────────── - const numStreaks = 10; - for (let i = 0; i < numStreaks; i++) { - const phase = (ts * 0.0004 + i * 0.63) % 1; - const x = (i / numStreaks) * W + Math.sin(ts * 0.0008 + i) * 60; - const yTop = waterlineY + 10 + Math.sin(ts * 0.001 + i * 1.3) * 18; - const yBot = yTop + 40 + Math.sin(ts * 0.0007 + i * 0.9) * 30; - const streak = cureCtx.createLinearGradient(x, yTop, x + 6, yBot); - streak.addColorStop(0, 'rgba(200, 245, 255, 0)'); - streak.addColorStop(0.4, `rgba(200, 245, 255, ${0.05 + 0.06 * Math.sin(ts * 0.002 + i)})`); - streak.addColorStop(1, 'rgba(200, 245, 255, 0)'); - cureCtx.fillStyle = streak; - cureCtx.fillRect(x, yTop, 5, yBot - yTop); - } - - // ── floating particles ───────────────────────────────────────────── - particles.forEach(p => { - p.x += p.speedX; - p.y += p.speedY; - // reset when reaching the surface - if (p.y < waterlineY + 5) { - p.y = H - 10; - p.x = Math.random() * W; - } - if (p.x < 0) p.x = W; - if (p.x > W) p.x = 0; - - const depth = (p.y - waterlineY) / (H - waterlineY); // 0 = surface, 1 = bottom - cureCtx.beginPath(); - cureCtx.arc(p.x, p.y, p.r, 0, Math.PI * 2); - cureCtx.fillStyle = `rgba(180, 240, 255, ${p.opacity * (0.4 + 0.6 * (1 - depth))})`; - cureCtx.fill(); - }); - - // ── animated surface waves (2 layers, different speeds & amplitudes) ─ - function drawWaveLine(amp, freq, speed, lineW, alpha) { - cureCtx.beginPath(); - for (let x = 0; x <= W; x += 3) { - const y = waterlineY - + Math.sin(x * freq + ts * speed) * amp - + Math.sin(x * freq * 1.7 + ts * speed * 0.6) * (amp * 0.4); - x === 0 ? cureCtx.moveTo(x, y) : cureCtx.lineTo(x, y); - } - cureCtx.strokeStyle = `rgba(210, 245, 255, ${alpha})`; - cureCtx.lineWidth = lineW; - cureCtx.stroke(); - } - - // primary wave - drawWaveLine(7, 0.012, 0.0018, 2.0, 0.70); - // secondary wave (slightly offset phase) - drawWaveLine(4, 0.018, 0.0024, 1.2, 0.40); - // micro ripple - drawWaveLine(2, 0.030, 0.0032, 0.7, 0.25); - - // ── foam/highlight on the crest ──────────────────────────────────── - cureCtx.beginPath(); - for (let x = 0; x <= W; x += 3) { - const y = waterlineY - + Math.sin(x * 0.012 + ts * 0.0018) * 7 - + Math.sin(x * 0.020 + ts * 0.0024) * 3; - x === 0 ? cureCtx.moveTo(x, y - 1) : cureCtx.lineTo(x, y - 1); - } - cureCtx.strokeStyle = 'rgba(255, 255, 255, 0.18)'; - cureCtx.lineWidth = 1; - cureCtx.stroke(); - - if (cureCanvas) cureAnimFrame = requestAnimationFrame(drawFrame); - } - - cureAnimFrame = requestAnimationFrame(drawFrame); -} - -function stopWater() { - if (!cureCanvas) return; - cureCanvas.style.opacity = '0'; - cancelAnimationFrame(cureAnimFrame); - setTimeout(() => { - cureCanvas.remove(); - cureCanvas = null; - }, 1400); -} - -function draculaClick() { - if (document.getElementById('lightning-canvas')) return; - - if (document.getElementById('cure-canvas')) stopWater(); - if (document.getElementById('typewriter-canvas')) stopTypewriter(); - if (document.getElementById('matrix-canvas')) stopMatrix(); - if (document.getElementById('rain-canvas')) stopRain(); - - const el = document.getElementById('interest'); - el.textContent = 'You have your whole life ahead of you. And I only offer death.'; - startLightning(); - isDay = false; - makenight(); - setTimeout(() => { - el.textContent = 'interest'; - stopLightning(); - }, 10000); -} - -let lightningCanvas, lightningCtx, lightningInterval, lightningFlashTimeout; - -function startLightning() { - lightningCanvas = document.createElement('canvas'); - lightningCanvas.id = 'lightning-canvas'; - lightningCanvas.width = window.innerWidth; - lightningCanvas.height = window.innerHeight; - Object.assign(lightningCanvas.style, { - position: 'fixed', top: 0, left: 0, - pointerEvents: 'none', zIndex: 9999, - opacity: '0', transition: 'opacity 0.3s ease' - }); - document.body.appendChild(lightningCanvas); - lightningCtx = lightningCanvas.getContext('2d'); - requestAnimationFrame(() => { lightningCanvas.style.opacity = '1'; }); - - function drawBolt(x1, y1, x2, y2, roughness, depth) { - if (depth === 0) { - lightningCtx.beginPath(); - lightningCtx.moveTo(x1, y1); - lightningCtx.lineTo(x2, y2); - lightningCtx.stroke(); - return; - } - const mx = (x1 + x2) / 2 + (Math.random() - 0.5) * roughness; - const my = (y1 + y2) / 2 + (Math.random() - 0.5) * roughness; - drawBolt(x1, y1, mx, my, roughness / 2, depth - 1); - drawBolt(mx, my, x2, y2, roughness / 2, depth - 1); - // random fork branch - if (depth > 2 && Math.random() < 0.4) { - const fx = mx + (Math.random() - 0.3) * roughness * 2; - const fy = my + Math.random() * roughness * 2; - lightningCtx.save(); - lightningCtx.globalAlpha *= 0.5; - drawBolt(mx, my, fx, fy, roughness / 2, depth - 2); - lightningCtx.restore(); - } - } - - function flashStrike() { - lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height); - - const numBolts = 1 + Math.floor(Math.random() * 3); - for (let i = 0; i < numBolts; i++) { - const startX = lightningCanvas.width * (0.1 + Math.random() * 0.8); - const endX = startX + (Math.random() - 0.5) * 300; - const color = Math.random() < 0.6 - ? `rgba(220, 180, 255, ${0.6 + Math.random() * 0.4})` // purple - : `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`; // white - - lightningCtx.strokeStyle = color; - lightningCtx.lineWidth = 0.5 + Math.random() * 2; - lightningCtx.shadowColor = color; - lightningCtx.shadowBlur = 18 + Math.random() * 30; - lightningCtx.globalAlpha = 1; - - drawBolt(startX, 0, endX, lightningCanvas.height * (0.4 + Math.random() * 0.5), 120, 7); - } - - // ambient flash on the body - document.body.style.transition = 'background 0.05s'; - document.body.style.background = `rgba(80, 0, 120, 0.08)`; - setTimeout(() => { document.body.style.background = ''; }, 80); - - // fade the bolt out - lightningFlashTimeout = setTimeout(() => { - lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height); - }, 80 + Math.random() * 120); - } - - // irregular strike cadence — like real lightning - function scheduleStrike() { - flashStrike(); - const delay = 600 + Math.random() * 2200; - lightningInterval = setTimeout(scheduleStrike, delay); - } - scheduleStrike(); -} - -function stopLightning() { - if (!lightningCanvas) return; - lightningCanvas.style.opacity = '0'; - clearTimeout(lightningInterval); - clearTimeout(lightningFlashTimeout); - setTimeout(() => { - lightningCanvas.remove(); - lightningCanvas = null; - }, 800); -} - - -function dragonTypewriterClick() { - if (document.getElementById('typewriter-canvas')) return; - if (document.getElementById('lightning-canvas')) stopLightning(); - if (document.getElementById('cure-canvas')) stopWater(); - if (document.getElementById('matrix-canvas')) stopMatrix(); - if (document.getElementById('rain-canvas')) stopRain(); - - const el = document.getElementById('interest'); - el.textContent = 'case file 36609...'; - startTypewriter(); - isDay = false; - makenight(); - setTimeout(() => { - el.textContent = 'interest'; - stopTypewriter(); - }, 10000); -} - -let twCanvas, twCtx, twAnimFrame; - -function startTypewriter() { - twCanvas = document.createElement('canvas'); - twCanvas.id = 'typewriter-canvas'; - twCanvas.width = window.innerWidth; - twCanvas.height = window.innerHeight; - Object.assign(twCanvas.style, { - position: 'fixed', - top: 0, - left: 0, - pointerEvents: 'none', - zIndex: -1, - opacity: '0', - transition: 'opacity 1.2s ease' - }); - document.body.appendChild(twCanvas); - twCtx = twCanvas.getContext('2d'); - requestAnimationFrame(() => { twCanvas.style.opacity = '1'; }); - - const W = twCanvas.width; - const H = twCanvas.height; - - // ── case file fragments ────────────────────────────────────────────── - const fragments = [ - 'SUBJECT: HARRIET VANGER', - 'CASE NO. 36609-74', - 'CLASSIFICATION: MISSING / PRESUMED', - '████████████ DECEASED ████████████', - 'LAST SEEN: HEDEBY ISLAND', - 'NOVEMBER 1, 1966', - 'INVESTIGATING OFFICER: ██████████', - 'FINANCIAL IRREGULARITIES NOTED', - 'SEE ATTACHMENT ██-C', - 'WENNERSTROM GROUP — CLASSIFIED', - 'DO NOT DISTRIBUTE', - 'SOURCE: CONFIDENTIAL', - 'SALANDER, L. — AUTHORIZED', - 'BLOMKVIST, M. — AUTHORIZED', - 'ALL OTHERS: ACCESS DENIED', - '████████████████████████████', - 'MOTIVE: UNKNOWN', - 'SUSPECT LIST: REDACTED', - 'EVIDENCE SEALED BY ORDER OF', - '████████ COURT ████████', - 'TRAUMA INDICATED / SEE REPORT 7', - 'LISBETH — CLEARANCE LEVEL 3', - 'THE DRAGON TATTOO', - '——————————————————————', - 'WHERE IS SHE.', - 'WHO KNOWS.', - 'FILE CLOSED: ██/██/████', - ]; - - // glitch chars pool - const glitchChars = '!@#$%&*?|\\/<>[]{}~^░▒▓█▄▀■□▪▫'; - function randGlitch() { - return glitchChars[Math.floor(Math.random() * glitchChars.length)]; - } - - // ── active lines on screen ─────────────────────────────────────────── - const lines = []; - let fragmentPool = [...fragments]; - - function pickFragment() { - if (!fragmentPool.length) fragmentPool = [...fragments]; - const i = Math.floor(Math.random() * fragmentPool.length); - return fragmentPool.splice(i, 1)[0]; - } - - function spawnLine() { - const text = pickFragment(); - const fontSize = 11 + Math.floor(Math.random() * 9); - const x = 40 + Math.random() * (W * 0.55); - const y = 60 + Math.random() * (H - 120); - const isRedact = text.includes('█') || Math.random() < 0.12; - const speed = 28 + Math.random() * 22; // chars per second - - lines.push({ - text, - x, y, - fontSize, - typed: 0, // chars revealed so far (float) - speed, - glitching: false, - glitchTimer: 0, - glitchStr: '', - opacity: 0.72 + Math.random() * 0.26, - isRedact, - done: false, - holdTimer: 0, - fadeOut: false, - fadeAlpha: 1, - stamp: Math.random() < 0.08, // rare CLASSIFIED/DENIED stamp - }); - } - - // spawn cadence - spawnLine(); - const spawnTimer = setInterval(() => { - if (lines.length < 14) spawnLine(); - }, 600); - twCanvas._spawnTimer = spawnTimer; - - let lastTs = null; - - function drawFrame(ts) { - if (!lastTs) lastTs = ts; - const dt = (ts - lastTs) / 1000; - lastTs = ts; - - twCtx.clearRect(0, 0, W, H); - - // faint scanline overlay — gives it that CRT/photocopy feel - for (let sy = 0; sy < H; sy += 3) { - twCtx.fillStyle = 'rgba(0,0,0,0.04)'; - twCtx.fillRect(0, sy, W, 1); - } - - lines.forEach((line, idx) => { - twCtx.font = `${line.fontSize}px "Courier New", Courier, monospace`; - - if (line.fadeOut) { - line.fadeAlpha -= dt * 0.6; - if (line.fadeAlpha <= 0) { - lines.splice(idx, 1); - return; - } - } - - const alpha = line.opacity * (line.fadeOut ? line.fadeAlpha : 1); - - if (!line.done) { - line.typed += line.speed * dt; - if (line.typed >= line.text.length) { - line.typed = line.text.length; - line.done = true; - } - - // random mid-type glitch burst - if (!line.glitching && Math.random() < 0.004) { - line.glitching = true; - line.glitchTimer = 0.18 + Math.random() * 0.22; - line.glitchStr = Array.from( - { length: 3 + Math.floor(Math.random() * 5) }, - randGlitch - ).join(''); - } - } else { - // after typing finishes, hold then fade - line.holdTimer += dt; - if (line.holdTimer > 3.5 + Math.random() * 3) { - line.fadeOut = true; - // spawn a replacement - setTimeout(spawnLine, 200 + Math.random() * 600); - } - - // occasional re-glitch on finished lines - if (!line.glitching && Math.random() < 0.003) { - line.glitching = true; - line.glitchTimer = 0.1 + Math.random() * 0.15; - line.glitchStr = Array.from( - { length: 2 + Math.floor(Math.random() * 4) }, - randGlitch - ).join(''); - } - } - - // resolve glitch burst - if (line.glitching) { - line.glitchTimer -= dt; - if (line.glitchTimer <= 0) { - line.glitching = false; - line.glitchStr = ''; - } - } - - // ── build visible string ─────────────────────────────────────── - const visible = line.text.slice(0, Math.floor(line.typed)); - let display = visible; - - // blinking cursor at the type head - if (!line.done) { - const blink = Math.floor(ts / 530) % 2 === 0; - display += blink ? '▌' : ' '; - } - - // append glitch burst after cursor - if (line.glitching) display += line.glitchStr; - - // ── color choice ─────────────────────────────────────────────── - // redacted blocks stay white-ish; normal text is the faded amber/grey - const isRedactLine = line.text.includes('█'); - if (isRedactLine) { - twCtx.fillStyle = `rgba(220, 215, 200, ${alpha})`; - } else if (line.stamp) { - twCtx.fillStyle = `rgba(180, 30, 30, ${alpha * 0.85})`; - } else { - // slight warm/cool variation per line - const warm = Math.random() < 0.5; - twCtx.fillStyle = warm - ? `rgba(210, 200, 180, ${alpha})` - : `rgba(185, 200, 195, ${alpha})`; - } - - twCtx.fillText(display, line.x, line.y); - - // ── underline for "stamp" lines ──────────────────────────────── - if (line.stamp && line.done) { - const mw = twCtx.measureText(line.text).width; - twCtx.strokeStyle = `rgba(180, 30, 30, ${alpha * 0.6})`; - twCtx.lineWidth = 1; - twCtx.beginPath(); - twCtx.moveTo(line.x, line.y + 3); - twCtx.lineTo(line.x + mw, line.y + 3); - twCtx.stroke(); - } - }); - - if (twCanvas) twAnimFrame = requestAnimationFrame(drawFrame); - } - - twAnimFrame = requestAnimationFrame(drawFrame); -} - -function stopTypewriter() { - if (!twCanvas) return; - twCanvas.style.opacity = '0'; - clearInterval(twCanvas._spawnTimer); - cancelAnimationFrame(twAnimFrame); - setTimeout(() => { - twCanvas.remove(); - twCanvas = null; - }, 1200); -} - - function matrixClick() { - if (document.getElementById('matrix-canvas')) return; - - if (document.getElementById('cure-canvas')) stopWater(); - if (document.getElementById('lightning-canvas')) stopLightning(); - if (document.getElementById('typewriter-canvas')) stopTypewriter(); - if (document.getElementById('rain-canvas')) stopRain(); - - const el = document.getElementById('interest'); - el.textContent = 'wake up, neo...'; - startMatrix(); - isDay = false; - makenight(); - setTimeout(() => { - el.textContent = 'interest'; - stopMatrix(); - }, 10000); -} - -let matrixCanvas, matrixCtx, matrixAnimFrame, matrixStopping = false; - -function startMatrix() { - matrixCanvas = document.createElement('canvas'); - matrixCanvas.id = 'matrix-canvas'; - matrixCanvas.width = window.innerWidth; - matrixCanvas.height = window.innerHeight; - Object.assign(matrixCanvas.style, { - position: 'fixed', - top: 0, - left: 0, - pointerEvents: 'none', - zIndex: -1, - opacity: '0', - transition: 'opacity 1.2s ease' - }); - document.body.appendChild(matrixCanvas); - matrixCtx = matrixCanvas.getContext('2d'); - requestAnimationFrame(() => { matrixCanvas.style.opacity = '1'; }); - - const W = matrixCanvas.width; - const H = matrixCanvas.height; - - const FONT_SIZE = 16; - const COLS = Math.floor(W / FONT_SIZE); - - // katakana unicode block ァ–ン (U+30A1–U+30F6) - const KATA_START = 0x30A1; - const KATA_END = 0x30F6; - function randKata() { - return String.fromCharCode( - KATA_START + Math.floor(Math.random() * (KATA_END - KATA_START + 1)) - ); - } - - // each column: how far down it has dripped (in rows) - // start negative so columns begin off-screen at staggered times - const drops = Array.from({ length: COLS }, () => ({ - y: -(Math.random() * 50), // staggered start above top - speed: 0.1 + Math.random() * 0.2, - glyphs: [] // trail of { char, age } - })); - - const TRAIL_LEN = 14; // how many glyphs in the tail - - function drawFrame() { - // dim the previous frame — this creates the fade trail - matrixCtx.fillStyle = 'rgba(0, 0, 0, 0.10)'; - matrixCtx.fillRect(0, 0, W, H); - - matrixCtx.font = `${FONT_SIZE}px monospace`; - - drops.forEach((drop, col) => { - const x = col * FONT_SIZE; - - // advance drop - drop.y += drop.speed; - - // push a new glyph at the head - if (drop.y >= 0) { - drop.glyphs.unshift({ char: randKata(), age: 0 }); - if (drop.glyphs.length > TRAIL_LEN) drop.glyphs.pop(); - } - - // age each glyph and draw - drop.glyphs.forEach((g, i) => { - g.age++; - // randomly mutate the character occasionally - if (Math.random() < 0.04) g.char = randKata(); - - const headY = Math.floor(drop.y) - i; - if (headY < 0 || headY * FONT_SIZE > H) return; - - const t = i / TRAIL_LEN; // 0 = head, 1 = tail - - if (i === 0) { - // head glyph — bright white flash - matrixCtx.fillStyle = `rgba(200, 255, 220, 0.98)`; - } else if (i < 3) { - // near-head — bright green - matrixCtx.fillStyle = `rgba(100, 255, 140, ${1 - t * 0.3})`; - } else { - // tail — fade to dark green - const alpha = Math.max(0, 1 - t * 1.1); - const green = Math.floor(180 - t * 120); - matrixCtx.fillStyle = `rgba(0, ${green}, 60, ${alpha})`; - } - - matrixCtx.fillText(g.char, x, headY * FONT_SIZE); - }); - - // reset column when it scrolls fully off the bottom - // reset column when it scrolls fully off the bottom - if (drop.y * FONT_SIZE > H + TRAIL_LEN * FONT_SIZE) { - if (matrixStopping) { - // don't recycle — park it off-screen so the trail dies - drop.y = -(TRAIL_LEN * 10); - drop.glyphs = []; - } else { - drop.y = -(Math.random() * 30); - drop.speed = 0.3 + Math.random() * 0.7; - drop.glyphs = []; - } - } - }); - - if (matrixCanvas) matrixAnimFrame = requestAnimationFrame(drawFrame); - } - - matrixAnimFrame = requestAnimationFrame(drawFrame); -} - -function stopMatrix() { - if (!matrixCanvas) return; - - // signal: no new heads, let existing trails die out - matrixStopping = true; - - // after trails have had time to fade, pull the canvas - setTimeout(() => { - cancelAnimationFrame(matrixAnimFrame); - matrixCanvas.style.opacity = '0'; - matrixCanvas.style.transition = 'opacity 1.2s ease'; - setTimeout(() => { - matrixCanvas.remove(); - matrixCanvas = null; - matrixStopping = false; - }, 1200); - }, 1800); -} - - - function bladerunnerClick() { - if (document.getElementById('rain-canvas')) return; // already running, do nothing - - if (document.getElementById('cure-canvas')) stopWater(); - if (document.getElementById('matrix-canvas')) stopMatrix(); - if (document.getElementById('typewriter-canvas')) stopTypewriter(); - if (document.getElementById('lightning-canvas')) stopLightning(); - - - const el = document.getElementById('interest'); - el.textContent = 'tears in rain'; - - startRain(); - - isDay = false - makenight(); - - setTimeout(() => { - el.textContent = "interest"; - stopRain(); - }, 10000); - } - - let rainCanvas, rainCtx, rainInterval; - - function startRain() { - rainCanvas = document.createElement('canvas'); - rainCanvas.id = 'rain-canvas'; - rainCanvas.width = window.innerWidth; - rainCanvas.height = window.innerHeight; - document.body.appendChild(rainCanvas); - rainCtx = rainCanvas.getContext('2d'); - - requestAnimationFrame(() => { rainCanvas.style.opacity = '1'; }); - - const drops = Array.from({ length: 120 }, () => ({ - x: Math.random() * rainCanvas.width, - y: Math.random() * rainCanvas.height, - speed: 8 + Math.random() * 7, - length: 12 + Math.random() * 20, - opacity: 0.2 + Math.random() * 0.4 - })); - - rainInterval = setInterval(() => { - rainCtx.clearRect(0, 0, rainCanvas.width, rainCanvas.height); - drops.forEach(drop => { - rainCtx.beginPath(); - rainCtx.moveTo(drop.x, drop.y); - rainCtx.lineTo(drop.x - 2, drop.y + drop.length); - rainCtx.strokeStyle = `rgba(174, 214, 241, ${drop.opacity})`; - rainCtx.lineWidth = 3; - rainCtx.stroke(); - - drop.y += drop.speed; - if (drop.y > rainCanvas.height) { - drop.y = -drop.length; - drop.x = Math.random() * rainCanvas.width; - } - }); - }, 30); - } - - function stopRain() { - if (!rainCanvas) return; - rainCanvas.style.opacity = '0'; - setTimeout(() => { - clearInterval(rainInterval); - rainCanvas.remove(); - rainCanvas = null; - }, 800); - } \ No newline at end of file diff --git a/source/tools.html b/source/tools.html deleted file mode 100644 index 2c297f7..0000000 --- a/source/tools.html +++ /dev/null @@ -1,50 +0,0 @@ -
- -
-

- The task here is simple: to reduce work, it's a simple investment. The choice of picking the correct - bottleneck is as important as the tool itself, as this gives it use. If it's never used, it never creates - value. The same goes for uptime, it's important that stability is almost perfect, as downtime undermines - the utility of having the tool. Remember that making and using a tool also creates debt for future - maintenance of the tool. -
- To adhere to this, I often choose Golang or Python as they are cheap to maintain. Golang has good - stability because it is easy to code and compile checks give early error detection. A word of caution on - AI generated tools: while they are cheap to make, the maintenance debt grows exponentially, as the code is - often poorly understood by the team and difficult to reason about over time. - -

-
-
- Portfolio Engine -
-

A lightweight portfolio and financial data backend written in Go. Tracks companies, - currencies, revenue reports, and trades across configurable time periods — replacing spreadsheets with a - proper database, REST API, and interactive shell.

- Roadmap -
    -
  • Price update endpoint
  • -
  • Multi-currency conversion
  • -
  • Frontend dashboard
  • -
  • Shares outstanding history database table
  • -
  • Earnings per share (EPS) based on shares owned
  • -
  • First-In, First-Out (FIFO) realized gains
  • -
  • Dividends earned and related taxes
  • -
  • Total tax obligations
  • -
- Git repo -
-
-
- Accounting Management -
-

- A lightweight, SQL-backed, web-based CLI system designed to track assets and liabilities. The goal is to - provide a simple, fast, and scalable financial tracking tool that can grow from personal use to more - advanced scenarios.

-
-
-
\ No newline at end of file