- Certificate: Hardcode approval gate — completely removed the auto-issue path;
`certRequiresApproval()` now strictly returns `true`, ignoring DB configuration.
- Certificate: Quiz completion now queues a pending record and triggers a
"Pending approval" notification instead of falsely confirming issuance.
- Certificate: Public endpoints now enforce a guard on `status=issued`; frontend
strips the auto-issue side effect and claim button (issuance restricted to admins).
- Chats tab: Refactor layout into a full-width centered banner above two
equal-height side-by-side panels (`grid-cols-1 lg:grid-cols-2`), utilizing
`max-h` and `overflow-y-auto` to eliminate whitespace bleed.
- Video player: Fix settings flyout prematurely closing on mouse movement (removed
`setSettingsOpen` from `poke`); add a click-outside listener via `mousedown`.
- Video player: Deduplicate controls — the speed pill now directly cycles playback
speed, leaving the gear icon as the single entry point for settings (eliminating
overlapping popovers).
- Video player: Implement full-bleed theater mode using the `-mx-[50vw] w-screen`
technique, keeping the progress strip constrained to `max-w-5xl` below.
Viewport:
- Add `viewport` export with `viewport-fit=cover` (enables CSS safe-area-inset-*
on bezel-less iOS/Android) and `interactive-widget=resizes-content` (prevents
layout jumps from virtual keyboard open/close)
iOS zoom prevention:
- input.tsx, textarea.tsx: change text-sm (14px) → text-base sm:text-sm (16px)
on mobile; iOS Safari auto-zooms any input with font-size < 16px
Touch targets:
- button.tsx: default h-10 → h-11 (44px), sm h-8 → h-9 (36px), icon h-10→h-11
matching the 44×44px iOS HIG and WCAG 2.5.5 minimum
- Add touch-manipulation to buttons, inputs, textarea to eliminate 300ms tap delay
Dialog → mobile bottom sheet:
- On mobile (< 640px): anchored to bottom with full width, rounded top corners,
slide-in-from-bottom / slide-out-to-bottom animation, drag handle affordance,
max-h-[90svh] + overflow-y-auto to prevent viewport overflow
- Safe area padding: pb-[max(1.25rem,env(safe-area-inset-bottom))]
- On desktop (sm+): unchanged centered modal with zoom animation
- Close button gets a 32×32 tap area on mobile
Mobile navigation:
- New bottom-nav.tsx: persistent 3-tab bottom bar (مسیر/مرکز/لیگ) shown below
md: breakpoint, hidden on desktop; 56px height + safe-area-inset-bottom;
active tab gets heavier stroke and scale
- Only shown for authenticated non-admin users
- app-shell.tsx: injects BottomNav; main content gets dynamic padding-bottom to
clear the bar (56px + safe area) on mobile, reverts to py-10 on md:
- Footer: safe-area-inset-bottom padding so content clears iPhone home indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>