diff --git a/website/blog/box-dimension-post-plan.md b/website/blog/box-dimension-post-plan.md deleted file mode 100644 index 7a42ae18e..000000000 --- a/website/blog/box-dimension-post-plan.md +++ /dev/null @@ -1,97 +0,0 @@ -# Implementierungsplan: Box-Dimension-Post (Bretagne/GB) - -## Ziel - -Ein Blogpost, der Kindern (und Erwachsenen) das Konzept der Box-Counting-Dimension -zeigt: erst am einfachen Beispiel (Linie, Kreis), dann spielerisch an echten -Küsten/Seen. Zwei interaktive Bausteine, ein gemeinsamer Kern. - -## Content-Gliederung des Posts - -1. **Einstieg**: Warum ist eine Küste "länger", je genauer man misst? - (Kurzer Verweis auf Mandelbrot, "How Long Is the Coast of Britain?", 1967) -2. **Konzept-Widget**: Linie/Kreis/gezackte Kurve + Gitter-Regler + Log-Log-Plot - → Kernaussage: d = log N(ε) / log(1/ε), Linie und Kreis beide d=1 -3. **Text-Brücke**: reale Küsten sind keine glatten Kurven → d wird > 1 - - Westküste Großbritannien: D ≈ 1,25 (Mandelbrot/Richardson) - - grobe Faustregel: sandige/glatte Küsten nahe D≈1, felsige Küsten D≈1,3+ -4. **Karten-Spiel**: Bretagne (felsig) vs. Kontrastregion (glatter) vs. optional - ein See, mit Gitter-Overlay direkt auf der Karte -5. **Abschluss**: eigene Beobachtung/Ergebnis der Leser:innen einordnen - -## Architekturentscheidung - -- **Kartenbibliothek**: Leaflet.js über `react-leaflet` (Begründung: KISS, - siehe Chat — MapLibre GL wäre für diesen Zweck Overkill) -- **Tiles**: CARTO Positron (kostenlos, kein API-Key, `© OpenStreetMap contributors` - Attribution einbauen) -- **Geodaten**: GSHHG (Global Self-consistent, Hierarchical, High-resolution - Geography Database) — public domain, exakt der Datensatz aus der - akademischen Coastline-Dimension-Literatur, enthält auch Seen (Level 2) -- **Gemeinsamer Kern**: eine einzige Box-Counting-Utility (reine Funktion, - kein React), die sowohl vom Canvas-Demo-Widget als auch vom Leaflet-Overlay - genutzt wird - -## Komponentenstruktur (Vorschlag) - -``` -website/components/box-dimension/ - boxCounting.ts # reine Logik: countIntersectingCells(points, cellSizePx) - ConceptDemo.tsx # Canvas-Widget: Linie/Kreis/Fraktal + Slider + Log-Log-Plot - LogLogPlot.tsx # gemeinsame Plot-Komponente (von beiden genutzt) - CoastPlayground.tsx # Leaflet-Karte + Gitter-Overlay + Regionsauswahl - GridOverlay.ts # custom L.Layer, zeichnet Gitter via latLngToContainerPoint - regions.ts # Liste { id, label, geojsonPath, center, zoom } - -website/public/data/coastlines/ - bretagne.geojson - contrast-region.geojson # z.B. glattere Küste zum Vergleich - lake-example.geojson # optional -``` - -## Datenaufbereitung (einmalig, vor dem Coden) - -1. GSHHG herunterladen (volle Auflösung reicht als Quelle) -2. Mit `mapshaper` oder `ogr2ogr` auf Bounding Box zuschneiden: - - Bretagne (grob: 47.2–48.9°N, -5.2–-1.0°E) - - Kontrastregion (z.B. glatterer Küstenabschnitt zum Vergleich) -3. Auf vernünftige Punktdichte vereinfachen (Douglas-Peucker), damit das - Gitter-Overlay im Browser performant bleibt — nicht die volle GSHHG-Auflösung - direkt ausliefern -4. Nach GeoJSON exportieren, Dateigröße prüfen (Ziel: einige hundert KB pro Region) -5. Dateien nach `website/public/data/coastlines/` legen - -## Implementierungsschritte - -- [ ] **Schritt 1**: `boxCounting.ts` aus dem bestehenden Demo-Code extrahieren - (reine Funktion, unabhängig von Canvas/Leaflet) -- [ ] **Schritt 2**: `ConceptDemo.tsx` als React-Komponente bauen - (Canvas-Ref + useEffect statt globaler DOM-Listener, sonst 1:1 aus der Demo) -- [ ] **Schritt 3**: `LogLogPlot.tsx` als eigenständige, wiederverwendbare - Komponente auslagern -- [ ] **Schritt 4**: GSHHG-Daten besorgen, zuschneiden, vereinfachen (siehe oben) -- [ ] **Schritt 5**: `CoastPlayground.tsx` — Leaflet-Karte mit TileLayer (CARTO) - + GeoJSON-Layer für die gewählte Region -- [ ] **Schritt 6**: `GridOverlay.ts` — custom Leaflet-Layer, zeichnet Gitter neu - bei `moveend`/`zoomend`, zählt Schnittzellen, meldet N(ε) nach oben -- [ ] **Schritt 7**: Regionsauswahl (Dropdown/Buttons) + Slider für Zellgröße - (in km, intern in Pixel umgerechnet je nach Zoomstufe) -- [ ] **Schritt 8**: `CoastPlayground` an `LogLogPlot` anschließen (gleiche - Komponente wie im Concept-Widget) -- [ ] **Schritt 9**: Attribution-Zeile einbauen (`© OpenStreetMap contributors`, - GSHHG-Quellenangabe) -- [ ] **Schritt 10**: Post-Text schreiben, Widgets einbetten, gegenlesen - -## Offene Entscheidungen (vor dem Start klären) - -- Welche zweite Region als Kontrast zur Bretagne? (glattere Küste oder See) -- Soll der Slider in "km Kantenlänge" oder in "Anzahl Kästchen" gedacht werden? -- Reicht eine feste Auswahl an Regionen, oder soll perspektivisch doch eine - Overpass-Live-Abfrage ergänzt werden (spätere Ausbaustufe, nicht für v1)? - -## Lizenz-/Attributionshinweise - -- OSM-Kartendaten (über CARTO-Tiles): ODbL, Attribution `© OpenStreetMap - contributors` ist Pflicht und muss sichtbar auf der Karte stehen -- GSHHG: public domain (Quellen WVS/WDBII), Software/Daten-Distribution - unter LGPL seit Version 2.2.2 — Quellenangabe im Post trotzdem fair diff --git a/website/blog/common_pool_patience.mdx b/website/blog/common_pool_patience.mdx new file mode 100644 index 000000000..9a8638a32 --- /dev/null +++ b/website/blog/common_pool_patience.mdx @@ -0,0 +1,393 @@ +--- +publishing_date: 2026-08-27 +title: Common-pool resources, expanded in the discount factor +category: "others" +description: Notes on the common-pool model — the tragedy derived for N users, the patient planner expanded order by order in the discount factor, and a textbook fishery worked through at the end. +--- + +**Common-pool resources** are a powerful and frequent model in economics. The idea is that nobody can be kept away from the pool, +and every unit one person takes is a unit nobody else can. Fish stocks +are the textbook case. So are groundwater basins, grazing land, road space at rush hour or the atmosphere, considered as a place to put carbon: no one can be +excluded from using it, and every tonne emitted uses up part of a finite budget. + +What follows are notes on the general model and then I apply them to the example of fishery. I wrote about the same fishery [earlier](/blog/14/) but this time it +is all math. + +## The commons + +A common-pool problem needs very little to write down. There is a **stock** $s_t$ — fish in the +sea, water in the aquifer, room left in the carbon budget. Several users draw on it; user $j$ +takes $b_j$, and what happens to the pool depends only on the total draw + +$$ +B_t = \sum_j b_j . +$$ + +Drawing pays. Write $u_{j,t}$ for what user $j$ earns in season $t$, and $u(B_t, s_t)$ for what +they earn between them — a season's payoff depends on how hard the pool is being worked and on how +much is in it. What makes this a problem rather than a list is that **the stock remembers**: +whatever is left, plus whatever grows back on its own, is the starting point for next season, + +$$ +s_{t+1} = \sigma(B_t, s_t) . +$$ + +Nothing in the derivation depends on the shape of $u$ or $\sigma$, which is the whole reason for +writing them this way. Users also care about seasons other than this one, and weight the future +geometrically: + +$$ +U_j = \sum_{t=0}^{\infty} \beta^{t}\,u_{j,t} . +$$ + +A geometric weight in time, exactly like a damping factor. $\beta$ near 1 is a patient user, +$\beta = 0$ one who does not think about next season at all. + +Three conventions, all to save trouble later. **A subscript is always a season** — $B_t$, $s_t$ — +and the user label $j$ drops away almost at once, because from the planner's point of view only +the total matters. **Derivatives are written $\partial$**: since $u$ and $\sigma$ each take two +arguments, a derivative has to say which one it acts on, so $\partial_B u$, $\partial_s u$, +$\partial_{BB} u$ — never a prime, never a subscript, the subscript slot being spoken for. And +$\big(\partial_B u\big)_t$ means that derivative **evaluated in season $t$**, which keeps the +subscript a season even there. + +The third is vocabulary. These are the quantities economists call **marginal**, and the word is +less forbidding than it looks: it is simply their way of saying _the derivative of_. What it does +not say on its own is _with respect to what_ — that lives in the phrase that follows. The noun +names what gets differentiated and the "of …" names what it is differentiated by, so +$\partial_B u$ is the marginal payoff **of one more unit drawn** and $\partial_s u$ the marginal +payoff **of one more unit left in the pool**. The "of" and the subscript on $\partial$ carry +exactly the same information; the notation is just the more honest of the two, since it can never +quietly leave the "of" out. + +## Independent actors + +So far $u(B,s)$ is what the pool pays out as a whole. To say what happens when $N$ users draw on +it independently, one thing has to be added: how that payout is divided. Take the obvious rule — +each user gets the share matching how hard it worked, + +$$ +u_j = \frac{b_j}{B}\,u(B,s) . +$$ + +That is the only new assumption in this section. + +Each user picks $b_j$ to make its own $u_j$ as large as possible, taking everyone else's draw as +given. Raising $b_j$ raises $B$ one for one, so + +$$ +\frac{\partial u_j}{\partial b_j} += \frac{u}{B} \;+\; \frac{b_j}{B}\Big(\partial_B u - \frac{u}{B}\Big) \;=\; 0 . +$$ + +The $-u/B$ in the bracket is the dilution of your own share: raising $b_j$ raises $B$ too, which +splits the pool more thinly. It comes from differentiating the share $b_j/B$, not from $u$. + +That bracket is worth a name of its own. Write + +$$ +D \;\equiv\; \frac{u}{B} - \partial_B u +$$ + +for how much less one more unit of effort earns than the units already working — the **damage** a +unit does. $D$ is positive whenever $u$ is concave in $B$ and pays nothing for no effort, +$u(0,s) = 0$ — and those are the only properties of $u$ this section needs. Diminishing returns +and crowding are not separate ingredients; they are two names for $D > 0$. With that, the +condition reads + +$$ +\frac{\partial u_j}{\partial b_j} += \underbrace{\frac{u}{B}}_{\text{what you collect}} +\;-\; \frac{b_j}{B}\underbrace{\vphantom{\frac{u}{B}}D}_{\text{the damage}} +\;=\; 0 , +$$ + +and if the $N$ users are alike they draw alike, so $b_j/B = 1/N$ and it becomes simply + +$$ +\frac{u}{B} \;=\; \frac{D}{N} . +$$ + +**That one line is the tragedy of the commons.** Read it as an instruction: draw until what you +collect equals the share of the damage you carry. You are paid $u/B$ for the extra unit — the full +going rate, because your share grows with your effort. The damage it does is $D$, but $D$ lands on +all $N$ users, so your own bill is $D/N$. + +It is a dinner split $N$ ways. You eat the whole extra dish and pay a fraction of it, so you order +too much — and so does everyone else, for exactly the same good reason. + +The consequence follows at once. Raising $N$ shrinks $D/N$, so $u/B$ has to shrink to match, and +$u/B$ only falls as $B$ grows. **More users, more effort, less left over** — with nobody behaving +badly. Each is doing the best available thing given what the others do. + +The two ends of that range are worth naming. + +- **$N = 1$** gives $u/B = D$, which is $\partial_B u = 0$: draw until one more unit adds nothing + to the pool. You are dining alone and pay the whole bill, so you order exactly as much as you + want. +- **$N \to \infty$** sends $D/N$ to zero, so $u/B = 0$ and $u = 0$. Your share of the bill has + vanished, and the pool ends up yielding exactly what it costs to work it — no more. + +## The planner + +The first of those limits is worth dwelling on, because it is the benchmark the rest of this post +measures against. A planner is usually introduced as somebody from outside — a manager, a +regulator, an authority with better information. None of that is needed. **The planner is simply +$N = 1$**: the same first-order condition with the externality switched off. + +And he will always try to maximise the total discounted payoff: + +$$ +\max_{\{B_t\}_{t=0}^{\infty}}\; U = \sum_{t=0}^{\infty} \beta^{t}\,u(B_t, s_t), +\qquad \text{subject to}\qquad s_{t+1} = \sigma(B_t, s_t), +$$ + +with $s_0$ given. Working this out in general is analytically not possible but likely we have a good +perturbation parameter in $\beta \ll 1$, with both the fleet and the payoff as polynomials: + +$$ +B_t = B_t^{(0)}+\beta B_t^{(1)}+\cdots, \qquad U = U^{(0)}+\beta U^{(1)}+\beta^{2} U^{(2)}+\cdots +$$ + +### Zeroth order + +At $\beta = 0$ only $t = 0$ survives and the planner maximises this season alone: + +$$ +U^{(0)} = u\big(B_0^{(0)}, s_0\big), \qquad \big(\partial_B u\big)_0 = 0 . +$$ + +The second equation is the **myopic rule** — the best fleet if next year did not exist — and it +defines a policy $B^{(0)}(s)$, one fleet for each stock. That is the entire zeroth order. + +It is worth seeing how far that is from what the independent actors do. Their condition was +$u/B = D/N$ with $D = u/B - \partial_B u$; eliminating $D$ between the two gives + +$$ +\big(\partial_B u\big)_{N\text{ users}} \;=\; (1-N)\,\frac{u}{B} , +$$ + +which is negative for every $N > 1$. They stop at a point where the pool's total payoff is already +_falling_ — past the top of the hill, on the way down. The planner, at $\partial_B u = 0$, stops +exactly on the summit. That is the precise sense in which it does better, and the gap between the +two is the whole of the tragedy. + +Keep the condition $\partial_B u = 0$ in view, though. It is about to do a great deal of work: at +every order from here on, some term arrives multiplied by it and vanishes. + +### First order + +Two things feed the $\beta^{1}$ coefficient: season 1 at zeroth order, and the first-order +correction to season 0's fleet. Expanding $u$ around $B_0^{(0)}$, + +$$ +U^{(1)} = \underbrace{\big(\partial_B u\big)_0\,B_0^{(1)}}_{=\;0} + u\big(B_1^{(0)}, s_1^{(0)}\big) +$$ + +and the first term vanishes — its bracket is the zeroth-order condition. **The correction to +today's fleet drops out of the first-order payoff.** + +What survives is the same one-season problem, one season later. Maximising over $B_1^{(0)}$ +returns the myopic rule at the new stock: + +$$ +B_1^{(0)} = B^{(0)}\big(s_1^{(0)}\big), \qquad s_1^{(0)} = \sigma\big(B_0^{(0)}, s_0\big) . +$$ + +So through first order + +$$ +U = u\big(B_0^{(0)}, s_0\big) + \beta\,u\big(B_1^{(0)}, s_1^{(0)}\big) + O(\beta^{2}), +$$ + +the same expression twice, one season apart. + +But notice what we have not got. $B_0^{(1)}$, the correction to _today's_ fleet and the thing we +actually want, has cancelled. It appears at second order. + +### Second order + +Three seasons now contribute, season $t$ entering at boat-order $2-t$. Two of the three collapse +on sight. + +**Season 0** contributes its second-order Taylor remainder. The $B_0^{(2)}$ piece arrives +multiplied by $\big(\partial_B u\big)_0 = 0$ and dies exactly as $B_0^{(1)}$ did one order ago, +leaving only the quadratic $\tfrac{1}{2}\big(\partial_{BB} u\big)_0\big(B_0^{(1)}\big)^2$. + +**Season 1** carries an explicit $\beta$, so it reaches $\beta^2$ through its own first-order +parts. Its $B_1^{(1)}$ piece is multiplied by $\big(\partial_B u\big)_1$ — zero for the same +reason. What survives is the dependence on the _stock_, because $s_1$ is not a constant: it +inherits a $\beta$-dependence from $B_0$, namely +$s_1^{(1)} = \big(\partial_B \sigma\big)_0\,B_0^{(1)}$. + +**Season 2** contributes a fresh myopic season. Altogether + +$$ +U^{(2)} = \underbrace{\tfrac{1}{2}\big(\partial_{BB} u\big)_0\big(B_0^{(1)}\big)^{2}}_{t=0} +\;+\; \underbrace{\big(\partial_s u\big)_1\,\big(\partial_B \sigma\big)_0\,B_0^{(1)}}_{t=1} +\;+\; \underbrace{u\big(B_2^{(0)}, s_2^{(0)}\big)}_{t=2} . +$$ + +$B_0^{(1)}$ now appears twice, and in two structurally different ways: **quadratically** through +season 0, **linearly** through season 1. That pairing is what makes the problem well posed. A +downward parabola plus a line has one interior maximum — with only the line the correction would +run away, and with only the parabola it would sit at zero and patience would never move anything. +The two terms are the cost of leaving the myopic optimum and the reward for the stock that buys. + +Setting $\partial U^{(2)} / \partial B_0^{(1)} = 0$: + +$$ +B_0^{(1)} = -\frac{\big(\partial_B \sigma\big)_0\;\big(\partial_s u\big)_1}{\big(\partial_{BB} u\big)_0} +$$ + +That is the whole result, and it is three derivatives of two functions. How hard the control +pushes the state, what a richer state is worth next season, and how sharply this season's profit +falls away from its peak — nothing else about a problem matters at this order. + +Note what $\big(\partial_s u\big)_1$ is: the **marginal value of one more unit left in the pool** — +how much extra next season earns from starting slightly fuller. It is an exchange rate between the +stock and money, and that is precisely the number you need in order to weigh a unit taken today +against a unit left for tomorrow. + +Since $\partial_{BB} u < 0$, the sign of the correction is the sign of +$\big(\partial_B \sigma\big)_0\big(\partial_s u\big)_1$: **the control moves toward whatever +raises tomorrow's value.** Both factors can carry either sign, and the two examples at the end of +this post are chosen so that each carries the opposite one. + +## Sustainability + +Sustainability sounds like a test a policy passes or fails. In this model it is better read as a +**destination**: a stock is steady when the draw exactly matches what grows back, + +$$ +\sigma(B, s) \;=\; s . +$$ + +That is not one condition but a family of them — for every level of effort, a stock at which the +pool stops moving. Draw too hard and the stock falls until the two balance; too gently and it rises +until they do. Leach works his whole dynamic chapter from this curve, and the useful fact is that +**every planner ends up somewhere on it** (§8.2 of _A Course in Public Economics_). So the question +is never whether a policy is sustainable in the long run, only _which_ resting point it reaches. + +That is what $\beta$ decides. At $\beta = 0$ the planner applies the myopic rule wherever it finds +itself and drifts to the resting point that rule implies — sustainable, and poor. As $\beta \to 1$ +the journey stops counting against an infinite stay at the destination, so the planner simply picks +the resting point with the **largest payoff**. + +Where it lands in between is a solved problem, and the answer is worth having even without the +derivation. The move, due to Clark and Munro, is to treat the stock as **capital**: a fish left in +the water is an investment like any other, and the planner holds the pool at the size where the +resource's own rate of return matches the discount rate — exactly the rule you would apply to a +bond or a factory. Everything in the worked example below is a special case of it. The derivation +is optimal control and it is done properly in Colin Clark's _Mathematical Bioeconomics_; the +original argument is Clark and Munro, _The economics of fishing and modern capital theory_, Journal +of Environmental Economics and Management **2** (1975). + +## Example: Fishery + +To put numbers on any of this we need an instance, and the fishery is the one you can picture. +John Leach sets it up in §8.1 of _A Course in Public Economics_, and I used it in an +[earlier post](/blog/14/). Two islands send $b_1$ and $b_2$ boats onto the same ground, so +$B_t$ is the fleet on the water and $s_t$ the fish stock. Three equations close it: + +- **Catch.** $y_t = q\,s_t\sqrt{B_t}$ — more boats land more fish, but with diminishing returns, + and a richer stock is easier to fish. The catchability $q$ says how good the gear is. +- **Renewal.** $g(s) = r\,s\,(1 - s/K)$ — the usual logistic regrowth, with intrinsic rate $r$ + and carrying capacity $K$. Fastest at half of $K$, zero at both ends. +- **What is left.** $s_{t+1} = s_t + g(s_t) - y_t$ — this season's catch sets next season's + starting point. + +The catch is shared in proportion to boats and each boat costs $c$ to send, so island $j$ earns +$u_j = b_j (v - c)$ in a season, where $v \equiv y/B = q\,s/\sqrt{B}$ is what one boat-trip is +worth. It falls as the ground gets crowded, which is the only reason any of this is interesting. +In the general notation of the last section, this fishery is the case + +$$ +u(B,s) = q\,s\sqrt{B} - c\,B, \qquad \sigma(B,s) = s + g(s) - q\,s\sqrt{B} . +$$ + +Throughout, $q = 0.01$, $c = 0.125$, $r = 0.03$, $K = 1000$ and $s_0 = 100$. + +### What the tragedy costs + +Take the $N$-user condition first. With $u = q s\sqrt{B} - cB$ the two pieces it balances are + +$$ +\frac{u}{B} = \frac{q\,s}{\sqrt{B}} - c , \qquad \partial_B u = \frac{q\,s}{2\sqrt{B}} - c , +$$ + +so the catch term appears at half strength in the second — that is what diminishing returns amount +to here. Substituting into +$\big(1-\tfrac1N\big)\tfrac{u}{B} + \tfrac1N \partial_B u = 0$, the $c$ terms combine to exactly +$-c$ and the rest to $\tfrac{q s}{\sqrt B}\big(1-\tfrac{1}{2N}\big)$, giving the equilibrium +fleet $B^{*}(N)$ — the total number of boats $N$ independent islands end up sending — in closed +form: + +$$ +B^{*}(N) = \Big(\frac{q\,s_0}{c}\Big)^{2}\Big(1-\frac{1}{2N}\Big)^{2} . +$$ + +| | one owner | two islands | open access | +| ------------------ | --------- | ----------- | ----------- | +| $N$ | 1 | 2 | $\to\infty$ | +| boats on the water | **16** | 36 | **64** | +| profit between all | **2.00** | 1.50 | **0** | + +Four times the fleet, for nothing at all. That gap is the tragedy of the commons, and it is the +distance any institution has to close. Two islands already give away most of it — 36 boats is +closer to open access than to the owner — which is worth knowing before assuming that a small +group will sort itself out. + +### The derivatives the expansion needed + +Everything in the general derivation ran on three derivatives, and for a fishery all three reduce +to derivatives of the catch. Profit is the catch minus a linear cost, $u = y - cB$, so +$\partial_B u = \partial_B y - c$ and $\partial_s u = \partial_s y$. The stock loses exactly what +is landed, $\sigma = s + g(s) - y$, so $\partial_B \sigma = -\partial_B y$ — which at the myopic +optimum, where $\partial_B y = c$, is exactly $-c$. One more boat costs the stock precisely $c$ +worth of fish, and the general correction becomes $c\,\partial_s y / \partial_{BB} y$. + +Here they are. + +| ingredient | general | for $y = q s\sqrt{B}$ | value | +| --------------------------------------- | ----------------------------------- | ----------------------------------- | ----------------- | +| myopic rule | $\partial_B y = c$ | $q s / 2\sqrt{B} = c$ | — | +| myopic fleet | $B^{(0)}(s)$ | $\left(q s / 2c\right)^{2}$ | **16 boats** | +| myopic profit | $y - cB$ at $B^{(0)}$ | $q^{2} s^{2} / 4c$ | **2.00** | +| value of a fish | $\partial_s y$ | $q^{2} s / 2c$ | 0.0395 at $s_1$ | +| curvature $\big(\partial_{BB} u\big)_0$ | $\partial_{BB}\, y$ at $B^{(0)}$ | $-2c^{3} / q^{2} s^{2}$ | $-0.0039$ | +| **correction $B_0^{(1)}$** | $c\,\partial_s y / \partial_{BB} y$ | $-q^{4} s_0^{2} s_1^{(0)} / 4c^{3}$ | $\mathbf{-1.263}$ | + +The zeroth order is the 16 boats and 2.00 promised at the start. One season of that fleet lands +$y_0 = 4.00$ and leaves $s_1^{(0)} = 98.7$. + +Notice what is missing from that last row. **The renewal function never appears.** $g$ enters the +correction only by setting what $s_1^{(0)}$ is, and nowhere else. It is tempting to assume a +patient planner is one who fishes below the growth rate; it is not. The planner is buying _stock_, +not _growth_ — a fuller sea is cheaper to fish next season, and that is the whole mechanism. + +The curvature explains the size of the effect: $-0.0039$ is a very flat peak, so a small price on +fish slides the fleet a long way. Concretely, it comes off the impatient 16 boats by about eight +percent per unit of patience. + +### Where the fishery ends up + +The expansion says which way the fleet moves; it says nothing about where the fishery settles. That +was Sustainability's question, and for this fishery the two ends of the patience range are very far +apart: + +| | myopic, $\beta = 0$ | far-sighted, $\beta \to 1$ | +| ------ | ------------------- | -------------------------- | +| stock | 69.8 | **518** | +| boats | 7.79 | **2.09** | +| catch | 1.95 | **7.49** | +| profit | 0.97 | **7.23** | + +A quarter of the boats landing four times the fish, on a sea seven times fuller. That is what +patience buys here, and it is not austerity — a fuller sea is cheaper to work, so more stock and +more catch arrive together. + +Solving the dynamic problem numerically shows the fishery walking there as $\beta$ rises: starting +from $s_0 = 100$, the long-run stock comes out at 69.7, 89.7, 375.9 and 503.2 for $\beta = 0$, +$0.9$, $0.99$ and $0.999$. Almost all of that journey happens in the last stretch of the patience +range — which is exactly the ground a small-$\beta$ expansion cannot cover. diff --git a/website/blog/kuesten_dimension.plan.md b/website/blog/kuesten_dimension.plan.md deleted file mode 100644 index 74da9bce3..000000000 --- a/website/blog/kuesten_dimension.plan.md +++ /dev/null @@ -1,317 +0,0 @@ -# Blog Post Plan: Wie lang ist die Küste der Bretagne? - -## Target Audience - -**Primär: mathematikinteressierte Kinder, etwa 10 bis 12 Jahre.** Das ist eine -*vierte* Zielgruppe, die es auf diesem Blog bisher nicht gibt — bewusst -gewählt, nicht "Kinder und Erwachsene gleichzeitig". Sekundär dürfen Erwachsene -mitlesen, aber kein einziger Satz wird für sie kalibriert. - -**Nicht persönlich.** Der Post ist für Kinder in diesem Alter allgemein -geschrieben, nicht für bestimmte Kinder. Keine Namen, keine -Familien-Anspielungen, kein "erinnert ihr euch". Ein Kind, das die Bretagne nie -gesehen hat, muss vom ersten Satz an genauso mitkommen. Die Bretagne ist das -*Beispiel*, nicht die gemeinsame Erinnerung. - -**Was sie können:** - -- Multiplizieren, Dividieren, Bruchteile, Prozente -- Flächen (Quadrat, Rechteck), Umfang -- Koordinaten, Maßstab auf einer Karte -- Zählen, Schätzen, Vergleichen — und sie machen das gerne - -**Was sie NICHT können (harte Grenze):** - -- **Logarithmen** — kommen erst in Klasse 9/10. Das ist die zentrale - Konsequenz für diesen Post: `d = log N(ε) / log(1/ε)` darf **nicht** im - Haupttext stehen. -- Potenzen mit gebrochenen Exponenten -- Steigung einer Regressionsgerade, log-log-Achsen - -**Was sie lernen sollen:** - -- Dass "Länge" bei einer zerklüfteten Küste keine feste Zahl ist, sondern vom - Messstab abhängt -- Dass man Zerklüftetheit *messen* kann — mit einer Zahl zwischen 1 und 2 -- Dass diese Zahl "Dimension" heißt und dass es Dinge gibt, die weder Linie - noch Fläche sind - -## Core Thesis - -**Wenn du deine Kästchen halb so groß machst, brauchst du bei einer geraden -Linie doppelt so viele, bei einer vollen Fläche viermal so viele — und bei -einer echten Küste irgendwas dazwischen. Genau diese Zahl dazwischen ist ihre -Dimension.** - -Warum das zählt: Dimension kennt man als 1, 2 oder 3 — Linie, Fläche, Raum. -Hier darf sie plötzlich 1,26 sein. Das ist der Moment, den der Post verkauft. - -## Der Verdopplungs-Trick (Kernentscheidung, ersetzt den Log-Log-Plot) - -Statt Steigung im Log-Log-Plot: **Halbierungsspiel.** - -| Was | Kästchen halbiert → so viel mehr gebraucht | Dimension | -|---|---|---| -| Gerade Linie | 2× | 1 | -| Kreis(-linie) | 2× | 1 | -| Volles Quadrat | 4× | 2 | -| Küste Bretagne | ca. 2,4× | ca. 1,3 | - -Die Zahlen der letzten Zeile sind **Platzhalter zur Illustration** — der -tatsächliche Wert steht erst nach Schritt 7 fest. - -Das Widget zeigt genau diesen einen Faktor als große Zahl. Der Weg von "2,4×" -zur Dimension "1,3" läuft über eine **visuelle Skala** (ein Zifferblatt/Balken -von 2 bis 4, beschriftet 1 bis 2) — kein Logarithmus, aber mathematisch -ehrlich, weil es genau `log₂` ist. - -Die Formel kommt in ein `` mit dem Titel *"Für Papa und die Großen"*. -Dort darf der Log-Log-Plot und `d = log N / log(1/ε)` stehen. Das ist der -einzige Ort im Post mit Notation. - -## Outline - -Zielumfang: **900–1200 Wörter Deutsch**, 4 Abschnitte. Nicht mehr. - -1. **Stell dir vor, du wanderst an der Küste der Bretagne** (~150 W) - Konkrete Szene, aber unpersönlich erzählt: eine Bucht, die von oben auf der - Karte winzig aussieht — und die zu Fuß ewig dauert, weil sie voller kleiner - Buchten in der Bucht steckt. Frage an den Leser: Wie lang ist die Küste - eigentlich? Antwort: Kommt drauf an, wie genau man hinschaut. Funktioniert - für jedes Kind, das die Bretagne noch nie gesehen hat — das Foto liefert - das Bild, der Text braucht keine gemeinsame Erinnerung vorauszusetzen. - *[TODO: Fred — welches Foto aus deinem Fundus zeigt eine Bucht mit - erkennbar zerklüfteter Küstenlinie? Das ist wichtiger als ein "schönes" - Bild.]* - -2. **Das Kästchenspiel** (~250 W) → **Widget 1: `KaestchenSpiel`** - Erst raten lassen, dann zählen. Gerade Linie, Kreis, volles Quadrat. - Schieberegler halbiert die Kästchen, das Widget zeigt den Faktor. - Ergebnis: Linie und Kreis → 2×, Quadrat → 4×. - -3. **Und jetzt eine echte Küste** (~300 W) → **Widget 2: `KuestenSpiel`** - Dieselbe Mechanik auf der Bretagne-Küste. Faktor liegt dazwischen. - Umschalten auf die Normandie: derselbe Regler, viel kleinerer Faktor. - Kernsatz: Die Küste ist "mehr als eine Linie, aber weniger als eine Fläche". - Einordnung: Mandelbrot hat das 1967 für Großbritannien gemacht, D ≈ 1,25. - - **Framing-Regel: "zerklüftet vs. glatt", nicht "Fels vs. Sand".** Die - Normandie-Kontrastküste ist Kreidefels und trotzdem fast gerade — eine - Material-Erklärung wäre also schlicht falsch und bricht am ersten - Gegenbeispiel. Der Post beobachtet die Form, er erklärt keine Geologie. - Spart nebenbei Wörter. - -4. **Selber malen** (~150 W) → **Widget 3 (Teil von Widget 1): Finger-Modus** - Sie zeichnen mit dem Finger auf dem iPad eine eigene Kurve und bekommen - deren Dimension. Glatter Bogen → nahe 1. Wildes Gekritzel → deutlich höher. - Das ist der Abschluss, nicht ein Fazit-Absatz. - -Plus: `` "Für Papa und die Großen" mit der Formel und dem Log-Log-Plot. - -## Interactive Elements - -**Architekturentscheidung: kein Leaflet, keine Kartentiles.** Begründung unten -unter "Mobile". Alles läuft auf *einem* Canvas-Widget mit austauschbarem Inhalt. - -**Dateiformat: `.mdx` mit importierten Komponenten**, nicht `.tsx` wie -`tragedy_of_commons_fishing.tsx`. Der Post-Text lebt als Markdown in -`kuesten_dimension.mdx`, die Widgets sind eigenständige Komponenten unter -`components/blog/`, die per `import` eingebunden werden — exakt das Muster von -`housing_risk_portfolio.mdx` (`ShockCalculator`, `RiskReality`) und -`etf_diversification_interactive.mdx`. - -``` -website/components/blog/box-dimension/ - boxCounting.ts # reine Logik: countCells(points, cellSizePx) -> N - BoxCanvas.tsx # Canvas: zeichnet Kurve + Gitter, hebt Trefferzellen hervor - KaestchenSpiel.tsx # Widget 1: Linie/Kreis/Quadrat/Finger-Modus - KuestenSpiel.tsx # Widget 2: Bretagne vs. glatte Küste, gleicher BoxCanvas - DimensionSkala.tsx # der Balken 2×…4× -> 1…2, ersetzt den Log-Log-Plot - shapes.ts # Linie, Kreis, Quadrat als Punktlisten - coasts.ts # vorverarbeitete Küstenumrisse, INLINE als Punktarrays - -website/blog/kuesten_dimension.mdx - # Frontmatter + Fließtext + vier import-Zeilen für KaestchenSpiel, - # KuestenSpiel, MyFigure, Foldable — kein export const meta, normales - # YAML-Frontmatter wie bei allen anderen .mdx-Posts -``` - -**Wiederverwendung aus dem Repo (nichts davon neu bauen):** - -- `components/Foldable.tsx` — für den Erwachsenen-Exkurs -- `components/MarkdownWithLatex.tsx` + KaTeX — für die Formel im Foldable -- `react-chartjs-2` / `chart.js` (bereits Abhängigkeit, siehe - `DiversificationRandomWalk.tsx`) — für den Log-Log-Plot im Foldable. - **Kein eigenes `LogLogPlot.tsx` bauen.** -- `components/blog/palette.ts` — Farbtokens, Panda-Regel 1 beachten -- `components/MyFigure.tsx` — Aufmacherfoto mit Bildunterschrift. Bilddatei - nach `website/public/blog/`, Referenz als `/blog/.jpg` (Muster: - `collect_imagegen.mdx`). Fürs iPhone vorher auf ~1200 px Breite runterrechnen. - -## Mobile (iPad / iPhone — Hauptplattform) - -Das war im ersten Plan gar nicht adressiert und kippt die größte Entscheidung: - -- **Leaflet fällt raus.** Eine pan-/zoombare Karte auf dem iPhone frisst - Scroll-Gesten — der Leser bleibt in der Karte hängen statt weiterzulesen. - Dazu: `moveend`/`zoomend`-getriggertes Neuzeichnen des Gitters, ein Slider - direkt neben einer schwenkbaren Karte auf 390 px Breite, plus - `leaflet` + `react-leaflet` (~150 KB) und Tile-Requests. Für null - didaktischen Mehrwert — die Kinder brauchen keine zoombare Weltkarte, sie - brauchen *einen* Küstenumriss. - Ersatz: fester Umriss auf Canvas, daneben ein statisches Kartenbild zur - Wiedererkennung ("das ist die Bretagne"). -- **Alles vertikal stapeln.** Canvas oben, Regler darunter, Ergebniszahl - darunter. Kein Nebeneinander von Canvas und Plot. -- **Touch first:** Regler mit ≥44 px Trefferfläche; der Finger-Modus in - Abschnitt 4 ist bewusst ein Touch-Feature, kein Maus-Feature. `touch-action` - auf dem Zeichen-Canvas setzen, sonst scrollt die Seite beim Malen. -- **Canvas mit `devicePixelRatio` skalieren**, sonst ist das Gitter auf dem - Retina-Display matschig — und ein matschiges Gitter ist hier der Inhalt. -- **Budget: < 100 KB** für alle Küstendaten zusammen (nicht "einige hundert KB - pro Region" wie im ersten Plan). Umrisse als Punktarrays direkt in - `coasts.ts` inlinen, kein Fetch aus `public/`. - -## Datenaufbereitung - -1. GSHHG holen (public domain), Level 1 Küste -2. Auf Bounding Box zuschneiden — Bretagne grob 47,2–48,9 °N / −5,2–−1,0 °E -3. Kontrastregion Normandie zuschneiden (siehe unten) -4. Vereinfachen (Douglas-Peucker) — **so wenig wie möglich**, denn genau die - kleinen Zacken sind das Messobjekt. Die Vereinfachung muss feiner sein als - das kleinste Kästchen, sonst misst das Widget die Vereinfachung statt die - Küste. -5. Auf feste Canvas-Koordinaten projizieren und als Punktarray exportieren - -### Kontrastregion: Normandie - -Gewählt: **Ouistreham bis Le Tréport**, grob 49,2–50,1 °N / −0,3–1,4 °E. - -Begründung — der springende Punkt ist nicht "glatter", sondern **vergleichbarer -Maßstab**: Box-Dimensionen sind nur fair vergleichbar, wenn beide Küsten über -denselben Bereich von Kästchengrößen gemessen werden. Die Normandie-Strecke -ist mit ~200 km ähnlich lang wie die Bretagne-Küste, liegt am selben Meer, hat -dieselben Gezeiten und dieselbe Kartenprojektion. Die einzige Variable, die -sich ändert, ist die Form. Beide liegen zudem auf derselben Frankreichkarte — -für die Kinder unmittelbar einordenbar. - -**Cotentin muss draußen bleiben.** Die Halbinsel (Cap de la Hague) ist selbst -zerklüftet und würde den Kontrast auffressen. Deshalb der Zuschnitt östlich der -Baie des Veys. - -*Verworfene Alternative Usedom:* Außenküste nur ~40 km — bei gleichen -Kästchengrößen würde man einen anderen Größenordnungsbereich messen als bei der -Bretagne. Zweite Falle: die Achterwasser-Seite ist stark verästelt, der -Gesamtumriss der Insel liefert einen *höheren* Wert als erwartet und hätte im -Widget das Gegenteil der Aussage gezeigt. - -**Validierungsschritt vor dem Schreiben (fehlte im ersten Plan):** erst messen, -was die Pipeline tatsächlich ausspuckt, dann den Text schreiben. Über nur ~1,5 -Größenordnungen und mit vereinfachten Daten kommt sehr wahrscheinlich *nicht* -exakt 1,25 heraus. Der Text darf deshalb keine Zahl versprechen, die das Widget -nicht liefert. Formulierung im Post: "ungefähr", plus ein ehrlicher Satz, dass -Mandelbrots Zahl mit besseren Daten gemessen wurde. - -## Tone & Style - -**Kinderton, kein Adressaten-Ton.** Vorbild: "Sendung mit der Maus" / -"Wissen macht Ah!" — die Du-Form bleibt, aber sie meint "du, die Leserin, der -Leser da draußen", nicht bestimmte Kinder mit Namen. Kein "ihr zwei", keine -Familienreferenz. - -- **Deutsch, Du-Form, aber generisch** — "du" als Ansprache an jeden Leser, - keine Namen, keine private Anekdote. -- Kurze Sätze. Kurze Absätze. Fragen statt Behauptungen. -- Erst raten lassen, dann auflösen — das ist das didaktische Rückgrat und der - Grund, warum die Widgets einen "Erst schätzen"-Schritt bekommen. -- Erklärstimme wie bei einem guten Erklärvideo: neugierig, nie belehrend, - gerne kleine Ausrufe ("Überraschung: ...", "Und jetzt kommt der Clou."). -- Keine Notation außerhalb des ``. "Kästchengröße", nicht "ε". - "Wie viel mehr", nicht "Skalierungsverhalten". -- Fachwörter genau zwei: **Dimension** und **Fraktal**. Beide beim ersten - Auftreten in einem Satz erklärt. - -## Sources & Research - -- Mandelbrot, *How Long Is the Coast of Britain? Statistical Self-Similarity - and Fractional Dimension*, Science 156 (1967) — Westküste GB D ≈ 1,25, - Südafrika D ≈ 1,02 als glatter Gegenpol -- Richardson (posthum, 1961) — die Originalmessungen dahinter -- GSHHG, Wessel & Smith — Küstendaten, public domain -- *[TODO: source]* Ein publizierter D-Wert speziell für die Bretagne. Falls - keiner auffindbar: keine Zahl behaupten, nur den selbst gemessenen Wert des - Widgets zeigen und als "unsere Messung" kennzeichnen. - -## Consistency Notes - -**Sprache: geklärt, Deutsch ist etabliert.** `sprit_national.mdx` (2026-05-28, -`tokenID: 197`) ist vollständig auf Deutsch, inklusive deutscher -SEO-Description. Kein Bruch, kein `lang`-Feld nötig — dem Muster dort folgen: -deutscher Titel, deutsche Description, normale `tokenID`. - -**Verwandte Posts (Struktur übernommen):** - -- `housing_risk_portfolio.mdx` und `etf_diversification_interactive.mdx` — - das `.mdx`-Format mit importierten Widget-Komponenten aus - `components/blog/`. Dieser Post folgt diesem Muster, **nicht** dem - `.tsx`-Format von `tragedy_of_commons_fishing.tsx` / - `prisoners_dilemma_interactive.tsx` (Widget und Text in einer Datei, - `export const meta` statt YAML-Frontmatter). Der `.mdx`-Weg hält den - Post-Text als reines Markdown lesbar und die Widgets unabhängig testbar — - passt außerdem besser zum Zielumfang von 900–1200 Wörtern, da die - `.tsx`-Beispiele mit 1600–2000 Zeilen zeigen, wie leicht das Format zum - Anwachsen einlädt. -- `cosmopol_democracy.mdx` (Prolog) und `housing_risk_portfolio.mdx` - (Abendessen-Szene) — beide öffnen mit einer konkreten Szene statt mit dem - Konzept. Abschnitt 1 macht dasselbe mit der Bretagne-Wanderung. -- Die Schreibprinzipien für das Nicht-STEM-Publikum (natürliche Sprache statt - Notation, konkretes Beispiel vor Verallgemeinerung) gelten hier verschärft. - -**Panda-Regeln** aus `website/CLAUDE.md` beachten — insbesondere Regel 1 -(keine JS-Variablen in `css({})`), relevant weil Canvas-Farben in JS *und* CSS -gebraucht werden → über `token()` aus `palette.ts`. - -## Getroffene Entscheidungen - -1. **Sprache:** Deutsch, öffentlicher Blogpost mit `tokenID`, analog - `sprit_national.mdx`. -2. **Kontrastregion: Normandie** (Ouistreham bis Le Tréport). Siehe - "Kontrastregion" oben — Begründung ist der vergleichbare Maßstab, nicht nur - die Glattheit. -3. **Aufmacher: eigenes Foto** der Bretagne-Küste über ``. Kein - Kartenbild nötig — das Widget zeigt den Umriss selbst herausgezoomt. -4. **Finger-Modus: ja.** Die Kinder sind Touch gewohnt; das wird der stärkste - Abschnitt des Posts. - -## Offene Punkte - -- Der gemessene Zahlenwert für beide Küsten steht erst nach Schritt 7 fest. - Bis dahin keine Zahl im Text festschreiben. -- `[TODO: source]` für einen publizierten D-Wert der Bretagne (siehe Sources). - -## Implementierungsschritte - -- [ ] **1** `boxCounting.ts` — reine Zählfunktion + Unit-Tests -- [ ] **2** `shapes.ts` — Linie, Kreis, Quadrat als Punktlisten -- [ ] **3** `BoxCanvas.tsx` — Canvas mit devicePixelRatio, Gitter, Trefferzellen -- [ ] **4** `DimensionSkala.tsx` — Faktor 2…4 → Dimension 1…2 -- [ ] **5** `KaestchenSpiel.tsx` — Widget 1 inkl. "Erst schätzen"-Schritt -- [ ] **6** GSHHG holen, Bretagne + Normandie zuschneiden, vereinfachen, nach - `coasts.ts` inlinen -- [ ] **7** **Messen und validieren** — welche Zahlen liefert das Widget - wirklich für Bretagne und Normandie? Ist der Unterschied auf einem - iPhone-Screen auf einen Blick sichtbar? Wenn nein: Zuschnitt nachjustieren, - nicht den Text schönen. -- [ ] **8** `KuestenSpiel.tsx` — Widget 2, Umschalter Bretagne/Normandie -- [ ] **9** Finger-Modus -- [ ] **10** ``-Exkurs mit Log-Log-Plot via `react-chartjs-2` -- [ ] **11** Post-Text schreiben, auf dem iPhone gegenlesen -- [ ] **12** Quellenangaben (Mandelbrot, GSHHG), `styleConventions.test.ts` grün -- [ ] **13** Nach Veröffentlichung: diese Plandatei löschen - -## Lizenz/Attribution - -- GSHHG: public domain (WVS/WDBII), Quellenangabe trotzdem fair -- OSM/CARTO-Attribution entfällt vollständig, weil keine Tiles und kein - Kartenbild mehr geladen werden — Nebeneffekt der Leaflet-Entscheidung -- Aufmacherfoto: eigenes Bild, keine Lizenzfrage diff --git a/website/blog/kuesten_dimension.todos.md b/website/blog/kuesten_dimension.todos.md deleted file mode 100644 index b3288389f..000000000 --- a/website/blog/kuesten_dimension.todos.md +++ /dev/null @@ -1,167 +0,0 @@ -# Critique: Welche Dimension hat die Küste der Bretagne? - -**Target audience:** Math-interested German-reading children in years 6 and 8 (roughly 11–14), on an iPad/iPhone. Confirmed with the author this round — they already know integer powers, so `2¹`/`2²` need no introduction; only the fractional exponent does. Generic "du", no family references (per plan). Adults read over the shoulder and are served by the appendix. -**Plan file:** Found (`kuesten_dimension.plan.md`), and now substantially stale — it still specifies `DimensionSkala.tsx`, the `` "Für Papa und die Großen", "10 bis 12 Jahre", "Potenzen mit gebrochenen Exponenten" as a hard limit (the post now teaches exactly that), and placeholder dimension values. Two of its promises remain undelivered: the opener photo and the 900–1200 word target. -**Overall impression:** The spine is strong and the exponent notation from the last round genuinely landed — `×2,3 = 2^1,19` turns "the dimension is between 1 and 2" from a claim into something the child reads off their own measurement. Two problems now stand out, and both are seams left by recent changes: the text still says the line grows "doppelt so viele, egal wie oft du halbierst" while the widget prints ×1,8 in 18px prose right next to it, and the new title poses a question the post never plainly answers. - -This round's review is below. The earlier review and its resolutions are kept underneath as a record. - -## Critical Issues - -- [x] **[§ Intro]** ~~The title asks for a dimension, the opening asks for a length, and nothing bridges them.~~ Fixed: the intro now announces the quarry — "Am Ende steht dabei eine einzige Zahl, die diese Küste beschreibt: ihre **Dimension**." — so the title's question is visibly in play from the first screen instead of surfacing 250 words later. - -- [x] **[§ Das Kästchenspiel]** ~~"doppelt so viele Kästchen, egal wie oft du halbierst" is contradicted by the widget on the same screen (×2,0 / ×1,8 / ×1,9).~~ Fixed on **both** sides, which turned out to be better than either alone: - - *The instrument.* The wobble was an artefact, not a property of lines: `LINE_SHAPE` ran from x=10 to x=90, and 80 units is not a multiple of the cell sizes, so both ends fell mid-cell. Spanning the full world lands both ends on a grid corner at every step — measured 4 · 8 · 16 · 32 · 64, exactly ×2 throughout, verified in `test/kuestenZahlen.test.ts`. - - *The text.* The circle genuinely wobbles (measured ×2,17 / ×1,92 / ×2,04 / ×2,00) and no shape change fixes that, so the post now says so and explains why: the form ends mid-box at the edges, that partial box still counts, and it weighs far more at a coarse grid than a fine one. That paragraph pays for itself three times — it justifies why the new line comes out exact, it explains the "im Schnitt" in the Fazit that stood uncommented, and it covers the draw widget, where a finger line wobbles most of all. - -- [x] **[§ Und jetzt eine echte Küste, § Selber malen]** ~~The post never tells the child to *operate* widgets 2 and 3.~~ Fixed by applying section 1's pattern to all three: "Halbier die Kästchen wieder, so oft es geht" before the coastline widget, "und halbier dann die Kästchen wie vorher" in the drawing section. - -- [x] **[§ Schluss]** ~~The post never answers its own title.~~ Fixed with a closing paragraph that says it plainly: "Bleibt die Frage aus dem Titel. Die Küste der Bretagne hat die Dimension 1,19: mehr als eine Linie, weniger als eine Fläche." - -- [x] **[§ Intro / frontmatter]** ~~No opener image.~~ **Withdrawn — this finding was wrong.** `Post.tsx:142` renders the `tokenID: 201` NFT image *before* the MDX content (floated left at 220px, centred up to 300px below 768px), and it was generated specifically as the hero for this post: an aerial view of the Brittany coast with bays inside bays. Verified in the browser at both widths — it sits at the top of the article, ahead of the first paragraph. - The real residue was smaller: the opening asked the child to *imagine* ("Stell dir vor") what was already on screen, and called it a map when it is an aerial view. Now fixed — the first sentence points at the picture instead. - -## Suggestions - -- [x] **[§ Das Kästchenspiel]** ~~"Im Widget steht **neben** deinem Ergebnis noch eine zweite Schreibweise" — it is not beside the result, it *is* the result.~~ Fixed, and it now also says when to expect it: "Wenn du alle fünf Halbierungen gemacht hast, schreibt das Widget das Ergebnis in einer zweiten Form auf." - -- [x] **[§ Und jetzt eine echte Küste]** ~~Two paragraphs make the same point at different precision.~~ Fixed — the vague half ("Sie liegen näher an ×2 als an ×4 — aber eben nicht genau bei ×2") is cut, since the exponent paragraph right after says it exactly. - -- [x] **[§ Und jetzt eine echte Küste]** ~~The Normandie paragraph is weaker than the widget it describes.~~ Fixed: "Die Bretagne kommt auf die Hochzahl 1,19, die Normandie nur auf 1,08." Both numbers are now pinned to the measurement by `test/kuestenZahlen.test.ts`. - -- [x] **[§ Selber malen]** ~~Nothing says where else this turns up.~~ Fixed: "für die Verästelungen in deiner Lunge, für Schneeflocken, für Blitze — und für einen Blumenkohl." - -- [x] **[§ overall]** ~~Around 900 words against the plan's 900–1200.~~ The additions this round (wobble explanation, closing answer, outlook, operating instructions) close the gap. - -- [ ] **[§ Anhang]** The plan fixes the vocabulary at "genau zwei" technical terms, Dimension and Fraktal. "Hochzahl" is now a third. It is a school word rather than jargon and almost certainly fine for years 6 and 8 — but the constraint was extended without being revisited. Resolved by retiring the plan file (see Nitpicks), not by changing the post. - -## Nitpicks - -- [x] **[§ Das Kästchenspiel]** ~~"wenn man **sie** halbiert" — "sie" points back at the line, but what gets halved are the boxes.~~ Fixed: "…wenn die Kästchen halb so groß werden?" - -- [x] **[§ Selber malen]** ~~A calm hand-drawn line is called "genau" a line of dimension 1, which overclaims when the child can see their own wobble.~~ Addressed by the wobble paragraph rather than by changing the threshold: the drawing section now says outright that a finger line wobbles more than the circle and why, so "im Schnitt ×2" is what the child expects to read. - -- [ ] **[§ plan file]** *(carried over)* `kuesten_dimension.plan.md` is now contradicted by the post in four places (see "Plan file" above), including a hard constraint the post deliberately broke. Per its own step 13 it should be deleted after publishing. - -- [x] **[§ frontmatter]** ~~`tokenID` is missing.~~ Resolved — `tokenID: 201`. - -- [x] **[§ Selber malen]** ~~Nothing prepares the child for the "Kleinste Kästchengröße erreicht." dead end.~~ Resolved — that sentence was removed and the button slot now becomes "Nochmal von vorn". - -## Identity check - -Measured against [`IDENTITY.md`](../IDENTITY.md). - -**Holds:** - -- *"The built thing is the content itself, not decoration around it."* Three working instruments carry the argument; the prose is scaffolding between them. This post is close to the purest expression of the claim on the site. -- *"Serif reads, sans operates."* Decided by content rather than container: the widget chrome is sans, the guidance line and the Fazit opt back into the reading face. That is the rule applied the way IDENTITY argues for, not the way the container would have imposed. -- *"Clean, not decorated."* The last round removed the tinted Fazit box and confined the accent colour to the data and one number. A colour audit would now find purple in exactly two roles. -- *"Honest."* The appendix admits the simplified data does not reach Mandelbrot's 1,25 and names the logarithm rather than hiding it. - -**Does not hold:** - -- ~~**"Ciechanowski: the article is visible immediately."** The post opens with three paragraphs before anything is visible.~~ **Withdrawn — wrong.** The NFT hero image is rendered above the article body by `Post.tsx`, so the article *is* visible immediately. See the retracted Critical item above. -- [x] **"Figures and tools may exceed [the prose measure] — they are the content, not decoration."** ~~`BoxCanvas` caps at `maxWidth: 360px`, so on a desktop the tool renders at half the width of the prose column.~~ Fixed: `maxWidth` rises to 560px from 768px up, mobile unchanged. Measured in the browser — the finest grid went from 5,6 px to 8,8 px per cell on a desktop, and that mesh is the thing the reader is asked to look at. -- [x] **"Deep, not dense" / "honest"** ~~are both dented by Critical #2: a page where the prose and the instrument disagree.~~ Resolved by fixing the instrument *and* explaining the residue — see Critical #2 above. - -**Added this round, not from the critique:** - -- [x] **The widgets were silent to screen readers.** Clicking "Kästchen halbieren" changed the guidance line, but nothing announced it — the entire measuring loop was visual only. The guidance line now carries `aria-live="polite"` in all three widgets (verified in the browser), and the canvas carries `role="img"` with a German `aria-label` describing what is drawn. The Messreihe was already real DOM and therefore readable. -- [x] **The numbers in the post are now pinned to the measurement.** `test/kuestenZahlen.test.ts` recomputes both coastline dimensions from `coasts.ts` and asserts that every value quoted in the MDX matches, plus that the line grows by exactly ×2 at every step. This coupling broke silently three times in this project and was each time caught rounds later by re-reading the post; it can no longer break unnoticed. -- [ ] **German posts are served under `lang="en"`.** `` follows the URL prefix, not the content (see `README.md` → Typography), so a screen reader pronounces this post with English phonemes. Affects `sprit_national.mdx` equally — a site-wide issue, not this post's to fix, but worth recording. - ---- - -# Earlier review — resolved unless marked open - -Three review passes: **A. Post content**, **B. Widget UX**, **C. Styling & identity**. - ---- - -# A. Post content - -## A — Critical Issues - -- [x] **[§ Anhang]** ~~A literal `[TODO: Log-Log-Plot via react-chartjs-2 einfügen, …]` sits in the published text (line 70).~~ Fixed: section rewritten in the exponent form ($N = (1/s)^d$), log-log plot dropped entirely (no value seen), TODO marker gone. Also converted from `` to a plain `## Anhang` heading — the user doesn't use Foldable in blog posts, they use a plain appendix-style section instead (matching `sprit_national.mdx`'s `## Technische Details`). - -- [x] **[§ Und jetzt eine echte Küste]** ~~The text claims the Bretagne ×-numbers lie "näher an ×4 als an ×2"~~ Fixed: reworded to "Sie liegen näher an ×2 als an ×4 — aber eben nicht genau bei ×2", matching the actual ≈×2,28 measurement while keeping the "still not a line" point. - -- [x] **[§ Selber malen]** ~~The text promises "so wild du kannst" but a scribble genuinely stays near dimension 1~~ Fixed: instruction changed from vague "wild" to concrete self-similar structure — "eine Linie mit ganz vielen kleinen Zacken darauf, wie ein Gebirgszug" — which is what box-counting actually rewards, plus the classifier fix below means even a modest result now reads honestly as "ein Fraktal" instead of "fast eine Linie". - -- [x] **[§ Und jetzt eine echte Küste / widget conclusion]** ~~The threshold in `Messreihe.tsx` (`<= 2.2`) was invalidated by the recalibration~~ Fixed: `explainFactor` restructured — the line/area edge cases narrowed to `≤2.05`/`≥3.95` (only genuine point-landings, verified exact 2.0/4.0 shapes), everything else (Bretagne 2,28, Normandie 2,12, any hand-drawn curve) always reads "Das ist ein Fraktal" with a näher-an-Linie/Fläche position hint (boundary at ×2,83 = 2^1.5, the multiplicative midpoint). No longer contradicts the "Küsten sind Fraktale" claim, and is robust to the next recalibration since it no longer hinges on a single knife-edge cutoff between "fractal" and "not". - -- [ ] **[§ Intro / frontmatter]** The plan's decision #3 was an opener photo of the Bretagne coast via ``, and the plan's §1 explicitly leans on it ("das Foto liefert das Bild, der Text braucht keine gemeinsame Erinnerung vorauszusetzen"). The post contains no image at all and doesn't import `MyFigure`. For a child who has never seen this coast, the entire opening is abstract prose — the weakest possible start for this audience. - -- [ ] **[§ frontmatter]** `tokenID` is missing. Every other published post carries it (`sprit_national.mdx` → 197, `housing_risk_portfolio.mdx` → 194). Confirm whether this is intentional pending minting, or an oversight. - -## A — Suggestions - -- [x] **[§ Das Kästchenspiel]** ~~The closing sentence "Wenn du beide Zahlen nebeneinanderlegst … ein Würfel würde 8× brauchen" is the densest passage in the post …~~ Replaced by the power-of-two notation: "×2 ist dasselbe wie $2^1$, ×4 dasselbe wie $2^2$. Die Hochzahl ist genau die Dimension — ein Würfel bräuchte entsprechend $2^3 = 8$." Same job, but the relationship is now visible as notation instead of asserted in words, and the cube follows from the rule rather than having to be believed. The readers are in year 6 and 8 and already know integer powers, so nothing new has to be taught here. - -- [x] **[§ Anhang]** ~~The plan required "ein ehrlicher Satz, dass Mandelbrots Zahl mit besseren Daten gemessen wurde"~~ Fixed as part of the same rewrite: "Mit den vereinfachten Daten hier … kommt man nicht exakt auf Mandelbrots 1,25" now explains the gap, and the redundant repeat of the Mandelbrot sentence (already stated in the main text above) was cut too, since it's no longer hidden behind a fold. - -- [ ] **[§ Selber malen]** Nothing in the text prepares the child for the "Kleinste Kästchengröße erreicht." dead end. See **B — Critical** for the widget side of this. - -- [ ] **[§ overall]** At roughly 800 words the post is under the plan's 900–1200 target. That is fine in itself, but the shortfall sits exactly where the missing photo and the missing Mandelbrot-honesty sentence would go. - -## A — Nitpicks - -- [ ] **[§ Das Kästchenspiel]** "Bevor du weiterliest: Was schätzt du — bei einer geraden Linie, wie viel mehr Kästchen braucht man, wenn man sie halbiert?" — "wenn man **sie** halbiert" grammatically refers back to the line, but the thing being halved is the boxes. A 10-year-old parsing carefully gets the wrong referent. - -- [x] **[§ Und jetzt eine echte Küste]** ~~The "obwohl" clause is doing a lot of work in one long sentence; splitting it would match the short-sentence rule.~~ Split into two sentences while the paragraph was being edited anyway. The paragraph now continues into the "slider" explanation of what a fractional exponent means — between $2^1 = 2$ and $2^2 = 4$ there is no gap, so the exponent can slide, and the coastline sits somewhere along the way. - -- [ ] **[§ plan file]** `kuesten_dimension.plan.md` is now stale: it still specifies `DimensionSkala.tsx` (removed), the "Für Papa und die Großen" Foldable label (depersonalized), and placeholder dimension values. Per its own step 13 it should be deleted after publishing — worth doing rather than leaving a contradictory document beside the post. - ---- - -# B. Widget UX - -## B — Critical Issues - -- [x] **[KuestenSpiel.tsx]** ~~Switching region resets the measurement series to step 0, wiping the previous region's numbers.~~ Fixed: the region switch no longer resets `stepIndex`, so it became an A/B toggle — same grid, other coast, different numbers, which is exactly the gesture the prose asks for. Once both coasts have been measured to the end, the Fazit carries one more sentence naming both factors ("Die Bretagne (×2,3) ist zerklüfteter als die Normandie (×2,1)"), so the comparison survives the toggle instead of living in memory. - -- [x] **[KuestenSpiel.tsx / KaestchenSpiel.tsx]** ~~Dead end at the last step … there is no way to start over.~~ Fixed: the grey sentence "Kleinste Kästchengröße erreicht." is gone entirely and the button slot itself becomes "Nochmal von vorn" (`secondary`) at the last step. It lands in the same position the child has clicked four times, which is why the first attempt — a reset button *beside* the grey sentence — went unnoticed and had to be redone. - -- [x] **[all three widgets]** ~~The interaction model silently changes between widgets … nothing in the UI signals the changed rule.~~ Fixed by showing rather than telling: widgets 2 and 3 display the count in the guidance line the moment the widget appears ("14 Kästchen berühren die Küste."), which demonstrates that the counting is automatic. An explicit caption ("Diesmal zählt der Computer für dich") was tried first and then removed — it was one element more for the same information. - -## B — Suggestions - -- [x] **[KaestchenSpiel.tsx]** ~~The guess step fires exactly once … four of the five steps are pure consumption.~~ Fixed: the prompt now shows at every unrevealed step, and from step 2 on it asks the question the whole post is about — "Schätz wieder: Doppelt so viele? Oder viermal so viele?" It also moved out of the 14px italic grey hint style into the guidance line at prose size, since an instruction set as a footnote reads as optional. - -- [x] **[Messreihe.tsx]** ~~The middle bar column has no header … a child can easily read the bar as the box size rather than as the count.~~ Fixed: the header now mirrors the row geometry, and "Anzahl der Kästchen" is left-aligned over the bar rather than right-aligned over the number, so the bar is explicitly labelled as the count. - -- [x] **[Messreihe.tsx]** ~~The size swatch shrinks to ~6px on the finest row … mostly reads as a dot.~~ Fixed differently than proposed: raising the floor to 10px made the last two rows *identical*, which is worse than small — those boxes really are half the size. The swatch is now filled instead of outlined (a 20px outline reads much like a 28px one; a solid square does not) and keeps the honest 28/20/14/10/7 progression. - -- [x] **[all widgets — also a styling item]** ~~Touch targets are below the 44px guideline … ~~Fixed, but **not** "for free" as this entry claimed: the recipe does not reach 44px either — `size: "md"` is ~37px and `size: "sm"` ~29px. It took an explicit `touchTarget` (`minHeight: 44px`) in `components/blog/box-dimension/styles.ts`, merged next to the recipe class the way `Post.tsx` merges `post.errorSpacing`. Not a second button definition, just one measurement the recipe does not carry. - -## B — Nitpicks - -- [x] **[KaestchenSpiel.tsx]** ~~In draw mode, `handlePointerDraw` resets `animStep` to 0 on every sampled point … jittery.~~ Fixed: the rewind now only fires when a genuinely new drawing starts (`drawnPoints.length === 0`). - -- [x] **[KaestchenSpiel.tsx / KuestenSpiel.tsx]** ~~The big "N Kästchen" readout (`bigNumber`) exists only in widget 1 … different visual weight in each widget without a stated reason.~~ Resolved by deleting `bigNumber` outright. It was a second 24px accent number competing with the Fazit for the same job; the count now lives in the guidance line as a sentence ("Jetzt sind es **41 Kästchen** — 2,3-mal so viele wie eben."), identical in all three widgets. The Fazit is the single loud number. - ---- - -# C. Styling & identity - -Measured against `website/README.md` (the design system) and `website/IDENTITY.md` (the reasoning). - -## C — Critical Issues - -- [x] **[KaestchenSpiel.tsx, KuestenSpiel.tsx]** ~~Three hand-written button styles violate "No component defines its own button" … the hand-rolled buttons have no focus outline.~~ Fixed: all four style functions are gone, replaced by `button({ visual: "secondary", size: "sm", active })` for the toggles (the same combination the network picker in `FacilitatorApproval.tsx` uses), `visual: "primary"` for the action and `visual: "ghost"` for "Löschen". Focus, hover and disabled come from the recipe base. Note the size split: toggles are `sm`, actions `md` — the article column is only 294px wide on a phone, and `md`'s 80px of horizontal padding stacks three toggles onto three rows. - -- [x] **[BoxCanvas.tsx]** ~~`HIGHLIGHT_FILL = "rgba(124, 58, 237, 0.22)"` hand-copies the `explore` purple as a literal.~~ Fixed, and it was not merely a latent risk: `#7C3AED` is violet-600, a visibly different purple from the `#7B3FA0` stroke drawn on top of it. The fill now uses `ctx.globalAlpha` with `ESSAY_ACCENT`, the same technique the shape fill already used. - -## C — Suggestions - -- [x] **[Messreihe.tsx]** ~~`borderRadius: "2px"` on the swatch invents a value outside the radius scale.~~ Fixed: `xs`. - -- [x] **[all widgets — judgment call, not a violation]** ~~The widgets inherit the serif reading face … worth an explicit decision either way.~~ Decided by content, not container: `fontFamily: "ui"` sits on the widget wrapper, and the two things a child *reads* — the guidance line and the Fazit — opt back into `reading` at prose size. Buttons, column headers and the table read as an instrument; the two prose blocks read as the post's own voice, which is also what makes the Fazit stand out without needing a tinted box. - -## C — Verified as correct (do not "fix") - -These look like violations at first glance but are consistent with documented exceptions — noted here so they don't get changed by mistake: - -- **Explore purple in a blog post.** The colour table assigns purple to `/lab` and blue (`brand`) to articles, so purple in a blog widget looks wrong. It isn't: `components/blog/palette.ts` establishes `ESSAY_ACCENT = token("colors.explore")` as the essay convention, and the README exempts blog-widget palettes from the colour audit ("A colour audit flags them as stray families; they are not"). -- **Water/land hex literals in `BoxCanvas.tsx`.** Same exception — they encode series identity (which thing is which), not brand. -- **Explicit px in padding shorthands** (`padding: "6px 12px"`). This is required, not sloppy: per `website/CLAUDE.md` rule 2, spacing tokens silently fail to resolve inside a multi-value shorthand, so explicit px is the documented workaround.