diff --git a/library/packet/packet-render.js b/library/packet/packet-render.js
index ac4f70533..428db099c 100644
--- a/library/packet/packet-render.js
+++ b/library/packet/packet-render.js
@@ -376,7 +376,11 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
.pray-head{position:absolute;inset-inline-end:${M}px;display:grid;grid-template-columns:repeat(${PRAY_COLS.length},${PRAY_CELL}px);
height:${PRAY_HEAD_H - 6}px;align-items:end;font:700 15px/1.2 Georgia,serif;color:#6f6a5c;text-align:center}
.pray-head span{text-wrap:balance;padding:0 0 5px;border-bottom:2px solid #3b463d}
- .fd-top{position:absolute;left:${M}px;top:${M + HEAD}px;width:${CW.toFixed(0)}px;height:${FD_TOP_H}px;overflow:hidden}
+ /* Page 1 of the Full detail list: the explanation, then the head row and the rows right under it (listPages). */
+ .fd-first{position:absolute;left:${M}px;top:${M + HEAD}px;width:${CW.toFixed(0)}px}
+ .fd-top{max-height:${FD_TOP_H}px;overflow:hidden}
+ .fd-rows{position:relative}
+ .fd-rows .pray-head{inset-inline-end:0}
.hd-r{position:absolute;right:${M}px;top:54px;font:400 19px Georgia,serif;color:#8c8170}
/* ⚠ box-sizing:border-box IS LOAD-BEARING HERE, NOT HOUSEKEEPING. Without it the row's height
is the CONTENT box and the padding + border are added OUTSIDE it, so the row renders taller
@@ -1191,7 +1195,14 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
PRAY_COLS.map((key) => `${esc(t(key))}`).join('')}`;
for (let start = 0, page = 0; start < groups.length; page += 1) {
const per = page === 0 ? FD_FIRST : PER_PAGE;
- const rowsTop = page === 0 ? FD_ROWS_TOP : M + HEAD;
+ // ★ PAGE 1'S ROWS FOLLOW THE EXPLANATION (2026-10-03). The explanation reserved a fixed FD_TOP_H and the
+ // rows sat below the reserve, so every language shorter than the tallest left a gap above the head row
+ // (0.36-1.30 in, measured in all 11). Now the explanation (at most FD_TOP_H) and the rows share one
+ // column (.fd-first), and the rows are placed inside .fd-rows, right under it. The page still holds
+ // FD_FIRST rows, sized for the full reserve, so a shorter explanation leaves the room at the page's foot.
+ // The rows stay absolutely placed (see below), only their origin moves: no row can reflow to a page.
+ const rowsTop = page === 0 ? PRAY_HEAD_H : M + HEAD;
+ const left = page === 0 ? 0 : M;
const slice = groups.slice(start, start + per);
// ★ ONE FULL-WIDTH ROW PER GROUP. The column index is gone: `top` advances by the row, and the
// three cells sit inside it. Absolute positioning is kept rather than switching to flow layout
@@ -1208,7 +1219,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
const img = src
? ``
: `