/* ============================================================================
   portrait.css — Kingdom of Au Lac · portrait layout module
   ============================================================================
   Loaded AFTER styles.css. Every rule below lives inside

       @media (orientation: portrait) and (max-width: 1100px)

   so landscape phones, landscape tablets and desktop are never reached by it.
   The max-width ceiling matters: `orientation: portrait` only means "taller
   than wide", which a narrow desktop window also satisfies. The ceiling keeps
   desktop on its current design.

   Nothing in styles.css is edited. Remove the <link> to this file and the
   layout returns to exactly what it is today.

   HUD map:
       top-left    audio
       top-right   login · save · GPS
       row 1       resource · diplomacy · territory
       row 2       army · inventory · NPC
   ========================================================================== */


/* ============================================================================
   1. METRICS + HUD BUTTON ROWS
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    /* ---------------------------------------------------------------------
       Undo the landscape-enforcement guard in styles.css.

       That block hides #map, #infobox, #npc-ui-container and the Mapbox
       controls with `visibility: hidden !important` in ANY portrait viewport.
       It is why the NPC button is missing and why opening the inventory does
       nothing — both elements are present and open correctly, just invisible.

       Phase 1 deletes that block from styles.css. Until then this override
       keeps portrait usable without editing an app file, and it stays
       harmless afterwards. `!important` is required only because the rule it
       overrides uses it; specificity is matched selector-for-selector. */
    #map,
    #infobox,
    #npc-ui-container,
    .mapboxgl-ctrl-group,
    .mapboxgl-ctrl-logo,
    .mapboxgl-ctrl-attrib {
        visibility: visible !important;
        pointer-events: auto !important;
    }

    :root {
        --pt-btn: 60px;
        --pt-edge: 8px;
        --pt-safe-top: env(safe-area-inset-top, 0px);
        --pt-safe-bottom: env(safe-area-inset-bottom, 0px);
        --pt-util-h: 34px;
        /* Row 1 clears the notch and the utility cluster above it. */
        --pt-row1: calc(var(--pt-safe-top) + var(--pt-edge) + var(--pt-util-h) + 10px);
        /* Row 2 clears the iOS home indicator. */
        --pt-row2: calc(var(--pt-safe-bottom) + 14px);
    }

    /* Shared metrics for all six HUD buttons. The base rules give them four
       different sizes (34px – 72px) depending on which block last matched;
       portrait normalises them so the two rows read as one system. */
    #statusButton,
    #diplomacyButton,
    #storyTutorialButton,
    #armyBuildUpButton,
    #infoboxButton,
    #npc-ui-container {
        position: fixed;
        width: var(--pt-btn);
        height: var(--pt-btn);
        min-width: var(--pt-btn);
        min-height: var(--pt-btn);
        max-width: var(--pt-btn);
        max-height: var(--pt-btn);
        margin: 0;
        padding: 0;
        transform: translateX(-50%);
        z-index: 1002;
    }

    #statusButton .button-icon,
    #diplomacyButton .button-icon,
    #armyBuildUpButton .button-icon,
    #infoboxButton .button-icon,
    #storyTutorialButton img {
        width: 100%;
        height: 100%;
        object-fit: contain;
    }

    /* Three buttons, equal spacing across the full width: centres at 1/6, 3/6
       and 5/6. Percentages mean this holds at every width from 320 to 1024
       with no per-device breakpoints. */
    #statusButton,
    #armyBuildUpButton {
        left: 16.667%;
        right: auto;
    }

    #diplomacyButton,
    #infoboxButton {
        left: 50%;
        right: auto;
    }

    #storyTutorialButton,
    #npc-ui-container {
        left: 83.333%;
        right: auto;
    }

    /* Row 1 — resource · diplomacy · territory */
    #statusButton,
    #diplomacyButton,
    #storyTutorialButton {
        top: var(--pt-row1);
        bottom: auto;
    }

    /* Row 2 — army · inventory · NPC */
    #armyBuildUpButton,
    #infoboxButton,
    #npc-ui-container {
        top: auto;
        bottom: var(--pt-row2);
    }
}


/* ============================================================================
   2. UTILITY CLUSTER — login · save · GPS (top-right), audio (top-left)
   ============================================================================
   Grouped by position and matching trim rather than by re-parenting the DOM.
   The GPS button is rendered by Mapbox inside the map's own control container,
   so a single shared background would mean moving that node with JavaScript.
   This keeps it CSS-only: two adjacent pills that read as one cluster.
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    #login-save-btns-container {
        position: fixed;
        top: calc(var(--pt-safe-top) + var(--pt-edge));
        right: calc(env(safe-area-inset-right, 0px) + 140px);
        gap: 8px;
        padding: 3px;
        border-radius: 6px;
        background: rgba(20, 14, 8, 0.72);
        border: 1px solid rgba(176, 141, 87, 0.6);
        z-index: 1003;
    }

    #login-save-btns-container button {
        height: 26px;
        font-size: 9px;
        padding: 0 8px;
    }

    /* Mapbox geolocate control — trimmed to match the pill beside it.
       `position` is restated because Mapbox anchors this container inside the
       map element; pinning it to the viewport keeps it aligned with the
       login/save pill, which is a direct child of <body>. */
    .mapboxgl-ctrl-top-right {
        position: fixed;
        top: calc(var(--pt-safe-top) + var(--pt-edge));
        right: calc(env(safe-area-inset-right, 0px) + var(--pt-edge));
    }

    .mapboxgl-ctrl-top-right .mapboxgl-ctrl-group {
        background: rgba(20, 14, 8, 0.72);
        border: 1px solid rgba(176, 141, 87, 0.6);
        border-radius: 6px;
        box-shadow: none;
    }

    /* Audio moves to the opposite corner. Its base rule is `right: 180px`,
       an offset measured against the login/save labels — that breaks as soon
       as the labels change length. Anchoring left removes the guesswork and
       balances the top edge. */
    #audio-toggle {
        top: calc(var(--pt-safe-top) + var(--pt-edge));
        left: calc(env(safe-area-inset-left, 0px) + var(--pt-edge));
        right: auto;
    }
}


/* ============================================================================
   3. RESOURCE STRIP — stays a compact strip, not a sheet
   ============================================================================
   Six icons in a row; blowing this up to full-screen would be wrong. It docks
   under row 1, centred, and keeps its existing open/close transition.
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    #resource-container.status-panel {
        left: 50%;
        top: calc(var(--pt-row1) + var(--pt-btn) + 10px);
        width: min(430px, calc(100vw - 2 * var(--pt-edge)));
        max-height: min(150px, calc(100dvh - 200px));
        transform: translateX(-50%) translateY(-8px);
    }

    #resource-container.status-panel.open {
        transform: translateX(-50%) translateY(0);
    }
}


/* ============================================================================
   4. PANELS AS FULL-SCREEN SHEETS
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    #diplomacyWindow,
    #armyBuildUpWindow,
    .story-tutorial-window,
    #infobox,
    .npc-panel,
    .master-cao-lo-window {
        position: fixed;
        /* styles.css has no universal border-box reset — it is applied to only
           a dozen rules by hand. Without it, `width: 100%` plus padding makes
           every sheet wider than the viewport by its horizontal padding, which
           clips the right edge of tabs, text and buttons. */
        box-sizing: border-box;
        left: 0;
        right: 0;
        top: var(--pt-safe-top);
        bottom: 0;
        width: 100%;
        min-width: 0;
        max-width: none;
        height: auto;
        max-height: none;
        margin: 0;
        border-radius: 0;
        border-left: none;
        border-right: none;
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        padding-bottom: calc(var(--pt-safe-bottom));
    }

    /* The base rules centre these with translate(-50%, -50%) or slide them in
       from the right edge. A full-bleed sheet needs neither. */
    #diplomacyWindow,
    #armyBuildUpWindow,
    .story-tutorial-window,
    .npc-panel,
    .master-cao-lo-window {
        transform: none;
        animation: ptSheetIn 240ms ease both;
    }

    /* Inventory keeps its open/close transform — only the direction changes,
       from "slide in from the right" to "rise from the bottom", matching the
       row its button sits in. */
    #infobox {
        transform: translateY(100%);
        transition: transform 240ms ease, opacity 180ms ease;
    }

    #infobox.open {
        transform: translateY(0);
    }

    @keyframes ptSheetIn {
        from { opacity: 0; transform: translateY(18px); }
        to   { opacity: 1; transform: translateY(0); }
    }
}


/* ============================================================================
   5. PANEL INTERNALS — every side-by-side split stacks
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    /* --- Diplomacy: a three-column row becomes three stacked blocks ---------
       Stacking .diplomacy-flex is only half the job. Each child carries its own
       hard landscape dimensions, and those survive the stack — .description-container
       is `width: 30vw` (117px on a 390px phone) at `font-size: 20px`, which is
       roughly five characters per line. Every child has to be unwound too. */
    #diplomacyWindow {
        flex-direction: column;
        flex-wrap: nowrap;
        align-items: stretch;
        justify-content: flex-start;
        padding: 16px 12px;
    }

    .diplomacy-flex {
        flex-direction: column;
        align-items: stretch;
    }

    /* Three clans on one row as equal columns instead of wrapping 2 + 1.
       Pairs with the display-only label shortening in ui.js (see handover):
       `clanName` stays intact as the key for clanImages and
       showAffiliatedClanWindow — only the rendered text drops " clan". */
    .affiliated-clans-container {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 6px;
        min-width: 0;
        max-width: none;
        margin-right: 0;
        margin-bottom: 12px;
    }

    .affiliated-clan-tab {
        margin-bottom: 0;
        padding: 8px 6px;
        font-size: 12px;
        letter-spacing: 0.5px;
        text-align: center;
        min-width: 0;
    }

    /* "Grow the art to fill" — the clan banner absorbs the leftover height so
       the sheet reads as full instead of top-heavy with dead space below. */
    .diplomacy-flex {
        flex: 1 1 auto;
        min-height: 0;
    }

    .image-slider-container {
        width: 100%;
        max-width: 100%;
        flex: 1 1 auto;
        min-height: 26dvh;
        max-height: none;
        justify-content: center;
        margin-bottom: 12px;
    }

    .image-slider-container img {
        height: 100%;
        max-height: none;
        object-fit: contain;
    }

    /* The shape this needs already exists in styles.css as
       `.description-container.clan-relations-mode` — it is simply scoped to one
       tab. Portrait applies the same treatment to all of them. */
    .description-container {
        width: 91%;
        height: auto;
        max-height: 38dvh;
        margin-left: 0;
        flex: 0 1 auto;
        line-height: 1.55;
    }

    /* Three tabs of equal width. The labels are shortened in ui.js (see the
       handover note) — this keeps them on one line whatever the wording. */
    #diplomacyWindow .tab-container {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 5px;
        width: 100%;
        margin-bottom: 10px;
    }

    #diplomacyWindow .tab {
        margin-right: 0;
        padding: 9px 6px;
        font-size: 12px;
        letter-spacing: 0.5px;
        text-align: center;
        min-width: 0;
    }

    /* --- Army build-up: one column, in reading order -----------------------
       type · artwork · description · upgrade · figures, then the controls that
       act on them. .army-panel is a grid in styles.css, so portrait only renames
       its areas — one set of markup serves both orientations.

       Note `display` is deliberately not set here. ui.js opens this window with
       an inline `style.display = 'flex'`, which beats any stylesheet rule. */
    #armyBuildUpWindow {
        padding: 18px;
        padding-top:22px;   /* clears the absolutely-positioned close button */
    }

    /* Content-sized rather than stretched to the sheet: the sheet is what
       scrolls, so the panel must report its true height to it. */
    .army-panel {
        flex: 0 0 auto;
        grid-template-columns: 1fr;
        grid-template-areas:
            "pick"
            "art"
            "desc"
            "upgrade"
            "data";
        gap: 10px;
    }

    /* The unit art is square (1000x1000), so height is what bounds it: sized by
       the cap, centred, and never wider than the sheet. Sizing it by height
       rather than width means the border hugs the figure instead of framing
       empty space beside it. */
    #armyBuildUpWindow #army-force-image {
        justify-self: center;
        width: auto;
        max-width: 100%;
        max-height: 35dvh;
    }

    /* One line each, as the column reads: equipment, then training. */
    .army-supply {
        flex-direction: column;
    }

    /* --- Territory / story drawer: left tab rail becomes a top tab strip --- */
    .story-tutorial-window {
        flex-direction: column;
        gap: 12px;
        padding: 20px;
    }

    .story-tutorial-window .tab-container {
        width: 100%;
        flex: 0 0 auto;
        flex-direction: row;
        overflow-x: auto;
        border-right: none;
        border-bottom: 2px solid #7a5c2e;
        padding: 0 0 8px;
        gap: 6px;
    }

    .story-tutorial-window .tab {
        flex: 0 0 auto;
        border-radius: 6px 6px 0 0;
        font-size: 14px;
        padding: 8px 12px;
    }

    /* --- NPC "An" panel: the 60/40 split that no breakpoint ever stacked ----
       .npc-panel-left and .npc-panel-right are direct children of .npc-panel
       (.npc-content lives inside the left one), so stacking happens here. */
    .npc-panel {
        flex-direction: column;
        padding-top: 44px;   /* clears the absolutely-positioned close button */
    }

    .npc-panel-left {
        width: 100%;
        flex: 1 1 auto;
        min-height: 0;
    }

    /* An's portrait sits on top as a banner and takes a generous share; the
       chat column below fills whatever is left, so there is no dead space. */
    .npc-panel-right {
        width: 100%;
        flex: 0 0 30dvh;
        height: 30dvh;
        order: -1;
    }

    .npc-character-image {
        object-fit: cover;
        object-position: center top;
    }

    /* --- Master Cao Lo ------------------------------------------------------
       Master_Caolo.png is square (1000x1000). The stacked phone layout gives it
       a 32%-height slot — roughly 390x270, aspect 1.44 — and `object-fit: cover`
       scales a square image to cover the width, so it renders 390x390 inside a
       270px box and loses 60px off the top and bottom.

       Making the slot square lets the whole figure show at full size with no
       crop and no letterboxing. `contain` stays as the safety net for the
       max-height case, where the slot is capped and stops being square. 
       The stack itself has to be restated here. styles.css only sets
       `flex-direction: column` inside @media (max-width: 768px), so a tablet
       held in portrait at 810px or 834px is still laid out as a ROW — which is
       what collapsed the window into a small square and a floating scroll. */
    .master-cao-lo-content {
        flex-direction: column;
    }

    .master-cao-lo-left {
        width: 100%;
        flex: 0 0 auto;
        height: 300px;
        aspect-ratio: 1 / 1;
        max-height: 42dvh;
    }

    .master-cao-lo-image {
        object-fit: contain;
        object-position: center;
    }

    .master-cao-lo-right {
        width: 100%;
        flex: 1 1 auto;
        height: auto;
        min-height: 0;
        margin: 0;
        /* Base is `contain`, which leaves the scroll small in a tall box. This
           is the same treatment the <=768px block already uses. */
        background-size: 98% 100%;
    }

    /* The message box was a fixed 62% of the scroll with asymmetric margins, so
       short text sat in the upper half and left the lower half of the parchment
       empty. Hugging the text lets the parent's centring actually centre it. */
    .master-cao-lo-message {
        height: auto;
        max-height: 72%;
        width: min(68%, 380px);
        margin: 0;
        font-size: 20px;
    }

    /* --- Inventory sheet ---------------------------------------------------
       Full-screen, but showing all three groups at once as equal thirds:
       Usable on top, Exploration in the middle, Craft Queue at the bottom.
       The tab buttons are dropped in favour of a text label on each group.

       The `!important` on display is unavoidable today: showTab() in state.js
       hides the two inactive groups with an inline `style.display = 'none'`,
       and only !important beats an inline style. A three-line guard in showTab
       would remove the need for it — that is app code, so it is not done here.
       Safe to make #infobox a flex column: it is shown and hidden by the .open
       class via transform and opacity, never by `display`. */
    #infobox {
        display: flex;
        flex-direction: column;
        padding: 0 16px 16px;
    }

    #close-infobox-button {
        align-self: flex-end;
        float: none;
    }

    /* Tabs replaced by a per-group text label. */
    #infobox .inventory-tabs-row {
        display: none;
    }

    #infobox #player-info {
        display: flex;
        flex-direction: column;
        flex: 1 1 auto;
        min-height: 0;
        gap: 8px;
        margin: 0;
        padding: 0;
        overflow: hidden;
    }

    /* "Your Inventory:" — the three group labels say it better. */
    #infobox #player-info > h4:first-child {
        display: none;
    }

    /* Usable on top, Exploration in the middle, Craft at the bottom. ui.js
       appends exploration before usable, so flex order does the swap without
       touching the DOM. */
    #infobox #usable-items { order: 1; }
    #infobox #exploration-items { order: 2; }
    #infobox #process-items-title { order: 3; }
    #infobox #craft-empty-state { order: 4; }
    #infobox #process-items { order: 5; }
    #infobox #process-button { order: 6; }

    /* The three growable groups. Equal `flex: 1 1 0` is what makes the thirds
       balance regardless of how much each one contains. */
    #infobox #usable-items,
    #infobox #exploration-items,
    #infobox #process-items {
        box-sizing: border-box;
        display: flex !important;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        flex: 1 1 0;
        min-height: 0;
        max-height: none;
        margin: 0;
        padding: 10px;
        border: 1px solid rgba(176, 141, 87, 0.3);
        border-radius: 8px;
        background: rgba(20, 14, 8, 0.4);
        overflow-y: auto;
    }

    /* Group labels, in place of the tab buttons. */
    #infobox #usable-items::before,
    #infobox #exploration-items::before {
        display: block;
        margin-bottom: 4px;
        font-family: 'Cinzel', Georgia, serif;
        font-size: 11px;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: rgba(212, 168, 39, 0.85);
    }

    #infobox #usable-items::before {
        content: 'Usable';
    }

    #infobox #exploration-items::before {
        content: 'Exploration';
    }

    #infobox #process-items-title,
    #infobox #process-button,
    #infobox #craft-empty-state {
        display: block !important;
        flex: 0 0 auto;
        margin: 0;
    }

    #infobox #process-items-title {
        font-family: 'Cinzel', Georgia, serif;
        font-size: 11px;
        letter-spacing: 0.14em;
        text-transform: uppercase;
        color: rgba(212, 168, 39, 0.85);
        text-align: center;
    }

    #infobox #process-button {
        box-sizing: border-box;
        width: 100%;
        max-width: none;
    }

    /* Item art keeps the 150px of the original design; only the height is let
       loose so it cannot overflow its third. */
    #infobox .slider-container {
        display: flex;
        flex-direction: column;
        align-items: center;
        min-height: 0;
        width: 100%;
    }

    /* The carousel arrows are `position: absolute; left/right: 0`, and neither
       .slider-container nor .carousel-row carries a single CSS rule in
       styles.css — so the arrows anchor to whichever ancestor happens to be
       positioned and end up pinned to the screen edges, with the right one
       clipped. Making the row the positioned ancestor and bounding its width
       puts them either side of the item image where they belong. */
    #infobox .carousel-row {
        position: relative;
        display: flex;
        align-items: center;
        justify-content: center;
        width: min(280px, 100%);
        margin: 0 auto;
        min-height: 0;
    }

    #infobox #usable-items img,
    #infobox #exploration-items img,
    #infobox #process-items img {
        width: 400px;
        height: 400px;
        max-width: 100%;
        max-height: 100%;
        object-fit: contain;
    }

    /* --- Minigame host ------------------------------------------------------
       Two clipping bugs, both in the parent app rather than in the games.

       1. styles.css sets `.minigame-window { height: calc(95% * 4 / 3) }` below
          768px — a 3:4 ratio forced onto a box that is not 3:4, which makes the
          iframe 126.67% of its container. #minigame-container is
          `overflow: hidden`, so the bottom fifth of every minigame is cut off.
          That is what hides the reign palace CTA button: #quest-actions is the
          last child, so it lands in the clipped zone.
       2. `.story-window` is `width/height: 100%` with `padding: 15px` and no
          border-box, so it overflows the viewport by 30px.

       Fixing these two improves all 20 minigames at once, without editing a
       single game. Deliberately scoped to portrait: the same 768px rule also
       clips small landscape phones, but landscape stays untouched as agreed. */
    .story-window {
        box-sizing: border-box;
    }

    .minigame-window {
        width: 100%;
        height: 100%;
    }

    /* --- Notification history / world chronicle ----------------------------
       `width: 340px` is hard-coded with no breakpoint anywhere, so it overflows
       any phone narrower than that. Width only — the transform centring is left
       alone. Two live elements share these rules: #npc-history-container and
       #event-history-container (npc.ui.js:62 and :85). */
    #npc-history-container,
    .npc-history-container {
        width: min(340px, calc(100vw - 24px));
    }

    /* --- Collect tray ------------------------------------------------------
       Centred instead of pinned bottom-left. The item image is the collect
       button (map.js), so it is scaled up for a comfortable tap target. */
    /* Centred with `inset: 0; margin: auto`, deliberately NOT with
       `transform: translate(-50%, -50%)`.

       styles.css animates this element with itemContainerAppear, whose
       keyframes set `transform: scale() translateY()`. An animated transform
       replaces the static one for the whole 0.5s run, so a transform-based
       centring is discarded while the animation plays: the tray lands with its
       top-left corner on the screen centre — down and to the right — bounces
       through the scale keyframes, then snaps into place when the animation
       ends. Centring through margin leaves `transform` free for the animation,
       which now plays correctly in the centre. */
    #items-container {
        position: fixed;
        inset: 0;
        margin: auto;
        width: min(320px, 88vw);
        min-width: 0;
        height: min(300px, 72dvh);
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 10px;
        padding: 20px 16px;
    }

    /* Dimming overlay.

       This cannot hang off #items-container::before. Within a stacking context
       the element's own background paints FIRST and negative z-index children
       paint on top of it (CSS 2.1 painting order, steps 1 then 2) — so that
       version dimmed the itemcontainer.png frame itself and the tray rendered
       as a plain dark box with the carousel floating in it.

       Anchoring it to body::after instead places it at z-index 1004: above the
       map and the HUD, below the tray's own 1005. The :not([data-pt-hidden])
       guard is a no-op in the real app, where the tray is added to and removed
       from the DOM, and lets the demo toggle it. Still no DOM change and no
       JavaScript. Tapping it does nothing — Close is the only way out. */
    body:has(#items-container:not([data-pt-hidden]))::after {
        content: '';
        position: fixed;
        inset: 0;
        background: rgba(8, 5, 3, 0.62);
        z-index: 1004;
    }

    #items-container .carousel-row {
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
    }

    #items-container .carousel-row img {
        width: 150px;
        height: 150px;
    }

    #items-container .item-label {
        margin: 0;
        text-align: center;
    }
}


/* ============================================================================
   6. TABLET PORTRAIT (744–1100px) — same layout, larger touch targets
   ============================================================================
   Starts at 744px, the narrowest real iPad (Mini 6th gen), so this boundary
   matches the phone/tablet split used by the type scale in section 7. The
   previous 601px was an invented number and left 601–743px devices with tablet
   spacing but phone-sized type.
   ========================================================================== */

@media (orientation: portrait) and (min-width: 744px) and (max-width: 1100px) {

    :root {
        --pt-btn: 76px;
        --pt-edge: 14px;
        --pt-util-h: 38px;
    }

    #login-save-btns-container button {
        height: 32px;
        font-size: 11px;
        padding: 0 12px;
    }

    /* NOTE: an earlier `#diplomacyWindow > *, .story-tutorial-window > * { …
       margin-left: auto; margin-right: auto }` lived here to cap line length.
       It was removed because auto margins on a flex item override
       `align-items: stretch` and shrink the item to its content width — which
       collapsed the SELECT button into a small pill AND squeezed .stw-panel,
       leaving the territory cards in a narrow column with wide empty margins.
       One rule, two symptoms. At 768px it also capped 744px to 720px, so it
       was buying almost nothing. Do not reintroduce it. */

    /* Spacing and sizes that genuinely need a tablet-sized screen to make
       sense. Type is NOT set here — see the fluid block below, which scales at
       every width instead of depending on this breakpoint matching. */
    #diplomacyWindow .tab { padding: 12px 8px; }
    .affiliated-clan-tab { padding: 11px 8px; }
    .image-slider-container { min-height: 32dvh; }
    .description-container { max-height: 34dvh; }
    #diplomacyWindow .select-button,
    #diplomacyWindow .close-button { padding: 13px 20px; }

    #armyBuildUpWindow #add-ten-units-btn { padding: 14px 18px; }
    .rlm-coloa-btn { padding: 12px 22px; }

    /* The .story-tutorial-window prefix is required: styles.css already scopes
       .rlm-scene that way, so a bare .rlm-scene would lose on specificity. */
    .story-tutorial-window .rlm-scene { height: 230px; }

    /* NPC companions chooser — wider, still two columns. Its own stacking rule
       is @media (max-width: 600px), which a tablet never crosses. */
    .npc-selection-content {
        max-width: 680px;
        padding: 34px 30px;
    }
}


/* ============================================================================
   7. TYPE SCALE — one tier per device class
   ============================================================================
   HOW TO ADJUST A FONT SIZE
   -------------------------
   Every size below is a variable set once per device tier. To change how big
   text is on one specific iPad, edit ONE number in that iPad's :root block —
   nothing else. The rules that consume the variables never need touching.

   The five variables:
       --pt-fs-body     body copy: clan description, army stats, card type
       --pt-fs-tab      tabs, clan tabs, action buttons
       --pt-fs-title    unit name, realm card title
       --pt-fs-symbol   the large realm card glyph
       --pt-fs-hint     small print ("Tap to learn more")

   Example: text too small on iPad Air only -> find the "iPad / iPad Air" block
   and raise --pt-fs-body from 18px to 20px. No other tier is affected.

   DEVICE WIDTHS (CSS px, portrait). Verify a new device with `innerWidth`
   in the console before adding a tier.
       iPad Mini 6th gen ............ 744
       iPad Mini 5th & earlier ...... 768
       iPad 9th gen (10.2") ......... 810
       iPad 10th gen / Air 4,5,M2 ... 820
       iPad Air 3rd gen (10.5") ..... 834
       iPad Pro 11" (all gens) ...... 834
       iPad Pro 12.9" (all gens) ... 1024
       iPad Pro 13" (M4) ........... 1032   <- above the old 1024 ceiling, which
                                               is why the whole module was dead
                                               on that device until now
   ========================================================================== */

/* --- Phones and small tablets (default) --------------------------------- */
@media (orientation: portrait) and (max-width: 743px) {
    :root {
        --pt-fs-body: 15px;
        --pt-fs-desc: 18px;
        --pt-fs-tab: 12px;
        --pt-fs-title: 17px;
        --pt-fs-symbol: 36px;
        --pt-fs-hint: 11px;
    }
}

/* --- iPad Mini — 744 and 768 -------------------------------------------- */
@media (orientation: portrait) and (min-width: 744px) and (max-width: 768px) {
    :root {
        --pt-fs-body: 17px;
        --pt-fs-desc: 19px;
        --pt-fs-tab: 14px;
        --pt-fs-title: 21px;
        --pt-fs-symbol: 44px;
        --pt-fs-hint: 13px;
    }
}

/* --- iPad and iPad Air — 810, 820 --------------------------------------- */
@media (orientation: portrait) and (min-width: 769px) and (max-width: 833px) {
    :root {
        --pt-fs-body: 18px;
        --pt-fs-desc: 20px;
        --pt-fs-tab: 15px;
        --pt-fs-title: 22px;
        --pt-fs-symbol: 46px;
        --pt-fs-hint: 14px;
    }
}

/* --- iPad Pro 11" and iPad Air 10.5" — 834 ------------------------------ */
@media (orientation: portrait) and (min-width: 834px) and (max-width: 1023px) {
    :root {
        --pt-fs-body: 19px;
        --pt-fs-desc: 21px;
        --pt-fs-tab: 16px;
        --pt-fs-title: 24px;
        --pt-fs-symbol: 48px;
        --pt-fs-hint: 14px;
    }
}

/* --- iPad Pro 12.9" and 13" — 1024, 1032 -------------------------------- */
@media (orientation: portrait) and (min-width: 1024px) and (max-width: 1100px) {
    :root {
        --pt-fs-body: 21px;
        --pt-fs-desc: 23px;
        --pt-fs-tab: 17px;
        --pt-fs-title: 26px;
        --pt-fs-symbol: 54px;
        --pt-fs-hint: 15px;
    }
}

/* --- The rules that consume the variables. Do not edit to change a size. -- */
@media (orientation: portrait) and (max-width: 1100px) {

    /* Diplomacy */
    .description-container {
        font-size: var(--pt-fs-desc);
        line-height: 1.6;
    }

    #diplomacyWindow .tab,
    .affiliated-clan-tab,
    #diplomacyWindow .select-button,
    #diplomacyWindow .close-button {
        font-size: var(--pt-fs-tab);
    }

    /* Army build-up */
    #armyBuildUpWindow #army-force-select {
        font-size: var(--pt-fs-title);
    }

    #armyBuildUpWindow #army-force-desc,
    #armyBuildUpWindow #army-force-cost,
    #armyBuildUpWindow #equipment-status,
    #armyBuildUpWindow #add-ten-units-btn {
        font-size: var(--pt-fs-body);
        line-height: 1.55;
    }

    /* Territory */
    .rlm-coloa-title { font-size: var(--pt-fs-title); }
    .rlm-coloa-sub,
    .rlm-coloa-btn { font-size: var(--pt-fs-tab); }
    .rlm-front-symbol { font-size: var(--pt-fs-symbol); }
    .rlm-front-type { font-size: var(--pt-fs-body); }
    .rlm-front-hint { font-size: var(--pt-fs-hint); }
}


/* ============================================================================
   8. NPC NOTIFICATION POPUP — docked just above the NPC button
   ============================================================================
   In portrait the NPC button sits in row 2 at left: 83.333%, so the popup
   follows it there instead of floating at the bottom centre.

   VERTICAL: the button's top edge is (--pt-row2 + --pt-btn) from the viewport
   bottom, so the popup's bottom sits 10px above that. Both values are the same
   variables that place the button, so the gap holds on every device tier.

   HORIZONTAL: anchored near the right edge rather than centred on the button.
   The button's centre is 16.667% from the right — 65px on a 390px phone — so a
   centred popup would hang off-screen. At `right: calc(10% - --pt-btn / 2)`
   with a 200px cap it clears the edge on every tier (390px phone: spans
   181–381px).

   ANIMATION: scrollUnroll/scrollRollUp in styles.css bake `translateX(50%)`
   into every keyframe, because the base layout centres the popup with
   `right: 50%` plus that transform. An animated transform replaces the static
   one for the whole run, so reusing those keyframes here would drag the popup
   half its width to the right while animating and snap on completion. The
   portrait copies below drop the translate and keep only the scaleY unroll,
   with the origin at the bottom so it grows upward out of the button.
   ========================================================================== */

@media (orientation: portrait) and (max-width: 1100px) {

    .npc-notification-popup {
        bottom: calc(var(--pt-row2) + var(--pt-btn) + 10px);
        top: auto;
        right: calc(10% - var(--pt-btn) / 2);
        left: auto;
        transform: none;
        max-width: min(200px, calc(100vw - 2 * var(--pt-edge)));
    }

    .npc-notification-popup.scroll-open {
        animation-name: ptScrollUnroll;
    }

    .npc-notification-popup.scroll-close {
        animation-name: ptScrollRollUp;
    }

    @keyframes ptScrollUnroll {
        0%   { opacity: 0; transform: scaleY(0); transform-origin: bottom center; }
        50%  { opacity: 1; }
        100% { opacity: 1; transform: scaleY(1); transform-origin: bottom center; }
    }

    @keyframes ptScrollRollUp {
        0%   { opacity: 1; transform: scaleY(1); transform-origin: bottom center; }
        100% { opacity: 0; transform: scaleY(0); transform-origin: bottom center; }
    }
}
