/* The printed BTJ-häftet (#3030 T9) — the landing, the archive and the reader.
   btj.css owns the palette, the type and every token used here; this sheet adds only what a
   reading surface needs. Prefix ph- ("printed häfte") throughout, deliberately NOT dh-: the two
   slices share a Swedish word and a legacy folder already, and a shared class name would be the
   third thing that has to be told apart by hand. */

.ph {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: var(--sp-5) var(--sp-4) var(--sp-8);
}

.ph-crumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-4);
    margin-bottom: var(--sp-3);
    font-size: var(--fs-sm);
}

.ph-head {
    margin-bottom: var(--sp-5);
}

.ph-head h1 {
    margin: 0;
}

.ph-count {
    margin: var(--sp-1) 0 0;
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
}

/* The landing: the intro reads as prose, the editions as a rail beside it — legacy's own shape
   (IntroBoxContent beside IntroMore). Below 55rem the rail goes under the prose rather than beside
   it, since a 260px column of dates in a phone's width is neither. */
.ph-landing {
    display: grid;
    gap: var(--sp-6);
}

@media (min-width: 55rem) {
    .ph-landing {
        grid-template-columns: minmax(0, 1fr) 20rem;
        align-items: start;
    }
}

.ph-intro > :first-child {
    margin-top: 0;
}

.ph-current {
    background: var(--c-surface);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--sp-4);
}

.ph-current h2 {
    margin-top: 0;
    font-size: var(--fs-lg);
}

.ph-noaccess {
    background: var(--c-tint-cool);
    color: var(--c-tint-cool-text);
    border-radius: var(--radius-sm);
    padding: var(--sp-3);
    margin: 0 0 var(--sp-4);
}

.ph-editions {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ph-edition + .ph-edition {
    border-top: 1px solid var(--c-border);
    margin-top: var(--sp-3);
    padding-top: var(--sp-3);
}

.ph-edition-name {
    font-weight: 600;
}

/* An edition that is listed and not linked. Muted rather than struck through or greyed to the point
   of illegibility: it exists, it is named on purpose, and it is coming. */
.ph-edition-unlinked {
    color: var(--c-text-muted);
}

.ph-edition-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1) var(--sp-3);
    margin: var(--sp-1) 0 0;
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
}

.ph-edition-state {
    background: var(--c-surface-2);
    border-radius: var(--radius-pill);
    padding: 0 var(--sp-2);
}

.ph-archive-link {
    margin: var(--sp-4) 0 0;
}

.ph-dh-link {
    margin-top: var(--sp-4);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--c-border);
    font-size: var(--fs-sm);
}

.ph-year + .ph-year {
    margin-top: var(--sp-6);
}

.ph-year-heading {
    font-size: var(--fs-lg);
    border-bottom: 1px solid var(--c-border);
    padding-bottom: var(--sp-2);
}

.ph-empty {
    color: var(--c-text-muted);
}

/* ---- the reader ------------------------------------------------------------------------- */

.ph-tools {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-bottom: var(--sp-3);
}

/* An author `display` beats the UA sheet's [hidden], so a control rendered hidden and revealed by
   script has to restate it or it is drawn on every page with no script at all — the restatement
   .copy-btn, .site-cart-menu and .site-adv-panel each need, and which HiddenAttributeConventionTests
   fails the build over. */
.ph-tools[hidden] {
    display: none;
}

.ph-tool {
    font: inherit;
    font-size: var(--fs-sm);
    min-height: var(--control-min);
    padding: 0 var(--sp-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
    background: var(--c-surface);
    color: var(--c-text);
    cursor: pointer;
}

.ph-tool[aria-pressed="true"] {
    background: var(--c-accent-soft);
    color: var(--c-accent-soft-text);
    border-color: var(--c-accent-soft);
}

.ph-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
    padding: var(--sp-2) 0;
}

.ph-nav-btn {
    min-height: var(--control-min);
    display: inline-flex;
    align-items: center;
    padding: 0 var(--sp-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-pill);
    background: var(--c-surface);
    text-decoration: none;
}

/* A control that cannot act is still drawn, so the row does not jump a button's width on the first
   and last page — and it is aria-hidden in the markup, since there is nothing to announce. */
.ph-nav-btn.is-disabled {
    color: var(--c-text-muted);
    background: var(--c-surface-2);
    cursor: default;
}

.ph-nav-position {
    margin: 0;
    color: var(--c-text-muted);
    font-size: var(--fs-sm);
}

.ph-nav-next {
    margin-inline-start: auto;
}

.ph-jump {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-basis: 100%;
    font-size: var(--fs-sm);
}

.ph-jump input {
    /* 16px is the floor: below it iOS zooms the page on focus and leaves the caller scrolled
       somewhere else (docs/DesignSystem.md). */
    font: inherit;
    font-size: var(--fs-control-min, 1rem);
    width: 5rem;
    min-height: var(--control-min);
    padding: 0 var(--sp-2);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-sm);
}

/* `color` is stated because the background is: btj.css's button floor paints white on burgundy, and
   a rule that takes the fill back and not the ink draws a white label on a white button — which is
   what this one did ("Visa" invisible) from the floor's arrival until this line. */
.ph-jump button {
    font: inherit;
    min-height: var(--control-min);
    padding: 0 var(--sp-3);
    border: 1px solid var(--c-border);
    border-radius: var(--radius-pill);
    background: var(--c-surface);
    color: var(--c-text);
    cursor: pointer;
}

/* The page itself. The ground is dark-ish so a white A4 page reads as a sheet rather than as more
   background — the one place in the shop that wants it, and the reason it is a container of its own
   rather than the page's. */
.ph-stage {
    display: flex;
    /* flex-start on BOTH axes, always, and the centring is the sheet's own auto margins below. A
       flex container that centres an overflowing item puts the overflow on both sides, and the start
       side of a scroll container is unreachable — so a zoomed page could not be dragged back to its
       own corner. The first fix toggled the container between center and flex-start on `is-zoomed`,
       and that is a toggle at exactly 100 %: at 105 % a page five percent wider than the fit is still
       narrower than the stage, and it snapped from the middle to the left edge. Reported as "at 105 %
       it moves to the left." Auto margins answer both cases with no toggle: they absorb free space
       when there is any and resolve to zero when there is none, per axis. */
    justify-content: flex-start;
    align-items: flex-start;
    gap: var(--sp-2);
    background: var(--c-surface-4);
    border-radius: var(--radius);
    padding: var(--sp-3);
    overflow: auto;
    scroll-margin-top: 6rem;
}

.ph-page {
    display: block;
    height: auto;
    max-width: 100%;
    /* Centred in the stage while it fits, flush to the start edge once it does not — see .ph-stage.
       Every margin is auto because a lone page is centred on both axes; a spread takes its two inner
       margins back below, or two auto gaps between the pages would push the pair apart. */
    margin: auto;
    background: var(--c-surface);
    box-shadow: var(--shadow);
}

.ph-reader.has-spread .ph-page:first-child { margin-inline-end: 0; }
.ph-reader.has-spread .ph-page + .ph-page { margin-inline-start: 0; }

.ph-page[hidden] {
    display: none;
}

/* Zoom: the image stops fitting the stage and the stage scrolls. width is set by the script from
   the profile it asked for, so nothing here decides a pixel size. */
.ph-reader.is-zoomed .ph-page {
    max-width: none;
    flex-shrink: 0;
}

.ph-reader.is-zoomed .ph-stage {
    /* The affordance for grab-to-pan, which is the whole statement that a zoomed page can be moved.
       The browser gives a finger that gesture for free and gives a mouse nothing, so without the
       cursor there is no way to find out it is there. */
    cursor: grab;
}

/* EVERY SURFACE IS FITTED, and the document page is the one that has to be told its height. The
   phone layer and helskärm size the stage by flex against a screen they own; here the stage sits
   in a scrolling document and would otherwise be as tall as its page — which is what the first cut
   shipped, and what it read as: 812 x 1148 in a 900px window, the top half of a sheet, a document
   to scroll and a Nästa sida that scrolled away with it. A reader shows a page. So the stage takes
   the viewport less the toolbar and the foot nav (measured: 68 + 112 plus the gaps), and
   `fitWidth` fits the sheet into it exactly as it does into the phone's.

   A DEFINITE height, not a maximum: `fitWidth` reads `clientHeight`, and on a stage whose height
   is its content that is the image's own height — a bound that is whatever the thing it bounds
   happens to be. min-height is the floor under a short window, where a page has to be scrolled
   rather than drawn at 120px. */
.ph-reader.is-fitted:not(.is-immersive):not(:fullscreen) .ph-stage {
    height: calc(100vh - 13rem);
    min-height: 20rem;
}

/* THE ONE RULE UNDER TWO DEFECTS. `.ph-reader-body` is `align-items: flex-start` for the document
   page, where the contents rail must not stretch to the height of a 1200px sheet. Inherited into a
   fitted layout it does the opposite of what the layout is for: in helskärm (a row) the canvas
   never stretched to the body's height, so the stage was as tall as its page and helskärm showed
   HALF a page; on the phone (a column) the canvas never stretched to the body's width, so a zoomed
   stage grew to the width of its image instead of scrolling — and `fitWidth`, measuring that
   widened stage, fed the growth back into the next size. Measured, not reasoned: stage 1389px tall
   in a 900px screen, stage 1047px wide in a 390px one. */
.ph-reader.is-fitted .ph-reader-body { align-items: stretch; }

/* With the stage fitted nothing scrolls past, so the reason the nav was drawn twice — a control at
   one end of a tall page is one you scroll to find — is gone on both fitted surfaces, not only the
   phone's. */
.ph-reader.is-fitted .ph-nav-head { display: none; }

.ph-reader.is-panning .ph-stage {
    cursor: grabbing;
}

/* A drag across a page must move the page, not select text under it or start a native image drag.
   The immersive reader says this already for its own gestures; a zoomed page says it at every
   width, because that is where the grab is. */
.ph-reader.is-zoomed .ph-page {
    -webkit-user-drag: none;
    user-select: none;
}

/* Two pages side by side. Each takes half the stage minus the gap, so a spread never overflows the
   column it is in — which is what makes the profile question a matter of the RENDERED width and not
   of the viewport's. */
.ph-reader.has-spread:not(.is-zoomed) .ph-page {
    max-width: calc(50% - var(--sp-1));
}

.ph-thumbs {
    display: flex;
    gap: var(--sp-2);
    overflow-x: auto;
    padding: var(--sp-3) 0;
}

/* Same restatement, and here it matters twice: the strip is hidden until it is opened AND empty
   until then, so without this it is an unexplained gap under the page on every load. */
.ph-thumbs[hidden] {
    display: none;
}

.ph-thumb {
    flex: 0 0 auto;
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    padding: 0;
    cursor: pointer;
}

.ph-thumb img {
    display: block;
    width: 5rem;
    height: auto;
    background: var(--c-surface);
}

.ph-thumb[aria-current="true"] {
    border-color: var(--c-accent);
}

/* Keep the index beside the sheet, and the controls within reach while reading. */
.ph-tools {
    position: sticky;
    top: 0;
    z-index: 5;
    padding: var(--sp-2);
    margin: 0;
    background: var(--c-surface);
    border: 1px solid var(--c-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.ph-tool {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    font-weight: 500;
}

.ph-tool[hidden] { display: none; }
.ph-tool:disabled { opacity: .4; cursor: default; }
.ph-tool:hover:not(:disabled) { background: var(--c-surface-2); }
.ph-tool:focus-visible, .ph-contents a:focus-visible, .ph-contents summary:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 3px;
}
.ph-tool-group { display: flex; gap: 2px; padding: 3px; border-radius: var(--radius-sm); background: var(--c-surface-2); }
.ph-tool-group .ph-tool { border-color: transparent; background: transparent; }
.ph-tool-group .ph-tool[aria-pressed="true"] { background: var(--c-surface); color: var(--c-accent); box-shadow: var(--shadow); }
.ph-tool-icon { font-size: 1.4rem; width: 2.75rem; padding: 0; }

/* Zoom, as one range rather than three buttons. min-height on the input is what gives a thumb a
   44px target — the track itself is a few pixels tall, and a control nobody can grab is the same
   defect as a control that is not drawn. */
.ph-zoom { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.ph-zoom-label { font-size: var(--fs-sm); color: var(--c-text-muted); }
.ph-zoom-range {
    flex: 1 1 8rem;
    min-width: 5rem;
    max-width: 14rem;
    min-height: var(--control-min);
    accent-color: var(--c-accent);
}
.ph-zoom-value {
    min-width: 3.5rem;
    font-size: var(--fs-sm);
    color: var(--c-text-muted);
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.ph-zoom-range:focus-visible { outline: 2px solid var(--c-accent); outline-offset: 3px; }
.ph-fullscreen { margin-inline-start: auto; }
.ph-reader-body { display: flex; gap: var(--sp-3); align-items: flex-start; }
.ph-reader-canvas { min-width: 0; flex: 1; }
/* align-self, because the body stretches its items now (a fitted canvas has to fill the row) and a
   rail that stretched with it would be a card as tall as the stage with twelve lines in it. */
.ph-contents { flex: 0 0 15rem; align-self: flex-start; position: sticky; top: 5.5rem; margin-top: var(--sp-3); border: 1px solid var(--c-border); border-radius: var(--radius); background: var(--c-surface); }
.ph-contents summary { padding: var(--sp-3); font-weight: 600; cursor: pointer; }
.ph-contents summary span { display: block; margin-top: var(--sp-1); color: var(--c-text-muted); font-size: var(--fs-sm); font-weight: 400; }
.ph-contents nav { max-height: calc(100vh - 15rem); overflow-y: auto; padding: 0 var(--sp-2) var(--sp-2); }
.ph-contents a { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); padding: var(--sp-2); border-radius: var(--radius-sm); color: var(--c-text); text-decoration: none; font-size: var(--fs-sm); }
.ph-contents a:hover { background: var(--c-surface-2); }
.ph-contents a[aria-current="location"] { background: var(--c-accent-soft); color: var(--c-accent-soft-text); font-weight: 600; }
.ph-contents .ph-subsection { padding-inline-start: var(--sp-4); }
.ph-section-page { color: var(--c-text-muted); font-variant-numeric: tabular-nums; }
/* HELSKÄRM IS A READER THAT OWNS THE SCREEN, so the sheet is fitted to the screen — the same
   question the phone answers, asked by a different surface. It used to keep the document layout: the
   page was sized to the CANVAS WIDTH and ran off the bottom, so fullscreen showed *less* of a page
   than the window did, and zoom started from a page that already did not fit. `overflow: hidden`
   with the stage as the one scroll container, because two nested ones mean a drag that sometimes
   moves the page and sometimes moves the reader. */
.ph-reader:fullscreen { background: var(--c-surface); overflow: hidden; padding: var(--sp-3); display: flex; flex-direction: column; }
.ph-reader:fullscreen .ph-tools { top: 0; }
.ph-reader:fullscreen .ph-reader-body { flex: 1; min-height: 0; }
.ph-reader:fullscreen .ph-reader-canvas { display: flex; flex-direction: column; min-height: 0; }
.ph-reader:fullscreen .ph-stage { flex: 1; min-height: 0; }
.ph-reader:fullscreen .ph-contents { max-height: 100%; }
.ph-reader:fullscreen .ph-contents nav { max-height: none; min-height: 0; }

/* ---- the fitted sheet, on whichever surface is fitted --------------------------------------
   Both the phone reader and helskärm fit the page to the screen, so the rule is stated once and
   keyed on the class the script sets for either (`is-fitted`). It carries `:not(.is-zoomed)` rather
   than being overridden later, which is the lesson of the defect it replaces: the immersive fit was
   (0,3,0) in a late media query and out-specified `.ph-reader.is-zoomed .ph-page` (0,2,0), so zoom
   silently drew at the fitted size — a state pair that must not overlap is better spelled as one
   selector that excludes the other than as two that race.

   NO `width` OR `height` DECLARATION HERE, and that is the load-bearing part. The script sizes the
   sheet by writing the width and height ATTRIBUTES, which are presentational hints — and any author
   `width: auto` beats a hint, at which point the image takes its natural raster width clamped by the
   maximum: always the fit, whatever the script wrote. Measured: 50 % on the phone drew 390px, the
   same as 100 %, while the desktop page — whose rule declares no width — drew 406px of 812. So the
   attributes size the sheet, the maxima are only the net for the frame before the script has
   measured, and `object-fit: contain` keeps that net from distorting the page while it holds. */
.ph-reader.is-fitted:not(.is-zoomed) .ph-page {
    max-height: 100%;
    max-width: 100%;
    object-fit: contain;
}

/* OVER THE SHEET, PINCH ZOOMS THE PAGE — on every surface, now that every surface is fitted. The
   browser's own pinch is taken away on the stage and nowhere else: `pan-y` leaves vertical scrolling
   alone (the document still scrolls past the stage at a desk) and hands the script the horizontal
   swipe and the pinch. Zoomed, the script owns the pan as well: a browser that pans while we pinch
   produces both at once, and the drift is worse than the momentum is worth. */
.ph-reader.is-fitted .ph-stage { touch-action: pan-y; }
.ph-reader.is-fitted.is-zoomed .ph-stage { touch-action: none; }
.ph-nav-btn { border-radius: var(--radius-sm); }
.ph-thumbs { max-height: 12rem; background: var(--c-surface-2); }

@media (max-width: 800px) {
    .ph-reader-body { display: block; }
    .ph-contents { position: static; }
    .ph-contents nav { max-height: 16rem; }
    .ph-tools { gap: var(--sp-1); }
    .ph-tool { padding-inline: var(--sp-2); }
    .ph-fullscreen { margin-inline-start: 0; }
}

/* ---- the phone: the reader IS the screen ------------------------------------------------------

   A häfte page is a whole A4 sheet of editorial layout, and the shop's ordinary page shape — chrome,
   crumbs, an h1, a toolbar, a nav row, the sheet, a second nav row, a footer — leaves about a third
   of a phone for the thing being read, with the rest of it a scroll away. So below 48rem the reader
   is a fixed layer over the page: one bar at the top, the sheet fitted to what is left, one bar at
   the bottom, and a swipe or a tap at either edge to turn. That is what an e-reader is, and it is an
   ADDITION rather than a port — the legacy tier hands the caller a PDF and lets the phone's own
   viewer do this, so there is no legacy behaviour to be faithful to and the shape has to earn its
   place on its own (docs/PrintedBookletAnatomy.md §5.8).

   EVERY RULE HERE IS GATED ON .is-immersive, WHICH ONLY THE SCRIPT SETS. No script means no fixed
   layer, no swipe and no tap zones — it means the ordinary document above, which is still a whole
   reader: an image, prev/next as real links and a jump form (#3149's one-statement rule). The width
   in the query below is the one haftet-flipbook.js matches on; if one moves, move both.

   TWO CONDITIONS, NOT ONE. A phone held sideways is 844 x 390: wider than the breakpoint and with
   less height than the document layout's chrome alone — measured, the sheet began at y=496 in a
   390px viewport, entirely below the fold. So height decides too: under 32rem of it there is no
   room for a document around a page, whatever the width says. */
@media (max-width: 47.99rem), (max-height: 31.99rem) {
    /* Nothing scrolls behind a layer that covers everything — body.has-open-drawer's rule, one
       surface further in, and stated inside the query for the same reason: above the breakpoint the
       class is taken off anyway, and an unconditional rule would freeze a desktop page if it were
       ever left on. */
    body.ph-immersive { overflow: hidden; }

    /* 100dvh, not 100vh: on iOS Safari the large viewport is what 100vh reports, so the bottom bar
       would sit under the URL chrome until the caller scrolled — and here nothing scrolls, so it
       would never come back. The vh line is the fallback for a browser that does not know dvh. */
    .ph-reader.is-immersive {
        position: fixed;
        inset: 0;
        z-index: 60;   /* over .site-header's 50, which is what it covers */
        display: flex;
        flex-direction: column;
        height: 100vh;
        height: 100dvh;
        margin: 0;
        padding: 0;
        background: var(--c-surface-4);
        overscroll-behavior: contain;
    }

    /* The top bar. Absolute rather than a flex row, so hiding the chrome gives its height back to
       the sheet instead of leaving a gap where it was. */
    .ph-reader.is-immersive .ph-tools {
        position: absolute;
        inset: 0 0 auto 0;
        z-index: 4;
        gap: var(--sp-1);
        flex-wrap: nowrap;
        align-items: center;
        margin: 0;
        padding: var(--sp-1) var(--sp-2);
        border: 0;
        border-bottom: 1px solid var(--c-border);
        border-radius: 0;
        transition: transform .18s ease, opacity .18s ease;
    }

    /* A glyph-only toolbar at this width, because there is room for six words or for six controls.
       The labels are CLIPPED rather than display:none — the <span> IS the button's accessible name,
       and hiding it outright leaves a control that announces as nothing at all. */
    .ph-reader.is-immersive .ph-tool > span:not([data-ph-fullscreen-label]),
    .ph-reader.is-immersive .ph-zoom-label {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        padding: 0;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
    }

    .ph-reader.is-immersive .ph-tool { padding-inline: var(--sp-2); }

    /* The range is the one control on this bar that wants whatever is left: the two glyph buttons
       and the readout are fixed, so it takes the slack rather than a width of its own. */
    .ph-reader.is-immersive .ph-zoom { flex: 1; min-width: 0; }
    .ph-reader.is-immersive .ph-zoom-range { max-width: none; }

    /* NOTHING here gives .ph-exit a display. It is rendered `hidden` and un-hidden by the script,
       and an author display on it would beat the UA sheet's [hidden] and draw it on every page —
       .ph-tool already carries both the inline-flex and the [hidden] restatement that pairs with it
       (HiddenAttributeConventionTests). */

    /* Two A4 pages side by side on a phone is two pages nobody can read, so the choice is not
       offered — and the script forces the single-page state on the way in rather than leaving the
       control pressed and invisible. Fullscreen goes for the opposite reason: this IS fullscreen,
       and on iOS Safari Element.requestFullscreen does not exist anyway. */
    .ph-reader.is-immersive [data-ph-spread],
    .ph-reader.is-immersive .ph-group-view,
    .ph-reader.is-immersive .ph-fullscreen { display: none; }

    .ph-reader.is-immersive .ph-reader-body,
    .ph-reader.is-immersive .ph-reader-canvas {
        display: flex;
        flex-direction: column;
        flex: 1;
        min-height: 0;
        gap: 0;
        margin: 0;
    }

    /* Both bars are absolute, so the sheet would otherwise run underneath them. The room they need
       is reserved here rather than by leaving them in the flow — which is the whole point: with the
       chrome hidden the padding goes and the sheet grows into it, instead of a gap sitting where a
       bar used to be. */
    .ph-reader.is-immersive .ph-reader-body {
        padding-block: calc(var(--control-min) + var(--sp-2));
        transition: padding .18s ease;
    }

    .ph-reader.is-immersive.is-bare .ph-reader-body { padding-block: 0; }

    /* The sheet takes everything the two bars leave. min-height: 0 is what lets it: a flex item's
       default minimum is its content, so without it a tall image pushes the bottom bar off screen
       rather than being fitted into what is left. */
    .ph-reader.is-immersive .ph-stage {
        flex: 1;
        min-height: 0;
        padding: 0;
        border-radius: 0;
        background: var(--c-surface-4);
    }


    /* The fit itself is `.ph-reader.is-fitted:not(.is-zoomed) .ph-page`, stated once for this
       surface and for helskärm. What is left here is about the GESTURE, not the picture: without it
       a long press on the sheet offers to save or drag the image, over a press the reader made to
       turn a page. */
    .ph-reader.is-immersive .ph-page {
        -webkit-user-drag: none;
        -webkit-touch-callout: none;
        user-select: none;
    }

    .ph-reader.is-immersive .ph-nav-foot {
        position: absolute;
        inset: auto 0 0 0;
        z-index: 4;
        flex-wrap: nowrap;
        align-items: center;
        gap: var(--sp-2);
        padding: var(--sp-1) var(--sp-2) max(var(--sp-1), env(safe-area-inset-bottom));
        background: var(--c-surface);
        border-top: 1px solid var(--c-border);
        transition: transform .18s ease, opacity .18s ease;
    }

    /* Words, not glyphs, and truncated rather than wrapped: "Föregående sida" is what the control
       has always said and a phone is not a reason to rename it, but a bar that grows a second line
       takes that line off the sheet. */
    .ph-reader.is-immersive .ph-nav-btn {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: var(--fs-sm);
    }

    .ph-reader.is-immersive .ph-nav-position {
        flex: 1;
        text-align: center;
        font-variant-numeric: tabular-nums;
    }

    /* The jump form is the no-script way to page 84, and here there is script: the thumbnail strip
       is the same job done with one drag instead of a tap, a keyboard and two more taps. Two controls
       for one choice is one too many, so the one that costs less on this surface stays. */
    .ph-reader.is-immersive .ph-jump { display: none; }

    /* Innehåll floats over the sheet instead of taking a column of it: closed it is a chip in the
       corner, open it is a panel, which is what <details> does natively — no second state to keep in
       sync, and no script. */
    .ph-reader.is-immersive .ph-contents {
        position: absolute;
        top: calc(var(--control-min) + var(--sp-2) + var(--sp-2));
        left: var(--sp-2);
        z-index: 3;
        flex: none;
        width: auto;
        max-width: calc(100% - var(--sp-4));
        max-height: calc(100% - 12rem);
        margin: 0;
        overflow: hidden;
        display: flex;
        flex-direction: column;
        box-shadow: var(--shadow);
        transition: opacity .18s ease;
    }

    .ph-reader.is-immersive .ph-contents:not([open]) { border-radius: var(--radius-pill); }
    .ph-reader.is-immersive .ph-contents summary { padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); }
    .ph-reader.is-immersive .ph-contents:not([open]) summary span { display: none; }
    /* min-height: 0 for the same reason the stage needs it — a flex item's default minimum is its
       content, so without it the list refuses to shrink and the panel grows past the max-height that
       was meant to make it scroll. */
    .ph-reader.is-immersive .ph-contents nav { max-height: none; min-height: 0; overflow-y: auto; }

    /* The picker sits above the bottom bar, over the sheet, and stays a horizontal strip — vertical
       would be a second layout of a control that already has one. */
    .ph-reader.is-immersive .ph-thumbs {
        position: absolute;
        inset: auto 0 calc(var(--control-min) + var(--sp-2)) 0;
        z-index: 3;
        max-height: 8rem;
        padding: var(--sp-2);
        background: var(--c-surface);
        border-top: 1px solid var(--c-border);
    }

    .ph-reader.is-immersive .ph-thumb img { width: 3.5rem; }

    /* Chrome away — a tap in the middle of the sheet. The bars slide off their own edges rather than
       fading in place, so what is uncovered is uncovered from the outside in and the sheet is never
       partly behind a half-transparent bar. visibility, not display: a bar that is display:none has
       no height to animate away from, and pointer-events keeps a hidden bar from taking the tap that
       would bring it back. */
    .ph-reader.is-immersive.is-bare .ph-tools {
        transform: translateY(-100%);
        opacity: 0;
        pointer-events: none;
    }

    .ph-reader.is-immersive.is-bare .ph-nav-foot {
        transform: translateY(100%);
        opacity: 0;
        pointer-events: none;
    }

    .ph-reader.is-immersive.is-bare .ph-contents,
    .ph-reader.is-immersive.is-bare .ph-thumbs {
        opacity: 0;
        pointer-events: none;
    }

    /* What the reader covers. Hidden as well as marked inert by the script, because a fixed layer
       over a document does not stop that document laying out — an h1 and a footer under the layer
       still make the body as tall as they are, which on iOS is a page that rubber-bands to nothing. */
    body.ph-immersive .ph-reader-page > .ph-crumbs,
    body.ph-immersive .ph-reader-page > .ph-head { display: none; }
}
