/* The reading editor (app/static/js/reading_editor.js).

   The dialog's own chrome: the deck/layout pickers, the step machinery, the
   overlay steps that float over the board, and the suit wheel. The board itself
   is reading-board.css and the card grid is card-grid.css — both shared with
   other pages. */

/* The setup step's deck and layout pickers. A dropdown rather than a
   select, because an option element cannot hold a picture — the cover and
   the spread are most of what tells one deck or one layout apart. One row
   definition serves both the open list and the closed button, so the
   control always looks like what it opens. */
.reading-picker-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    min-width: 0;
}

.reading-picker-thumb {
    display: block;
    flex: 0 0 auto;
    /* Big enough that a Celtic Cross still reads as a Celtic Cross: eleven
       slots letterboxed into a row have little room to say anything. */
    width: 4.5rem;
    height: 3rem;
    /* Letterboxed: a deck cover is portrait and a Celtic Cross spread is
       wide, and both have to sit in the same row without distorting. */
    object-fit: contain;
}

/* The open list gets the taller row: a picture is what it is being opened
   for, and eleven Celtic Cross slots or a cover's artwork at row height are
   a smudge. Only the list — the closed controls keep the standard box, so
   the deck and layout ones stay the same height as each other. */
.reading-picker-menu .reading-picker-thumb {
    height: 4rem;
}

/* Each kind keeps its own shape at that height: a spread is landscape, a
   cover is a card (7:12, so 2.35rem of the 4rem row). */
.reading-picker-menu .reading-picker-thumb-layout {
    width: 6rem;
}

.reading-picker-menu .reading-picker-thumb-deck {
    width: 2.35rem;
}

/* Which leaves the name less width, so in the list it wraps instead of
   being cut: there is room in a four-rem row, and "Card of the Day" is
   exactly the sort of name that stops meaning anything truncated. The
   closed control keeps the .text-truncate the row carries, because it has
   one line and a caret to fit. */
.reading-picker-menu .reading-picker-row .text-truncate {
    overflow: visible;
    white-space: normal;
}

/* A deck cover is a card: portrait, at the same 7:12 the card grid uses.
   Left in the layout thumb's landscape box it letterboxes into a sliver
   with empty margins either side, and pushes every name to the right of
   space nothing is using. */
.reading-picker-thumb-deck {
    width: 1.75rem;
}

/* The stored spread SVG carries .spread-preview, which is width/height
   100%, so it fills whichever box it is dropped into — this is that box. */
.reading-picker-thumb > svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* A deck with no cover: a placeholder that holds the row's height, so the
   names stay in one column whether or not every deck has a picture. */
.reading-picker-thumb-empty {
    border: 1px dashed var(--tblr-border-color, #dadfe5);
    border-radius: var(--tblr-border-radius, .25rem);
}

/* Nine layouts at 2.5rem a row is taller than a dropdown should be. */
.reading-picker-menu {
    max-height: 20rem;
    overflow-y: auto;
}

.reading-editor-body {
    position: relative;
    height: calc(100vh - 12rem);
    min-height: 24rem;
    padding: 0;
    /* A column of two: the stage, which takes what is left, and the
       miniature's band under it at a fixed height. The setup step is a third
       child of this column and sizes to its own content, which is why the body
       is not simply the stage. */
    display: flex;
    flex-direction: column;
}

.reading-stage {
    position: relative;
    flex: 1 1 auto;
    /* A flex item will not shrink below its content without this, and the
       board's content is a canvas that can be any size at all. */
    min-height: 0;
}

/* Every step inside the stage fills it, rather than inheriting a chain of
   percentage heights from a flex item. The setup step is outside the stage and
   keeps its own height, which is the whole reason this is scoped. */
.reading-stage > .reading-step {
    position: absolute;
    inset: 0;
}

/* Over the board rather than in the flow, so a message never resizes the
   canvas underneath it. */
.reading-editor-alert {
    position: absolute;
    top: .75rem;
    left: .75rem;
    right: .75rem;
    z-index: 5;
    margin: 0;
}

/* .alert sets its own display, which outranks the browser's [hidden]. */
.reading-editor-alert[hidden] {
    display: none !important;
}

/* The setup step is a short form, so it sizes to its content instead of
   leaving a tall empty dialog. */
.reading-editor-body.is-setup {
    height: auto;
    min-height: 0;
}

/* Which means nothing scrolls there — and the .overflow-auto the step
   carries for the taller steps then has only one effect: clipping the deck
   and layout dropdowns to the height of the form above them. !important
   because .overflow-auto is a utility and brings its own. */
.reading-editor-body.is-setup .reading-step {
    overflow: visible !important;
}

/* .btn sets its own display, which outranks the browser's [hidden] rule —
   the same clash as .reading-step[hidden] below. */
.modal-footer [data-footer][hidden] {
    display: none !important;
}

.reading-step {
    height: 100%;
}

/* Several steps carry Bootstrap's .d-flex, which is display:flex !important
   and so beats the browser's plain [hidden] { display: none } rule — without
   this they all stay on screen at once and the empty full-height suit wheel
   covers the step that should be showing. Attribute + class outranks the
   utility's single class, so this wins. */
.reading-step[hidden] {
    display: none !important;
}

/* The significator row is shown only for a layout that has a slot for it
   (layouts.layout.has_significator), by setting [hidden] on it from the
   server on first paint and from reading_editor.js on every change. It
   carries .d-flex for its own alignment — the same clash as above, so the
   same answer, or the row stays on screen for every layout. */
[data-significator-row][hidden] {
    display: none !important;
}

/* The suit, card, confirm and display steps float over the board rather
   than replacing it, so the cards already laid out stay visible while the
   next one is picked. The veil is the dialog's own background at less than
   full strength: enough to lift the picker off a busy spread, sheer enough
   that the cards behind stay recognisable. Tune that balance with
   --reading-overlay-alpha, which is why the alpha is a variable rather than
   baked into the rgba() — raising it is safe now that the controls above
   carry their own surfaces. It
   stays below .reading-editor-alert (z-index 5) so a save error still
   lands on top of whichever step is showing. */
.reading-step.is-overlay {
    position: absolute;
    inset: 0;
    z-index: 4;
    background: rgba(var(--tblr-body-bg-rgb, 255, 255, 255), var(--reading-overlay-alpha, .75));
}

/* Every control in an overlay step lies on top of the board, and the veil
   dims that board without hiding it — so each one carries its own surface,
   the way the Compose client backs its wheel labels and bottom bars. Without
   this a suit icon or a slot name competes with whatever card art happens to
   be under it and is easy to lose. Scoped to .is-overlay: the same markup on
   a solid background needs none of it. */
.reading-step.is-overlay .suit-wheel-item,
.reading-step.is-overlay .card-grid-item,
.reading-step.is-overlay [data-suit-slot-label],
.reading-step.is-overlay [data-card-slot-label],
.reading-step.is-overlay .reading-overlay-caption {
    padding: .25rem .5rem;
    border: 1px solid var(--tblr-border-color, #dadfe5);
    border-radius: var(--tblr-border-radius, .25rem);
    background: var(--tblr-bg-surface, #fff);
    box-shadow: var(--tblr-box-shadow-sm, 0 .125rem .25rem rgba(0, 0, 0, .075));
}

/* The two slot-name strips are block children of a flex column, so they
   stretch the full width unless told to shrink to their text. */
.reading-step.is-overlay [data-suit-slot-label],
.reading-step.is-overlay [data-card-slot-label] {
    align-self: center;
    max-width: 100%;
}

/* The caption the confirm and display steps put under their picture — card
   name, slot name, and on display the meaning — as one panel rather than a
   chip per line. A meaning is a paragraph, so it gets a readable measure
   rather than shrinking to its longest line, and keeps its own height: the
   column is short, and letting flex take the difference out of the text
   leaves a clipped line behind a border. */
.reading-step.is-overlay .reading-overlay-caption {
    align-self: center;
    flex: 0 0 auto;
    max-width: min(36rem, 100%);
    padding: .5rem .75rem;
}

/* Which means the picture is what yields the space. An <img> will not shrink
   past its own height without this, whatever its max-height says. */
.reading-step.is-overlay .card-preview {
    min-height: 0;
}

/* The hover affordance the flat originals got from the pointer alone. */
.reading-step.is-overlay .suit-wheel-item:hover,
.reading-step.is-overlay .card-grid-item:hover {
    border-color: var(--tblr-border-active-color, #a0aec0);
}

.suit-wheel {
    position: relative;
    width: 100%;
    height: 100%;
    min-height: 20rem;
}

.suit-wheel-item {
    position: absolute;
    left: 50%;
    top: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    background: none;
    border: 0;
    padding: .5rem;
}

.suit-wheel-item img {
    width: 4rem;
    height: 4rem;
    object-fit: contain;
}

.card-preview {
    max-height: 60vh;
    object-fit: contain;
}

.card-preview.is-reversed {
    transform: rotate(180deg);
}

/* --- the reading in miniature (reading_editor.js renderMini) ------- */

/* The band at the foot of the dialog. One height for every layout, because the
   whole point is that it does not move as the spread changes underneath it —
   but a share of the viewport rather than a slab, because the fit is bound by
   height and the tall spreads are the ones that need the room: a Celtic Cross
   is 1200x1640, so every pixel of band is worth about a fifth of a pixel of
   card to it. The floor is what this band used to be, so a short screen keeps
   its board; the ceiling stops a tall one from handing the board's own space
   to a picture of it. */
.reading-mini {
    position: relative;
    flex: 0 0 auto;
    height: clamp(9rem, 26vh, 18rem);
    overflow: hidden;
    border-top: 1px solid var(--tblr-border-color, #dadfe5);
    background: var(--tblr-bg-surface-secondary, #f1f5f9);
}

/* Plain [hidden] would do — this is a div with no display of its own — but the
   rule is stated for the same reason the others above are: every hidden piece
   of this dialog is hidden the same way. */
.reading-mini[hidden] {
    display: none !important;
}

/* What the scale is measured against, so the spread keeps a margin off the
   band's edges without the arithmetic having to know about padding. */
.reading-mini-viewport {
    position: absolute;
    inset: .5rem;
}

/* Centred in the viewport and scaled from its middle, the same shape as the
   pan/zoom canvas the board uses — this one just never moves. */
.reading-mini-canvas {
    position: absolute;
    left: 50%;
    top: 50%;
    transform-origin: center center;
    /* Until fitMini() has had a band with a width to measure — which, inside a
       dialog that is still fading in, is not on the first render. Unscaled it
       would be the board at full size for that instant. */
    visibility: hidden;
}

/* One slot. A button, not a div, so the spread can be walked with a keyboard:
   the board's own slots are tapped through the pan/zoom layer and cannot be,
   which is another thing this band quietly adds. */
.reading-mini-slot {
    position: absolute;
    transform-origin: center center;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.reading-mini-slot img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    pointer-events: none;
}

/* The same fade the board gives an empty slot, so "not cast yet" reads the
   same in both places. */
.reading-mini-slot.is-empty img {
    opacity: .45;
}

/* The slot being filled, and the only marker on screen while a picker is up —
   the board's own ring is behind the veil then. Three things at once, because
   a Celtic Cross card here is sixteen pixels wide and one of them alone is not
   enough to find it by:

   - a band of primary with a gap of band colour inside it, the two-ring trick
     the board uses, so the edge survives against card art of any colour;
   - a step up in size, which is what the eye actually catches in a grid of
     identical rectangles;
   - a lift out of the paint order, so the ring is not covered by a card that
     overlaps this one — the Celtic Cross crosses two of them.

   Every width is a multiple of --mini-px, which fitMini() sets to whatever one
   screen pixel is worth in board units. The canvas is scaled as a whole, so a
   plain `2px` here would be scaled with it and arrive at a sixth of a pixel. */
.reading-mini-slot.is-current {
    --mini-scale: 1.25;
    box-shadow:
        0 0 0 calc(var(--mini-px, 1px) * 2) var(--tblr-bg-surface-secondary, #f1f5f9),
        0 0 0 calc(var(--mini-px, 1px) * 5) var(--tblr-primary, #206bc4);
    z-index: 1;
}

.reading-mini-slot:hover img,
.reading-mini-slot:focus-visible img {
    opacity: 1;
    filter: brightness(1.08);
}

/* Stated in --mini-px for the same reason the ring above is, or the focus
   ring is invisible on exactly the layouts that need it most. */
.reading-mini-slot:focus-visible {
    outline: calc(var(--mini-px, 1px) * 2) solid var(--tblr-primary, #206bc4);
    outline-offset: var(--mini-px, 1px);
}
