feat: produce worker deployment + web producer/collective/branding UI + mobile CSS (wave-2c)

Deployment (t4g2): media-worker gains font-dejavu (drawtext lower-thirds)
and a dedicated produce-worker compose service running src/produce.ts —
claims video_processing_jobs compose|mix by lane, renders assembled/
overlaid/voiced cuts, registers derived library assets. Deliberately its
own process so a remote-lane box needs only Postgres + S3 + ffmpeg.

Web: compose media attachments UI (t2), Producer panel in the video
drawer (t5), collective creation + branding settings + shell branding
(brandweb), mobile CSS (mcss: touch targets, icon sizing, safe areas).
This commit is contained in:
glass beadian 2026-10-14 20:40:49 +00:00
parent 1183d69849
commit d9e8cdf789
18 changed files with 2384 additions and 20 deletions

View file

@ -74,6 +74,94 @@ html, body { max-width: 100%; }
[data-mobile-main] .mobile-stack-table td > * { max-width: 100%; }
}
/* ---------------------------------------------------------------------------
Phone tier (≤640px) — native feel, touch targets, safe areas, overflow.
Scoped globally (body-wide) rather than to [data-mobile-main] because the
auth screens (/login, /signup, /join, /submit, /oidc) and the body-portal
sheets render outside the main shell and were getting none of this.
These rules are unlayered, so they outrank Tailwind's `@layer utilities`
without needing !important.
--------------------------------------------------------------------------- */
@media (max-width: 640px) {
/* -- Native feel: no tap highlight flash, no double-tap-zoom delay on
controls (pinch zoom stays available — `manipulation` only disables
the double-tap gesture), no iOS landscape text inflation, no document
rubber-band behind the app shell. -- */
html {
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
overscroll-behavior: none;
overflow-x: hidden;
}
body {
max-width: 100%;
overflow-x: hidden;
-webkit-tap-highlight-color: transparent;
}
body :where(button, a[href], [role="tab"], [role="button"], [role="switch"]) {
touch-action: manipulation;
}
/* -- ≥44px touch targets for buttons / icon-buttons (glyph buttons here are
plain <button>, bottom-nav tabs are <a>, segmented control is role=tab,
sheet drag handle is role=button). min-width is limited to controls that
are already box-shaped so inline text links keep their flow shape. -- */
body :where(button, a[href], [role="tab"], [role="button"], [role="switch"]) {
min-height: 44px;
}
body :where(nav button, nav a[href], [role="tab"]) {
min-width: 44px;
}
/* -- 16px minimum on text-entry controls: iOS Safari auto-zooms the viewport
on focus whenever the field's computed font-size is under 16px, which
reads as janky/non-native. Non-text inputs excluded (their size is
visual, not textual). -- */
body :where(
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="button"]):not([type="submit"]):not([type="reset"]),
select,
textarea
) {
font-size: 16px;
}
/* -- Icon sizing. Icons size via width/height attributes from React props,
not a CSS variable or utility class — but HTML sizing attributes are
presentational hints, so author CSS still wins without touching TSX.
Bottom-nav glyph: 24px → 22px (target band 20–22px; top of band keeps
it clearest at phone density). Inline SVG icons: ≥16px floor so the
14px spinners and 11px arrow glyphs stop rendering sub-legibly. -- */
nav[aria-label="Primary navigation"] img {
width: 22px;
height: 22px;
}
body :where(svg) {
min-width: 16px;
min-height: 16px;
}
/* -- Safe areas. Bottom is already covered app-wide via --mobile-safe-bottom
(bottom nav, sheets, compose's fixed action bar). This adds the other
three edges on the shell column that holds top bar + main + bottom nav
— the notch/home-indicator sides that only bite in landscape.
max() keeps the value at 0 on devices without insets, and degrades to
no padding at all on browsers without :has(). -- */
:has(> main[data-mobile-main]) {
padding-top: max(0px, env(safe-area-inset-top, 0px));
padding-left: max(0px, env(safe-area-inset-left, 0px));
padding-right: max(0px, env(safe-area-inset-right, 0px));
}
/* -- Horizontal overflow guards: belt-and-braces on the scroller plus a
cap on replaced media, the classic cause of stray phone scroll. -- */
main[data-mobile-main] {
max-width: 100%;
}
body :where(img, video, canvas, iframe, svg) {
max-width: 100%;
}
}
::selection {
background: var(--color-accent-dim);
}