/*
 * PDF Whiffle - app-specific styles.
 *
 * Framework/Tabler CSS arrives via the Catanari.Framework RCL (/css/framework.css); this file
 * only carries styles owned by PDF Whiffle's own app-local controls (PP6). Keep it small:
 * pages never style anything - controls do.
 */

/* ── WhiffleDocCard ─────────────────────────────────────────────────────────
   The card look itself is stock framework/Tabler (PV5) - nothing here styles it.
   The one exception: Card.imageTop enforces no aspect ratio, so a portrait scan
   next to a landscape one would make the grid jitter in height. Pin the ratio. */

.pp-doc-card .card-img-top {
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

/* Mobile-first: every button on a page body is a >=44px touch target (WCAG 2.5.5). Stock
   Tabler buttons land at 40px and the ghost icon buttons at 40x40 - the same correction the
   crop toolbar and page tiles already make below, applied once for the whole app rather than
   per-control (this is a phone-first tool; there are no mouse-only screens). */
.page-body .btn {
    min-width: 44px;
    min-height: 44px;
}

/* ── WhiffleCropEditor ──────────────────────────────────────────────────────
   Full-viewport crop/rotate overlay. Sits above Tabler's modals (z-index 1055)
   because it IS the modal surface while it's up. */

.pp-crop-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background: rgba(15, 23, 42, 0.97);
}

.pp-crop-canvas {
    flex: 1 1 auto;
    min-height: 0;          /* let the canvas shrink inside the flex column */
    width: 100%;
    max-width: 100%;
    max-height: 100%;
    touch-action: none;     /* we own every gesture on this surface */
    cursor: crosshair;
}

.pp-crop-toolbar {
    flex: 0 0 auto;
    padding: 0.75rem;
    /* Keep the buttons clear of the home indicator / rounded corners on phones. */
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: rgba(15, 23, 42, 0.98);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.pp-crop-toolbar-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

/* Mobile-first: every control in the crop toolbar is a >=44px touch target. */
.pp-crop-toolbar-inner .btn {
    min-width: 44px;
    min-height: 44px;
}

/* Push the settle buttons (Cancel/OK) away from the tools on wider screens; on a
   phone the row simply wraps and this collapses to nothing. */
.pp-crop-toolbar-inner .pp-crop-settle {
    margin-inline-start: auto;
}

/* "Save and take another" (camera-plus) closes the row on the right - the batch-scanning
   shortcut sits under the thumb, next to OK, because it IS an OK with a follow-on. */
.pp-crop-toolbar-inner .pp-crop-another {
    /* Icon-only: no label to pad around, so keep it square rather than letting the
       button's text padding widen it past its neighbours. */
    padding-inline: 0;
}

/* Phone widths: all six buttons must sit on ONE row - wrapping puts the settle buttons on
   a second line and the row reads as two toolbars. Slightly smaller targets are the
   price of fitting a 320-390px screen (Rob's call, 2026-07-23: fit beats 44px here). */
@media (max-width: 575.98px) {
    .pp-crop-toolbar {
        padding: 0.5rem;
        padding-bottom: calc(0.5rem + env(safe-area-inset-bottom, 0px));
    }

    .pp-crop-toolbar-inner {
        flex-wrap: nowrap;
        gap: 0.375rem;
    }

    .pp-crop-toolbar-inner .btn {
        min-width: 40px;
        min-height: 40px;
        /* Tighter than the 0.5rem it was: the sixth button (save-and-take-another) has to
           fit a 320px screen alongside the two labelled settle buttons. */
        padding-inline: 0.375rem;
        font-size: 0.8125rem;
    }

    /* The labelled buttons are the only flexible width left, so let them give first
       rather than pushing the icon tools off the edge. */
    .pp-crop-toolbar-inner .pp-crop-settle {
        flex: 0 1 auto;
        min-width: 0;
    }
}

/* ── Page grid cell ─────────────────────────────────────────────────────────
   Each grid column holds [insert-point][tile] stacked (EditorPage.rebuild): the
   rail can't be a sibling column or it would eat a whole 6-wide slot and break
   the 2-up phone layout. The cell is a column so both stretch to its width. */

.pp-cell {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.pp-cell > .card {
    flex: 1 1 auto;
}

/* ── WhiffleInsertPoint ─────────────────────────────────────────────────────
   The "add a page here" rail above each tile. Quiet until you touch it, but
   always a >=44px target (WCAG 2.5.5) - this is a phone-first tool and there is
   no hover to reveal it with. */

.pp-insert-zone {
    display: flex;
}

.pp-insert-rail {
    /* Override the app-wide 44px min-WIDTH: the rail spans its cell instead. */
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 44px;
    margin-bottom: 0.25rem;
    padding: 0;
    color: var(--tblr-secondary);
    background: transparent;
    border: 1px dashed var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    opacity: 0.55;
    transition: opacity 0.12s, color 0.12s, border-color 0.12s;
}

.pp-insert-rail:hover,
.pp-insert-rail:focus-visible {
    color: var(--tblr-purple);
    border-color: var(--tblr-purple);
    opacity: 1;
}

/* A drag over the gap lights the rail it will insert at. */
.pp-insert-zone.cat-file-drop-over .pp-insert-rail {
    color: var(--tblr-purple);
    border-style: solid;
    border-color: var(--tblr-purple);
    opacity: 1;
}

/* ── WhiffleAddBlock ────────────────────────────────────────────────────────
   The card-sized add tile that ends the grid (PV9). Sized to match a page tile
   so the grid reads as "...pages, then the slot for the next one". */

.pp-add-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.5rem;
}

.pp-add-zone {
    display: flex;
    flex: 1 1 auto;
}

/* Match .pp-tile-thumb's height so the add tile lines up with the page tiles. */
.pp-add-dropzone {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 160px;
    padding: 0.75rem;
    text-align: center;
}

/* Two full-width labelled buttons stacked. They MUST wrap: side-by-side they don't fit a
   2-up tile on a 390px phone (~183px wide) and "Title page" gets clipped off the edge. */
.pp-add-actions {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.pp-add-actions .btn {
    width: 100%;
    min-height: 44px;
}

/* WhiffleCameraButton wraps its <button> in a span, so the span needs the width too. */
.pp-add-actions .pp-camera {
    display: block;
    width: 100%;
}

/* ── WhifflePageTile ────────────────────────────────────────────────────────
   One page of a document: thumbnail, page-number badge, action row. */

/* The thumbnail is a <button> (tap it to edit the page), so the button chrome
   has to go: it must still look like the plain image well it replaced. */
.pp-tile-thumb {
    position: relative;
    /* The size context the draft mark scales against (its font-size is in cqw), so the mark
       fits a narrow 2-up phone tile as happily as a wide desktop one. */
    container-type: inline-size;
    display: flex;
    align-items: center;
    justify-content: center;
    /* A consistent tile height regardless of page aspect, so the grid stays even. */
    height: 160px;
    width: 100%;
    padding: 0.5rem;
    background: rgba(0, 0, 0, 0.25);
    border: 0;
    border-radius: var(--tblr-border-radius) var(--tblr-border-radius) 0 0;
    overflow: hidden;
    cursor: pointer;
}

.pp-tile-thumb:focus-visible {
    outline: 2px solid var(--tblr-purple);
    outline-offset: -2px;
}

.pp-tile-img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;   /* never crop or distort the page in its own thumbnail */
    display: block;
}

.pp-tile-badge {
    position: absolute;
    top: 0.375rem;
    inset-inline-start: 0.375rem;
}

/* The title-page marker (PV8) sits opposite the number so the two never collide. */
.pp-tile-kind {
    position: absolute;
    top: 0.375rem;
    inset-inline-end: 0.375rem;
}

/* ── Draft mark (PV10) ──────────────────────────────────────────────────────
   The tile's copy of the DRAFT watermark. This is CSS, while the SAME mark in the
   built PDF is a canvas composite (Services/DraftStamp.ts). The asymmetry is
   deliberate: on a tile the mark is decoration that must appear and vanish the
   instant the switch flips, and re-encoding every thumbnail to manage that would
   be absurd; in the PDF it has to be real pixels. Same look, different medium.

   Never baked into the stored image either way - that is what keeps it undoable. */

.pp-draft-overlay {
    position: absolute;
    /* Centre on the thumbnail, then rotate - matching DraftStamp's -30deg. */
    top: 50%;
    inset-inline-start: 50%;
    transform: translate(-50%, -50%) rotate(-30deg);
    display: none;
    font-weight: 700;
    /* Scales with the tile and clamps at both ends: a fixed size would overflow a 2-up
       tile on a 390px phone (~183px wide) exactly as the canvas stamp's first cut did. */
    font-size: clamp(1rem, 13cqw, 2.25rem);
    letter-spacing: 0.08em;
    line-height: 1;
    white-space: nowrap;
    color: rgba(220, 40, 40, 0.32);
    -webkit-text-stroke: 1px rgba(220, 40, 40, 0.22);
    /* The thumbnail is a <button>: without this the mark would eat its own clicks. */
    pointer-events: none;
    user-select: none;
}

.pp-draft-overlay.pp-draft-on {
    display: block;
}

/* The four tile actions, as a GRID rather than a squeezed row.

   History worth keeping: v2 stage 07 tried to fit all four on one line inside a 2-up
   390px tile. The arithmetic never worked - the card-body's `p-2` leaves ~165px of
   content box and four 44px touch targets need 176px - so the row bled into the card
   padding with a gap that collapsed to ZERO. The buttons then sat edge-to-edge, ran to
   the tile's outer edge, and the two tiles' rows read as one undifferentiated strip of
   eight buttons (Rob, 2026-07-25). Fitting four across was the wrong invariant.

   Now: 2x2 on a phone, 1x4 from `sm` up where the width genuinely exists. Every button
   keeps its >=44px target (WCAG 2.5.5), there is always a visible gap between them, and
   nothing bleeds past the card padding - so one tile's actions can never touch the
   next tile's. Two rows cost ~50px of tile height, which is the honest price. */
.pp-tile-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.375rem;
}

@media (min-width: 576px) {
    .pp-tile-actions {
        grid-template-columns: repeat(4, 1fr);
    }
}

/* Mobile-first: every tile action is a >=44px touch target (WCAG 2.5.5). The grid track
   owns the width, so `min-width` here is a floor, not a sizing instruction. */
.pp-tile-actions .btn {
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding-inline: 0;
}

/* ── WhiffleCameraStream ────────────────────────────────────────────────────
   The live viewfinder used by the quickfire capture overlay. Letterboxed
   inside a dark frame so a portrait phone camera in a landscape box (or the reverse)
   reads as "the camera", not as a broken image. */

.pp-camera-stream {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    /* Tall enough to frame a document, short enough to leave the buttons on screen. */
    aspect-ratio: 3 / 4;
    max-height: 60vh;
    overflow: hidden;
    background: #000;
    border-radius: var(--tblr-border-radius);
}

.pp-camera-stream video {
    width: 100%;
    height: 100%;
    /* `contain`, not `cover`: a grab captures the WHOLE frame, so the viewfinder must
       show the whole frame too - cropping the preview would make people frame shots
       that come out with the edges they carefully excluded. */
    object-fit: contain;
}

/* ── WhiffleQuickCaptureOverlay ─────────────────────────────────────────────
   The quickfire capture screen: viewfinder above, one fat shutter below. Same
   z-index band as the crop overlay - while it is up, it IS the app. */

.pp-quick-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background: #000;
}

.pp-quick-view {
    position: relative;   /* anchors the shutter flash overlay below */
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    align-items: center;
    justify-content: center;
}

/* The stream control fills the whole viewfinder area, overriding the boxed
   default the control carries on its own. */
.pp-quick-view .pp-camera-stream {
    height: 100%;
    aspect-ratio: auto;
    max-height: none;
    border-radius: 0;
}

.pp-quick-status {
    flex: 0 0 auto;
    padding: 0.5rem 0.75rem;
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.8125rem;
    text-align: center;
}

.pp-quick-status .pp-quick-problem {
    color: var(--tblr-red);
}

.pp-quick-bar {
    flex: 0 0 auto;
    display: grid;
    /* Sides equal so the shutter stays dead centre however wide the counter gets. */
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem;
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: rgba(15, 23, 42, 0.98);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.pp-quick-bar-side {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.pp-quick-right {
    justify-content: flex-end;
}

/* A shutter you cannot miss with a thumb, and that does not move when the
   counter beside it grows a digit. */
.pp-quick-shutter {
    width: 76px;
    height: 76px;
    padding: 0;
    border-radius: 50%;
    font-size: 1.75rem;
}

/* Stands in the shutter's place when the camera will not open: wider than the shutter,
   because it carries words rather than an icon. */
.pp-quick-fallback {
    min-height: 56px;
    max-width: 100%;
    white-space: nowrap;
}

/* No camera. A torch with nothing to light and a counter that can only ever read 0 are
   just taking the room the one useful button needs at 390px. */
.pp-quick-noshutter .pp-quick-torch,
.pp-quick-noshutter .pp-quick-count {
    display: none;
}

.pp-quick-count {
    min-width: 1.5rem;
    color: #fff;
    font-size: 1.125rem;
    font-weight: 600;
    text-align: center;
}

.pp-quick-torch-on {
    color: #000;
    background: var(--tblr-yellow);
    border-color: var(--tblr-yellow);
}

/* A brief white wash confirms the shutter fired - on a still subject nothing
   else on screen changes, so without it you cannot tell it worked. */
.pp-quick-flash .pp-quick-view::after {
    content: '';
    position: absolute;
    inset: 0;
    background: #fff;
    opacity: 0.55;
    pointer-events: none;
}

/* ── WhiffleCropBoxCanvas ───────────────────────────────────────────────────
   A transparent canvas laid over whatever it is framing. It fills its parent,
   so the parent has to be a positioned box (.pp-quick-view and
   .pp-preset-view both are). */

.pp-cropbox {
    position: absolute;
    inset: 0;
    touch-action: none;   /* a drag on the rectangle must not scroll the page under it */
}

.pp-cropbox canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* Advisory mode: a guide to line the paper up inside, not a thing to grab -
   so every press goes through it to whatever is behind. */
.pp-cropbox-static {
    pointer-events: none;
}

/* ── WhiffleVisualPresetEditor ──────────────────────────────────────────────
   The quick-scan screen with the shutter taken out: what you are framing here
   is the framing itself. Same z-index band as the other full-screen overlays. */

.pp-preset-overlay {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background: #000;
}

.pp-preset-view {
    position: relative;   /* anchors the crop box overlay */
    display: flex;
    flex: 1 1 auto;
    min-height: 0;
    align-items: center;
    justify-content: center;
}

.pp-preset-view .pp-camera-stream {
    height: 100%;
    aspect-ratio: auto;
    max-height: none;
    border-radius: 0;
}

.pp-preset-status {
    flex: 0 0 auto;
    padding: 0.5rem 0.75rem;
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.8125rem;
    text-align: center;
}

.pp-preset-status .pp-preset-problem {
    color: var(--tblr-red);
}

.pp-preset-bar {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.75rem;
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    background: rgba(15, 23, 42, 0.98);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

/* Four turns across the full width, and the actions likewise: at 390px there is
   no room for a row that mixes them, and both want >=44px targets. */
.pp-preset-turns,
.pp-preset-actions {
    display: flex;
    gap: 0.5rem;
}

.pp-preset-turns .btn,
.pp-preset-actions .btn {
    flex: 1 1 0;
    min-height: 44px;
}

/* ── WhiffleQuickShotRow ────────────────────────────────────────────────────
   The review list: one picture per row, big enough to judge, with the three
   decisions worth making quickly. */

.pp-shot-list {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.pp-shot-row {
    display: flex;
    /* Tabler's own .card is a flex COLUMN, so the direction has to be stated or the preview
       and its buttons stack instead of sitting side by side. */
    flex-direction: row;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem;
}

/* A SQUARE preview well: a contained image can then be rotated 90 degrees with
   a plain transform and still fit, which is what makes rotating free here. */
.pp-shot-preview {
    position: relative;
    flex: 0 0 auto;
    width: 120px;
    height: 120px;
    padding: 0;
    background: rgba(0, 0, 0, 0.25);
    border: 0;
    border-radius: var(--tblr-border-radius);
    overflow: hidden;
    cursor: pointer;
}

/* --pp-shot-rot is a CONTINUOUS degree count (... -90, 0, 90, 180, 270, 360 ...) written by
   WhiffleQuickShotRow, deliberately NOT the folded 0-270 its .pp-shot-rot-* marker classes
   carry. A folded angle makes this transition take the long way round whenever it wraps:
   rotate-left from 0 lands on 270, which animates 270 degrees CLOCKWISE - the picture visibly
   spinning back the opposite way to the button that was pressed. */
.pp-shot-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    transform: rotate(var(--pp-shot-rot, 0deg));
    transition: transform 0.15s ease;
}

/* Set for exactly one write, when the preview's pixels are replaced by an already-rotated
   version of themselves and the angle has to jump rather than turn. */
.pp-shot-img.pp-shot-nosnap {
    transition: none;
}

.pp-shot-badge {
    position: absolute;
    top: 0.25rem;
    left: 0.25rem;
}

/* 2x2 of >=44px targets, for the same reason the page tiles are 2x2: four in a
   row do not fit beside a 120px preview on a 390px phone. */
.pp-shot-actions {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.375rem;
    flex: 1 1 auto;
}

.pp-shot-actions .btn {
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding-inline: 0;
}

@media (min-width: 576px) {
    .pp-shot-actions {
        grid-template-columns: repeat(4, 1fr);
    }
}
