Saqr ConsultingDesign sheets
Saqr Consulting

Changelog

What changed in each revision, and why
Record

Corrections are applied to the current revision rather than kept as a historical archive, so this is where you find out whether a number you noted has moved, and why. Newest first.

Revision E — the site around the sheets

Rev D finished the set. This revision is about everything around it: finding your way, the blog, and a sweep of every sheet for things that looked wrong. No sheet's arithmetic changed. test-arithmetic.mjs compared every readout on seventeen sheets across forty input sets before and after, and every answer matched.

The donate button is gone

The site no longer asks for donations. The button, its "Not wired up yet" stub and the donations section of the privacy page are removed, and the copy that compared corrections to donations is reworded. The sheets are free with no ask attached.

Signed: Fayssal B.

The credit line on every page now reads Fayssal B. instead of an unnamed METU Aerospace Engineering graduate.

A header on every page

Until now, everything that was not the page in front of you was at the bottom: the cross-sheet strip, and the reference pages in a row of small capitals under it. Every page now opens with a header:

  • Sheets, a menu grouped by the stage of a study;
  • Learn, for the guide, the worked studies, the exercises, the report and the reference pages;
  • Blog, About and a search box.

Under the header there is a breadcrumb, and on a sheet, links to the one before and the one after it in the order a study is worked. Like the strip, the sheet links carry the figures you have already worked out. The strip and the footer row are still where they were. On a phone the header folds into one Menu button, and it works without JavaScript.

A guide, and a blog

How to use the sheets is a new page and the first item in the Learn menu. It covers where to start, the order of a study, how figures travel and how to keep a design, how to read what a sheet tells you, and how far to trust the numbers.

The blog is ready and empty. Posts are files in the repository, built into pages with the site's own chrome, with drafts, scheduled posts, reading time and an Atom feed (feed.xml). An HTML body from outside has anything that could run stripped out of it, and a test throws fifteen attack strings at that step. docs/BLOG.md describes how a writing service can publish here later.

What a sweep of every sheet found

Every sheet and tool was looked at, at desktop and at phone width. These were fixed:

WhereWhat was wrong
Unit converterNo side padding; mmHg showed 759.9999 for one standard atmosphere
Sheet 02The axis title sat on the 10k and 30k labels; an empty box sat under "A section of your own"
Every sheet"55.3 to 55.3" when a picker cannot move the figure; picker text ran under the arrow; the CSV button touched the card edge
Two at a time"Runs from 15.3 to 15.3" prose, and a picker that pushed the page sideways on a phone
Sheet 03The neutral-point line ran through two labels
Sheet 05The chart caption was cut off, and a percentage sat on the 50% tick
AtmosphereA unit spilled out of its card, a label sat on the axis, and the notes cited a red line that was not drawn
Validity panelReynolds number used a fixed viscosity and disagreed with the sheet's own by about 3%
Sheet 12-0.00 and -0.0000
Sheets 09 and 16Notes that contradicted the table beside them
Seven chartsLabels struck through by curves; labels now draw on top, with a halo
Every chart on a phoneLabels at three to four pixels; a chart now keeps a readable size and scrolls sideways in its own frame

The changelog and the methods page both carried the privacy page's subtitle, because a template replace had silently matched nothing. Those replaces now fail loudly.

Corrections to results

These change a figure a sheet gives you, so a number taken from Revision D may differ:

WhereWhat was wrongNow
Sheet 04The maximum lift coefficient started at 1.35, the section value, where every other sheet and the hint beside the box use the whole-wing 1.215Starts at 1.215; on the default trainer cruise power 60 W (was 56) and endurance 60 min (was 64)
Sheet 08A propeller windmilling at cruise showed as 80% efficient; load and cell count were not held to their boxesWindmilling shows no efficiency; load and cells held, which moves RPM, pitch speed and tip Mach on some designs
Sheet 16Climb and loiter were flown at the stall itself on the default trainerFlown at the minimum-power speed or 1.2 times the stall, whichever is faster; mission 16.0 Wh (was 15.5), loiter 26.8 min (was 28.7)
Unit converterkg/m² and N/m² wing loadings were a hundred times too large: 553 kg/m² for a 55.3 g/dm² wing5.53 kg/m² and 54.2 N/m²
Sheet 01, AtmosphereIn imperial units the address bar and a saved design file carried ounces and feet under names every sheet reads as grams and metres, so an imperial link reopened a 2.4 kg model as an 85 g oneLinks and files are metric throughout; the unit choice travels with them
Sheet 01Switching to imperial rounded each box to a tenth and computed on the rounding, so a 50 g model became 51 g with an aspect ratio of 6.04 rather than 6.15, and switching back left 51 g, 398.8 mm, 83.8 mm and 48.3 mm in the boxesThe switch changes what is shown and nothing else; there and back leaves every box and figure as it was, and the readout says the mass in ounces
Sheets 04, 11, AtmosphereA negative pack, a negative tail or fin arm, and an elevation of -1,000,000 m were computed onHeld at the edge of each box's range, and the notes say so
Sheet 14Clicking the best glide ratio, shown 1:9.2, copied 1Copies 9.2

The prose was corrected too: "an 8S pack", "an 11.2 mm horn" and "1 minute" where the sheets had written "a 8S pack", "a 11.2 mm horn" and "1 minutes", and the Reynolds tool's tail and fuselage notes follow the figures beside them, where they had called a tail longer than the wing "-58% lower" and a short, slow fuselage "long and turbulent".

Where the site lives

The site is served by Cloudflare Pages, which answers every .html address with a redirect to the same address without it. Canonical links, the sitemap and link previews now name the address that is actually served. The offline cache stores each page once, as a plain response, whichever address it was asked for by. Safari and Firefox have refused redirected responses for a page load, and Safari on a phone is exactly the reader the offline cache exists for.

Checks

The suite now has fifty-one checks, run in four parallel shards in CI. The new ones:

  • check-header.py: the header and the blog are as generated, and every page names its own address.
  • test-header.mjs: the menus open, close and return focus, and the sheet links agree with the strip.
  • test-blog.py: the blog's sanitiser and its publishing rules.
  • test-promises.mjs: four sheet behaviours the launch checklist promised and nothing was checking.
  • test-extremes.mjs: every box on every sheet set to nonsense — minus a million, nothing — with no broken figure, no computing on it, and no chart label off its drawing.
  • test-units.mjs: every unit converter row against the definitions, both ways.

The offline test now also runs against a server that redirects like Cloudflare does.

Revision D — the set completed, and a report that reads it back

Rev C left the site at nine sheets. It is sixteen now, with three tools and seven reference pages behind them, and a design is no longer something you hold in a browser tab: it is a file you keep, and a report that reads it back as one document.

Thirty-three pages. Still no build step, still no framework, still nothing stored anywhere.

Seven more sheets — the set is sixteen

SheetWhat it settles
10Structures & flight envelopeThe V-n diagram with FAR-23 gust lines, root bending, and the spar cap that carries it
11Control surfacesHinge moments, the servo torque they demand, and roll rate from pb/2V
12Lateral & directional stabilityWeathercock stability and dihedral effect. The one sheet with no atmosphere block: neither derivative depends on density
13Take-off, landing & field performanceGround roll, the distance over a 15 m obstacle, and landing roll, with the rolling-friction table
14Glide & sink polarBest glide, minimum sink, speed to fly, and what a turn costs
15Landing gear & ground handlingWheel loads, tip-back, turnover and propeller clearance. The only sheet that draws the aeroplane to scale rather than plotting a curve
16Mission energy budgetClimb, cruise, loiter, descent and reserve against the pack — and the payload-against-loiter curve

Sheet 16 makes the whole nameplate account for itself: the mission, the reserve held back, and the energy left in the cells below usable voltage. Nothing disappears silently. Climb and loiter are flown at the minimum-power speed, clamped to the stall when that speed is below it — the same clamp Sheet 14 applies to minimum sink, deliberately, because two sheets computing the loiter speed differently would be a defect.

Three tools and seven reference pages

Tools. An atmosphere calculator that exposes the shared block behind its own inputs; a Reynolds number calculator with skin friction and boundary layer thickness, and the only logarithmic axis on the site; and a unit converter covering every pair the sheets use, with the exact factors.

Reference. Nomenclature (62 symbols, with every sign convention stated), Sources (the bibliography, and every compiled figure on the site listed), Methods (18 sections, generated from docs/CALCULATIONS.md), Glossary (66 terms, taken from the sheets' own prose rather than a textbook index), Materials (properties, with the structural tables read out of Sheets 10 and 05 at build time rather than retyped), Studies (four designs through all sixteen sheets), and now the Report.

A design is a file you keep

Every page with inputs gained a Save this design and a Load a design button. The design is a plain JSON file the reader downloads. There is no account and no storage: privacy.html promises no cookies, no local storage and no session storage, and putting the design in a file the reader holds is how that promise is kept rather than worked around.

The file accumulates. Save on Sheet 01, load on Sheet 13, fill in what that sheet needs, save again, and the file carries both. Sheet 01 calls the wingspan span and every other sheet calls it b; the alias lives in one place and nowhere else.

A file carries two things, and the second one took a second attempt. The first version applied only the fields the reader had typed, and that is not the whole design: Sheet 01 works the wing area out and has no box for it, Sheet 04 has a box and cannot work it out. Following the link between them carried the computed area, as it always has. Loading the file did not, so a design carried by file quietly held two different wing areas — 28.4 dm² on one sheet and an untouched 43.4 on the next.

The published figures are applied now as well. Not through one parameter-to-field table, which would have been the obvious way and would have been wrong: vel is cruise speed in km/h on Sheet 07 and in metres per second on Sheet 02, so one table puts 45 km/h into an m/s box. Instead each sheet declares its own pairing, and it already did — fromURL(key, id) is what every sheet calls to read the query string, and it now remembers each pair it is given. A file fills exactly the boxes a nav link fills, by exactly the same pairing, and a sheet that stops reading a parameter stops accepting it from a file at the same moment.

The design report

saqr-report.html renders a design file as one printable document: the aircraft, what each sheet worked out, everything the sheets flagged, where the sheets disagree, and which sheets have not been run yet.

It computes nothing. Every figure in it was worked out by a sheet, under that sheet's assumptions, and a report that quietly re-derived a number could disagree with the page the reader got it from.

The shape of it was decided by the site's own headers. _headers sets frame-ancestors 'none', so no page here can be framed — not even by another page here — and a report cannot go and read the sheets. So the sheets hand their answers over instead: each writes its readout and its flagged notes into the design file as it is saved. That is the stronger arrangement anyway.

The section worth having is where the sheets disagree. The file records what each sheet published as well as what the reader typed, kept per sheet rather than merged, so the report can say that Sheet 01 makes the wing 28.4 dm² and Sheet 04 thinks it is 43.4. A quantity two sheets disagree about has made every answer downstream of it wrong on one of them.

It found a real one the first time it ran: the design-file gap above, which is why that gap is described in the past tense. A file carried through the sheets now leaves the report with nothing to flag — and it still flags a file made before the fix, which is what anyone who carried a design in the last week will be loading.

Every table exports as CSV

Twenty-eight tables across eighteen pages now have a quiet CSV button under them. A third shared block, held identical everywhere by the same check as the other two.

It reads the table out of the page when you click rather than from the data behind it, so a table that rebuilds as you change an input exports what is on the screen. Fields are quoted properly — half the tables here have a thousands separator in them, and 1,013 is one field — merged cells are padded so every row is the same width, and the file opens with a byte-order mark, because the sheets are full of °, ² and · and Excel without one reads them as the local codepage.

Naming the file was most of the work, and the first attempt got it wrong on twelve pages out of eighteen: the site labels a table three different ways and the name-finder knew one of them.

Sheet 02's airfoil matrix is the dataset most worth exporting and is not in this, because it is built from divs rather than a table. It belongs with the open-dataset item on the roadmap.

What moves it

Under the readout on every sheet that computes one, a panel that answers the question a set of figures cannot: which of your inputs is this answer actually resting on? Pick a figure and every numeric field is ranked by how hard it moves it, as per cent out per cent in so that millimetres and grams can be compared against each other.

It needed no per-sheet code. It measures by driving the sheet — set a field to 1.1×, read the readout, set it to 0.9×, read again, put it back — and that works on all seventeen only because every sheet already recomputes synchronously on an input event and writes its answer into the readout. A convention adopted for the reader's sake turned out to be an interface.

A central difference over ±10% rather than a derivative, because these sheets are full of clamps and piecewise fits, and a slope measured over a span you might actually move a design is worth more than a tangent at a point.

The panel reports its own resolution, and that is the part worth reading. It reads the figure the sheet prints: a best glide shown as 9.2 cannot be read finer than 0.1/(9.2 × 0.2) = ±0.05 of elasticity, so it says ±0.05 rather than four digits it has not got. On Sheet 14 the panel gives best glide as +0.98 for the span, +0.54 for span efficiency, −0.49 for wing area and −0.49 for parasite drag, and nothing at all for weight. Best glide goes as √(AR·e/CD0) and does not depend on weight, so those are +1, +0.5, −0.5, −0.5 and zero — read to the precision the sheet prints them at.

The address bar is the design

Change a figure on any sheet and the URL becomes the design. Reload it and the work is still there; bookmark it and it comes back; paste it into a forum thread and whoever opens it sees your aeroplane rather than the defaults. There is a Copy link button beside Save and Load, and a Name this design field whose text rides along in the link so it says what it is.

What goes in is exactly what the sheet reads, taken from the pairing the sheet already declares — the same one that fills the boxes from a design file. A full design comes to about 130 characters.

The link also carries the revision that made it. Open one under a different revision and the figures still fill in, but the page says so rather than letting you assume the answer is the one you were sent — these sheets are corrected in place, so that is the difference between a link that can be cited and a link that merely works. Reproducing the old answer as well would need the old sheet still to exist at its own URL, which is a hosting decision and is on the roadmap as one.

It is not a short link, and that is a decision rather than an omission. A short one needs a server holding the mapping, and this site has none, which is why it can be hosted anywhere, read off a memory stick, and still work in five years. A long URL that carries the design outlives a short one that stops resolving when somebody stops paying for it.

Defects found and fixed

Rev C's habit of publishing its own mistakes continues.

  • Span efficiency did not travel between the five sheets that use it. The glider study came out with a best glide of 17.5 on Sheet 04 and 18.8 on Sheet 14, while the other three designs agreed. e was an input on Sheets 04, 06, 13, 14 and 16 and published by none of them, so the glider was 0.90 on the glide polar and still 0.78 on the power sheet. Every sheet was right on its own, which is why nothing had caught it. It only appears when one design is carried through the whole set — which is what the worked studies do, and they caught it before a word of them was written.
  • Forty call-to-action buttons did nothing at all. The donate and corrections links are deliberately unwired until there is a real address for them, but on fourteen pages clicking one did not even say so: no navigation, no message, just a # on the URL. A placeholder that stays silent reads as a broken site rather than an unfinished one. tools/check-links.py now fails if a placeholder has no stub behind it.
  • Eleven pages had no <footer> landmark, including the cover. The landmark sweep that should have caught it had a hand-written list of pages to check, and the list stopped at Sheet 09. tools/check-landmarks.mjs now reads the page list from the directory. A checker with a hand-maintained list of what to check is a checker that goes stale silently.
  • Sheet 16 and Sheet 09 disagreed about the same battery. Mine, found by writing the agreement checks rather than by reading the code.
  • Sheet 03 handed the reader a signed tail load with the sign convention stated only in a source comment. It is on the page now, and every convention on the site is on the nomenclature page.
  • The methods page skipped from h1 to h3 and had dead links in its own table of contents — the slug was built from the raw markdown and the id from the rendered HTML, so anything containing an ampersand did not match. Both fixed, and the heading-level check that found the first then found six more pages doing the same.
  • The readout captions on nine sheets had no styling at all. .sm was emitted by every sheet written after Sheet 09 and styled by none of them, so a caption meant to sit quietly under a figure was set in body text at full ink, as loud as the number above it.
  • Every Save and Load button went white-on-white when you hovered it, and none of them had a focus ring. Both rules named var(--accent), and --accent is not a token this site defines — it is not defined anywhere in the repository. An invalid var() does not fall back to anything; it unsets the property. So the hover left white text on a transparent button, and the focus rule replaced the site's own perfectly good :focus-visible outline with nothing at all, making those the only controls on the site a keyboard user could not see. The unit converter's own input focus ring had the same fault, from the same variable, and predates all of this.
  • A unit converter that published nothing would have emptied a reader's design. Pages with no figures of their own still have to pass the query string through to their nav links. The ordered-pair sweep could not see this, because a page with no fields is never a useful source in it; there are 348 explicit pass-through checks now.

And one in this revision's own new code, caught by reading the rendered page rather than the diff: the report's principal-figures loop declared its value with var and never reset it, so every quantity missing from the file inherited the previous one. It reported the propeller efficiency as the static thrust, the cruise power and the climb power as well — four plausible numbers, all wrong, all 0.65.

What holds it together

Eighteen checks, none of which run at page load. tools/check-all.sh runs them all, and .github/workflows/checks.yml runs that on every push:

check-shared.pyThe six shared blocks — atmosphere in 13 pages, design file and table export in 18 each, sensitivity and validity in 17 each, citation in 32 — are byte-identical everywhere, and it names the diverging line
check-links.py1,048 links resolve, including page names written into scripts, and every placeholder admits it when clicked
check-landmarks.mjsOne h1, a main, a footer, a skip link and no skipped heading level, on every page in the directory
check-agreement.mjsWhere two sheets compute the same quantity, they agree
test-handoff.mjs306 ordered pairs of sheets, 811 parameter arrivals: every parameter the destination accepts arrives with the value the source sent
test-designfile.mjsA design file accumulates across sheets, refuses what is not one, carries computed figures into the boxes that read them and only those, and renders in the report as the sheets meant it
test-csv.mjsEvery table exports as a CSV a spreadsheet can open: square, named, and not overwriting the table above it
test-sensitivity.mjsThe sensitivity panel against cases with a known closed form, to a tolerance taken from how many digits the sheet prints
test-permalink.mjsEvery sheet's address bar round-trips its design, a bare visit is left alone, and a sensitivity sweep does not rewrite the URL from its own nudges
test-search.mjsThe index covers every page, and fifteen plain queries land on the right one
test-keyboard.mjs1,500 tab stops: the skip link is first, every control shows focus, nothing is unreachable and nothing traps
test-citation.mjsEvery page cites itself with its own revision and URL, and every BibTeX key is unique and compiles
check-markup.pyEvery container tag balanced, every page registering the service worker, every head complete
test-validity.mjsThe validity panel agrees with the sheet, moves with the air, and reacts when a figure leaves its range
test-provenance.mjsThe airfoil dataset, the methods documentation and the sheet's filter agree about which pitching moments were measured
check-release.pyA cached file did not change without a CACHE bump, no placeholder host shipped, and no page grew a new dead button
test-prose.mjsGenerated sentences read as sentences
run-study.mjsDrives one design through all sixteen sheets
build-studies.pyThe worked studies are generated from committed traces, so every figure in them came from a run

Service worker at saqr-v45.

Search, indexed by what each page answers

Thirty-three pages is past the point where a reader can be expected to know which sheet holds their question, and the site has no server to search with. saqr-search.html carries its own index — 1,100 entries, generated from the pages as they render, inlined so it works offline and sends nothing anywhere.

It indexes what each page answers, not what it mentions. Searching a calculation site for "stall speed" should land on the sheet that computes the stall speed, not on the four that discuss it. So the index is each sheet's readout labels, its field labels, its table columns, the notes it can raise and its headings, plus every glossary term and every symbol.

Getting the ranking right took three passes, and every failure taught something. A page that works out the thing has to beat a page that defines it by more than an exact match is worth, or "tail volume" lands on the glossary rather than Sheet 03. Repeated matches have to count for less each time, or the unit converter wins everything — it has fifty field labels, and any quantity you can name appears in three of them. And a short hand-written list of other names is unavoidable: nothing on this site is labelled "ground roll", "Vne", "L/D" or "wetted area", and those are what a reader types.

Fifteen plain queries are asserted to land on the right page. One of them asks for the top two instead: "best glide" is genuinely ambiguous between Sheet 04, which labels a figure exactly that, and Sheet 14, which is the glide polar sheet. Pretending there is one right answer would only have taught the ranking to lie.

The sheet strip is grouped by the stage of a study

Twenty equal tiles in one grid was right at four sheets and says nothing at nineteen. The strip now has seven labelled stages, in the order a design is worked — sizing, aerodynamics, stability and control, propulsion, structures, performance, then the tools — so it shows that Sheets 01, 05 and 06 are a loop you go round and that 08 and 09 are the same question asked twice.

It is generated by tools/build-nav.py rather than hand-copied onto nineteen pages, which is how the next fault got in.

The unit converter listed itself twice. Once plain, once as the current page, in a twenty-link strip nobody reads end to end. check-links.py now fails on the same target appearing twice in one nav. The handoff test had been quietly counting it all along: its pass-through total fell from 348 to 342 when the duplicate went, which is exactly the six parameters it had been checking twice.

The accessibility pass, as four measurements

Contrast. Every text node on all 33 pages, computed against its own background — properly composited, because half these sheets tint a row with something like rgba(31,94,80,.06) and reading that as opaque makes a perfectly readable table look like a 2 to 1 failure. One genuine failure in the whole site, and it was not the warning red: #A90E14 on #F4F6F7 is 6.9 to 1 and passes with room. It was the glossary's A–Z bar, where a letter with no terms under it was 1.44 to 1 — not muted, invisible. Those letters are darker now, and hidden from a screen reader, because a bare "E" that goes nowhere is noise in a list of places you can jump to.

A text equivalent for every drawing. Twenty-one are shown to a reader and ten had only a title, because the title-and-description pattern was adopted partway through the set and the earlier sheets never caught up. All twenty-one have both now: the title names the picture, the description says what it shows and what to look for in it.

Keyboard paths and focus states. 1,500 tab stops across 33 pages. On every page the first stop is the skip link, every control visibly changes when focused, every control on the screen is reached, and nothing traps.

Two near-misses worth recording, because both would have been reported as site faults and both were faults in the measurement. Chrome gives a scrollable region its own tab stop so a keyboard user can scroll it, which makes the code blocks on the methods page tab stops with no tabindex of their own — the first version of the sweep did not know that and concluded the entire footer was unreachable. And identifying a tab stop by its position on the screen is wrong, because the page scrolls under it as you tab: three pages looked half unreachable until that was keyed to the document instead.

What is still owed is a person with an actual screen reader. Everything above is what a machine can see.

Every page tells you how to cite it

A Cite this page fold under the title block on all 32 pages that have one, in plain text and BibTeX, with a copy button. It reads the name and revision out of the title block and the URL out of the canonical link, so there is nothing to keep in step: a sheet that is revised produces a new citation the moment its title block says so.

That matters more here than on most sites. These pages are corrected in place rather than archived by version, so a figure quoted from one is only reproducible if the revision letter travels with it — which is what the revision letters have been for since Rev B, and nothing on the page had ever said so.

Two faults found by writing the test rather than the feature: the cover's canonical URL ends in a slash, so its BibTeX key came out saqr--2026; and BibTeX is LaTeX underneath, so a sheet called Glide & Sink Polar pasted into a bibliography would not compile.

Six pages never registered the service worker. Found while fitting the block: 404, About, Privacy, Terms, the changelog and the methods page had no registration script at all. A visitor whose first landing was the About page — a plausible arrival from a search result — got no offline cache until they happened to open a sheet, which is most of the site's offline story missing.

And fourteen pages carried a stray </script>. Left by an earlier repair of mine that inserted a closing tag on pages that had not lost one. A stray closing tag closes nothing and is silently dropped, so every check passed: the JavaScript parsed, the blocks were identical, the landmarks were in place. tools/check-markup.py is new and counts container tags, because the mistakes HTML forgives are the ones that survive longest.

Where each method stops being valid

Under the readout on every sheet that computes one: the range each method holds over, the reader's own figure, and what goes wrong outside it. The sheets have always said this in prose, which is the right place for the argument and the wrong place for the answer to "am I inside it right now".

Four limits, because those are the ones that are the same across the whole set — the ISA troposphere, the Magnus vapour fit, incompressible flow at M 0.3, the Reynolds floor under the flat-plate friction laws, and the aspect ratio under the lifting-line corrections. The Mach row is worked against the air you entered rather than a standard day, so the same speed reads as a higher Mach number on a cold morning, which is true and is the sort of thing a table of fixed numbers cannot tell you. A row appears only if the sheet has the fields for it; four sheets are subject to none of them and say nothing rather than showing an empty box.

The limit that belongs to one sheet — where the actuator disc stops, where the parabolic polar stops — stays in that sheet's notes. Those are arguments, not ranges, and the roadmap says so rather than claiming the item finished.

One fault found by writing the test rather than the panel: span is in millimetres and wing area in square decimetres, and dividing by 1e2 instead of 1e4 reported every wing on the site as aspect ratio 554, comfortably out of range.

The oldest sheets now say as much about their answers as the newest

Every sheet written after Sheet 09 puts a line under each readout figure saying what it means or when to worry. The four written before it did not: Sheet 04 gave three of its five figures nothing at all, Sheet 03 two of its four, Sheet 01 two of its five. The sheets a reader meets first were the ones saying least about their own answers.

They all carry one now, and each line carries a figure the sheet had already worked out rather than a platitude — Sheet 04's cruise power says how much of it reaches the air, Sheet 01's stall speed gives the approach speed, Sheet 03's neutral point gives the band of centres of gravity that will fly. check-landmarks.mjs fails on a readout figure with nothing under it.

Writing Sheet 03's line found a fault in the reading of it: the forward limit of the CG band is the smaller percentage of chord, so putting the aft end first read as "anywhere from 30% to 25% MAC will fly", which is backwards.

Every sheet points at its own section of the methods page

Immediately under the figures: How these are worked out — the equations behind that sheet, their sources, and what each one assumes. Until now the only route was the footer link, which lands at the top of a sixty-eight-heading page.

It is per sheet and not per figure, and that is a measurement rather than a compromise. Matching all 98 readout figures against the 68 method headings by name lands five exactly, and two of the nine near-matches are wrong — "Structure", which is a mass fraction on Sheet 05, matches the structures sheet. The methods page is organised by method, not by answer, so a link per figure would need 98 derivations written rather than 98 links wired. The roadmap says so.

The anchors are read off the built page rather than guessed from the sheet name, so a reworded heading takes its link with it. check-links.py was resolving filenames only and would have passed a stale fragment; it now checks the fragment too, verified by breaking one.

Two things handled rather than papered over. The atmosphere tool has no section of its own — its model is documented inside Sheet 01, where it is first used — so it points there and says so rather than pretending. And the title blocks use two formats, "Wing Sizing — 01" and "09 — Battery & Electrical System"; reading only the trailing number found eleven sheets and skipped five in silence.

You can now filter the airfoil table to what was actually measured

Every row of the airfoil dataset has carried a provenance flag since Rev B, and every card shows Cm source: measured or computed. What was missing was the ability to act on it. The Show filter has a third option — Only sections with a measured Cm — which leaves 15 of 27 and says so: twelve are hidden because their pitching moment is computed rather than read off a tunnel.

It matters most to the reader it matters most for. On a tailless model the pitching moment is the whole argument, and a computed one is a different kind of number from a measured one.

The count now appears in three places — the dataset, the methods documentation, and the banner the filter puts up — and a check asserts all three agree. A provenance convention written only in prose drifts from its data; that is the failure this prevents, and it was verified by editing the sentence and watching the build fail.

The checks run themselves now

tools/check-all.sh runs all twelve in the order that fails fastest, and .github/workflows/checks.yml runs it on every push and pull request. Until now they were twelve commands somebody had to remember.

tools/check-release.py is new, and covers the gap between editing a page and it reaching a reader. Chiefly the CACHE bump: everything but a page is served cache-first, so a stylesheet or an icon changed under an unchanged CACHE is served from the old cache indefinitely — the correction that was the point of the edit is exactly what a returning visitor does not get. DEPLOYMENT.md has called this "the one people forget" since Rev C, and it is now checked rather than remembered. It also fails on a placeholder host anywhere in the HTML, the sitemap or the manifest, and on the count of unwired href="#" buttons rising above the forty that are deliberate.

Four sheets changed their answers, and here is what moved

If you have quoted a figure from Sheets 10, 13 or 16, read this section. Nothing here is a correction of arithmetic; all four are models that were named as missing in the roadmap and are now there, and three of them move numbers that were already on the page.

Sheet 10 — the spanwise load is Schrenk's, not a pure ellipse. The sheet took the lift on a half-wing as elliptical, which puts its centroid at 0.4244 of the semi-span whatever the wing looks like. Schrenk's approximation averages that ellipse with a load shaped like the wing you are actually building. For a constant-chord wing — which most trainers are — the centroid moves to 0.4622 s and the root bending moment rises 8.9%, in the unconservative direction. The sheet's own where people go wrong note had been warning for months that assuming a uniform load is fifteen per cent wrong that way; assuming an ellipse under a rectangle was most of the same mistake and quieter about it. Taper the wing and the two converge: at a taper ratio of 0.4 the correction is under three per cent. On the four worked studies the root moment rose 9.0%, 7.2%, 7.3% and 6.6% by taper ratio, and the trainer's spar caps went from 12.0 to 13.1 mm². The tip chord is a new input and exists only for this; a missing one is read as a constant chord, which is the conservative shape and what the sheet assumed before it asked.

Sheet 13 — it knows about launches now. Most small unmanned aircraft never touch a wheel, and the sheet had nothing to say to them. A launch is not a short take-off run: nothing accelerates the aeroplane after release except the propeller. It asks for 1.2 Vs at release, prints the energy the arm or the bungee has to put in, and says whether the propeller can take it from there. Below the stall — which is what most hand launches of anything heavy are — it integrates the fall: 0.2 m in the 0.5 s it takes to reach flying speed. A propeller that cannot beat the induced drag of a wing held at maximum lift fails the launch outright rather than printing a height. A thrown aeroplane needs no take-off run, so the readout's Field needed is now Ground needed and is the landing alone for a thrown aeroplane.

Sheet 16 — the wind is in the cruise. The cruise speed is an airspeed and the power to hold one does not change with the wind; what changes is the clock, and the pack pays by the second. Four cases, all closed form: into it, downwind, out and back, and crabbing across it. The result worth having is that an out-and-back is always slower than still air, by 1/(1−(w/V)²), however little wind there is — at a quarter of your airspeed 7% more time, at half 33%, at three quarters 129%. The intuition it displaces, that the legs cancel, is the common one. A wind at or above the airspeed ends the mission rather than printing a large number.

Sheet 14 — MacCready speed to fly. Additive: leave the new box at zero and nothing on the page changes. Put a number in — what you think the next thermal will give — and the sheet answers the cross-country question rather than the gliding-down one. On the default trainer expecting 2 m/s, fly at 53 km/h against a best-glide speed of 39, for 27.3 km/h across the ground against 24.7. This entry was in the roadmap as needing "a thermal model this sheet does not have". It does not: it needs one forecast number from the pilot. It also turned out to be the same objective the sheet was already sweeping — the classic into-wind speed to fly is the zero-climb case of the MacCready one.

The worked studies publish the whole run

The studies page quoted the dozen figures its prose argued with and threw the rest away. Each design now carries what every sheet was told — each input with its unit, and what arrived on the query string from the sheet before — and every figure it returned: 86 per design, 344 across the four, none of them typed.

The handoff column shows what is new or changed at each step rather than the whole set, and that turned out to be the shape of a design study: eleven parameters after the constraint diagram, nineteen by the airfoil sheet, twenty-five by the battery, then nothing changing for the last six sheets.

The four aeroplanes are specified now, not sketched. Each states how it is built, its body — fuselage, finish, wing root, tail, undercarriage — its control geometry, and how it leaves the ground. Closing the drag gap moved all four build-ups and only two of them towards the category the brief assumed, which is the honest result rather than a fault: a polished moulded airframe with no undercarriage really does have less skin than the type it belongs to is known for. Specifying the controls produced findings the studies could not have had: the trainer does not fit its own field — off a 60 m strip in 16 m and needing 68 m, all of it the landing — its ailerons roll it at 295°/s, the FPV's pod gives back 46% of what its fin produces, and the glider's 5% fin leaves the lateral balance at 5.90.

And the two-variable loop is run rather than asserted. Every study holds the wing it was given and iterates the mass; the page had said for three revisions that letting the area move as well takes longer. It takes eleven passes, not two, and it converges on a different aeroplane: 43.4 → 31.1 dm² of wing, 2,107 → 1,734 g, aspect ratio 5.5 → 7.8. Every figure improves and the wing cube loading goes from 7.4 to 10.1. It is no longer a trainer. The loop converged on the requirements it was given, and none of them said docile.

The sheets link into the glossary

Sixty-five terms, and nothing linked to them: a reader meeting static margin on Sheet 03 had to already know the page existed. There are 141 links now, the first use of each term in the prose of each of the nineteen sheets and tools, and no more — every occurrence underlined would be unreadable. The reference pages are deliberately left out, because the methods notes name fifty-four of the sixty-five terms and a reference does not need footnotes to another one.

The links are generated and idempotent: the tool unwraps what it wrote last time and re-links from the prose, so they are a function of the writing rather than of when it was last run.

Sheet 11 says how far forward the balance point can go

The aft limit was always Sheet 03's — the neutral point, less whatever static margin you want. The forward one is a question about the elevator, and nothing on the site answered it. At a forward balance point the wing's lift and the section's camber are both pitching the nose down, and the tail has to pull down hard enough to answer them; with the elevator on the stop at the stall, that sets how far forward you can go.

Three directions are the lesson, and each is on the sheet rather than asserted. A longer arm or a bigger tail moves the limit forward. More elevator throw does the same. More camber moves it aft, because the camber spends the authority before the pilot asks for any — which is why a heavily cambered wing with a small tail is a combination that will not flare. Where the limit lands behind the quarter chord, the sheet says the aeroplane will run out of elevator and mush rather than printing a number that looks like an answer.

The tail arm now travels between the sheets, so Sheet 03 and Sheet 11 are describing one tail, and a check compares the tail volume they each work out from it.

The same sheet now answers what a club field asks every afternoon: how much crosswind can this aeroplane land in? Full rudder trims a sideslip against the fin's own weathercock stiffness, and at an approach flown at 1.3 times the stall that sideslip is a crosswind. The stiffness comes from Sheet 12 rather than from the fin alone, because that sheet knows what the fuselage takes back out of what the fin makes — on a deep-nosed model, nearly half.

The default trainer holds 5.0 m/s. Of the four worked studies, the trainer manages 15 km/h and the FPV 19; the survey aircraft and the glider manage seven, which is an ordinary afternoon. Both were already flagged for not tracking straight, and this is the same shortage of fin from the other end: a long wing raises the fin volume's denominator and the dihedral effect at once, so the span that makes those two efficient is the span that makes their fins too small. Two sheets, one fix, and neither would have said so alone.

The four worked studies state their tails now — tailplane, fin and arms — which they had been leaving to Sheet 03's defaults. Together they say something worth having: on all four designs the forward limit lands at or ahead of the leading edge. On a model the elevator is almost never what limits how far forward you can balance, which is the opposite of full-size practice, where the forward limit is usually set by the elevator in the flare. Twenty-five degrees of throw on a tail a fifth of the size of the wing is an authority no certificated aeroplane has.

Four more exercises, for the four new models

Eighteen to twenty-two. The wind on an out-and-back — which asks you to predict whether the legs cancel before you set the wind, because they do not. A hand launch below the stall, which is not a failed launch and the sheet says why. The speed to fly between thermals, which is faster than the speed that goes furthest and for a reason worth having. And what taper does to a spar, where the lift is identical and the root moment is not.

Each of them needed a link that could set the input it is about, and those inputs only started reaching the address bar in the section below. As ever, no number in an answer is typed: they are filled from a live run of the same link the exercise hands the reader, and a check drives all twenty-two again and fails if one has moved.

Fixing one of them found a display bug that has been there since the page was built: six sheet names contain an ampersand, the heading they are read from already writes it as &amp;, and the page escaped it a second time — so Structures & Spar Sheet had been reading as Structures &amp;amp; Spar Sheet on every exercise that used it.

A link now carries the whole sheet

Paste a sheet's address into a forum thread and the person who opens it gets your aeroplane. That was half true. The URL carried the parameters that travel between sheets — weight, span, chords, drag — and dropped everything local to the sheet you were on: 164 inputs across the nineteen sheets and tools never reached the address bar. The drag build-up carried its wing and lost twenty-two of its own twenty-eight boxes, so ten minutes spent describing a fuselage went nowhere.

Every sheet registers its own inputs now. The surface you take off from, the spar material, the mission profile, the build method, the fuselage you described — all of it is in the link, and a reload or a bookmark brings it back. Nothing about the cross-sheet handoff changed: these are keys no sheet publishes and no other sheet reads.

A check sets all 257 inputs on every sheet, reloads whatever the address bar then says, and compares every one. It tests the promise rather than the mechanism — not that the URL changed, but that you got your aeroplane back.

What the mission budget was apologising for, and what was actually missing

Sheet 16's scope statement warned about the segments it does not integrate — the take-off run, the acceleration to cruise, the turn onto station — and told you to add ten per cent before believing the total. Putting a number on that warning retired it. The kinetic energy of the whole speed-up from standing still to the cruise speed bounds every transition in the profile put together, and on the sheet's own trainer it is 0.088 Wh: half a per cent of the mission.

What was actually missing had no line at all. Everything that draws from the pack and is not the propeller — receiver, servos, autopilot, telemetry, and the camera or sensor the mission exists for — runs for every second the aeroplane is switched on. Ten watts of it is 3.07 Wh, 17% of the mission, twice what the entire climb costs, and 7.8 minutes off the loiter. The term the sheet warned about was worth half a per cent; the term it never mentioned was worth twenty.

It is charged twice, and the second charge is the one that gets left out: the load takes its watts out of the pack before the loiter starts, and it raises the burn rate once the loiter does start. Of those 7.8 minutes, 2.0 are the transit energy and 5.8 the raised rate. Charging only the obvious half reports 29.4 minutes of possible loiter against the true 23.7.

The descent has stopped printing as free. Power off at the cruise speed the aeroplane sinks at the rate its own drag pays for — 1.40 m/s, 71 seconds, 0.89 km of ground on the trainer — so the propeller costs nothing and the avionics cost what they cost all the way down. That 0.89 km is ground the cruise distance was already charged for. Rather than credit it back and change what the reader typed into the distance box, the sheet states the over-charge, about 1.1 Wh, and says which way it errs.

The four worked studies now state what each aircraft draws instead of inheriting a default: 3 W for the trainer, 8 W for the FPV camera and its video transmitter, 12 W for the survey aircraft's camera and companion computer, 1 W for the glider. Run with the draw and without it, they say something none of them says alone: the aeroplane built for endurance is the one a continuous draw hurts most. The survey aircraft has the biggest pack of the four and the longest loiter, and it loses 65 minutes of it — 30% — to a camera being switched on, against the trainer's 9%.

The reason is the loiter burn each one is compared against. The survey aircraft is the most efficient airframe of the four, needing 30 W to hold 3.2 kg up, and that efficiency is exactly what leaves nothing for the payload's watts to hide behind; the draggy trainer needs 39 W and barely notices its 3 W. Clean airframes have expensive payloads. A payload is two bills, its mass and its watts, and on a sensor aircraft the watts are frequently the larger.

Three drawings that were the wrong shape, and ten sections you can now take away

Sheet 02 drew all twenty-seven of its rows with one construction: the NACA four-digit equations, evaluated at each row's published thickness and camber. For the seven four-digit sections that is exact, and the footer said the rest were schematics to compare shapes by.

Three of them were not schematics of anything. A NACA 23012's camber peaks at 15% of the chord and the drawing put it at 40%. A curved plate has no thickness form and was being given one. A KFm-2 is a step, which no smooth equation produces. The site's caution was aimed at the figures, which are indicative, and not at the outlines, which for those three were a different section.

Ten of the twenty-seven are published constructions or plain geometry rather than data sets — the seven four-digit sections, the 23012, the 5% circular-arc plate and the KFm-2 step. All ten are drawn from their own equations now, and each will hand you a .dat file at the chord in the Reynolds box; the button says which chord, because that box is behind a toggle and a file at a size you did not choose is worse than no file. Every row states whether its outline is the section or a likeness.

The other seventeen are published coordinate sets under their designers' own terms. Republishing those is a licensing decision rather than a technical one, so they keep the schematic and say so.

The one ± on the site was sweeping things that are not uncertainties

The sensitivity panel cannot nudge a picker — a category is not a quantity — so it sweeps one instead: onto every other row it offers, with the answer read off the sheet each time. What falls out is the only ± this site puts on an answer.

It was sweeping the wrong things. Two of the pickers added in this revision are facts rather than estimates: whether the aeroplane leaves the ground on wheels or out of a hand, and which way the cruise lies in the wind. A reader is not uncertain about either. Both were being swept, and Sheet 13 published a ground roll of ±19 m, 89.8%, when the figure from its two genuinely estimated pickers is ±10.

The sweep was also counting the carpet plot's own controls. It skipped its own and not those, so Sheet 13 reported its spread as coming from 7 pickers when the sheet has three. Those controls move nothing the readout prints, so no figure was ever wrong by it — but a count that is wrong is a claim that is wrong.

Both are fixed, and the picker inventory is now a manifest in the check: 37 pickers across the seventeen sheets, 26 swept into the ± and 11 kept out as facts, each named. A picker added and not classified fails rather than quietly joining the ±. The panel's row list is compared against that manifest too, because a check that filters the page the same way the block filters it cannot see the block's own mistake — and this one could not, until it did.

An address, a licence, and a line left unsigned

Three of the four things standing between this and an announcement were never code. They are decided now.

There is an address. help@saqr.com.qa, on the corrections button of all twenty pages that carry one, with the subject line already filled in. The site has been asking to be corrected since Rev C and giving nobody a way to do it; the button that apologised for itself is gone.

There is a licence. All rights reserved, free to use. The figures the sheets give you are yours &mdash; in a design, a build, a thesis, a paid job, without asking &mdash; along with the design files, the CSV exports and the airfoil coordinates the site computes for you. The material itself is not for redistribution, and there is an address to ask at with a stated bias towards yes.

The part that took the thinking is what is not ours to license, and the notice says so in three pieces. The measured airfoil figures belong to the people who measured them and are credited on the sources page. The ten sections the site generates are published constructions &mdash; the NACA ones United States government work, the curved plate and the stepped section plain geometry &mdash; so the coordinates it hands you carry no claim of ours. The seventeen it does not generate stay with their designers, which is why the site points at them rather than reproducing them. A blanket claim over a page that republishes other people's measurements would have been wrong. terms.html carries the reader-facing half, where it has been promising since Rev C that the licence "will be stated here".

The credential line stays unsigned. Every page says a METU Aerospace Engineering graduate with no name, and that was a question rather than an oversight. The answer is no, so the launch check no longer asks &mdash; it watches instead, and says something if that line is ever reworded, because it is the site's whole claim to authority.

One of the four is left: the donate buttons. The processor is decided &mdash; SkipCash, which settles to a Qatar-registered entity &mdash; and what is missing is the payment link itself.

Contrast, measured rather than assumed

These sheets are meant to be read outdoors, where anything marginal indoors is unreadable. Nothing measured it: the nearest check fails a control the colour of its own background, which catches something that has vanished and nothing that is merely hard to read.

It found nothing. Twelve thousand seven hundred and seventy-nine pieces of text across thirty-four pages, and the closest is 5.05:1 against a minimum of 4.5. That is a real margin rather than a pass by rounding, and it is worth saying plainly because most of this revision's audits have found something.

check-contrast.mjs is here anyway, because the palette is eight custom properties and a regression is one hexadecimal digit. Lightening --ink-soft by a few points fails forty-four colour and size combinations, and would not have looked broken to whoever did it.

One warning is in the check's own header for whoever edits it next. The first version reported the site failing badly — dark ink on dark green at 2.05:1, which would have been unreadable and obvious to anyone who had looked at the page — because it read rgba(31, 94, 80, 0.06) as a solid colour rather than as a six per cent tint over white. Every failure it found was its own.

Six sheets whose pickers were never styled

class="sel" is on the pickers of fourteen pages. The rule that styles it was in the inline stylesheet of eight of them. On the other six — Sheets 10, 11, 12, 13, 15 and 16, which is most of what this revision added — every picker rendered as the browser's own default: 19 pixels tall against the 34 of a styled one, no border, no padding, nothing to say it belonged to the form around it. A control that does not look like a control is a poor thing to hand somebody standing in a field, and it had been that way since those sheets shipped.

The class was written into the markup and the rule was left behind, which is what happens to a style pasted into each page rather than shared. It is shared now, and the eight inline copies are gone with it.

check-classes.py is the fortieth check and fails on a class the markup uses that no screen rule defines. That word is the whole check: sel was mentioned in a stylesheet on all fourteen pages, in the print block that strips the padding off every control, so the first version of this passed the very defect it was written for. Being mentioned is not being styled. It found two more on the way — one a genuine miss, where a sheet set aside on the studies page read exactly like a sheet that had objected, because the class marking it had no rule; the other a selector hook that is meant to be unstyled and is now named as one.

The phone-at-the-field promise, tested the way it is made

The cover says the sheets work on a phone at the field. A field is where there is no signal, so that sentence is a claim about the service worker: everything has to be in the cache before the signal goes, and a page left out of the worker's list is a page that is simply gone.

Nothing tested it. One check makes sure each precached asset has a cache rule of its own, which is about the CDN; another makes sure CACHE was bumped when a cached file changed. Neither would notice a page added to the site and forgotten in the worker's list, and that has no symptom at all until somebody is standing in a field with no signal, which is the one moment the site exists for.

test-offline.mjs serves the tree, lets the worker install and fill its cache, cuts the network, and then opens every page the worker claims to have — not the page that was already open, which any browser manages, but the other thirty-three, each navigated to cold with the network down. A title, a main element, and on a sheet a readout with figures in it, because a sheet whose scripts did not come out of the cache loads as a form with no answers and looks fine until you read it.

Thirty-four pages open offline and seventeen of them still compute. Proved three ways: a sheet dropped from the worker's list, a shared script dropped from it, and the fetch handler stopped from serving the cache at all, which fails all thirty-four.

The cautions were printing as an empty space

DEPLOYMENT.md has always carried a manual step: Ctrl-P on any sheet. Navigation and support blocks should disappear; drawing, readout and notes should remain. A step in a checklist is a step nobody reaches for, so it is a check now. The sentence itself turned out to be true — and running it found two things the manual step would not have.

The collected mistakes printed as nothing at all, on every sheet. The shared stylesheet hides that fold's summary, which was written believing the body would then show. It does not: a closed <details> renders its summary and nothing else, and no CSS opens one — display:block on the element, on its children, with !important, and content-visibility were each tried and none of them does it. So the rule meant to tidy away a redundant heading is what made the whole fold blank, and the part of a design sheet most worth carrying to a workbench came out empty.

saqr-print.js is a sixth extracted block: it sets the open attribute before the print dialogue and puts every fold back exactly as the reader left it afterwards, whether the pages came out or the dialogue was cancelled.

The studies page printed its CSV bar, because it carries its own stylesheet rather than the shared one, so rules the shared sheet gives every other page have to be repeated in its generator.

And one thing I got wrong on the way, caught by trying to break it. I had read the print rules with a pattern that stopped at the first closing brace, so it missed each sheet's full print stylesheet entirely, and I added a rule hiding the support block and wrote up a defect that did not exist — the sheets have hidden it with !important all along. The mutation that should have made the check fail did not, which is what the mutation is for. The rule is gone and so is the claim.

Two sheets that said they agreed with each other, and nothing checking it

Where two sheets work out the same quantity, a check proves they get the same answer. Two claims made in this revision were not among them.

Sheet 16's descent says, in the code and in the scope statement, that it is the same glide Sheet 14 draws, read at the cruise speed rather than at the best-glide one. That is now an inequality the check enforces, and a tight one: the descent covers 0.89 km from 100 m, which is 8.9:1, and Sheet 14 finds a best glide of 9.2:1 for the same aeroplane. A glide cannot beat the best glide, and it cannot sink slower than the minimum sink. Take the sink at the wrong speed and the descent comes down at 13:1, which is impossible — that is the mutation that proves the case.

Sheet 12's readout tells the reader that Sheet 03 checks this against its own band, about the fin volume. It does, in a note that fires only when the fin is outside that band, which is why the new case uses a deliberately small fin: a fin in band is a fin the check cannot read. Seven agreements to nine.

What a revision letter actually pins, which is not what the page said

Every page carries a note above its citation: a figure taken off one belongs in your working with a revision letter against it — these pages are corrected in place, so "Rev C" is what tells a reader which numbers you had.

The first half is right and the second is not. Corrections are applied in place, and that is exactly why the letter alone cannot pin a figure: Sheet 16's mission total moved by a watt-hour inside Rev A when the avionics draw was added to it, this revision. Two readers citing Sheet 16, Rev A would be quoting different numbers.

What pins it is the letter and the date it was taken, and the citation the page generates has carried both all along — Saqr Consulting. Mission Energy Budget, Rev A. 2026. &hellip; (accessed 14 September 2026). The note now says so, and points at the changelog, which is where the site already promised to record whether a number has moved and why. Thirty-three pages, one wording.

Three sheets telling readers they could not do what they do

Every sheet ends with a paragraph saying what it models and what it leaves out. Adding a model updates the notes that appear beside the answer, and leaves that paragraph alone. Both halves describe the sheet; only one of them gets touched.

Sheet 13 said it does not model hand launches, bungees or catapults, with a picker on it offering all three. Sheet 14 said air moving up or down is outside it entirely, while asking for the climb you expect in the next thermal. Sheet 11 described hinge moments and roll rate and said nothing at all about the elevator authority and the crosswind above it.

All three now say what the new models do and, more usefully, where they stop. A launch is a release speed and nothing else &mdash; the thrower's arm, the bungee's force against its extension and the catapult's stroke are all outside it. The speed to fly assumes you will find the next thermal and that it climbs at the rate you entered. The crosswind is linear, and past about twenty degrees a low-aspect-ratio fin begins to stall and nothing in the model knows it.

The Where people go wrong folds had the same gap and are fixed with it: enlarging the fin in Sheet 11's box and believing the crosswind, reading a thrown aeroplane's ground figure as a take-off run, treating a launch that dips below the stall as a failed one, and setting the MacCready to the climb you just had rather than the one you expect next. Eighty-three collected mistakes to eighty-eight.

No check can read a paragraph of prose about a model, so this one is a habit rather than a mechanism, and it is on the roadmap in those words.

One thing did break, and the right thing caught it. Putting a link to the changelog in the citation note tripped build-changelog.py, which asserted that a page links to the changelog exactly once. What it meant to guard was that the footer's link had been found and marked as the current page; the page-wide count held only for as long as nothing else on a page pointed here. It checks the footer link now.

Five words the sheets used and the glossary did not have

New models bring new vocabulary, and the glossary does not grow by itself. Depth of discharge, minimum sink, spanwise lift distribution, spar cap and speed to fly were all being used in the prose of sheets that had nowhere to send a reader who did not know them. Sixty-six terms to seventy-one, and the linker picks them up wherever each is first used.

And the page went on telling readers sixty-six. The terms went in, the changelog recorded the growth, and the two meta descriptions that state the count — the sentence a search result shows — were never touched. It is the last line anyone edits, because adding the term is the work. They say seventy-one now, and check-glossary.py compares the stated count against the terms it already counts on every run, so the number cannot drift again. Proved by putting sixty-six back, and by adding a term and leaving the sentence alone.

Two older copies of the same number were further behind still: ARCHITECTURE.md and build-glossary-links.py both described a glossary of sixty-five. Correcting those turned up a second figure that had gone quietly stale beside it — the methods notes were said to name fifty-four of the terms and the nomenclature page thirty. Measured now: fifty-nine and thirty-six. Worth noting how nearly that one slipped through, because replacing sixty-five with seventy-one everywhere would have left fifty-four of the seventy-one — a sentence with a fresh number in it and a stale claim, which reads more convincingly than the version it replaced.

The glossary was the small version of this, so the sweep became the forty-third check. Sixteen free calculation sheets is on the cover and in nineteen footers, the sixteen sheets check each other in four of the about page's descriptions, Twenty-seven curated sections once. Thirty copies of two numbers, none of them derived from anything, and a Sheet 17 would leave every one wrong at once — starting with the page most people read. They are written in words, which is why the prose is good and also why no search for 16 finds them.

check-counts.py derives both: the sheets from build-nav.py's groups, the same list the cross-sheet strip is built from, and the sections from the dataset in the airfoil sheet. Adding a sheet changes what it expects in the commit that adds the sheet. Proved by adding a Sheet 17, which fails all thirty at once, and by deleting a section from the dataset.

Its first version reported fifty-nine failures and every one was its own. Two sheets disagreed about the loiter speed is not a claim that the site has two sheets; the ten sections the page generates is not a claim that there are ten. Prose counts subsets far more often than totals. Narrowing to the <word> sheets still left eleven, because the two sheets that compute it is a subset with a definite article in front. No grammar separates the readings — only the surrounding words do — so each pattern was run over the site and kept only if every hit was a claim about the whole set. Four survived, and they match thirty claims and nothing else.

That leaves it deliberately incomplete, and the file says so: a new phrasing would escape it. Worth the trade. A check with fifty-nine false alarms is one nobody reads, and an incomplete check that is always right still fails the day a sheet is added.

And then the check's own docstring. It opened by saying where the counts live — the about page says it in four places, the studies page in five … fifteen copies of one number. The about page was right. The studies page has one, and there are thirty copies of two numbers. The file explaining why stated counts go stale shipped with stated counts that were wrong on the day it was written: not stale, simply never counted. They are counted now, and it says so.

Four numbers in one day that nobody had derived from anything — the glossary's, the architecture table's, a roadmap headline's, and the check's own. The habit that argues for is smaller than any check: a number in prose is a claim, and a claim gets measured before it is written down.

Two methods the sheets name after the people who published them were missing from the bibliography: Schrenk's spanwise approximation, which Sheet 10 sizes its spar with, and the speed-to-fly theory Sheet 14 works its cross-country speed from. Both are there now, and the second says plainly that the sheet derives the optimum from its own polar rather than reproducing a published ring or table — it is where to read the theory, not where a figure came from.

And three soft numbers this revision added were missing from What has no source, which is the page that exists so the site is honest about which of its figures are compiled rather than measured: Sheet 11's tail dynamic-pressure ratio, Sheet 13's 28 km/h hand-launch convention, and Sheet 16's default three watts of avionics. A list of soft numbers that quietly stops being complete is worse than no list.

The symbol table was making a claim nothing kept true

saqr-nomenclature.html lists each symbol with the sheets that use it. That last column is a claim about the rest of the site, and adding a model to a sheet does not touch this page.

Sheet 11 grew an elevator authority and a crosswind that read the tail volume, the tail arm, the tail efficiency, the section pitching moment and the weathercock derivative. Not one of those five rows mentioned Sheet 11. Thirteen symbols from six new models were missing altogether. Fifty-four rows to sixty-seven.

The column is derivable rather than remembered: most symbols are an input the reader types, and an input has an id, so the sheets carrying cnb are the sheets that use Cn-beta. Each row names the ids that embody it and the check derives the list. The derivation reproduces the hand-built column exactly for every mapped row, which is what says the mapping is right rather than a new opinion of mine — it turned up one disagreement and that was the check's own bug. The rows whose column is prose, and the ones for figures the sheets print and never take, are named individually, so a symbol added and left unclassified fails rather than going unchecked.

Seven more checks, and what they found

Twenty-six to thirty-seven. check-studies.py rebuilds the studies page from its four committed traces and compares byte for byte. check-glossary.py re-runs the linker and fails if anything would change. test-schrenk.mjs, test-wind.mjs, test-launch.mjs, test-macready.mjs and test-hotel.mjs each check a new model against the closed forms it comes from, at several operating points, with the arithmetic done outside the page.

Every one was verified by breaking the thing it checks — and one of those mutations found a check that was not checking: the ground a thrown aeroplane needs passed whether the code took the landing or the larger of take-off and landing, because on the sheet's own defaults the landing is the larger. It took a wrong version of the sheet to notice.

test-sections.mjs measures each generated section off the saved file — thickness, camber, where each peaks, and the thickness form itself — then reads the file back through the sheet's own .dat reader, so two independent paths through the page have to agree about one shape. It caught a gap in itself before it caught anything else: a mutation that gave the curved plate a four-digit thickness form passed clean, because measuring the maximum thickness says nothing about how it is distributed. A plate is the same thickness everywhere and a NACA section is not. It measures that now.

One flake, twice, and the same cause both times: fill on a number box occasionally appends rather than replaces. Setting 0 over a 10 gave 100 and two assertions failed at the end of a run with plausible-looking numbers. Setting 5 km/h of wind over 20 gave 205 — faster than the aeroplane, so the cruise covers no ground, so Sheet 16 correctly printed no time at all and the check reported the sheet broken. Both times the sheet was right and the test was not. Six checks share a setter now that clears, types and reads back, and says so loudly if a value will not stick.

test-hotel.mjs also had to be written twice. The first version worked out each segment's expected energy from the table's own printed minutes and watts, which are rounded for display: the climb really lasts 33.3 seconds and the table says 0.6 minutes, so the expectation was 8% out before the sheet had done anything wrong. And it flaked about one run in three, because fill on a number box occasionally appends rather than replaces — setting 0 over a 10 gave 100 — with no symptom but two assertions failing at the end with plausible numbers. A setter that does not check what it set is a test that reports the wrong thing.

And regenerating the site for this change surfaced a generator quietly undoing an earlier one. build-methodlinks.py used to paste the method link's styling into each page's own <style>, guarding against a second copy by looking for the rule there. When that rule was extracted to saqr-blocks.css, the guard stopped matching — so every run since had been adding another inline copy to all nineteen pages, defeating the extraction in silence. The script no longer carries the rule at all; it strips any inline copy it finds and fails if the shared stylesheet has lost it.

Two defects the existing checks caught in this work, both mine: a new picker with no accessible name, and a page that lost its structured data because a generator that writes a whole page from scratch ran after the one that adds it. check-markup.py now wants the structured-data block in every head, which is the fifth thing it asks for.

A privacy page that had promised more than the truth

The owner picked Cloudflare for visitor numbers, on the reasonable ground that Cloudflare already hosts the site. That name covers two quite different things, and the difference is the whole of this change. Cloudflare Web Analytics is a beacon &mdash; a script from static.cloudflareinsights.com that every page would have to load. The host dashboard counts the requests Cloudflare is already proxying, server-side, with nothing added to the page at all. This site takes the second. No script, no cookie, no fourth-party origin, and _headers keeps script-src at 'self', so the beacon could not load even if somebody pasted it into a sheet.

That still could not be done quietly, because privacy.html had promised more than was true. It said there were no analytics, and that the host's request records were "not analysed for behaviour" &mdash; and counting visits is analysing them, in aggregate. The page now says what is counted, what is not (nothing a reader types, no cookie, no profile, no identity carried between sites), and that the counting adds nothing to the page a reader loads. It was that page's own rule: adding anything that collects data would mean rewriting this page first. Written down in Rev C, and this is the first time it has had to be obeyed. The page records that it changed, and why, rather than reading as though it had always said this.

check-promises.py is the forty-second check, and it exists because a promise written in prose is the easiest claim on this site to break by accident. It reads the Content-Security-Policy out of _headers and rejects any origin that is not this site, and it scans every page for a src or href that fetches from another domain &mdash; the two things the privacy page invites a reader to verify and no reader can. Proved by loosening script-src for the beacon and by pasting the beacon into a sheet; both fail by name.

It checks one thing that is not a claim: that every page links the privacy page from inside its own footer. A pledge is worth nothing on a page nobody can find, and a new page shipping without the site furniture has no symptom — the page works, and only the promise is missing. Not a grep for the link either: a page carrying it outside the footer fails, which is the version of this that looks fine in a search.

And the storage sentence — No cookies. No local storage. No session storage. No database. — is a claim about the code rather than about intent, so it is checked as one: localStorage, sessionStorage, document.cookie, indexedDB and openDatabase fail anywhere in the shipped pages and scripts. The service worker's cache is the one exception, because the page names it and says what is in it, which is the difference between disclosed and hidden.

The way that sentence stops being true is not malice. It is somebody remembering the last design so a return visit resumes where it left off — a good idea, wanted on the roadmap since Rev C, and deliberately not done because it would make a published promise false. That reasoning lived in a roadmap entry, which is to say it lived nowhere anyone would look while writing the convenience. Now the convenience and the promise have to change in the same commit. Proved with the mutation somebody would actually write, and again as an inline script in one sheet, and again as an inline onclick handler, which is the path a check that only reads <script> bodies would miss.

Its first version failed on this very paragraph. Scanning the whole file rather than the code in it, the changelog page named all five APIs in prose and the check reported the site keeping cookies, local storage, session storage, a database and an openDatabase call — five promises broken, none of them real. Naming an API is not calling it. It reads <script> bodies and inline handlers now, which is the only place any of those words can do anything.

The failure it guards has a shape worth naming: adding analytics to a static site is one script tag and one CSP relaxation, done months apart by somebody who never read the privacy page, which goes on making the old claim for as long as nobody looks. A privacy claim that has quietly stopped being true is worse than one never made, because the reader took it on trust.

Turning the dashboard on is a click in the Cloudflare account. Nothing in this repository has to change when that happens.

The seventeen airfoils, decided rather than deferred

Ten of the twenty-seven sections on Sheet 02 are generated from their own published constructions and download as coordinates. The other seventeen are measured coordinate sets belonging to the people who measured them, and since Rev C the site has been carrying that as an open question: a licensing decision, not a technical one.

The decision is to leave them. Those rows keep a schematic, say in as many words that it is a likeness and not the section, and send the reader to UIUC and Airfoil Tools for the coordinates themselves &mdash; which is what they have been doing, with the difference that it is now a choice somebody made rather than a gap nobody had got to. test-sections.mjs holds the ten that are published to the measured standard, so the line between the two groups cannot blur without a check failing. It would reopen only on a designer's own terms permitting republication, and that is a question per section rather than one answer for all seventeen.

Still open

  • MH64 pitching moment sign. Carried from Rev B and Rev C, still unresolved. Needs a primary source, not a code change.
  • Rev B spot-checks against the cited sources. Same reason.
  • The donate buttons. The processor is decided &mdash; SkipCash &mdash; and what is missing is the payment link itself, which comes from the merchant account rather than from anything here.

Revision C — atmosphere, privacy, launch preparation

Sheet 09 added — Battery & Electrical System, Rev A

The set is nine sheets, and this closes the last of the three weak inputs in the original four.

Sheet 04 works out current as power divided by nominal voltage. That is the same as assuming the pack has no internal resistance at all. Sheet 09 models the pack properly: an ideal voltage source behind a fixed resistance, which makes the current the smaller root of R·I² − V·I + P = 0 rather than a simple division.

The honest result is that it depends, and the sheet says which case you are in. On the trainer the set defaults to, a 4S 5000 mAh pack drawing 4 A at cruise sags 0.04 V and the simplification is harmless — under one per cent, and the sheet tells you Sheet 04 can be trusted as it stands. On a tired 3S 1300 at 16 mΩ a cell the same airframe pulls 29.5 A at 22.7C, sags 1.42 V, and loses 14 W as heat inside the cells. Same method, opposite conclusion.

There is a power no pack can exceed. Delivered power is a parabola peaking at V²/4R, where half the energy is heat and the voltage has halved. Ask for more and the quadratic has no real root: the sheet reports that the pack cannot do it rather than returning a number. A usable ceiling is perhaps a third of that theoretical peak, and the chart shows how far from it you are.

Pack mass agrees with Sheet 05 to the gram, because both use the same compiled energy densities.

Two corrections to earlier sheets came out of building this one, and both were mine:

  • Sheet 06 labelled electrical power as shaft power. The figure divides by the propeller and motor efficiencies, so it is what the pack delivers, not what reaches the propeller. Relabelled, and the note now says explicitly that the propeller and motor have already taken their share.
  • Sheet 08 took those same figures as shaft power, which understated them by the motor and controller efficiency. It now takes electrical watts, carries its own motor efficiency, and derives shaft power itself. The cruise and climb efficiencies move to 70.2% and 56.1%; the fourteen-point gap that is the point of that sheet is unchanged.

Sheet 04's battery defaults also described a different aeroplane from Sheet 05 — 3S 2200 against 4S 5000 for the same 2400 g trainer. Now aligned.

Service worker at saqr-v9.


Sheet 08 added — Propeller Matching, Rev A

The set is eight sheets. This one closes an admission the site has been carrying since Rev B.

CHANGELOG-revB.md conceded that a single propeller efficiency is a cruise value and that climb properly needs a map across advance ratios. Sheets 04 and 06 have both been using one number for both conditions. Sheet 08 works out what that number actually is at each.

The finding, on the trainer every other sheet defaults to: 68.6% at cruise, 53.8% in the climb. A gap of fifteen points, on the same propeller, on the same aeroplane. Nothing is wrong with the propeller. Pushing 286 W through a disc that needs 60 W to cruise means throwing air backwards much faster, and the kinetic energy in that wake is lost. Efficiency is a property of the operating point, not of the propeller — which is exactly what a fixed figure cannot express.

The sheet also carries the two limits momentum theory cannot see:

  • Tip Mach. Past about 0.75 the tip sections lose lift and gain drag sharply, efficiency falls away and the propeller becomes very loud. It is a speed limit, not a power limit, and small high-Kv setups routinely run into it.
  • Pitch speed. Actuator-disc theory works from disc area and will happily promise thrust at any speed. A real fixed-pitch propeller stops pulling as the aircraft approaches its pitch speed. The chart draws that as a wall and shades the region past 80% of it, where the curves mean nothing and the answer is more pitch rather than more power.

Static thrust is quoted on a different basis and the sheet says why. At zero advance the blade is deeply stalled, and the disc model overstates it by about a third — 1,887 g against the 1,391 g a figure of merit of 0.50 gives. Static thrust is also the least useful number on the page: an aeroplane that is not moving is not flying.

As with Sheet 07, shipping this needed a change to two existing sheets. Propeller efficiency on Sheets 04 and 06 was a <select>, so a computed value would have been silently ignored on arrival. Both are now a category list that writes into an editable figure.

One bug caught in review: Sheet 08 was built from a stylesheet copied before the navigation grid fix, so it shipped its first draft with the old wrapping flex row. All eight sheets now carry the grid.

Service worker at saqr-v8.


Sheet 07 added — Parasite Drag Build-Up, Rev A

The set is seven sheets. This one replaces the weakest input on the site.

Sheets 04 and 06 both took the parasite drag coefficient from a dropdown of seven airframe categories. That is a guess dressed as a figure. Sheet 07 builds it up: wetted area and skin friction at the Reynolds number each component actually sees, form factors for thickness, interference for how badly surfaces meet, and drag areas for the wheels, legs and antennas bolted to the outside.

Three things are worth calling out.

The landing gear is typically a fifth of the total on a trainer, and the legs usually cost more than the wheels they carry — a round wire has roughly five times the drag of a streamlined section the same thickness. No category dropdown can tell you that, and it is the cheapest drag on any model to remove.

It reads low, and the sheet says so plainly. Component build-up reliably lands below measured drag, and more so at model Reynolds numbers where laminar separation bubbles add drag no flat-plate correlation knows about. The default trainer comes out at 0.0303 against the 0.040 of the category the other sheets default to: 24% under. The sheet asks which category you would otherwise have guessed and reports the gap against that, rather than against whichever category sits nearest the answer — which would agree by construction and tell you nothing. The advice on the sheet is to size with the category figure and use the build-up for the breakdown.

Below Re 100,000 the sheet flags itself as optimistic by an amount the method cannot estimate, rather than quietly returning a number.

Shipping it required a change to two existing sheets. The drag coefficient on Sheets 04 and 06 was a <select>, so a computed value would have been silently ignored on arrival. Both are now a category list that writes into an editable figure, which is the same pattern Sheet 05 uses for areal density.

The navigation strip also changed. As a wrapping flex row it left a single sheet stretched across its own row once there were eight links; it is now a grid of even columns that wraps tidily and will keep doing so as the set grows.

Service worker at saqr-v7.


Sheet 06 added — Constraint Diagram & Design Point, Rev A

The set is six sheets. This is the tool that makes the others one method rather than six calculators.

Every requirement becomes a boundary on a plot of wing loading against power loading. Stall speed is a wall on the right — past it the wing is too small to land slowly enough. Cruise, climb, sustained turn and ground roll are floors, each demanding a minimum power at a given wing loading. What survives all of them is the feasible region, and the design point sits at its corner: the largest wing loading the stall limit allows, at whatever power the tallest boundary demands there.

The sheet names the binding constraint, and that is the point of drawing it. Only one requirement is setting the motor. Relax that one and the aeroplane gets cheaper; relax any other and nothing happens at all.

A flat practice floor in W/kg is drawn alongside the derived boundaries — 100 for a trainer, 300 for unlimited vertical — compiled from build practice rather than from the polar. When it is the binding boundary the sheet says so explicitly, because that means the equations think the aeroplane could meet its stated brief on less power than anyone would actually enjoy flying.

Where it is soft is stated on the sheet, not buried: one cruise propeller efficiency is used at every speed, so the launch and climb boundaries sit lower than the truth, which is the optimistic direction.

Sheets 01, 05 and 06 now close the loop. Sheet 06 sizes the wing and motor from the requirements given a mass. Sheet 05 works that mass back up from the structure the wing implies. Sheet 01 draws the planform and checks the stall. Run them against each other until they stop disagreeing; two or three passes is normal.

Validation is the part worth reporting. With the trainer that Sheets 01, 04 and 05 all default to, this sheet returns 59 W for cruise and 286 W for climb, or 329 W installed. Sheet 04's own cover text, written long before this sheet existed, describes that aircraft as one that "cruises on sixty watts and still wants three hundred installed". The stall-limited wing area lands at 42.7 dm² against Sheet 01's 43.4 dm². Every one of the five boundaries was confirmed to bind in at least one realistic configuration, from a hand-launched glider to a short-field trainer to a 3D aerobatic model.

Also: the four original sheets still said "Four sheets" in their support panels after Sheet 05 shipped. All six now agree. Service worker at saqr-v6.


Sheet 05 added — Weight & Mass Budget, Rev A

The set is five sheets. Every other sheet keys off all-up mass, and until now that was a number the user typed from intuition. Sheet 05 builds it up group by group.

Structure comes from wing area × areal density, which is how model builders actually estimate it, because a wing's mass tracks its area far more closely than its span. The fuselage deliberately gets no formula — a model fuselage runs from a carbon boom to a scale cowl with a retract bay and nothing parametric spans that honestly — so it is set as a ratio of wing structure that the user can see and argue with.

Pack mass comes from capacity at 140 Wh/kg for LiPo, the same figure Sheet 04 uses for endurance, so the two sheets cannot disagree about a battery.

Three things worth noting:

  • The areal densities are the softest data on the site, compiled from build practice rather than measured, and are labelled that way on the sheet, in the budget table's basis column, and in CALCULATIONS.md. Every one is overridable, and the sheet offers to take a measured wing mass instead and derive the density from it. That is the path by which the compiled table eventually gets replaced with something real.
  • The fraction bands are higher for structure than full-size practice (35–65%, not 30–45%). That is not an error. A full-size aeroplane spends 30–40% of its mass on fuel and payload; most models carry neither, so structure takes the share fuel would.
  • Sheets 01 and 05 form a loop, and the sheet makes it visible. Arrive from Sheet 01 and if the budget disagrees with the mass the wing was sized for by more than 8%, it says so and tells you which way the error runs. That loop is the design method; the rest of the set hangs off it.

The defaults reconstruct the same 2400 g, 43.4 dm² trainer that Sheets 01 and 04 default to, landing within 0.1% with every group inside its band. The budget was checked for internal consistency across five configurations from a composite glider to a 5.4 kg survey aircraft.

Also in this change: the cover page and manifest now say five sheets, the four existing sheets carry Sheet 05 in their navigation, and the <title> on all four said "Saqr Aero" while every other tag said "Saqr Consulting" — now consistent. Service worker at saqr-v5.


Rev B was a technical review of the data. Rev C is a correction to the conditions those data are evaluated in, plus the work needed to put the site on its real domain.

Sheets 01, 02 and 04 are now Rev C. Sheet 03 has no atmosphere model in it and is unchanged at Rev B.

The atmosphere was wrong for the region the site is published from

Three sheets needed air density and each one did something different:

SheetRev B behaviour
01 Wing SizingDensity varied with field elevation, temperature fixed at the standard lapse
02 Airfoil MatrixReynolds helper hardcoded 1.225 and 1.789e-5, sea level only
04 Power & Endurancevar RHO = 1.225 for the entire drag polar and power curve

So the same aircraft could come out of Sheet 01 and Sheet 02 with two different Reynolds numbers, and every endurance figure on Sheet 04 was computed for a standard 15 °C sea-level day regardless of what the user had entered anywhere else.

All three now call one shared airProps(). Temperature and relative humidity are inputs. Leave the temperature blank and the standard atmosphere is assumed, which reproduces the Rev B numbers exactly, and the sheet now says so in a note rather than leaving it implied.

Why it matters here

The standard atmosphere assumes 15 °C at sea level. Doha is at sea level and is not 15 °C. Measured against the standard day:

ConditionsDensityStall speedReynolds numberDensity altitude
Standard, sea level1.225——0 m
45 °C, dry1.110+5.1%−16.1%1020 m
45 °C, 60% RH1.086+6.2%−17.9%1240 m
35 °C, 70% RH1.129+4.2%−12.5%845 m

The stall speed error ran optimistic, which is the wrong direction for a safety-relevant number and the opposite of every correction made in Rev B. The Reynolds error matters more, because Sheet 02 ranks sections by Reynolds band and an 18% shift moves sections across band edges.

Humidity is included because humid air is less dense than dry air, not more — water vapour weighs less than the air it displaces. It is a smaller effect than temperature, but a humid Gulf summer adds a further percent or so on top.

Model

Standard pressure at geopotential altitude, then density from the ideal gas law at whatever temperature and humidity apply, with the water-vapour partial pressure taken from the Magnus form. Sutherland for viscosity at the actual temperature; the humidity correction on viscosity is well under a percent and is not applied. Density altitude is reported so the figures can be checked against any other source.

Sea-level standard density is now derived from the gas law rather than the rounded 1.225 of the tables, so density altitude comes out at exactly zero on a standard day.

Verified

Standard sea level returns 1.224978 kg/m³ and 1.789 × 10⁻⁵ Pa·s. Standard 1000 m returns 1.1116 kg/m³ and a density altitude of exactly 1000 m. Every figure in the table above was reproduced in a browser against the shipped code.

Fonts are self-hosted

The three families came from Google's CDN, so every page load sent the visitor's IP address and user agent to a third party. The site promises no account, no email and no tracking; that promise is now true. It also removes a render-blocking request to another origin and means a first visit works offline, not only a second one.

Latin and latin-ext subsets, WOFF2, in fonts/, under the SIL Open Font License 1.1 with the licence text alongside them. See docs/CREDITS.md. The pages now make no cross-origin requests at all, which was confirmed by loading all six with request logging on.

Service worker

Bumped to saqr-v4. The Google Fonts branch is deleted, since there is nothing cross-origin left to cache. 404.html and the font files are precached, and the offline fallback now returns a real response instead of possibly undefined when the cover page is not in the cache.

saqr-lockup.png was removed from the precache list: 142 KB pushed into every visitor's cache for a file no page loads.

Launch preparation

  • The saqr-consulting.example placeholder is replaced with the real domain in all 26 places across five pages, the sitemap and robots.txt. Canonical host is www.saqrconsulting.com.qa; the apex should 301 to it.
  • Sitemap lastmod refreshed.
  • A leftover sentence promising that "everyone who bought gets it" is gone. Nobody buys anything; the site is donation funded.
  • an METU → a METU on all five pages.
  • The cover page listed all four sheets as Rev A while the sheets themselves said Rev B. The cards now carry the correct letter for each sheet.

Code

carry() guarded its arguments as a && b && isFinite(v) || isString(v) in three sheets and with the parentheses in the fourth. Traced against every value the sheets actually pass, both forms agreed, so this was not a live bug — but four copies of one function that differ is a bug waiting for its next edit. All four are now identical.

The shared atmosphere block is duplicated verbatim across three sheets, marked as such, because the no-build-step architecture has no mechanism for sharing code. ROADMAP.md Phase 6 proposes extracting it into a plain static file.

Still open

  • MH64 pitching moment sign. Carried over from Rev B, still unresolved. It is applied as negative with a warning on computation alone, with no tunnel polar located. Resolving it needs a primary source, not a code change.
  • Rev B spot-checks. The sample of corrected airfoil figures has still not been checked against the cited sources. Same reason.

Revision B — technical review applied

All changes below come from an independent review against published tunnel and computational sources. Verdicts on the airfoil table: 22 CHANGE, 4 UNSURE, 1 OK. Stability: 31 OK, 1 change. Drag and power: 30 OK, no changes.

Sheet 01 — Wing Sizing

2D section data separated from the finite-wing correction. The dropdown now gives two-dimensional section maximum lift measured at model Reynolds numbers, and a single 0.90 factor converts it to a whole wing. Previously the two were conflated in one number.

Section typewasnow (2D)effective (×0.90)
Undercambered, thin1.501.451.31
Flat-bottom, Clark Y1.351.351.22
Semi-symmetric1.201.151.04
Symmetric1.050.950.86
Flat sheet foam0.900.850.77

Stall speeds rise 5–11%. Every change is in the conservative direction.

Sheet 02 — Airfoil Matrix

Maximum lift reduced where measurement disagreed with the compiled figure: NACA 2412 1.25→1.03 · 4412 1.45→1.20 · 6412 1.60→1.45 · 23012 1.50→1.15 · 0009 1.00→0.80 · 0012 1.10→0.90 · 0015 1.15→1.05

Pitching moment corrected: SD7037 −0.09→−0.057 · MH64 +0.005→−0.005 (sign) · NACA 63₁-412 −0.07→−0.095

Reynolds bands narrowed to tested evidence on 16 sections. The largest withdrawals: RG15 600k→300k, S1223 500k→300k, E423 500k→300k, NACA 0012 1M→700k.

Rows that were doing too much work:

  • AG35–38 → now AG35 only. The other three had no matching data and shared its row.
  • KFm-2 → flagged as a geometry family, not a section. Nearest test used a 15% NACA 0015 measuring 0.78.
  • Cambered plate → flagged. Source was a finite AR6 wing at 1.3% thick with no moment measured.
  • NACA 63-412 → renamed 63₁-412; band restricted to Re 3M, the only data that exists.

Geometry corrected to the sections actually tested: S3021 8.5/2.6 → 9.5/3.0 · E205 10.5/2.2 → 10.5/3.0

New: every row states whether its pitching moment was measured or computed. 15 measured, 12 computed. Shown in the spec panel on each result.

Stall reclassified: NACA 0018 gentle → abrupt (hysteretic with deep lift loss); SD7037 and SD7062 gentle → moderate, with abrupt low-Re behaviour noted.

Sheet 03 — Balance & Tail

Trim tail load formula corrected.

was:  CL_t = (Cm0 − CL·SM) / (η·V_H)
now:  CL_t = (Cm0 + CL·(h − h_ac,w)) / (η·V_H)

The moment arm is CG to wing aerodynamic centre, not the static margin — those are different quantities and the old form conflated them, overstating tail download by roughly a factor of two. Warning threshold moved 0.50 → 0.35 to suit the corrected scale.

Effect: trainer with Clark Y −0.39 → −0.19 · cargo with S1223 −0.62 → −0.39 (still warns) · symmetric pattern ship −0.10 → +0.07, correctly showing a small upload rather than a download.

Fuselage allowance reworded as a preliminary estimate only, to be computed from the fuselage pitching moment slope once its geometry is known.

Sheet 04 — Power & Endurance

No figures changed. Added the caveat that a single propeller efficiency is a cruise value and that climb properly needs a propeller map across advance ratios.

Not yet actioned

  • MH64 pitching moment sign is applied as negative with a warning, but rests on computation with no tunnel polar found. Confirm before relying on it for a tailless design.
  • Values were transcribed from the review; spot-check a sample against the cited sources (UIUC Vols 1/2/5, NACA TR-586, TR-824, SAND80-2114, Timmer, SoarTech 8, Laitone) before launch.