/* ===== In-place file preview (FilePreviewViewer.razor) =====
   The viewer sits in a detail-view layout item and has to fill whatever height that item is given.
   Every rule here is scoped to `.entirety-file-preview`, so no other layout item is affected. */

.entirety-file-preview {
    /* What a page looks like standing on the surface. Shared, because the PDF and the Word viewer are
       two different libraries drawing the same thing and had drifted into two different looks: one on
       near-black with no edge, one on grey with a darker edge and a rounded frame around the lot.

       The ground is the same grey the application shell puts behind a detail view, taken from the
       Design System token the shell itself reads so it follows the theme. A page has to sit on
       something for its edge to mean anything, and this is what the rest of the screen already is. */
    --file-preview-ground: var(--dxds-color-surface-neutral-subdued-rest, #f4f4f3);
    --file-preview-page-edge: #d3d6db;
    --file-preview-page-shadow: 0 2px 6px rgba(16, 26, 51, 0.10);
    --file-preview-frame: #d3d6db;

    display: flex;
    flex-direction: column;
    /* The layout item sizes the host; the preview claims all of it. */
    flex: 1 1 auto;
    height: 100%;
    /* Without a floor the surface collapses to nothing in a layout group that has no explicit height,
       which reads as a broken editor rather than an empty one. It is a floor only — the flex-basis
       above still lets it grow to whatever the layout item offers. */
    min-height: 240px;
}

/* Every surface — PDF, rich text, memo — fills the remaining height, so all file types end at the
   same place and none of them makes the popup body scroll.
   `display: block` matters for the PDF viewer: its root is a custom element, which defaults to
   `display: inline` and would ignore flex sizing entirely. */
.entirety-file-preview__viewer {
    display: block;
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    height: 100%;
}

/* Text preview: monospace, and scrolling rather than wrapping, so structured formats (json, xml, csv)
   keep their alignment. */
.entirety-file-preview__text textarea {
    height: 100% !important;
    font-family: Consolas, "Cascadia Mono", "Courier New", monospace;
    font-size: 12.5px;
    line-height: 1.45;
    white-space: pre;
    overflow: auto;
}

/* ===== Images ===== */

.entirety-file-preview__image-area {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: auto;
    padding: 8px;
    /* A neutral ground so that transparent PNGs and pale scans both read as a picture with edges
       rather than bleeding into the form behind them. */
    background: #f4f5f7;
    border-radius: 4px;
}

.entirety-file-preview__image {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* ===== Placeholder / status panel =====
   Used for every state with nothing to render: no file, unsupported format, oversized file, a failed
   read, and the brief loading window. */

/* ===== Height chain =====
   A detail-view layout item is content-sized by default, so each viewer ended at its own natural
   height and whatever was around it grew a scrollbar instead. Every ancestor between the view's own
   container and the viewer has to be given a definite height for the preview to fill anything.

   Every rule is scoped with `:has(.entirety-file-preview)` so only the chain that actually contains a
   preview is touched — every other view keeps its default content sizing.

   The inner rules hang off `.detail-view-content`, which is in the chain whether the detail view is
   shown in a popup or as a full page. That prefix is not decoration: DevExpress styles these same
   elements, and without it the rules match but lose on specificity — the layout group stays a block,
   the height stops there, and the preview sits at its minimum with the page empty below it.

   What is above `.detail-view-content` differs between the two. In a popup the modal body drives the
   height and its wrappers have to be told to pass it on. On a page the application shell has already
   sized them, and their height is left strictly alone: `.main` sits in a plain block panel, where
   flex sizing does nothing and only the height it is handed keeps it tall. */

.dxbl-modal-body:has(.entirety-file-preview) {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* On a page the shell hands this one its height but leaves it a block, so nothing inside it can
   stretch. Only the direction is set — its height comes from being a flex item of `.main`, and taking
   that over is what collapsed the whole view to the preview's minimum. */
.main-content:has(.entirety-file-preview) {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.dxbl-modal-body:has(.entirety-file-preview) .main:has(.entirety-file-preview),
.dxbl-modal-body:has(.entirety-file-preview) .main-content:has(.entirety-file-preview),
.detail-view-content:has(.entirety-file-preview) {
    display: flex;
    flex: 1 1 auto;
    align-items: stretch;
    min-height: 0;
    min-width: 0;
    height: auto;
}

/* The form layout's own wrappers. Direction is left alone — one of these is the horizontal group that
   puts the fields beside the preview, and forcing a column on it would stack the two side by side
   groups on top of each other. */
.detail-view-content:has(.entirety-file-preview) .dxbl-fl:has(.entirety-file-preview),
.detail-view-content:has(.entirety-file-preview) .dxbl-row:has(.entirety-file-preview),
.detail-view-content:has(.entirety-file-preview) dxbl-form-layout-group:has(.entirety-file-preview) {
    display: flex;
    flex: 1 1 auto;
    align-items: stretch;
    min-height: 0;
    height: auto;
    /* A flex item refuses to shrink below its content unless told it may. One of these is the row that
       puts the form beside the preview, and without this a page zoomed past the frame widens the row,
       then the dialog, instead of scrolling where it is. */
    min-width: 0;
}

/* The row that directly holds the preview's own layout item stacks vertically — its children are
   full-width columns, and without an explicit column direction the leftover height is never handed
   down to them. */
.detail-view-content:has(.entirety-file-preview)
    .dxbl-row:has(> dxbl-form-layout-item > .dxbl-fl-ctrl > .entirety-file-preview) {
    flex-direction: column;
    flex-wrap: nowrap;
}

/* The preview's layout item and its control cell. */
.detail-view-content:has(.entirety-file-preview) dxbl-form-layout-item:has(> .dxbl-fl-ctrl > .entirety-file-preview),
.detail-view-content:has(.entirety-file-preview) .dxbl-fl-ctrl:has(> .entirety-file-preview) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    height: auto;
}

/* ===== Printing text and image previews =====
   Driven by `js/file-preview.js`, which sets both marker classes for the duration of the print.
   `visibility` rather than `display` because hiding ancestors with `display: none` would take the
   target subtree down with them — visibility is overridable on descendants, display is not. */

@media print {
    body.entirety-file-preview-printing * {
        visibility: hidden !important;
    }

    body.entirety-file-preview-printing .entirety-file-preview--printing,
    body.entirety-file-preview-printing .entirety-file-preview--printing * {
        visibility: visible !important;
    }

    /* Lifted out of the modal and pinned to the sheet, otherwise it prints wherever the dialog
       happened to sit and is clipped by the dialog's own bounds. */
    body.entirety-file-preview-printing .entirety-file-preview--printing {
        position: fixed;
        inset: 0;
        margin: 0;
        padding: 0;
        min-height: 0;
        overflow: visible;
        background: #fff;
    }

    /* The screen chrome — grey plate, rounded corners, scrollbars — is noise on paper. */
    body.entirety-file-preview-printing .entirety-file-preview--printing .entirety-file-preview__image-area,
    body.entirety-file-preview-printing .entirety-file-preview--printing .entirety-file-preview__message {
        background: none;
        border-radius: 0;
        overflow: visible;
    }

    /* A textarea clips to its scroll viewport when printed; let it run to its full height instead. */
    body.entirety-file-preview-printing .entirety-file-preview--printing textarea {
        height: auto !important;
        overflow: visible !important;
        border: none !important;
    }
}

.entirety-file-preview__message {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    text-align: center;
    color: #8b95a8;
    font-size: 13px;
    background: #f4f5f7;
    border-radius: 4px;
}

/* ===== Zoom =====
   `--file-preview-zoom` is set on the host by the viewer and carries the level chosen in the toolbar.
   The PDF and rich-text viewers apply it themselves through their own ZoomLevel property; these rules
   exist for the two surfaces that have no zoom of their own, so one control still governs all four. */

/* Text scales by font size rather than transform: the memo keeps its own scrollbars and line wrapping
   that way, where a scaled transform would leave it laying out at the original width. */
.entirety-file-preview__text textarea {
    font-size: calc(12.5px * var(--file-preview-zoom, 1));
    line-height: 1.45;
}

/* An image scales by width and is allowed to overflow into the scrolling area, so zooming in pans
   rather than squashing the picture back into the box. */
.entirety-file-preview__image {
    width: calc(100% * var(--file-preview-zoom, 1));
    max-width: calc(100% * var(--file-preview-zoom, 1));
    height: auto;
    max-height: none;
    object-fit: contain;
}

/* Only centre while the image is smaller than the frame; once zoomed past it, centring would clip the
   top-left corner out of reach. */
.entirety-file-preview__image-area {
    align-items: safe center;
    justify-content: safe center;
}

/* ===== PDF (Gotho.BlazorPdf / pdf.js) =====
   Every rule is scoped under `.entirety-file-preview__pdf`, so the library's own stylesheet — which
   is linked globally, because pdf.js needs its text-layer rules to be — governs nothing outside the
   preview. */

/* The library toolbar goes, for the same reason the DevExpress one did: the Word surface has none,
   the view already carries zoom, print and download, and two toolbars disagree about one document.
   Its paging buttons are not missed — the viewer runs in scroll mode, so every page is simply there. */
.entirety-file-preview__pdf .blazorpdf-toolbar {
    display: none;
}

/* Height chain, and where the scrolling happens.

   The frame is sized by what the view has left over, never by what is inside it: `flex-basis: 0`.
   A PDF renders every page into one column, so a frame sized by its content is as tall as the whole
   document — which pushes a scrollbar onto the popup body, or in a full-page view simply runs off the
   bottom of a shell that does not scroll, leaving page two onwards unreachable. */
.entirety-file-preview__pdf {
    display: flex;
    flex-direction: column;
    flex: 1 1 0;
    min-height: 0;
    /* Overriding the shared `height: 100%` above, which would resolve against an auto-height
       ancestor and hand the frame back to its content. */
    height: auto;
    overflow: hidden;
    /* And the same for width. A page zoomed past the frame has to scroll inside it; left to grow, it
       widens the dialog instead — and then fit-width measures against a frame the document has just
       stretched, and concludes the document already fits. */
    min-width: 0;
    max-width: 100%;
}

.entirety-file-preview__pdf .blazorpdf-container {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
}

/* This is the element that scrolls. The library ships it as `overflow-y: hidden`, which works for a
   viewer that fills the browser window and nothing else: given a frame shorter than the document, it
   clips the pages and — being a scroll container all the same — swallows the wheel, so the document
   sits there refusing to move. `overflow-x` it already sets to auto, which is what pans a zoomed-in
   page sideways. */
.entirety-file-preview__pdf .blazorpdf-pdf {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

/* The page column ships as `height: 100vh`, which is the viewport rather than the frame it sits in,
   and leaves a tall gap under a short document. Its own `overflow: auto` goes with it: the pane above
   is the scroller now, and a second scroller nested inside it that never has anything to scroll only
   confuses the wheel.

   Its width is deliberately left alone: everything from here down to the page is sized by the page
   itself, and that is why fit-width has to be measured against the frame rather than against the
   column — see `pdfFitRatio` in js/file-preview.js. */
.entirety-file-preview__pdf .blazorpdf__scroll {
    height: 100%;
    overflow: visible;
}

/* The ground the pages sit on. The viewer paints it near-black across four elements — three in its
   stylesheet and one inline, which the Colors parameter in the component handles. A dark plate under a
   document reads as a media player rather than as paper on a desk, and it is the only dark surface in
   the app. */
.entirety-file-preview__pdf .blazorpdf-pdf,
.entirety-file-preview__pdf .blazorpdf-pdf__pdf-container,
.entirety-file-preview__pdf .blazorpdf__scroll {
    background-color: var(--file-preview-ground);
}

/* Which leaves a white page invisible on a white ground, so each one gets an edge.

   Drawn as a shadow rather than a border: a border would grow the canvas box, and both the fit
   measurement and the text-layer alignment read those boxes to the pixel. The library's own
   bottom-only rule goes, or the ring would double along that edge. */
.entirety-file-preview__pdf .blazorpdf__scroll-page {
    border-bottom: none;
    box-shadow: 0 0 0 1px var(--file-preview-page-edge), var(--file-preview-page-shadow);
}

/* The frame around the surface. The rich-text viewer draws one of its own and the PDF viewer does
   not, which is one of the ways the two read as different features; both get it here. */
.entirety-file-preview__pdf,
.entirety-file-preview .dxreRoot {
    border: 1px solid var(--file-preview-frame);
    border-radius: 4px;
}

/* ===== Word =====
   The rich-text viewer arrives with a look of its own: a grey ground, a darker edge on the page, and a
   rounded frame around the whole control. Next to the PDF surface — the same thing, a document standing
   on a surface — the two read as two different features, so this brings it onto the shared values.

   Three classes deep on purpose. DevExpress styles these elements itself and injects its theme
   stylesheet at runtime, so source order decides nothing and only specificity does. The ground is not
   here at all — that one is painted with !important and comes from the variable set above. */
.entirety-file-preview .dxreRoot {
    background-color: var(--file-preview-ground);
}

/* The ground is one of the few things DevExpress paints with !important, so no rule of ours can win
   it outright. It reads a variable first, though, and that is the way in — set on the element that
   reads it rather than on an ancestor, because the theme defines its own value further down the tree
   and the nearer definition is the one that counts. */
.entirety-file-preview .dxreRoot .dxreView {
    --dxre-view-background-color: var(--file-preview-ground);
}

/* The edge stays a border rather than becoming a shadow ring like the PDF's: it is already there, and
   swapping it would change the page box that fit-width measures. A 1px border and a 1px ring in the
   same colour are the same line on screen. */
.entirety-file-preview .dxreRoot .dxrePage {
    border-color: var(--file-preview-page-edge);
    box-shadow: var(--file-preview-page-shadow);
}

/* One global rule bleeds out of the library's stylesheet: a bare `mark` selector, painted for its
   find-in-document highlighting. Find is switched off on this surface and `mark` is used elsewhere in
   the app, so the browser default is put back and the highlight kept where it belongs. */
mark {
    background: Mark;
    color: MarkText;
}

.entirety-file-preview__pdf mark {
    background: rgba(255, 255, 0, 0.46);
    color: transparent;
}
