diff --git a/apps/storybook/src/Foundations.stories.tsx b/apps/storybook/src/Foundations.stories.tsx
index 112c54a..7846cbf 100644
--- a/apps/storybook/src/Foundations.stories.tsx
+++ b/apps/storybook/src/Foundations.stories.tsx
@@ -708,13 +708,7 @@ function EaseRow({ name }: { name: string }) {
// A preset player - clicking Play remounts the sample with the literal `animate-*` class so
// the ACTUAL Tailwind utility runs. `animationName` is the preset's label, also used as the
// badge text; the remount (key bump) restarts the "out" presets so they visibly animate away.
-function PresetPlayer({
- cls,
- animationName,
-}: {
- cls: string;
- animationName: string;
-}) {
+function PresetPlayer({ cls, animationName }: { cls: string; animationName: string }) {
const [tick, setTick] = useState(0);
return (
@@ -876,7 +870,8 @@ function Motion() {
style={{
fontSize: 'var(--text-sm)',
color: 'var(--color-text)',
- background: 'color-mix(in srgb, var(--color-warning) 12%, var(--color-surface))',
+ background:
+ 'color-mix(in srgb, var(--color-warning) 12%, var(--color-surface))',
borderLeft: '3px solid var(--color-warning)',
borderRadius: 'var(--radius-sm)',
padding: '0.5rem 0.75rem',
diff --git a/docs/feedback/0017-form-controls-ios-zoom.md b/docs/feedback/0017-form-controls-ios-zoom.md
new file mode 100644
index 0000000..8edbc07
--- /dev/null
+++ b/docs/feedback/0017-form-controls-ios-zoom.md
@@ -0,0 +1,33 @@
+# 0017 - Form controls auto-zoom on iOS
+
+Source: rogueoak.com subscribe form (rogueoak feedback 0002) - tapping a canopy `Input` on a phone
+zoomed the page. The same latent bug is in every canopy consumer, so it is fixed at the source here.
+
+## Symptom
+
+On iOS Safari, focusing a canopy `Input`, `Textarea`, or `Select` trigger zoomed the whole page in -
+a jarring shift the user then had to undo. Reproduced on rogueoak.com; applies to any consumer with
+these controls on a phone.
+
+## Root cause
+
+iOS Safari auto-zooms to a focused form control whenever its computed `font-size` is below **16px**.
+All three field controls used `text-sm` (14px) in their base class, so every one tripped the zoom.
+A platform rule the component defaults did not account for - not a consumer bug, so a per-app
+override (what rogueoak shipped first) is the wrong layer. The design system owns the default.
+
+## Fix
+
+Set the focusable field controls to `text-base md:text-sm`: **16px on mobile** (no iOS zoom) and
+**14px from the `md` breakpoint up** (preserves the denser desktop typography). Applied to `Input`,
+`Textarea`, and the `Select` trigger. The `SelectItem` / `SelectLabel` dropdown rows stay `text-sm` -
+they are not focusable text fields, so they never zoom. `md` is a width breakpoint, not a touch test,
+but that is the right proxy here: iOS auto-zoom is a small-viewport phone behavior, and phones sit
+below `md`. Each control gains a test asserting `text-base md:text-sm` (and not bare `text-sm`).
+
+## Learning
+
+**Interactive form controls must default to font-size >= 16px on mobile.** A 16px minimum on
+focusable fields is a design-system-level guarantee, not a per-app override - fix it once in the
+component so every consumer inherits it. Use `text-base md:text-sm` when the desktop design wants a
+denser 14px. Verify the computed size, not just the class.
diff --git a/docs/overview/learnings.md b/docs/overview/learnings.md
index efc8881..6fc1ec4 100644
--- a/docs/overview/learnings.md
+++ b/docs/overview/learnings.md
@@ -535,3 +535,16 @@ or a mid-flight glitch. For any spring/overshoot demo, size the stage for the **
`travel x max-bezier-y`, add clearance) and prove the extreme is in-bounds by frame-sampling the
live animation or freezing at the computed peak. Never ship a motion affordance you have not
watched fire (a CSS `:hover` re-declaring an identical `animation-name` restarts nothing).
+
+## Form controls must default to >=16px on mobile (iOS auto-zoom)
+
+iOS Safari auto-zooms the page when a focused ``/`