/* Cache Pal v4 — art direction A, "cozy storybook pixel", worn in v4.1 as the FULL Franuka UI kit
   (ART_PASS_PLAN, OWNER OVERRIDE): peach 9-slice panels, octagon slots, ribbon titles, framed
   orbs, bookmark tabs, Franuka bars, buttons and icons, and the Franuka pixel faces for titles and
   labels. Loaded after app.css. Everything here is namespaced (--v4-*, .v4-*) so the old sheet
   cannot fight it; the few element rules below are deliberate overrides of app.css's dark shell.
   The kit's art is adopted from the drive by tools/adopt-art.mjs (tools/art/intake/ui.json) into
   img/v4/ui/ and img/icons/chrome/fr-*.png, and drawn at whole scales: chrome at 2x (one art
   pixel = 2 CSS px), buttons at 3x (so a 15px button is a 45px target), the verb orbs at 1.5x on
   a phone (five must fit 390px at >= 44px) and 2x on desktop. */

@font-face { font-family: 'Pixelify Sans'; src: url('../fonts/PixelifySans-Medium.ttf') format('truetype'); font-weight: 400 700; font-display: swap; }
@font-face { font-family: 'Silkscreen'; src: url('../fonts/Silkscreen-Regular.ttf') format('truetype'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Outfit'; src: url('../fonts/Outfit-Regular.ttf') format('truetype'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Outfit'; src: url('../fonts/Outfit-Bold.ttf') format('truetype'); font-weight: 700; font-display: swap; }
/* Franuka's own faces (P0128 07_FONTS): designed at 8px (text) and 17px (title), crisp at whole
   multiples: text at 16/24/32, titles at 34. */
@font-face { font-family: 'FantasyRPG Text'; src: url('../fonts/FantasyRPGtext.ttf') format('truetype'); font-weight: 400 700; font-display: swap; }
@font-face { font-family: 'FantasyRPG Title'; src: url('../fonts/FantasyRPGtitle.ttf') format('truetype'); font-weight: 400 700; font-display: swap; }

:root {
    /* The warm-wood table's outline ink (tools/art/intake/warm-wood.json: #3d2936 -> #433023). */
    --v4-ink: #433023;
    --v4-ink-soft: #6d4a33;
    --v4-cream: #FFF3DD;
    /* Franuka's peach parchment (BGbox_01A's fill): the paper every panel is made of. */
    --v4-parchment: #ffc2a1;
    --v4-parchment-dark: #daa684;
    --v4-tan: #daa684;
    --v4-peach: #ffc2a1;
    --v4-peach-deep: #ffae70;
    --v4-wood: #ca8455;
    --v4-wood-light: #daa684;
    --v4-wood-dark: #884e28;
    --v4-yellow: #ffee83;
    --v4-gold: #f0b541;
    --v4-green: #63ab3f;
    --v4-green-dark: #3b7d4f;
    --v4-red: #e64539;
    --v4-red-deep: #ad2f45;
    --v4-bang: #e64539;
    --v4-sky: #8ED2FF;
    --v4-sky-deep: #4FB6E8;
    --v4-slate: #1b1e2b;
    /* The selected-label red: AA (>= 4.5:1) on the wood dock and on peach. */
    --v4-on: #7a1626;

    --v4-font-title: 'FantasyRPG Title', 'FantasyRPG Text', 'Pixelify Sans', system-ui, sans-serif;
    --v4-font-head: 'FantasyRPG Text', 'Pixelify Sans', 'Outfit', system-ui, sans-serif;
    --v4-font-num: 'FantasyRPG Text', 'Silkscreen', 'Pixelify Sans', monospace;
    /* Long reading text keeps a legible body face (the override's guardrail). */
    --v4-font-body: 'Outfit', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --v4-emoji: 'Noto Color Emoji', 'Apple Color Emoji', 'Segoe UI Emoji', sans-serif;

    /* The kit's pieces (img/v4/ui, 1x art; a slice is in art pixels). */
    --fr-box: url('../img/v4/ui/box.png');
    --fr-box-gold: url('../img/v4/ui/box-gold.png');
    --fr-paper: url('../img/v4/ui/paper.png');
    --fr-wood: url('../img/v4/ui/wood.png');
    --fr-chip: url('../img/v4/ui/chip.png');
    --fr-sign: url('../img/v4/ui/sign.png');
    --fr-ribbon: url('../img/v4/ui/ribbon.png');
    --fr-ribbon-peach: url('../img/v4/ui/ribbon-peach.png');
    --fr-tick: url('../img/v4/ui/tick.png');
    --fr-divider: url('../img/v4/ui/divider.png');
    --fr-hand-down: url('../img/v4/ui/hand-down.png');

    --v4-hud-h: 70px;
    /* The dock's whole height on a phone, its wooden rim above and below included. */
    --v4-dock-h: 110px;
    --v4-focus: 0 0 0 3px var(--v4-yellow), 0 0 0 6px var(--v4-ink);
    --v4-outline-txt: 2px 0 0 var(--v4-ink), -2px 0 0 var(--v4-ink), 0 2px 0 var(--v4-ink), 0 -2px 0 var(--v4-ink),
        2px 2px 0 var(--v4-ink), -2px 2px 0 var(--v4-ink), 2px -2px 0 var(--v4-ink), -2px -2px 0 var(--v4-ink), 0 4px 0 var(--v4-ink);
}

/* The world behind every screen: a warm wood-plank table. The shell is exactly the window:
   no default body margin to push it past the edge. */
body {
    margin: 0;
    background: #8A552A url('../img/v4/tiles/wood.png') repeat;
    background-size: 488px 240px;
    image-rendering: pixelated;
}

.v4-app {
    position: relative;
    box-sizing: border-box;
    min-height: 100dvh;
    font-family: var(--v4-font-body);
    color: var(--v4-ink);
    -webkit-font-smoothing: antialiased;
    image-rendering: auto;
}

/* Franuka's glove: the pointer over everything that answers a tap (Hand01_Up, pre-scaled 2x: a
   CSS cursor cannot be drawn pixelated). */
.v4-app :is(a[href], button, [role="tab"], [role="button"], summary, select, label[for]):not(:disabled) { cursor: url('../img/v4/ui/cursor.png') 6 0, pointer; }

.v4-app canvas { image-rendering: pixelated; }
.v4-e { font-family: var(--v4-emoji); font-style: normal; line-height: 1; display: inline-block; }
/* A chrome icon (GameIcon): a 16px Franuka icon at a whole scale; its emoji fallback in the same box. */
.v4-ico { display: inline-block; flex: none; image-rendering: pixelated; vertical-align: middle; }
/* A chrome icon that names its thing on hover (the title rides this wrapper). */
.v4-ico-tip { display: inline-flex; flex: none; }
.v4-ico-e { width: var(--s); height: var(--s); font-size: calc(var(--s) * .72); display: inline-grid; place-items: center; }
.v4-num { font-family: var(--v4-font-num); letter-spacing: 0.02em; }
.v4-head { font-family: var(--v4-font-head); font-weight: 500; }
.v4-title { font-family: var(--v4-font-title); font-weight: 400; letter-spacing: .04em; }
.v4-outline-txt { color: var(--v4-cream); text-shadow: var(--v4-outline-txt); }
.v4-sr { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.v4-app :is(a, button, [role="tab"], [tabindex]:not([tabindex="-1"])):focus-visible { outline: none; box-shadow: var(--v4-focus); border-radius: 6px; }

/* ---- the Franuka 9-slice frames, at 2x (border-image width = 2 x the art slice) ----
   The old class names stay, so every screen that framed itself with them wears the kit:
   panel and parchment → the peach box (BGbox_01A), chip → the slim HUD plate (BannerSmall_03A),
   bubble → the box (with the continue tick), plate → the wooden signboard (BannerMedium_04A),
   wood → the plank frame (BGbox_05A), tab → the gold selected box (BGbox_02A). New: box, gold,
   paper (the torn note, BGbox_04A). A frame's border may be narrower than its art: the art then
   overlaps the padding's edge, never the layout. */
.v4-frame-panel { border: 18px solid transparent; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-frame-box { border: 14px solid transparent; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-frame-gold { border: 14px solid transparent; border-image: var(--fr-box-gold) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-frame-parchment { border: 14px solid transparent; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-frame-paper { border: 16px solid transparent; border-image: var(--fr-paper) 10 fill / 20px stretch; image-rendering: pixelated; }
.v4-frame-chip { border: 9px solid transparent; border-image: var(--fr-chip) 6 fill / 12px stretch; image-rendering: pixelated; }
.v4-frame-bubble { border: 14px solid transparent; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
/* The signboard is lettered in ink: a cream title on its tan board would fall under AA. */
.v4-frame-plate { border-style: solid; border-color: transparent; border-width: 10px 12px 14px; border-image: var(--fr-sign) 7 7 9 7 fill / 14px 14px 18px stretch;
    image-rendering: pixelated; color: var(--v4-ink) !important; text-shadow: none !important; }
.v4-frame-wood { border: 14px solid transparent; border-image: var(--fr-wood) 10 fill / 20px stretch; image-rendering: pixelated; }
.v4-frame-tab { border: 12px solid transparent; border-image: var(--fr-box-gold) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-frame-panel > *, .v4-frame-parchment > *, .v4-frame-chip > *, .v4-frame-tab > *, .v4-frame-box > *, .v4-frame-gold > * { image-rendering: auto; }

/* ---- the kit's other Franuka pieces ---- */
/* A title ribbon (BannerMedium_02C): FantasyRPG Title capitals in ink on gold, its tails hanging. */
.v4-ribbon {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px; box-sizing: border-box;
    min-height: 44px; padding: 0 4px 2px; margin: 0;
    border-style: solid; border-color: transparent; border-width: 12px 26px 16px;
    border-image: var(--fr-ribbon) 7 14 9 14 fill / 14px 28px 18px stretch; image-rendering: pixelated;
    font-family: var(--v4-font-title); font-weight: 400; font-size: 26px; line-height: 1; letter-spacing: .04em;
    color: var(--v4-ink); text-shadow: none; white-space: nowrap;
}
.v4-ribbon.peach { border-image-source: var(--fr-ribbon-peach); }
/* A gold filigree rule (Divider_07). */
.v4-divider { display: block; height: 10px; margin: 10px 0; border: 0; background: var(--fr-divider) center / 96px 10px repeat-x; image-rendering: pixelated; }
/* A Franuka bar (Slider01): the groove at 2x, its fill strip stretched to --v (0..1). */
.v4-bar { position: relative; display: block; box-sizing: border-box; height: 18px; min-width: 40px;
    border: 8px solid transparent; border-image: url('../img/v4/ui/bar-box.png') 4 fill / 8px stretch; image-rendering: pixelated; }
.v4-bar > i { position: absolute; left: -2px; top: -2px; height: 4px; width: calc((100% + 4px) * var(--v, 0)); max-width: calc(100% + 4px);
    background: url('../img/v4/ui/bar-green.png') 0 0 / 100% 100% no-repeat; image-rendering: pixelated; }
/* The fill's colour, by class: the needs (orange hunger, pink happiness, yellow energy, blue
   hygiene, purple smarts), plus red and green. */
.v4-bar.orange > i { background-image: url('../img/v4/ui/bar-orange.png'); }
.v4-bar.pink > i { background-image: url('../img/v4/ui/bar-pink.png'); }
.v4-bar.yellow > i { background-image: url('../img/v4/ui/bar-yellow.png'); }
.v4-bar.blue > i { background-image: url('../img/v4/ui/bar-blue.png'); }
.v4-bar.purple > i { background-image: url('../img/v4/ui/bar-purple.png'); }
.v4-bar.red > i { background-image: url('../img/v4/ui/bar-red.png'); }
/* The continue tick under a speech box (Tick01_Down, 3x). */
.v4-tick { display: block; width: 15px; height: 15px; background: var(--fr-tick) 0 0 / 15px 15px no-repeat; image-rendering: pixelated; }
/* A "something waits here" mark on a scene: Franuka's alert pop (P0095 Emote_alert, its rest
   frame) at 2x, a peach thought bubble with the red "!". The "!" text stays for readers that
   see no image, drawn transparent; each page keeps its own position for the mark. */
.v4-app .v4-alert.v4-alert {
    width: 32px; height: 28px; min-width: 0; min-height: 0; padding: 0; box-sizing: border-box;
    color: transparent; font-size: 0; text-shadow: none;
    background: url('../img/v4/ui/emote-alert.png') -128px 0 / 160px 28px no-repeat;
    border: 0; border-image: none; border-radius: 0; box-shadow: none; image-rendering: pixelated;
    filter: drop-shadow(0 2px 0 rgba(67, 48, 35, .55));
}
.v4-app .v4-alert.v4-alert::before, .v4-app .v4-alert.v4-alert::after { display: none; }
/* The pointing glove of a hint (Hand01_Down, 3x), bobbing over what to tap. */
.v4-hand { display: block; width: 24px; height: 33px; background: var(--fr-hand-down) 0 0 / 24px 33px no-repeat; image-rendering: pixelated;
    filter: drop-shadow(0 3px 0 rgba(67, 48, 35, .45)); }
@media (prefers-reduced-motion: no-preference) {
    .v4-tick { animation: v4-tick-bob .8s steps(2) infinite; }
    .v4-hand { animation: v4-hand-bob .9s steps(3) infinite; }
}
@keyframes v4-tick-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(2px); } }
@keyframes v4-hand-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }

/* ---- buttons (Button_02A red, Button_03A gold, Button_01A wood), at 3x: a 45px target ---- */
.v4-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px; box-sizing: border-box;
    min-height: 45px; min-width: 96px; padding: 0 4px; margin: 0;
    border-style: solid; border-color: transparent; border-width: 15px 21px 18px;
    border-image: url('../img/v4/ui/btn-gold.png') 5 7 6 7 fill / 15px 21px 18px stretch; image-rendering: pixelated;
    background: none; color: var(--v4-ink); text-decoration: none; text-shadow: none;
    font-family: var(--v4-font-head); font-weight: 400; font-size: 24px; line-height: 1; white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.v4-btn:active:not(:disabled) { border-image-source: url('../img/v4/ui/btn-gold-down.png'); }
.v4-btn.red { border-image-source: url('../img/v4/ui/btn-red.png'); color: var(--v4-cream); text-shadow: var(--v4-outline-txt); }
.v4-btn.red:active:not(:disabled) { border-image-source: url('../img/v4/ui/btn-red-down.png'); }
.v4-btn.wood { border-image: url('../img/v4/ui/btn-wood.png') 5 7 5 7 fill / 15px 21px 15px stretch; color: var(--v4-cream); text-shadow: var(--v4-outline-txt); }
.v4-btn.wood:active:not(:disabled) { border-image-source: url('../img/v4/ui/btn-wood-down.png'); }
.v4-btn:disabled { filter: saturate(.3) opacity(.6); cursor: not-allowed; }

/* ================================================================== the shell ==== */
.v4-hud {
    position: fixed;
    z-index: 60;
    left: 10px;
    right: 10px;
    top: max(10px, env(safe-area-inset-top));
    height: 60px;
    display: flex;
    align-items: center;
    gap: 6px;
    pointer-events: none;
}
.v4-hud > * { pointer-events: auto; }
.v4-hud .v4-grow { flex: 1; pointer-events: none; }
.v4-logo { display: none; }

/* The active Pal: their portrait in a framed peach box (the HUD orb), the name on a slim plate. */
.v4-palchip { display: flex; align-items: center; position: relative; text-decoration: none; color: var(--v4-ink); min-width: 0; }
.v4-ava { position: relative; flex: none; width: 58px; height: 58px; box-sizing: border-box; z-index: 2; overflow: hidden;
    border-style: solid; border-color: transparent; border-width: 0; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-ava .v4-face { position: absolute; left: 9px; top: 11px; width: 40px; height: 40px; }
.v4-ava .v4-ico { position: absolute; left: 13px; top: 13px; }
.v4-palchip .v4-nm { margin-left: -6px; padding: 0 4px 0 8px; height: 44px; box-sizing: border-box; display: flex; align-items: center;
    font-family: var(--v4-font-head); font-size: 24px; font-weight: 400; z-index: 1; min-width: 0; max-width: 34vw; }
.v4-palchip .v4-nm span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.1; }

.v4-coins { display: flex; align-items: center; gap: 4px; height: 44px; box-sizing: border-box; padding: 0 4px 0 0; text-decoration: none; color: var(--v4-ink); }
.v4-coins .v4-ico, .v4-coins .ii { margin: -6px 0 -6px -4px; }
.v4-coins b { font-family: var(--v4-font-num); font-weight: 400; font-size: 24px; line-height: 1; }
/* The bell (a sealed letter: what waits for you) and the gear: square Franuka boxes. */
.v4-ibtn { width: 46px; height: 46px; box-sizing: border-box; display: grid; place-items: center; position: relative; padding: 0; cursor: pointer; color: var(--v4-ink); text-decoration: none; background: none;
    border-style: solid; border-color: transparent; border-width: 0; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }
.v4-ibtn .v4-dot { position: absolute; top: -10px; right: -10px; min-width: 22px; height: 22px; box-sizing: border-box; padding: 0 5px; border-radius: 11px; background: var(--v4-red-deep);
    box-shadow: 0 0 0 2px var(--v4-cream), 0 0 0 4px var(--v4-ink); color: #fff; font: 400 16px/22px var(--v4-font-num); font-style: normal; text-align: center; }

.v4-dock {
    position: fixed;
    z-index: 60;
    left: 0;
    right: 0;
    bottom: 0;
    box-sizing: border-box;
    height: calc(var(--v4-dock-h) + env(safe-area-inset-bottom));
    padding: 0 6px env(safe-area-inset-bottom);
    display: flex;
    justify-content: space-around;
    align-items: center;
}
/* A dock door: its Franuka icon (2x) on the plank, lifted into a framed peach box when open, and
   its name in ink lettering (the open one in deep red), both AA on the plank's tan. */
.v4-dk { display: flex; flex-direction: column; align-items: center; gap: 2px; text-decoration: none; color: var(--v4-ink);
    min-width: 64px; min-height: 64px; justify-content: center; }
.v4-dk-ico { width: 56px; height: 56px; box-sizing: border-box; display: grid; place-items: center; border-radius: 0; }
.v4-dk .v4-lbl { font-family: var(--v4-font-head); font-size: 24px; line-height: 1; font-weight: 400; color: var(--v4-ink); text-shadow: none; }
.v4-dk.active .v4-dk-ico { transform: translateY(-4px); }
.v4-dk.active .v4-lbl { color: var(--v4-on); }
.v4-dk:hover .v4-dk-ico { transform: translateY(-2px); }
.v4-dk.active:hover .v4-dk-ico { transform: translateY(-4px); }

/* The page area between the HUD and the dock. */
/* The page area is exactly the band between the HUD and the dock, so "the middle of the page"
   is the middle of what the player can see. */
.v4-main {
    position: relative;
    box-sizing: border-box;
    margin: var(--v4-hud-h) 0 calc(var(--v4-dock-h) + env(safe-area-inset-bottom));
    padding: 10px 6px 14px;
    min-height: calc(100dvh - var(--v4-hud-h) - var(--v4-dock-h) - env(safe-area-inset-bottom));
}
.v4-side { display: none; }

/* Old pages render inside a framed dark page on a parchment mat; their lanes restyle them. */
.v4-main > .cp-shell {
    max-width: 720px;
    margin: 0 auto;
    padding: 0.75rem 0.75rem 5.5rem;   /* the v3 shell's own box: old pages were tuned inside it */
    background: var(--v4-slate);
    color: var(--cp-ink);
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    border: 10px solid var(--v4-parchment);
    outline: 3px solid var(--v4-ink);
    box-shadow: 0 0 0 9px var(--v4-wood), 0 0 0 12px var(--v4-ink), 0 6px 0 12px var(--v4-ink);
    margin-top: 12px;
    margin-bottom: 12px;
    width: calc(100% - 24px);
}
.v4-main .cp-storage-fault.write { max-width: 720px; margin: 8px auto 0; }
.v4-main > .cp-shell:has(> .cp-page:last-child > .cp-empty:last-child) {
    min-height: calc(100dvh - var(--v4-hud-h) - var(--v4-dock-h) - 168px);
}

/* A scene screen (map, room) takes the whole page; the HUD and dock float over it. The scene is
   its own stacking context (z-index 1): places lay their overlays out against it and the stall
   sheets beside it. The kit's sheets and pops leave it through the top layer (.v4-layer below). */
.v4-app:has(.v4-scene) .v4-main { margin: 0; padding: 0; min-height: 100dvh; }
.v4-app:has(.v4-scene) .v4-main > .cp-shell { max-width: none; margin: 0; padding: 0; background: none; border: 0; outline: 0; box-shadow: none; width: auto; font-family: inherit; color: inherit; }
.v4-scene { position: fixed; inset: 0; z-index: 1; overflow: hidden; }
/* The rise-in moves the page with a transform, and a transformed ancestor would become the
   scene's containing block: the map would collapse to nothing for the first 180ms. */
.v4-app:has(.v4-scene) .cp-page { animation: none; }

/* A v4 page that is not a scene: parchment panels on the table, no dark frame. */
.v4-app:has(.v4-sheetpage) .v4-main > .cp-shell { background: none; border: 0; outline: 0; box-shadow: none; padding: 0; color: var(--v4-ink); font-family: var(--v4-font-body); }

/* The top layer for the kit's sheets and pops: js/v4ui.js shows each [data-v4-layer] as a manual
   popover the moment it lands, which lifts it above every stacking context on the page (a scene's,
   a z-indexed place header's), the HUD and the dock, without moving it in the DOM: its styles
   and its place in the page stay where they were. Until it is shown (or where popovers are
   unsupported, or the script never ran) it is display: contents, never hidden: its children
   simply draw in place and their own z-indexes decide, as before. */
.v4-layer { display: contents; }
.v4-layer:popover-open {
    display: block; position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none;
    margin: 0; padding: 0; border: 0; background: none; color: inherit; overflow: visible; pointer-events: none;
}
.v4-layer:popover-open > * { pointer-events: auto; }
.v4-layer::backdrop { display: none; }

/* Toasts sit above the dock. */
.v4-app .cp-toast { bottom: calc(var(--v4-dock-h) + 16px + env(safe-area-inset-bottom)); }
/* A toast is a small peach Franuka box: ink words in the body face (a sentence), a drop below it. */
.v4-app .cp-toast {
    box-sizing: border-box; min-height: 48px; padding: 0 4px;
    background: none; color: var(--v4-ink);
    border: 14px solid transparent; border-radius: 0; border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated;
    box-shadow: none; filter: drop-shadow(0 4px 0 rgba(67, 48, 35, .45));
    font-family: var(--v4-font-body); font-size: 17px; line-height: 1.3;
}
.v4-app .cp-toast-x { color: var(--v4-ink-soft); }
.v4-app .cp-toast-again { color: var(--v4-on); font-family: var(--v4-font-num); }

/* ================================================================== desktop ==== */
@media (min-width: 1024px) {
    .v4-app {
        display: grid;
        grid-template-columns: 104px minmax(0, 1fr) 266px;
        grid-template-rows: 64px minmax(0, 1fr);
        column-gap: 14px;
        row-gap: 7px;
        padding: 8px 14px 2px;
        height: 100dvh;
        max-width: 1440px;
        margin: 0 auto;
        overflow: hidden;
    }
    .v4-app:not(:has(.v4-side-card)) { grid-template-columns: 104px minmax(0, 1fr); }

    .v4-hud { position: static; grid-column: 1 / -1; grid-row: 1; height: 64px; gap: 8px; }
    .v4-logo { display: flex; align-items: center; gap: 8px; margin-right: 18px; text-decoration: none; }
    .v4-logo canvas { width: 48px; height: 48px; margin: 0 -6px; }
    .v4-logo span { font-family: var(--v4-font-title); font-size: 34px; letter-spacing: .06em; line-height: 1; color: var(--v4-yellow); text-shadow: 3px 3px 0 var(--v4-ink); white-space: nowrap; }
    .v4-palchip .v4-nm { max-width: 260px; }

    .v4-dock {
        position: static;
        grid-column: 1;
        grid-row: 2;
        height: auto;
        margin: 7px 0 12px;
        flex-direction: column;
        justify-content: flex-start;
        gap: 10px;
        padding: 6px 0 0;
    }
    .v4-dk-ico { width: 72px; height: 72px; }
    .v4-dk-ico .v4-ico { width: 48px; height: 48px; }
    .v4-dk .v4-lbl { font-size: 24px; }

    .v4-main {
        grid-column: 2;
        grid-row: 2;
        min-height: 0;
        margin: 13px 13px 19px;
        padding: 0;
        overflow: auto;
        overscroll-behavior: contain;
        background: var(--v4-parchment);
        /* The page hangs in Franuka's plank frame (BGbox_05A, 2x), drawn outside the page box
           (outset) so the page keeps its size and its scroll; 13px out, as the old frame was. */
        border-style: solid; border-color: transparent; border-width: 0;
        border-image: var(--fr-wood) 10 / 20px / 13px stretch;
        image-rendering: pixelated;
    }
    .v4-main > * { image-rendering: auto; }
    .v4-main > .cp-shell { box-shadow: 0 0 0 9px var(--v4-wood), 0 0 0 12px var(--v4-ink); margin: 24px auto; }
    /* The framed page keeps its frame's margins on a scene page too. */
    .v4-app:has(.v4-scene) .v4-main { overflow: hidden; min-height: 0; margin: 13px 13px 19px; }
    .v4-app:has(.v4-scene) .v4-main > .cp-shell { height: 100%; }
    .v4-scene { position: absolute; }

    .v4-side {
        display: flex;
        grid-column: 3;
        grid-row: 2;
        flex-direction: column;
        gap: 12px;
        margin: 7px 0 12px;
        min-height: 0;
        overflow: auto;
    }
    .v4-side:empty { display: none; }

    .v4-app .cp-toast { bottom: 28px; }
}

/* ================================================================ v4 pages ==== */
/* Bag, Book, Settings and the place shells: parchment panels straight on the table. */
.v4-sheetpage { max-width: 760px; margin: 0 auto; padding: 0 4px 8px; color: var(--v4-ink); font-family: var(--v4-font-body); }
.v4-pagehead { display: flex; justify-content: center; margin: 4px 0 10px; }
/* A page's title hangs on the gold ribbon (BannerMedium_02C) in the Franuka title face. */
.v4-pagehead h1 {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px; margin: 0; min-height: 50px; box-sizing: border-box; padding: 0 6px 2px;
    border-style: solid; border-color: transparent; border-width: 12px 26px 16px;
    border-image: var(--fr-ribbon) 7 14 9 14 fill / 14px 28px 18px stretch; image-rendering: pixelated;
    font-family: var(--v4-font-title); font-weight: 400; font-size: 26px; letter-spacing: .04em; line-height: 1; color: var(--v4-ink); text-shadow: none;
}
.v4-pagehead h1 .v4-e { font-size: 24px; text-shadow: none; }
.v4-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(86px, 1fr)); gap: 14px 8px; justify-items: center; }
.v4-row-actions { display: flex; flex-wrap: wrap; gap: 16px 10px; justify-content: center; margin-top: 12px; }
.v4-hint { margin: 2px 0 10px; font-size: 17px; line-height: 1.35; text-align: center; }
.v4-tile {
    position: relative; display: flex; flex-direction: column; align-items: center; gap: 6px; width: 100%; max-width: 112px; min-height: 96px; box-sizing: border-box;
    padding: 4px; color: var(--v4-ink); text-decoration: none; font-family: var(--v4-font-head); font-weight: 400; font-size: 16px; line-height: 1.1; text-align: center;
}
.v4-tile .v4-e { font-size: 32px; }
.v4-tile canvas { width: 48px; height: 48px; }
.v4-tile .v4-count { position: absolute; right: -6px; bottom: -6px; min-width: 24px; height: 24px; padding: 0 5px; box-sizing: border-box; border-radius: 12px; background: var(--v4-ink); color: var(--v4-cream); font: 400 16px/24px var(--v4-font-num); }
.v4-tile.gone { filter: grayscale(.8) opacity(.75); }
.v4-subtitle { margin: 18px 0 10px; font-family: var(--v4-font-head); font-weight: 500; font-size: 19px; text-align: center; }

.v4-frame-panel .v4-big-lbl, .v4-frame-parchment .v4-big-lbl { color: var(--v4-ink); text-shadow: none; }
.v4-dk.active .v4-dk-ico { border-image: var(--fr-box) 9 fill / 18px stretch; image-rendering: pixelated; }

@media (prefers-reduced-motion: reduce) {
    .v4-app:has(.v4-scene) .cp-page { animation: none; }
    .v4-app *, .v4-app *::before, .v4-app *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

/* ================================================================== turn me upright ==== */
/* Ruling Q37: only a phone on its side (a coarse pointer in a short landscape window). */
.v4-turn { display: none; }
@media (orientation: landscape) and (max-height: 540px) and (pointer: coarse) {
    .v4-turn {
        position: fixed;
        inset: 0;
        z-index: 9500;
        display: grid;
        place-items: center;
        background: linear-gradient(#F7E2B4 0 62%, #D99E5E 62%);
        color: var(--v4-ink);
    }
    .v4-turn-in { display: flex; flex-direction: column; align-items: center; gap: 8px; }
    .v4-turn-pal { width: 144px; height: 144px; image-rendering: pixelated; }
    .v4-turn-egg { font-size: 96px; }
    .v4-turn-hint { margin: 0; font-family: var(--v4-font-head); font-size: 20px; }
    .v4-turn .v4-bubble { margin-bottom: 18px; }
}
