Bandmakers · design pass two
The first pass was read off the code. This one was done with the app running on a phone, opening one screen at a time — which is the only way most of this could have been found. Then the two things that would have had it rejected.
Everything else on this page is tidying. These three were doing real damage across the whole app, and all three were invisible until it was running.
Home · Bookings · Progress · Profile · all five staff tabs
Inside the native tab bar, iOS adds the top safe area to a scroll view by itself — and every screen was also padding by it. The same 62pt was counted twice, so Home's greeting started 135pt down instead of 70. A tenth of the first screenful, on every tab, spent on nothing.
Counted once. One shared hook owns the top of every scrolling screen, and the content container is made a pixel taller than the window so the rule iOS applies is the same on a short screen and a long one.
Measure before tuning. The obvious fix — contentInsetAdjustmentBehavior="never" — does nothing here; it is not reaching the native view at all. Both values were tried on the device and neither moved a pixel. That is written down in the code so nobody spends the hour again.
Profile → the staff switch
The row pushed the staff sign-in, the route guard saw a parent standing on an auth screen and replaced it on the next frame. The screen flashed and the app was back on Home. A row that looks like a destination and is a no-op — the entire staff side was unreachable from inside the app.
The one auth screen a signed-in parent is allowed to reach. Staff are a separate account with separate tokens, so signing into one has nothing to do with being signed into the other, and the only way in is from a screen you reach while already signed in as a parent.
Booking → review
Review was numbered as a sixth step in a five-step flow. Underneath it, the button read "Payment — not built yet" — a developer's note sitting on a customer's screen — and when the quote failed the screen was a dead end with no way back to the choices that caused it.
Review is outside the count and shows its own kicker instead. The button says Pay; the foot note carries the truth that card payment is not open yet. A failed quote offers Pick another time.
This is the last screen before a parent decides whether this academy is careful with their child and their card. It was the screen with the arithmetic error on it.
The rule you gave me after the two profile headers. Nobody decided to have four kinds of list — it accumulated one screen at a time. Six shared components now cover what was written out by hand between eleven and fifty-nine times each.
Staff Desk · Timetable · Branches · Teachers · Books · Stock · Codes · Bundles · Accounts · Family · Payments · Progress · Trial · Careers
Identical rounded cards repeated down the page, with a target-sized dead zone between every pair of tappable rows.
One card, 1px separators, and a hand-drawn glyph on every row so the list can be scanned rather than read.
Branches (7) · Concerts (17) · Teachers (3) · Stock (2 per row)
A full-width filled button inside every card. On Concerts that was seventeen black slabs; on Branches, seven — for something you do standing at a printer a few times a year.
The row itself opens the thing. Where an action has to stay visible it is a quiet chip that sizes to its own words.
At most one primary per screen — and a control repeated once per row is never the one thing the screen is for.
Discount codes · Bundles · Staff accounts
Codes and bundles carried a bordered button reading Switch on / Switch off — the opposite of the current state, spelled out. Staff rows were worse: each offered the two states the person was not in, so "Demo Teacher A · Suspended · Left" looked like a description of them.
Codes and bundles use a real switch — the same control the Instruments screen two rows away was already using. Account status is a three-way segmented control that shows the state and offers the change in one object.
Copy that said something the system did not mean. These are the ones a parent or a front-desk person would have acted on.
Lesson prices, and the Academy row that leads to it
The headline counted every instrument missing any tier and called them all unbookable. Drums with no Expert price books perfectly well with a Core teacher. Meanwhile the Academy hub counted only the ones with no price at all — so two screens showed different numbers for what looked like the same fact.
3 instruments cannot be booked, and a second line saying three more are missing a price at one rate only. The hub and the screen agree.
Never round a partial problem up into a total one. The staff member who fixes the wrong three loses an afternoon.
Both sign-in screens
"Email or password is incorrect" cleared only on the next submit. After a sign-out it was still sitting in red above two empty boxes, telling a parent their blank password was wrong — and it pushed the Sign in button down as it appeared.
Editing either box clears it, because the thing the message describes is no longer what is in the box.
Discount codes
A code that ended in January and one that had used its single use both showed a working Switch on. The server refuses them at checkout either way — a control that does nothing is worse than no control.
They read Expired and Used up, and dates print as "ended 1 January 2026" rather than 2026-01-01.
Four controls removed something real, immediately, with no undo anywhere in the app — and three of them were the loudest thing on their screen.
Timetable · Closed days · Branches · Staff accounts
A red bordered button on every row, firing on the first tap. Retiring a slot stops parents booking it. Removing a closure puts a holiday back on sale. Replacing a branch code invalidates the printed sign on the door — and it sat next to the button you press to read that sign.
A quiet red word, and a confirmation that names the exact thing: "Sunday 11:00 stops being bookable. Lessons already booked into it keep their place."
Confirmations only for the genuinely irreversible — use them anywhere else and people learn to tap through them. Suspending a staff account still passes straight through; marking someone as left does not.
The staff "add a weekly time" form asked four questions and every one was a fully expanded list: 2 teachers, 7 branches, 7 instruments, 7 days. Twenty-three rows to answer four questions, so the only thing you actually type sat two screens below the fold.
Timetable · Day minimums · Closed days · Prices · Cash · Place a make-up · Books
Four walls of rows in a row. The weekday was a seven-row list — while a one-row day strip was already sitting in the same component file, used elsewhere.
Once a choice is made the list shows the answer and a Change. The whole form is now four rows and a text box. It only ever collapses onto a real answer — with nothing chosen the list stays open, because a collapsed empty row is a question the form is hiding from you.
The instrument field said "optional" and gave you seven instruments with no way to say any — so it could never be finished, so it never collapsed. "Optional" has to be a choice you can make.
Across both sides
· "This time is already taken." printed in full on eight consecutive rows
· core /
legacy /
cash /
accessory printed raw
from the database
· "Zamalek · Zamalek", "Sahel · Sahel"
· Space Mono caps under every bar on Reports
· "2026-08" as a cohort label
· A lone "1" floated 350pt from its section heading
· A key glyph drawn as a magnifying glass; a book drawn as the pause symbol
· A section headed "Concerts" containing one row called "Concerts"
· "Closures." on a screen reached from a row called "Closed days"
· Three names for one search box
· "Move to Open to everyone"
· "Events 17"
· A Taken tag, and the 45 minutes every slot shares stated once in the foot
note instead of eight times
· One shared place turns a column value into something a person reads
· A subtitle only when it adds something
· The display face on section headings and nowhere else
· "August 2026"
· The count rides with the label
· Both redrawn, and a globe added for Language
· Folded into "Schedule & events"
· The screen takes the row's wording
· One
· "Open to everyone" — a button says what happens when you press it
· "Events (17)"
This was the worst thing in the app and it was not a design problem — it needed a change on the server.
Booking step 3 · GET /catalog/instruments
Vocals, Bass and Violin were live choices like any other. You picked one, chose a branch, a teacher and a time — four steps — and the wall came at the review screen: "Pricing is not set for this teacher and instrument yet." The step before payment, at the exact moment the app was asking to be trusted with a card.
Shown, disabled, with the reason — and a note saying to ask at reception. Nobody walks four steps into a wall.
The flag comes from the server and mirrors the quote's price lookup exactly, so it can never disagree with what the checkout will do. It is computed over the teachers who actually teach the instrument, not the instrument alone — an instrument priced only at Expert is bookable if an Expert teaches it and not otherwise. That caught a fourth one nobody had counted: Cello is priced, but its only teacher is inactive.
Both would have bounced the submission on the first review, whatever the rest of the app looked like.
Guideline 5.1.1(v) · Profile
Apple has required in-app account deletion since 2022 for any app that lets you create an account. There was no such screen. The endpoint had existed the whole time — carefully written, destroying every stored photo and video, deleting the personal records, cancelling live lessons, freeing the email — and nothing in the app ever reached it.
A screen that states plainly what goes and what is kept: payment records stay, because Egyptian tax rules require it. One confirmation, not a typed phrase — Apple is equally clear that deletion must not be made difficult.
A route with no way in is not a feature. That is this project's own rule about what "done" means, and it had been broken quietly since the endpoint was written.
assets/icon.png
The stock blue "A" with the construction guides still visible, untouched since the project was scaffolded. Rejection on sight, and the first thing anyone would have seen on their home screen.
Ink ground, yellow Anton B — the same pairing as the Home hero card and the check-in tile, set in the app's own display face rather than something picked for the icon alone. Regenerates from a script, so any size is one command.
Not design problems. These need the academy or a decision from you.