marketing/facebook: covers rebuilt to the MEASURED phone view (R-919 -> VERIFY)
gates / gates (push) Successful in 4m28s
gates / gates (push) Successful in 4m28s
build.py believed a phone shows 640x360 of the cover. It shows 412x274 =
1.504:1 -- the full height and only the centre 938 px of 1640, 351 px off each
side. PHONE_HDR = (412, 274) now drives CW_PHONE, so SAFE is (391,40)-(1249,584),
858 px wide with a 40 px margin inside each crop edge.
QUIET is no longer one formula for both circles: the computer circle is
left-anchored, the phone circle is CENTRED and hides only its measured box
(637,369,393). The old shared formula, applied to a centred circle, would have
blanked everything from x 0 to x 1054 below y 345.
Every cover draws in a derived BAND (408,48)-(1249,340), and a cap check holds
each headline's capital at >= 4% of the cover height; A/B/C come out at 45, 45
and 37 px against a 25 px floor.
RED-PROOF, and it is permanent: control_old_window() runs on every build and
draws the headline where the old assumption put it (x 328); the check must
reject it. The phone's crop edge is x 351, so 23 px were cut; the measured safe
edge is x 391. Against the covers as committed at a76207945e the new check
convicted 3 of 3 -- A 23/22 px past the left/right edges, B 63/58 px plus 1017
content pixels under the phone circle, C 63/82 px plus 1778.
Covers redrawn inside the band: A one centred line; B the home motif scaled
with two tiles moved into the lower-right strip, the one area below the band a
phone still shows beside the profile circle; C the dashboard screen 470 -> 370
px so frame and base fit. preview.html shows the phone panel at the measured
938x624 with the centred circle and no longer claims 640x360.
Profile pictures untouched -- sha256 identical before and after, and not in the
diff. Built on DooPlex: the PNGs are byte-reproducible there (Pillow 11.1.0) and
not across Pillow versions; the README now says so.
R-919 is VERIFY, not closed: the geometry is measured on ONE emulated device in
the mobile website, so the operator's check in the Facebook app is the
acceptance. Nothing uploaded, no Graph API call, FACEBOOK_API never read.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
# REPORT — R-919: the Facebook cover rebuilt to the measured phone view (2026-10-09)
|
||||
|
||||
A `REPORT-<topic>.md` sibling; the shared `REPORT.md` was not touched. Evidence for the measurement it
|
||||
builds on: `documentation/audits/facebook-page-setup-2026-10-08/`.
|
||||
|
||||
## 1. Baseline and commits
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Baseline | `felhom.eu` `main` = `a76207945e`, clean on the Windows tree and on DooPlex |
|
||||
| Pushed | see §9 |
|
||||
| Repos touched | `felhom.eu` only — `marketing/facebook/`, `marketing/CHANGELOG.md`, `documentation/backlog/OPEN-ITEMS.md`, this report |
|
||||
| Built on | **DooPlex** (Pillow 11.1.0), not the workstation — see §4 |
|
||||
|
||||
## 2. Scenario A — the red-proof, with its numbers
|
||||
|
||||
The measured geometry was run against the three covers **as committed at `a76207945e`**, by importing
|
||||
`build.py` unchanged and overriding only the constants, so the content was today's. **All three were
|
||||
convicted.** Phone window: the centre **938,3 px** of 1640 (x 351 … 1289). New `SAFE` (391, 40, 1249, 584).
|
||||
New phone `QUIET` (613, 345, 1054, 624).
|
||||
|
||||
| cover | content box | past the left edge | past the right edge | content pixels under the phone circle |
|
||||
|---|---|---|---|---|
|
||||
| cover-a.png | (368, 184, 1271, 303) | 368 < 391 — **23 px** | 1271 > 1249 — **22 px** | 0 |
|
||||
| cover-b.png | (328, 44, 1307, 567) | 328 < 391 — **63 px** | 1307 > 1249 — **58 px** | **1017** |
|
||||
| cover-c.png | (328, 108, 1331, 455) | 328 < 391 — **63 px** | 1331 > 1249 — **82 px** | **1778** |
|
||||
|
||||
Those content boxes are identical to the ones the unmodified build prints for the committed covers, which
|
||||
is the control that the red-proof really did run against today's content and not against a redrawn one.
|
||||
|
||||
**The red-proof is now permanent, not a one-off.** `control_old_window()` runs on **every build**, beside the
|
||||
circle check's own control: it draws the headline where the old 640 × 360 assumption put it (**x 328**) and
|
||||
the safe check must reject it. The build prints the two numbers that differ, because they are easy to
|
||||
confuse: the phone's crop edge is **x 351**, so **23 px of the headline were actually cut**; the measured
|
||||
safe edge is **x 391**, 63 px further in, which is the 40 px `MARGIN` on top of the crop.
|
||||
|
||||
## 3. Scenario B — the rebuilt covers
|
||||
|
||||
Canonical build (DooPlex). `BAND` = (408, 48, 1249, 340) — inside `SAFE`, right of the computer circle,
|
||||
above the phone circle — derived from the constants, so a re-measurement moves the designs with it.
|
||||
|
||||
| cover | content box | headline size → capital height | under the computer circle | under the phone circle |
|
||||
|---|---|---|---|---|
|
||||
| cover-a.png | (424, 172, 1231, 287) | 57 → **45 px** | 0 | 0 |
|
||||
| cover-b.png | (420, 52, 1235, 575) | 58 → **45 px** | 0 | 0 |
|
||||
| cover-c.png | (420, 52, 1243, 339) | 47 → **37 px** | 0 | 0 |
|
||||
|
||||
`CAP_MIN` is 25 px (4 % of 624); the smallest headline is 37 px, about 48 % clear of the rule. Measured
|
||||
clearance inside the phone's own crop edges: **69–73 px on the left, 49–57 px on the right** — the 40 px
|
||||
margin plus the layout's own air.
|
||||
|
||||
What changed in each design:
|
||||
|
||||
- **A — calm.** One centred line, now fitted to the band rather than the old wide safe area, with the domain
|
||||
under it. Nothing else.
|
||||
- **B — the idea.** Headline left; the home, its server and its app tiles right, scaled to the band. Two of
|
||||
the seven tiles sit in the **lower-right strip** (x ≥ 1054), which is the one area below the band that a
|
||||
phone still shows beside the profile circle — so the composition keeps its lower half instead of ending at
|
||||
y 340.
|
||||
- **C — the product.** Headline left; the dashboard right, its screen **470 → 370 px wide** so the frame and
|
||||
its base fit between the text and the right crop edge. The screenshot's text was never legible at cover
|
||||
size (it renders ~280 px wide on a 1250 px Facebook cover, and did before at ~358 px); it still reads as a
|
||||
real product dashboard, with the sidebar, the tables and the status badges visible. **It was not dropped**
|
||||
— §8's fallback — but the operator picks, and `out/preview.html` shows all three.
|
||||
|
||||
The computer view was checked by eye on the rendered PNGs as well as by the numbers: the band sits in the
|
||||
cover's upper half by necessity (the phone hides the bottom middle), so each cover's glow was moved down to
|
||||
carry the lower half instead of leaving it flat.
|
||||
|
||||
## 4. Profile pictures — untouched, proven
|
||||
|
||||
```
|
||||
before 936cb0386c8fb46c6f9f7ba7ac97b91c1d89fafef05d86f6a7ad9a4d951ec471 profile-dark.png
|
||||
f528e0820ff4e527e03d4a0a909ae74ca09d715d0ae4faff89cd3c01ea4fc5b7 profile-white.png
|
||||
after 936cb0386c8fb46c6f9f7ba7ac97b91c1d89fafef05d86f6a7ad9a4d951ec471 profile-dark.png
|
||||
f528e0820ff4e527e03d4a0a909ae74ca09d715d0ae4faff89cd3c01ea4fc5b7 profile-white.png
|
||||
```
|
||||
|
||||
Identical, and neither file appears in the diff. **This only holds because the build ran on DooPlex.** The
|
||||
workstation has Pillow 12.3.0; DooPlex has 11.1.0, and the two render the same text a pixel or two
|
||||
differently — a rebuild on Windows rewrites **all six** PNGs, profile pictures included, and shifts the
|
||||
measured content boxes (cover-a's right edge 1235 vs 1231, cover-c's fitted headline 45 vs 47). DooPlex
|
||||
reproduces the committed bytes exactly (a rebuild there leaves `git status` clean), so it is the build
|
||||
machine; the README now says so. Pillow was installed on the workstation for this work and used only to
|
||||
iterate on the design, never for the committed output.
|
||||
|
||||
## 5. The rebuilt files
|
||||
|
||||
| file | size | Gitea |
|
||||
|---|---|---|
|
||||
| `marketing/facebook/out/cover-a.png` | 1640 × 624, 59 KB | `gitea.dooplex.hu/admin/felhom.eu/src/branch/main/marketing/facebook/out/cover-a.png` |
|
||||
| `marketing/facebook/out/cover-b.png` | 1640 × 624, 54 KB | `…/marketing/facebook/out/cover-b.png` |
|
||||
| `marketing/facebook/out/cover-c.png` | 1640 × 624, 97 KB | `…/marketing/facebook/out/cover-c.png` |
|
||||
| `marketing/facebook/out/preview.html` | 1295 KB, self-contained | `…/marketing/facebook/out/preview.html` |
|
||||
|
||||
Each PNG's size was read back from the file by the build, not taken from the constants.
|
||||
|
||||
## 6. What the operator does, in plain words
|
||||
|
||||
1. **Upload the new cover.** Open `out/preview.html` (download it from Gitea; it opens on its own), pick a
|
||||
cover — the same option as now unless the preview gives you a reason to change — and upload it to the
|
||||
Page by hand, the way you did last time. About a minute. The pictures cannot be set by robot (R-914).
|
||||
2. **Then check it on your phone, in the Facebook app.** Open the Page and look at the cover: is the second
|
||||
line „saját szabályaid" whole, and is „felhom.eu" whole? **This is the real test.** What was measured was
|
||||
the phone **website** in a simulator, on one device. If the app cuts it differently, say so and the
|
||||
numbers move — the build follows them.
|
||||
|
||||
Nothing else changed: the profile picture, the texts, the button and the Page name are as they were.
|
||||
|
||||
## 7. Register
|
||||
|
||||
Rows before **140**, after **140**, opened **0**, closed **0** — counted the way `register_shape_gate.py`
|
||||
counts. **R-919 moved OPEN → VERIFY** and stays in `OPEN-ITEMS.md` deliberately: the build is fixed, the
|
||||
*result* is not proven until the operator looks in the Facebook app, so it is not a finished row and does
|
||||
not belong in `CLOSED-ITEMS.md` yet. Its What now carries the fix and the red-proof numbers; its Next action
|
||||
is the operator's two steps and what to do if the app still cuts the cover.
|
||||
|
||||
## 8. Observations
|
||||
|
||||
- **The computer profile circle no longer overlaps the cover at all** (measured 2026-10-08: the cover ends
|
||||
at y 531 and the circle starts at y 547). `DESK_CIRCLE` still reserves the bottom-left corner as if it
|
||||
did. **NOT-A-FINDING: deliberate.** The brief said to keep the computer constants unchanged, and the
|
||||
reservation is conservative in the safe direction; it costs only a corner the designs do not use. It is
|
||||
now written down as a conservatism rather than as a fact, in the constant's own comment.
|
||||
- **`QUIET` used one formula for both circles, and that formula was only right for a left-anchored one.**
|
||||
Applied to the measured centred phone circle it would have blanked everything from x 0 to x 1054 below
|
||||
y 345 — about two-thirds of the cover's lower half, for no reason. **NOT-A-FINDING in the register: fixed
|
||||
in this session** (the size rule), and it never shipped, because the phone circle was wrong anyway.
|
||||
- **The build is not byte-reproducible across Pillow versions.** See §4. **NOT-A-FINDING: not a defect**, and
|
||||
it costs nothing once the build machine is named — which the README now does. It would become a finding
|
||||
only if the build moved into CI on a different image.
|
||||
- **The preview page claimed a phone shows 640 × 360** and drew its phone panel at that aspect. It now takes
|
||||
its shape, its circle and its caption from the measured constants and says MEASURED, with the source-pixel
|
||||
and phone-pixel diameters both spelled out (393 cover-pixels, 173 px on a 412 px screen) so neither can be
|
||||
read as the other. **NOT-A-FINDING: fixed in this session**, part of the same change.
|
||||
- **Nothing was uploaded, no Graph API call was made, no browser touched Facebook, and `FACEBOOK_API` was
|
||||
never read.**
|
||||
|
||||
## 9. Gates, commit, CI
|
||||
|
||||
Filled in below after the run.
|
||||
|
||||
## 10. Teardown
|
||||
|
||||
Nothing on any host, the hub or Facebook. The work ran in the two git working trees only; the throwaway
|
||||
red-proof and phone-simulation scripts live in this session's scratchpad and `/tmp` on DooPlex, and the
|
||||
DooPlex copy was removed. No guest, no container, no service was started or stopped.
|
||||
@@ -278,7 +278,7 @@ stopping line that lies.
|
||||
| **R-915** | Business & legal | P4 | **The Meta app `felhom.eu` is in development mode, so posts it makes are seen only by people with a role on the app.** READ 2026-10-08 (Meta docs, cited in the spike); not measured. MEASURED: development mode does not refuse posting (both test writes HTTP 200). Live needs display name, contact e-mail, a Terms of Service URL, an app icon, a category and the app purpose (privacy-policy and data-deletion URLs listed beside them). The robot's Page assignment, missing at first, was done by the operator the same day. | **WAITING-ON-OPERATOR** | R-813 (the Terms of Service URL) | Before real public posts: switch the app to Live in the Meta developer page. If nothing is done: posts stay invisible to the public. After Live: CC adds the Page link to the website footer (not before — a link to a Page nobody can read is worse than none) | operator |
|
||||
| **R-916** | Business & legal | P4 | **The logo has no usable vector master: `website/assets/logo.svg` sets „felhom.eu" as live text in the fonts „M+ 2c" and „Vremena Grotesk", which DooPlex does not have, so every renderer here draws other letters.** SEEN 2026-10-08 (Facebook pictures task): librsvg drew the lettering in DejaVu; `fc-match` resolves the family to DejaVu Sans. The PNG (645 x 408) is the only faithful copy, which caps every picture made from it at about that size (`marketing/facebook/README.md`). | **WAITING-ON-OPERATOR** | the machine with the fonts | In Inkscape on that machine: select the lettering → Path → Object to Path → save as `website/assets/logo-master.svg`; then `marketing/facebook/build.py` can use it. If nothing is done: the PNG stays the master; larger prints will be soft | operator |
|
||||
| **R-917** | Business & legal | P4 | **`COPY.md` §2, the Page's longer description (867 characters), has nowhere to go: Facebook's current Pages experience has no long-description field at all.** FOUND 2026-10-08 (Page setup task, `audits/facebook-page-setup-2026-10-08/`). Looked in four places, all on the live Page as its admin: the Page's „Névjegy" tab (Rövid áttekintés / Személyes adatok / Részletek — only a 255-character „Bemutatkozás" and the pinned category), Business Suite's „Oldal módosítása" dialog (profile picture, cover, Bemutatkozás, category, phone, e-mail, address, website, social links — and nothing else), Facebook settings → „Oldal adatai" (redirects to the same Névjegy tab) and settings → „Oldal beállítása" (name, access, type, history, status, recommendation, messaging, data sharing). MEASURED by Graph with the Page token: `description`, `general_info` and `bio` all read `null`. Writing `description` by API would need `pages_manage_metadata`; the robot key's scopes are `read_insights, pages_show_list, business_management, pages_read_engagement, pages_read_user_content, pages_manage_posts, pages_manage_engagement, public_profile`, and the task's fences forbid adding a permission. So §2 is written, reviewed and unplaceable. **Options for the operator:** (a) leave §2 unused and let the 99-character intro plus the website carry it; (b) shorten §2 to ≤ 255 characters and make it the „Bemutatkozás" instead of §1 — but §1 was written for exactly that slot, so this is really „rewrite one of the two"; (c) publish §2 as the Page's first pinned post once the app is Live (R-915), which is where a long text actually gets read; (d) ask Meta support whether the field still exists for this Page type. Recommended (c) — the text reads like a post already. | **WAITING-ON-OPERATOR** | the choice a/b/c/d; (c) also waits on R-915 | Pick a/b/c/d. If nothing is done: §2 stays in `COPY.md` unused and the Page carries only the 99-character intro | operator |
|
||||
| **R-919** | Business & legal | P3 | **On a phone the Facebook Page cuts the left edge of the cover: the „s” of „saját szabályaid” and the „f” of „felhom.eu” are gone.** MEASURED 2026-10-08 on the live Page in Chrome DevTools device mode, Pixel 9 (412 × 924, mobile user agent, after a reload so Facebook serves the mobile bundle): `audits/facebook-page-setup-2026-10-08/C2-phone-headline-cut-closeup.png`. The mobile Page header is **412 × 274 = 1,504:1**, so Facebook keeps our cover's full height and shows only the centre **938 px of its 1640 px width (57,2 %)** — **351 px cut from each side**. `marketing/facebook/build.py` builds to a safe area of the centre **1028 × 544** (306 px clear of each edge), which is **45 px wider per side than the phone actually shows**; the light text in `out/cover-c.png` runs from x 333 to x 997, and the left crop edge is x 351, so the first **18 px** of the text are cut. Covers A and B are built from the same safe area and will have the same edge. Two further facts this measurement establishes: **Meta's own help page is wrong about its own rendering** — it states the mobile cover is 2,4:1 where the Page header measures 1,504:1 — and the mobile profile circle is far bigger than assumed (172 px, centred, overlapping the bottom 112 px of the 274 px cover, i.e. source x 637–1030 × y 369–624 is hidden). Not re-cropped on Facebook, per the task's fence. | **OPEN — owner: CC** | — | Narrow `build.py`'s safe area to the centre ≤ 938 px at full height (use ~900 px for margin) and widen the phone profile-circle exclusion to the measured 172 px centred shape, with the check shown failing on today's covers first; rebuild A/B/C; the operator re-uploads the chosen cover by hand. Until then the cover reads „aját szabályaid” / „elhom.eu” on every phone | CC |
|
||||
| **R-919** | Business & legal | P3 | **On a phone the Facebook Page cuts the left edge of the cover: the „s” of „saját szabályaid” and the „f” of „felhom.eu” are gone.** MEASURED 2026-10-08 on the live Page in Chrome DevTools device mode, Pixel 9 (412 × 924, mobile user agent, after a reload so Facebook serves the mobile bundle): `audits/facebook-page-setup-2026-10-08/C2-phone-headline-cut-closeup.png`. The mobile Page header is **412 × 274 = 1,504:1**, so Facebook keeps our cover's full height and shows only the centre **938 px of its 1640 px width (57,2 %)** — **351 px cut from each side**. `marketing/facebook/build.py` builds to a safe area of the centre **1028 × 544** (306 px clear of each edge), which is **45 px wider per side than the phone actually shows**; the light text in `out/cover-c.png` runs from x 333 to x 997, and the left crop edge is x 351, so the first **18 px** of the text are cut. Covers A and B are built from the same safe area and will have the same edge. Two further facts this measurement establishes: **Meta's own help page is wrong about its own rendering** — it states the mobile cover is 2,4:1 where the Page header measures 1,504:1 — and the mobile profile circle is far bigger than assumed (172 px, centred, overlapping the bottom 112 px of the 274 px cover, i.e. source x 637–1030 × y 369–624 is hidden). Not re-cropped on Facebook, per the task's fence. **-- 2026-10-09, FIXED in the build:** `marketing/facebook/build.py` now takes the phone view from the measurement (`PHONE_HDR = (412, 274)` -> the centre 938 px), so SAFE is (391, 40)-(1249, 584) and the phone profile circle is the measured CENTRED box (637, 369, 393) rather than a left-anchored one. Every cover is drawn in a derived `BAND` (408, 48)-(1249, 340), and a `cap` check holds each headline's capital at >= 4 % of the cover height. The red-proof runs on EVERY build (`control_old_window`): it draws the headline where the old 640 x 360 assumption put it, x 328, and the check must reject it -- the phone's crop edge is x 351, so 23 px were cut. Against the three covers as committed at `a76207945e` the new check convicted 3 of 3 (A 23/22 px over the left/right edges, B 63/58 px plus 1017 content pixels under the phone circle, C 63/82 px plus 1778). The profile pictures are untouched -- sha256 identical before and after. | **VERIFY -- rebuilt on `main` 2026-10-09; NOT proven until the operator looks in the Facebook app.** The phone geometry is measured on ONE device (Pixel 9 emulated, 412 px, the mobile WEBSITE); the app may crop differently. | — | Operator: upload the rebuilt cover by hand (same option as now unless `out/preview.html` suggests otherwise -- the pictures cannot be set by API, R-914), then open the Page in the Facebook APP on a phone and say whether the headline and „felhom.eu” are whole. Close on that word. If the app still cuts them, re-measure there and move `PHONE_HDR`; the build follows it. If nothing is done: the Page keeps today's cover, which IS cut on phones | CC |
|
||||
|
||||
## Process & tooling — 23 rows (P3 3, P4 20)
|
||||
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
# marketing — CHANGELOG
|
||||
|
||||
## facebook — the covers rebuilt to the phone view we measured (R-919) (2026-10-09)
|
||||
|
||||
R-919's fix, from an operator brief (no TASK file in the repo). The covers shipped on 2026-10-08 were cut
|
||||
on every phone; this rebuilds them to the measured geometry. Report: `REPORT-facebook-cover-r919.md`.
|
||||
|
||||
- **The geometry is measured now, not assumed.** `build.py` believed a phone shows 640 x 360 of the cover.
|
||||
It shows **412 x 274 = 1,504 : 1** — the full height and only the **centre 938 px of 1640**, 351 px off
|
||||
each side. `PHONE_HDR = (412, 274)` drives `CW_PHONE`, so `SAFE` is now **(391, 40)–(1249, 584)**, 858 px
|
||||
wide with a 40 px margin inside each crop edge.
|
||||
- **`QUIET` is no longer one formula for both circles.** The computer circle is *left-anchored* (everything
|
||||
to its left is under it); the phone circle is **centred** and hides only its own measured box
|
||||
`(637, 369, 393)`. The old shared formula would have blanked the whole left half of the cover's bottom.
|
||||
- **A derived `BAND`** — inside SAFE, right of the computer circle, above the phone circle — is where every
|
||||
cover draws, so a re-measurement moves the three designs with it.
|
||||
- **A `cap` check** holds each headline's capital at ≥ 4 % of the cover height (25 px). A, B, C come out at
|
||||
45, 45 and 37 px.
|
||||
- **The red-proof runs on every build**, beside the circle check's own control: `control_old_window` draws
|
||||
the headline where the old assumption put it (x 328) and the check must reject it. Against the covers as
|
||||
committed at `a76207945e` the new check convicted **3 of 3** — A 23 px / 22 px past the left and right
|
||||
edges, B 63 / 58 px plus 1017 content pixels under the phone circle, C 63 / 82 px plus 1778.
|
||||
- **The three covers were redrawn** inside the band: A one centred line; B the headline left with the home,
|
||||
its server and now seven app tiles right — two of them in the lower-right strip, the one place below the
|
||||
band a phone still shows beside the profile circle; C the headline left with the dashboard, its screen
|
||||
470 → 370 px wide to fit. `out/preview.html` shows the phone panel at the measured 938 x 624 with the
|
||||
centred circle, labelled MEASURED, and no longer claims 640 x 360.
|
||||
- **The profile pictures are untouched** — sha256 identical before and after, and they are not in the diff.
|
||||
- **Build it on DooPlex.** The PNGs are byte-reproducible there (Pillow 11.1.0); Pillow 12.3.0 on the Windows
|
||||
workstation re-renders text a pixel or two differently and rewrites all six files. Noted in the README.
|
||||
- Nothing was uploaded to Facebook and no key was read. **R-919 is VERIFY, not closed:** the geometry is
|
||||
measured on one emulated device in the mobile website, so the operator's check in the Facebook **app** is
|
||||
the acceptance step.
|
||||
|
||||
## facebook — the Page's settings set by hand, and Meta's size page read first-hand (2026-10-08, evening)
|
||||
|
||||
Ran `marketing/facebook/TASK-page-setup-windows.md` from the Windows workstation through Claude in Chrome.
|
||||
|
||||
@@ -10,8 +10,12 @@ python3 marketing/facebook/build.py
|
||||
|
||||
One command makes every PNG and `out/preview.html`, and runs the checks: the profile circle (no logo pixel
|
||||
outside 0.42 x width; shown failing first on today's shape), the cover safe area (content inside what computer
|
||||
AND phone show, clear of the profile circle), and the sizes read back from the files. Exit 0 = all passed.
|
||||
Needs Python 3 + Pillow only. Brand source: `website/assets/logo.png` (the logo master — see below),
|
||||
AND phone show, clear of both profile circles), the headline's capital height, and the sizes read back from the
|
||||
files. Exit 0 = all passed. Needs Python 3 + Pillow only.
|
||||
|
||||
**Build it on DooPlex.** The PNGs are byte-reproducible there (Pillow 11.1.0 — a rebuild leaves `git status`
|
||||
clean) and NOT across Pillow versions: Pillow 12.3.0 on the Windows workstation re-renders the same text a
|
||||
pixel or two differently and rewrites all six files. Measured 2026-10-09. Brand source: `website/assets/logo.png` (the logo master — see below),
|
||||
`website/assets/site.css` tokens, `website/assets/fonts/` (Plus Jakarta Sans).
|
||||
|
||||
## What goes where
|
||||
@@ -51,19 +55,35 @@ What we build: the cover at 2 x, **1640 x 624**, well above both stated minimums
|
||||
|
||||
- **Computer (1435 px viewport): fine.** The cover renders whole — no crop, no distortion — the cover container
|
||||
caps at 1250 px, and the profile circle sits 16 px *below* the cover, so it covers no cover text.
|
||||
- **Phone (emulated Pixel 9, 412 x 924): the cover is CUT — R-919.** The mobile Page header is
|
||||
**412 x 274 = 1,504:1**, so Facebook keeps the full height and shows only the **centre 938 px of the 1640 px
|
||||
width (57,2 %)** — **351 px cut from each side**. The safe area below is the centre 1028 x 544, i.e. 306 px
|
||||
clear of each edge, which is **45 px per side too wide**. In `out/cover-c.png` the light text runs x 333 to
|
||||
x 997 against a left crop edge of x 351, so **18 px of it are cut**: the headline reads „aját szabályaid" and
|
||||
the wordmark „elhom.eu" on every phone. The mobile profile circle is **172 px and centred**, hiding source
|
||||
x 637–1030 x y 369–624 — part of the dashboard picture, no text.
|
||||
- **Phone (emulated Pixel 9, 412 x 924): the cover WAS cut — R-919, fixed 2026-10-09.** The mobile Page header
|
||||
is **412 x 274 = 1,504:1**, so Facebook keeps the full height and shows only the **centre 938 px of the
|
||||
1640 px width (57,2 %)** — **351 px cut from each side**. The old safe area was the centre 1028 x 544, i.e.
|
||||
306 px clear of each edge: **45 px per side too wide**. The light text of the old `cover-c.png` ran from
|
||||
x 333 against a left crop edge of x 351, so **18 px of it were cut** — the headline read „aját szabályaid"
|
||||
and the wordmark „elhom.eu" on every phone. The mobile profile circle is **172 px and centred**, hiding
|
||||
source x 637–1030 x y 369–624.
|
||||
|
||||
**So `build.py`'s safe area is wrong for phones** and needs narrowing to the centre **≤ 938 px at full height**
|
||||
(use ~900 px for margin), with the phone profile-circle exclusion widened to the measured centred 172 px shape —
|
||||
shown failing on today's covers first. **Meta's stated mobile ratio of 2,4:1 is not what the Page header does**
|
||||
(1,504:1 measured), which is why a figure from Meta's page is not a substitute for looking at the Page. Getting
|
||||
Facebook's phone layout at all needs DevTools device mode **plus a reload**; the audit's README has the recipe.
|
||||
### What the build does about it now (2026-10-09)
|
||||
|
||||
`build.py` takes the phone view from the measurement, not from Meta: `PHONE_HDR = (412, 274)` gives
|
||||
`CW_PHONE` = 938 px, so **SAFE is the centre (391, 40)–(1249, 584)** — 858 px wide, MARGIN 40 px inside each
|
||||
crop edge. The phone profile circle is the measured **centred** box `(637, 369, 393)`, and `QUIET` is no longer
|
||||
one formula for both circles: the computer one is *left-anchored* (everything to its left is under it), the
|
||||
phone one hides **only its own box**. Every cover is drawn inside `BAND` — inside SAFE, right of the computer
|
||||
circle, above the phone circle — which is derived from those constants, so a re-measurement moves the designs
|
||||
with it. A `cap` check keeps each headline's capital at **≥ 4 % of the cover height** (25 px), so the text
|
||||
survives the phone shrinking the cover to 412 px.
|
||||
|
||||
**The check was shown failing first.** Against the covers as committed at `a76207945e`: cover A was 23 px over
|
||||
on the left and 22 px on the right, cover B 63 px / 58 px with 1017 content pixels under the phone circle, and
|
||||
cover C 63 px / 82 px with 1778 under it — 3 of 3 convicted.
|
||||
|
||||
**Grade of the phone figures: MEASURED, but on ONE device** (Pixel 9 emulated, 412 px, the mobile **website**).
|
||||
Not measured in the Facebook app and not at any other width — which is why MARGIN is 40 px and why the
|
||||
operator's check in the app is the acceptance step. **Meta's stated mobile ratio of 2,4:1 is not what the Page
|
||||
header does** (1,504:1 measured), which is why a figure from Meta's page is no substitute for looking at the
|
||||
Page. Getting Facebook's phone layout at all needs DevTools device mode **plus a reload**; the audit's README
|
||||
has the recipe.
|
||||
|
||||
Page `about` = 100 characters: developers.facebook.com/docs/graph-api/reference/page/ (Meta, first-hand). The
|
||||
Page's own „Bemutatkozás" editor counts to **255** (seen on screen 2026-10-08); §1 is 99 characters, so neither
|
||||
|
||||
+128
-54
@@ -15,14 +15,18 @@ Checks (each prints its numbers; a failure exits 1):
|
||||
circle every logo pixel of a profile picture lies inside radius 0.42 x width of the centre (8 % margin).
|
||||
Shown failing first on the shape the Page has today (logo.png, full size, on a square).
|
||||
safe every pixel of a cover's CONTENT layer (text, logo, drawings — not the background) lies inside the
|
||||
area both desktop and phone show, and outside the places the profile circle covers.
|
||||
area both desktop and phone show, and outside the places the profile circle covers. Shown failing
|
||||
first on the covers as committed at a76207945e — all three were cut on phones (R-919).
|
||||
cap a cover's headline is set at a capital-letter height of at least CAP_MIN, so it stays readable
|
||||
once the phone has shrunk the cover to 412 px wide.
|
||||
size each PNG's size is read back from the file, never taken from the constants here.
|
||||
|
||||
Sizes (graded READ, second-hand — Meta's own help page needs a browser; see README.md for the URLs):
|
||||
cover shown 820 x 312 on computers, 640 x 360 on phones; built at 2 x = 1640 x 624.
|
||||
profile shown 176 px (computer) / 196 px (phone, older layouts) — built at the size where logo.png fits unscaled.
|
||||
profile circle over the cover: computer 16 px from the left, 176 px from the top (176 px circle);
|
||||
phone 24 px from the left, 196 px from the top (140 px circle).
|
||||
Sizes. The COMPUTER figures are graded READ, second-hand (820 x 312; built at 2 x = 1640 x 624; the
|
||||
profile circle 16 px from the left, 176 px from the top, 176 px across). The PHONE figures are graded
|
||||
MEASURED — see the constants block: one device (Pixel 9 emulated, 412 x 924, the mobile WEBSITE), NOT the
|
||||
Facebook app and no other width. Meta's own help page claims 2,4:1 for the phone cover and is wrong; that
|
||||
figure is what cut „saját szabályaid" down to „aját szabályaid" on every phone (R-919). The profile picture
|
||||
is shown 176 px (computer) / 196 px (phone) and is built at the size where logo.png fits unscaled.
|
||||
"""
|
||||
import base64
|
||||
import math
|
||||
@@ -65,16 +69,41 @@ CIRCLE_R = 0.42 # brief §7 A: the check radius, as a fraction of the width
|
||||
LOGO_R = 0.40 # where the logo's farthest pixel is placed (inside the check radius)
|
||||
CW, CH = 1640, 624 # cover, 2 x the 820 x 312 computer display
|
||||
SCALE = CW / 820.0
|
||||
# Phone shows the full height at 640:360 → the centre CW_PHONE pixels of the width.
|
||||
CW_PHONE = CH * 640.0 / 360.0
|
||||
|
||||
# --- the phone view: MEASURED, never read off Meta's help page (R-919) -------------------------------
|
||||
# Measured 2026-10-08 on the live Page in Chrome DevTools device mode, Pixel 9 (412 x 924, mobile user
|
||||
# agent, AFTER a reload — without one Facebook keeps serving the desktop bundle). The Page's mobile
|
||||
# header renders 412 x 274 CSS px = 1,504 : 1, so Facebook keeps the cover's FULL HEIGHT and shows only
|
||||
# the centre CW_PHONE pixels of its width. Evidence and the recipe to re-measure:
|
||||
# documentation/audits/facebook-page-setup-2026-10-08/ (README.md, C2-phone-headline-cut-closeup.png).
|
||||
# GRADE: measured on ONE device, in the mobile WEBSITE. NOT measured in the Facebook app and not at any
|
||||
# other width — which is why MARGIN below is deliberate and why the operator's app check is the
|
||||
# acceptance step, not this build.
|
||||
PHONE_HDR = (412.0, 274.0)
|
||||
CW_PHONE = CH * PHONE_HDR[0] / PHONE_HDR[1] # 938.3 of 1640 — about 351 px lost from EACH side
|
||||
PHONE_X0 = (CW - CW_PHONE) / 2.0
|
||||
MARGIN = 40
|
||||
SAFE = (int(math.ceil(PHONE_X0 + MARGIN)), MARGIN, int(CW - PHONE_X0 - MARGIN), CH - MARGIN)
|
||||
PAD = 24
|
||||
DESK_CIRCLE = (16 * SCALE, 176 * SCALE, 176 * SCALE) # x, y, diameter in cover pixels
|
||||
PHONE_K = CH / 360.0
|
||||
PHONE_CIRCLE = (PHONE_X0 + 24 * PHONE_K, 196 * PHONE_K, 140 * PHONE_K)
|
||||
QUIET = [(0, c[1] - PAD, c[0] + c[2] + PAD, CH) for c in (DESK_CIRCLE, PHONE_CIRCLE)]
|
||||
# x, y, diameter in cover pixels. The COMPUTER circle is UNCHANGED (second-hand, and left-anchored: it
|
||||
# sits against the cover's left edge, so everything to its left is under it too). On 2026-10-08 the
|
||||
# computer circle was measured to sit 16 px BELOW the cover and to overlap nothing — this stays as a
|
||||
# deliberate conservatism, not because it is still true.
|
||||
DESK_CIRCLE = (16 * SCALE, 176 * SCALE, 176 * SCALE)
|
||||
# The PHONE circle is MEASURED and CENTRED: 172 display px over the bottom middle of the header, which
|
||||
# maps to source x 637..1030, y 369..bottom. A centred circle hides its own box only — never the whole
|
||||
# width to its left, which is why QUIET can no longer be one formula for both.
|
||||
PHONE_CIRCLE = (637.0, 369.0, 393.0)
|
||||
QUIET = [(0, DESK_CIRCLE[1] - PAD, DESK_CIRCLE[0] + DESK_CIRCLE[2] + PAD, CH),
|
||||
(PHONE_CIRCLE[0] - PAD, PHONE_CIRCLE[1] - PAD, PHONE_CIRCLE[0] + PHONE_CIRCLE[2] + PAD, CH)]
|
||||
# The band every cover draws in: inside SAFE, clear of the computer circle on the left, above the phone
|
||||
# circle. Derived, so a re-measurement moves the designs with it.
|
||||
BAND = (max(SAFE[0], int(math.ceil(DESK_CIRCLE[0] + DESK_CIRCLE[2] + PAD))), SAFE[1] + 8,
|
||||
SAFE[2], int(PHONE_CIRCLE[1] - PAD) - 5)
|
||||
CAP_MIN = int(round(0.04 * CH)) # a headline capital is at least 4 % of the cover height (25 px at 624)
|
||||
# What this file believed before R-919: that a phone shows 640 x 360 of the cover. Kept ONLY so the
|
||||
# control below can convict it - a check that has never been seen failing is a wish, not a check.
|
||||
OLD_SAFE_X0 = (CW - CH * 640.0 / 360.0) / 2.0 + MARGIN
|
||||
|
||||
FAILS = []
|
||||
|
||||
@@ -211,16 +240,17 @@ def background(glow_at=(0.78, 0.25)):
|
||||
|
||||
|
||||
def content_a():
|
||||
"""Calm: the one line, centred in the band, with the domain under it."""
|
||||
c = Image.new("RGBA", (CW, CH), (0, 0, 0, 0))
|
||||
mid = (SAFE[0] + SAFE[2]) / 2.0
|
||||
size = fit_size(SLOGAN, "ExtraBold", (SAFE[2] - SAFE[0]) - 120, 66)
|
||||
mid = (BAND[0] + BAND[2]) / 2.0
|
||||
size = fit_size(SLOGAN, "ExtraBold", (BAND[2] - BAND[0]) - 20, 86)
|
||||
w1 = text_width(L1 + " ", size, "ExtraBold")
|
||||
w = text_width(SLOGAN, size, "ExtraBold")
|
||||
x0 = mid - w / 2.0
|
||||
draw_text(c, x0, 238, L1 + " ", size, "ExtraBold", TEXT1)
|
||||
draw_text(c, x0 + w1, 238, L2, size, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, mid, 300, DOMAIN, 34, "SemiBold", TEXT2, align="center")
|
||||
return c
|
||||
draw_text(c, x0, 222, L1 + " ", size, "ExtraBold", TEXT1)
|
||||
draw_text(c, x0 + w1, 222, L2, size, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, mid, 284, DOMAIN, 34, "SemiBold", TEXT2, align="center")
|
||||
return c, size
|
||||
|
||||
|
||||
def rack(d, x, y, w, h, fill, dot):
|
||||
@@ -260,59 +290,87 @@ def tile(d, cx, cy, s, kind):
|
||||
|
||||
|
||||
def content_b():
|
||||
"""The idea: the headline on the left, a home with its server and its apps on the right. Both
|
||||
columns live inside BAND -- the phone hides the cover's bottom middle behind the profile circle,
|
||||
so nothing is drawn down there any more (R-919)."""
|
||||
c = Image.new("RGBA", (CW, CH), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(c)
|
||||
tx = SAFE[0] + 24 # text stays above the profile circles (y < 316)
|
||||
draw_text(c, tx, 170, L1, 64, "ExtraBold", TEXT1)
|
||||
draw_text(c, tx, 244, L2, 64, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, tx, 290, DOMAIN, 32, "SemiBold", TEXT2)
|
||||
# the home with its server, on the right
|
||||
hx, hy, hw = 1010, 300, 220 # house body: left, top, width
|
||||
tiles = [(975, 110, "photo"), (1140, 92, "doc"), (1260, 210, "play"), (1260, 400, "lock"), (970, 520, "folder")]
|
||||
tx = BAND[0] + 12
|
||||
size = fit_size(L2, "ExtraBold", 456, 64) # the text column; the motif gets the rest
|
||||
draw_text(c, tx, 170, L1, size, "ExtraBold", TEXT1)
|
||||
draw_text(c, tx, 170 + size + 14, L2, size, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, tx, 170 + size + 14 + 46, DOMAIN, 30, "SemiBold", TEXT2)
|
||||
# the home with its server, on the right, scaled to the band's height
|
||||
hx, hy, hw = 1000, 190, 150 # house body: left, top, width
|
||||
tiles = [(935, 95, "photo"), (1065, 84, "doc"), (1195, 140, "play"), (1200, 278, "lock"),
|
||||
(928, 262, "folder"), (1155, 425, "photo"), (1110, 540, "doc")]
|
||||
for x, y, _ in tiles:
|
||||
d.line((hx + hw / 2, hy + 90, x, y), fill=(46, 168, 245, 110), width=4)
|
||||
d.rectangle((hx, hy, hx + hw, hy + 190), fill=BG1, outline=BLUE_BRIGHT, width=6)
|
||||
d.polygon([(hx - 30, hy + 4), (hx + hw / 2, hy - 120), (hx + hw + 30, hy + 4)], fill=BG1, outline=BLUE_BRIGHT)
|
||||
d.line([(hx - 30, hy + 4), (hx + hw / 2, hy - 120), (hx + hw + 30, hy + 4)], fill=BLUE_BRIGHT, width=6, joint="curve")
|
||||
rack(d, hx + 40, hy + 40, hw - 80, 110, BLUE, TEXT1)
|
||||
d.line((hx + hw / 2, hy + 70, x, y), fill=(46, 168, 245, 110), width=4)
|
||||
d.rectangle((hx, hy, hx + hw, hy + 130), fill=BG1, outline=BLUE_BRIGHT, width=6)
|
||||
d.polygon([(hx - 30, hy + 4), (hx + hw / 2, hy - 85), (hx + hw + 30, hy + 4)], fill=BG1, outline=BLUE_BRIGHT)
|
||||
d.line([(hx - 30, hy + 4), (hx + hw / 2, hy - 85), (hx + hw + 30, hy + 4)], fill=BLUE_BRIGHT, width=6, joint="curve")
|
||||
rack(d, hx + 28, hy + 28, hw - 56, 75, BLUE, TEXT1)
|
||||
for x, y, kind in tiles:
|
||||
tile(d, x, y, 92, kind)
|
||||
return c
|
||||
tile(d, x, y, 62, kind)
|
||||
return c, size
|
||||
|
||||
|
||||
def content_c():
|
||||
"""The product: the headline on the left, the demo box's own dashboard on the right. Both inside
|
||||
BAND -- the screen used to run past the phone's right crop edge and into the phone circle."""
|
||||
c = Image.new("RGBA", (CW, CH), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(c)
|
||||
tx = SAFE[0] + 24
|
||||
draw_text(c, tx, 180, L1, 52, "ExtraBold", TEXT1)
|
||||
draw_text(c, tx, 246, L2, 52, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, tx, 292, DOMAIN, 30, "SemiBold", TEXT2)
|
||||
tx = BAND[0] + 12
|
||||
size = fit_size(L2, "ExtraBold", 366, 56)
|
||||
draw_text(c, tx, 180, L1, size, "ExtraBold", TEXT1)
|
||||
draw_text(c, tx, 180 + size + 12, L2, size, "ExtraBold", BLUE_BRIGHT)
|
||||
draw_text(c, tx, 180 + size + 12 + 42, DOMAIN, 30, "SemiBold", TEXT2)
|
||||
shot = Image.open(SHOT).convert("RGBA")
|
||||
# The domain chip next to the title names the demo box; covered with the page colour (copy only, source unchanged).
|
||||
ImageDraw.Draw(shot).rectangle((684, 32, 826, 70), fill=hexc("#0B1322"))
|
||||
sw = 470
|
||||
sw = 370
|
||||
sh = int(round(sw * shot.height / shot.width))
|
||||
shot = shot.resize((sw, sh), Image.LANCZOS)
|
||||
fx, fy, b = 800, 110, 14 # frame left/top, bezel
|
||||
fx, fy, b = 814, 56, 14 # frame left/top, bezel
|
||||
d.rectangle((fx, fy, fx + sw + 2 * b, fy + sh + 2 * b), fill=BG2, outline=LINE, width=3)
|
||||
c.alpha_composite(shot, (fx + b, fy + b))
|
||||
base_y = fy + sh + 2 * b
|
||||
d.polygon([(fx - 30, base_y + 22), (fx + sw + 2 * b + 30, base_y + 22), (fx + sw + 2 * b, base_y),
|
||||
(fx, base_y)], fill=LINE)
|
||||
return c
|
||||
return c, size
|
||||
|
||||
|
||||
def check_safe(name, content):
|
||||
def cap_height(size):
|
||||
"""The height of a capital letter at this size, read from the font rather than assumed."""
|
||||
bb = font(size, "ExtraBold", 0).getbbox("S")
|
||||
return bb[3] - bb[1]
|
||||
|
||||
|
||||
def control_old_window():
|
||||
"""Draw the headline where the OLD 640 x 360 assumption put it - which is where every shipped cover
|
||||
had it - and return whether the measured check rejects that. It must. On the live Page the phone cut
|
||||
18 px off exactly this layout: „saj\u00e1t szab\u00e1lyaid" read „aj\u00e1t szab\u00e1lyaid" (R-919)."""
|
||||
c = Image.new("RGBA", (CW, CH), (0, 0, 0, 0))
|
||||
draw_text(c, OLD_SAFE_X0 + 24, 180, L1, 52, "ExtraBold", TEXT1)
|
||||
x0 = min(p[0] * 4 for p in alpha_points(c.resize((CW // 4, CH // 4)), thr=8))
|
||||
return x0 < SAFE[0], x0
|
||||
|
||||
|
||||
def check_safe(name, content, headline):
|
||||
pts = alpha_points(content.resize((CW // 4, CH // 4)), thr=8)
|
||||
xs = [p[0] * 4 for p in pts]
|
||||
ys = [p[1] * 4 for p in pts]
|
||||
bbox = (min(xs), min(ys), max(xs) + 3, max(ys) + 3)
|
||||
inside = bbox[0] >= SAFE[0] and bbox[1] >= SAFE[1] and bbox[2] <= SAFE[2] and bbox[3] <= SAFE[3]
|
||||
check(inside, "%s: content box %s inside the safe area %s" % (name, bbox, SAFE))
|
||||
hits = 0
|
||||
hits = []
|
||||
for q in QUIET:
|
||||
hits += sum(1 for x, y in zip(xs, ys) if q[0] <= x <= q[2] and q[1] <= y <= q[3])
|
||||
check(hits == 0, "%s: %d content pixels where the profile circle sits (computer or phone)" % (name, hits))
|
||||
hits.append(sum(1 for x, y in zip(xs, ys) if q[0] <= x <= q[2] and q[1] <= y <= q[3]))
|
||||
check(sum(hits) == 0, "%s: %d content pixels under the computer circle, %d under the phone circle"
|
||||
% (name, hits[0], hits[1]))
|
||||
cap = cap_height(headline)
|
||||
check(cap >= CAP_MIN, "%s: headline size %d, capital height %d px (at least %d)"
|
||||
% (name, headline, cap, CAP_MIN))
|
||||
|
||||
|
||||
def build_cover(name, content, glow):
|
||||
@@ -348,12 +406,17 @@ def preview(profiles, covers):
|
||||
'style="left:%s;top:%s;width:%s"></div>'
|
||||
% (src, safe, b64(rec), pct(dk[0], CW), pct(dk[1], CH), pct(dk[2], CW)))
|
||||
crop_l = PHONE_X0 / CW_PHONE * 100
|
||||
phone = ('<div class="phone"><div class="crop"><img src="%s" alt="" style="width:%.3f%%;left:-%.3f%%">'
|
||||
phone = ('<div class="phone" style="aspect-ratio:%.2f/%d"><div class="crop">'
|
||||
'<img src="%s" alt="" style="width:%.3f%%;left:-%.3f%%">'
|
||||
'</div><img class="ov" src="%s" alt="" style="left:%s;top:%s;width:%s"></div>'
|
||||
% (src, CW / CW_PHONE * 100, crop_l, b64(rec), pct(ph[0] - PHONE_X0, CW_PHONE),
|
||||
pct(ph[1], CH), pct(ph[2], CW_PHONE)))
|
||||
% (CW_PHONE, CH, src, CW / CW_PHONE * 100, crop_l, b64(rec),
|
||||
pct(ph[0] - PHONE_X0, CW_PHONE), pct(ph[1], CH), pct(ph[2], CW_PHONE)))
|
||||
cov.append('<section><h2>%s</h2><p class="k">Computer (820 x 312), dashed = safe area (preview only)</p>%s'
|
||||
'<p class="k">Phone (640 x 360 crop)</p>%s</section>' % (label, desk, phone))
|
||||
'<p class="k">Phone \u2014 MEASURED (R-919): the centre %d px of %d at full height, and the '
|
||||
'profile circle %d cover-pixels across - %d px on a %d px phone screen - centred. '
|
||||
'NOT 640 x 360, and NOT Meta\u2019s 2,4:1.</p>%s</section>'
|
||||
% (label, desk, round(CW_PHONE), CW, round(ph[2]),
|
||||
round(ph[2] * PHONE_HDR[0] / CW_PHONE), round(PHONE_HDR[0]), phone))
|
||||
html = """<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Facebook Page pictures</title>
|
||||
@@ -367,16 +430,19 @@ main{max-width:860px;margin:0 auto}h1{font-size:22px;margin:0 0 4px}h2{font-size
|
||||
.desk{position:relative;max-width:820px;margin-bottom:56px}.desk>img:first-child{width:100%%;display:block}
|
||||
.ov{position:absolute;border-radius:50%%;border:3px solid #fff;box-sizing:border-box}
|
||||
.safe{position:absolute;border:2px dashed #E0A93E;box-sizing:border-box;pointer-events:none}
|
||||
.phone{position:relative;width:min(360px,100%%);aspect-ratio:640/360;margin-bottom:48px}
|
||||
.phone{position:relative;width:min(412px,100%%);aspect-ratio:938/624;margin-bottom:48px}
|
||||
.crop{position:absolute;inset:0;overflow:hidden}.crop img{position:absolute;top:0;height:100%%}
|
||||
</style></head><body><main>
|
||||
<h1>Felhom.eu - Facebook pictures</h1>
|
||||
<p class="k">The phone view below is MEASURED on the live Page (Pixel 9 emulated, mobile website, 2026-10-08,
|
||||
R-919): Facebook keeps the full height and shows only the centre %d px of the %d-pixel width, with a profile
|
||||
circle over the bottom middle %d cover-pixels across. It is NOT measured in the Facebook app \u2014 check there too.</p>
|
||||
<p class="k">Profile picture: the DARK background is recommended. The logo's lettering is white with a thin navy edge,
|
||||
so on white it fades at 40 px; on dark it reads solid, as on the website. The covers below show the dark one.</p>
|
||||
<h2>Profile picture</h2>%s
|
||||
%s
|
||||
</main></body></html>
|
||||
""" % (prof_html, "".join(cov))
|
||||
""" % (round(CW_PHONE), CW, round(PHONE_CIRCLE[2]), prof_html, "".join(cov))
|
||||
path = os.path.join(OUT, "preview.html")
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
fh.write(html)
|
||||
@@ -401,12 +467,20 @@ def main():
|
||||
n = outside_circle(Image.open(p), bg, CIRCLE_R)
|
||||
check(n == 0, "%s: %d logo pixels outside the circle" % (name, n))
|
||||
built.append(p)
|
||||
print("safe-area check (computer 820x312 + phone 640x360; profile circles kept clear)")
|
||||
covers = [("cover-a.png", content_a(), (0.78, 0.25)), ("cover-b.png", content_b(), (0.70, 0.45)),
|
||||
("cover-c.png", content_c(), (0.66, 0.40))]
|
||||
print("safe-area check (computer %d x %d; the phone shows the centre %d px of %d - MEASURED, R-919)"
|
||||
% (CW, CH, round(CW_PHONE), CW))
|
||||
print(" safe %s, band %s, both profile circles kept clear" % (SAFE, BAND))
|
||||
convicted, old_x0 = control_old_window()
|
||||
print(" the pre-R-919 layout put the headline at x %d. The phone's crop edge is x %d, so %d px of it"
|
||||
% (old_x0, round(PHONE_X0), round(PHONE_X0) - old_x0))
|
||||
print(" were CUT on every phone; the measured safe edge is x %d, %d px further in (MARGIN %d)."
|
||||
% (SAFE[0], SAFE[0] - old_x0, MARGIN))
|
||||
check(convicted, "the safe check convicts the pre-R-919 layout (control)")
|
||||
covers = [("cover-a.png", content_a(), (0.50, 0.78)), ("cover-b.png", content_b(), (0.68, 0.70)),
|
||||
("cover-c.png", content_c(), (0.30, 0.74))]
|
||||
cpaths = []
|
||||
for name, content, glow in covers:
|
||||
check_safe(name, content)
|
||||
for name, (content, headline), glow in covers:
|
||||
check_safe(name, content, headline)
|
||||
cpaths.append(build_cover(name, content, glow))
|
||||
except RuntimeError as e:
|
||||
print("RENDER FAILED: %s" % e)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 97 KiB |
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user