/* Tutorial-specific styles.
   Visual language: "moonlight focus" — warm gold cores with violet falloff
   (same DNA as #card-side-hint), slow 1.6-2.4s breathing, never a strobe,
   never a transform on the card itself. The heavy lifting is done by the
   cue layer (tutorial-cues.js): rings, comets and a darkening veil. */

/* ------------------------------------------------------------------ */
/* Forced-move gating (pointer-events is the gate; visuals assist)     */
/* ------------------------------------------------------------------ */

/* Dim non-interactive elements during forced moves - ONLY player's elements */
.tutorial-forced #player-hand .card:not(.tutorial-allowed) {
    opacity: 0.4;
    pointer-events: none;
}

.tutorial-forced #player-board .card:not(.tutorial-allowed) {
    opacity: 0.4;
    pointer-events: none;
}

.tutorial-forced #end-turn-btn:not(.tutorial-allowed) {
    opacity: 0.4;
    pointer-events: none;
}

/* While the cue veil is up it does the darkening; relax the per-element
   dim so the scene stays readable instead of double-darkening. */
body.tutorial-veil-active .tutorial-forced #player-hand .card:not(.tutorial-allowed),
body.tutorial-veil-active .tutorial-forced #player-board .card:not(.tutorial-allowed) {
    opacity: 0.8;
}

/* Floating chrome above the veil must not compete with the spotlight — but it
   must still be USABLE. Reported: "being in tutorial should not grey out the
   options". At `opacity: 0.25` the menu read as disabled, and it is the only
   way to reach audio, full screen and the exit, so a learner who wanted to mute
   the game had to guess it was still pressable.

   The exit keeps its dim: during the guest tutorial it is the Skip button, it
   sits outside the menu by design, and the spotlight genuinely is the thing to
   look at. The MENU does not. */
body.tutorial-veil-active #skip-tutorial-btn {
    /* RECESSIVE, NOT ABSENT. At 0.25 this reads as "there is no Skip button" on
       a phone, where the control is a 36px ✕ rather than a worded slab: a
       quarter-opacity hairline cross over painted table art is not something a
       player finds. The spotlight is still plainly the brighter thing; the exit
       just has to remain a control the eye can land on. */
    opacity: 0.55;
    transition: opacity 0.45s ease;
}

/* And it comes all the way back the moment it is touched or focused, so a
   player who has found it gets an unambiguous target to press. */
body.tutorial-veil-active #skip-tutorial-btn:hover,
body.tutorial-veil-active #skip-tutorial-btn:focus-visible,
body.tutorial-veil-active #skip-tutorial-btn:active {
    opacity: 1;
}

/* ------------------------------------------------------------------ */
/* A modal question owns the screen                                    */
/* ------------------------------------------------------------------ */

/* Reported from a phone: "tutorial text is rendered above return to lobby
   confirmation text. if player presses return to lobby that text should
   obviously be rendered above tutorial text."

   The stacking was already right and was never the lever. Measured on the real
   signed-in tutorial at the 844x288 page box in Chromium, Firefox and WebKit —
   with the tip beside the dialog, and again with it forced onto the dialog's
   own seat — `elementFromPoint` returned the confirmation at every sampled
   point in the overlap, and the tip's luminance fell to 0.32-0.44 of its
   unobstructed value, which is exactly what the scrim's `rgba(6, 8, 20, 0.72)`
   predicts. What the player actually sees is that the coach mark SURVIVES: a
   gold-bordered, near-opaque panel of instructions sitting next to a question
   about ending the match. Two things to read, one decision to make.

   So the tutorial stops coaching for as long as the question is up. Not
   painting it is also the only cure immune to the one thing no run here can
   check: `.tutorial-tip` carries a `backdrop-filter` and therefore its own
   compositing layer, and iOS Safari — where the report came from — is not
   Playwright WebKit.

   `visibility`, never `display`: the tip's box is measured every frame by
   `positionTipNearElement`, `positionSkipButton` and the cue layer's
   `ornamentBlockers`, so dropping it out of layout would move the exit control
   out from under the player's thumb while the dialog is open.

   The veil deliberately stays. It darkens the board behind the dialog, which is
   the direction this fix is going in; removing it would hand contrast back to
   the very thing that was competing.

   Unscoped, and asserted to be: the last two versions of this mistake were
   written inside a phone media query because the report came from a phone, and
   left the identical collision on every desktop size.
   Contract: tests/unit/styles/tutorialYieldsToConfirm.test.js */
body.mf-confirm-open .tutorial-tip,
body.mf-confirm-open #tutorial-cue-layer {
    visibility: hidden;
}

/* The allowed card rises above its fanned siblings so the cue ring wraps
   a fully visible card instead of slicing across overlapping neighbors. */
.tutorial-forced #player-hand .card.tutorial-allowed,
.tutorial-forced #player-board .card.tutorial-allowed {
    z-index: 20;
}

/* Dim other potential attack targets during forced attack step */
.tutorial-attack-portrait #player-portrait {
    /* indicators.css deliberately forces portraits visible. This scoped
       override wins only while the player portrait is a disabled target. */
    opacity: 0.4 !important;
    pointer-events: none;
}

.tutorial-attack-portrait #opponent-board .card {
    opacity: 0.4;
    pointer-events: none;
}

.tutorial-attack-portrait #player-deck,
.tutorial-attack-portrait #opponent-deck,
.tutorial-attack-portrait #player-deck-wrapper,
.tutorial-attack-portrait #opponent-deck-wrapper,
.tutorial-attack-portrait #opponent-hand .card {
    opacity: 0.4;
    pointer-events: none;
}

/* Ensure the highlighted target stays visible */
.tutorial-attack-portrait #opponent-portrait.tutorial-highlight {
    opacity: 1;
    pointer-events: auto;
    filter: none;
}

/* Elements referenced by tutorial tips: a gentle luminance lift only —
   the cue layer draws the actual ring. (The old 2px gold ::after border
   read as a debug rectangle and is gone.) */
.tutorial-highlight {
    position: relative;
    z-index: 100;
}

/* ------------------------------------------------------------------ */
/* Tutorial tip panel                                                  */
/* ------------------------------------------------------------------ */
.tutorial-tip {
    position: fixed;
    background: linear-gradient(160deg, rgba(16, 11, 28, 0.97), rgba(31, 19, 46, 0.95));
    backdrop-filter: blur(1vmin);
    -webkit-backdrop-filter: blur(1vmin);
    border: 0.1vmin solid rgba(255, 215, 130, 0.55);
    border-radius: 1.2vmin;
    box-sizing: border-box;
    padding: 1.8vmin 2.2vmin;
    width: 40vmin;
    max-width: calc(100vw - 2vmin);
    max-height: calc(100vh - 2vmin);
    max-height: calc(100dvh - 2vmin);
    overflow-y: auto;
    color: #eedbf1;
    font-family: 'Spectral', Georgia, serif;
    font-size: 1.85vmin;
    line-height: 1.55;
    /* The explanation must never sit behind the persistent in-game volume
       control (10002). Story action cards remain the top interactive layer. */
    z-index: 10003;
    box-shadow:
        0 0.5vmin 2.4vmin rgba(0, 0, 0, 0.6),
        0 0 2.6vmin rgba(124, 90, 255, 0.28),
        inset 0 0 2.4vmin rgba(124, 90, 255, 0.08);
    animation: tutorial-tip-appear 0.35s ease-out;
}

.tutorial-tip::before {
    content: '✦';
    color: #ffd782;
    margin-right: 0.8vmin;
    text-shadow: 0 0 1vmin rgba(255, 215, 130, 0.75);
}

.tutorial-tip strong {
    color: #ffd782;
}

.tutorial-tip-dismiss {
    display: block;
    margin-top: 1.2vmin;
    padding: 0.6vmin 1.4vmin;
    background: rgba(255, 215, 130, 0.12);
    border: 0.1vmin solid rgba(255, 215, 130, 0.45);
    border-radius: 0.6vmin;
    color: #ffe9b0;
    font-family: 'Cinzel', 'Georgia', serif;
    font-weight: 600;
    font-size: 1.4vmin;
    letter-spacing: 0.08em;
    cursor: pointer;
    text-align: center;
    transition: background 0.2s, border-color 0.2s;
}

.tutorial-tip-dismiss:hover {
    background: rgba(255, 215, 130, 0.24);
    border-color: rgba(255, 215, 130, 0.7);
}

@keyframes tutorial-tip-appear {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* Tutorial tip positioning helpers - JS calculates exact positions */
.tutorial-tip.position-above {
    transform-origin: bottom center;
}

.tutorial-tip.position-left {
    transform-origin: right center;
}

.tutorial-tip.position-center {
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.tutorial-tip.position-right {
    transform-origin: left center;
}

/* Skip Tutorial and Return to Lobby are the SAME control — only the label
   changes — so they must read as the same control. This base seat used to be
   the old arrangement (above End Turn and offset 14vmin further right), which
   is not where the measured positioner puts it, so the button jumped on the
   first layout pass and sat somewhere else entirely for the whole tutorial.
   Declare the seat the rail actually uses: the lane beneath End Turn, sharing
   its right edge and its width. */
#skip-tutorial-btn {
    position: fixed;
    /* BELOW END TURN, MEASURED FROM END TURN'S OWN TOKENS.

       This was `calc(50% + 3.6vmin)`, which clears End Turn only while `vmin` is
       large. End Turn sits at `50% - (--mf-touch + 1.5 * --mf-dvmin) / 2` and is
       `--mf-touch` tall, so its bottom moves with the viewport while a flat
       3.6vmin does not: at 1400x640 the offset is 23px and clears by 6px, at an
       844x280 phone page box it is 10px and OVERLAPS by 10px. Measured during
       the guest tutorial — the one state where this button still floats — as
       808px² of it sitting on End Turn.

       Mirroring End Turn's own formula and adding the same lane gap puts the
       button one row below it at every size, which is what the seat always
       meant. */
    top: calc(
        50% + (var(--mf-touch, 44px) + 1.5 * var(--mf-dvmin)) / 2
        + 1.5 * var(--mf-dvmin)
    );
    right: var(--end-turn-right, 9.26vmin);
    transform: none;
    min-width: 28vmin;
    box-sizing: border-box;
    margin: 0;
    /* 44px WCAG tap-target floor on every pointer type; vmin padding still
       grows the button past the floor on large screens. The measured
       positioner reflows the End Turn group around this border box. */
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.75vmin 1.5vmin;
    font-size: 1.45vmin;
    line-height: 1.1;
    white-space: nowrap;
    /* Match the End Turn command layer. StoryFeed owns the layers immediately
       above this so narration can never be painted underneath the exit action. */
    z-index: 9450;
}


/* Stronger cue for the forced End Turn button only (the cue layer draws
   the surrounding ring; this keeps the button itself luminous). */
#end-turn-btn.tutorial-click-target {
    outline: 0.2vmin solid rgba(255, 219, 140, 0.85);
    outline-offset: 0.3vmin;
    animation: tutorial-click-target-glow 1.7s ease-in-out infinite;
}

#end-turn-btn.tutorial-click-target::before {
    content: '';
    position: absolute;
    inset: -0.55vmin;
    border: 0.22vmin solid rgba(255, 219, 140, 0.85);
    border-radius: 0.9vmin;
    box-shadow:
        0 0 1.4vmin 0.35vmin rgba(255, 215, 130, 0.55),
        0 0 3.4vmin 0.8vmin rgba(124, 90, 255, 0.35);
    pointer-events: none;
    animation: tutorial-click-target-halo 1.7s ease-in-out infinite;
}

@keyframes tutorial-click-target-glow {

    0%,
    100% {
        box-shadow:
            0 0 1vmin 0.2vmin rgba(255, 215, 130, 0.45),
            0 0 2.4vmin 0.45vmin rgba(124, 90, 255, 0.25);
    }

    50% {
        box-shadow:
            0 0 1.7vmin 0.45vmin rgba(255, 222, 150, 0.8),
            0 0 4.2vmin 0.9vmin rgba(124, 90, 255, 0.5);
    }
}

@keyframes tutorial-click-target-halo {

    0%,
    100% {
        opacity: 0.7;
    }

    50% {
        opacity: 1;
    }
}

/* ------------------------------------------------------------------ */
/* Cue layer (tutorial-cues.js)                                        */
/* ------------------------------------------------------------------ */

/* main.css sets `* { pointer-events: auto }`, so the layers must force
   none on every descendant or the veil would eat the very click the
   tutorial is asking for. */
#tutorial-cue-layer,
#tutorial-cue-layer *,
#tutorial-veil-layer,
#tutorial-veil-layer * {
    pointer-events: none !important;
}

#tutorial-veil-layer {
    position: fixed;
    inset: 0;
    /* Above #fx-layer (9400) so the scrim keeps focus during the tutorial,
       below hover-enlarged cards (9500) and spell announce (9600) so the
       game's reward moments always play above the darkness. Ladder locked
       by tests/unit/ui/zLayerLadder.test.js. */
    z-index: 9420;
}

.cue-veil {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.cue-veil-scrim {
    fill: #070412;
    fill-opacity: 0;
    transition: fill-opacity 0.45s ease;
}

.cue-veil--active .cue-veil-scrim {
    fill-opacity: 0.52;
}

/* Mid-drag / server action in flight: lift the darkness, the player acted. */
.cue-veil--suspended .cue-veil-scrim {
    fill-opacity: 0.1;
}

/* Hover-enlarged cards are trapped in lower stacking contexts and would
   render darkened under the scrim — soften while the player inspects. */
.cue-veil--soften .cue-veil-scrim {
    fill-opacity: 0.12;
}

#tutorial-cue-layer {
    position: fixed;
    inset: 0;
    z-index: 9998;
    /* Above everything interactive, below tips (10000), skip (10001)
       and the drag clone (15000). */
}

.cue {
    position: absolute;
    inset: 0;
}

.cue>* {
    transition: opacity 0.35s ease;
}

/* !important because the comet's fade rides an inline style the rAF loop
   writes each frame — suspension must beat it. */
.cue--hidden>*,
.cue-layer--suspended .cue>* {
    opacity: 0 !important;
}

/* Exception: while actively dragging, the destination ring stays lit —
   it is the one cue that still matters mid-gesture. */
body.card-dragging .cue-layer--suspended .cue--drag .cue-ring--target {
    opacity: 1 !important;
}

/* While the anchor is hover-enlarged the player is already engaged;
   ornaments step back instead of chasing the scaling card. */
.cue--anchor-hovered>* {
    opacity: 0 !important;
}

.cue-ring {
    position: absolute;
    box-sizing: border-box;
    border: 0.26vmin solid rgba(255, 219, 140, 0.92);
    box-shadow:
        0 0 1.1vmin 0.25vmin rgba(255, 215, 130, 0.5),
        0 0 3vmin 0.6vmin rgba(124, 90, 255, 0.3),
        inset 0 0 1.4vmin rgba(255, 222, 150, 0.28);
    animation: cue-ring-breathe 1.8s ease-in-out infinite;
}

@keyframes cue-ring-breathe {

    0%,
    100% {
        border-color: rgba(255, 219, 140, 0.7);
        box-shadow:
            0 0 0.9vmin 0.2vmin rgba(255, 215, 130, 0.38),
            0 0 2.4vmin 0.5vmin rgba(124, 90, 255, 0.22),
            inset 0 0 1vmin rgba(255, 222, 150, 0.18);
    }

    50% {
        border-color: rgba(255, 228, 165, 1);
        box-shadow:
            0 0 1.6vmin 0.4vmin rgba(255, 220, 140, 0.72),
            0 0 4.2vmin 0.9vmin rgba(124, 90, 255, 0.45),
            inset 0 0 1.8vmin rgba(255, 222, 150, 0.35);
    }
}

/* Look-here shimmer: quieter, lavender, no ornaments. */
.cue-ring--look {
    border: 0.18vmin solid rgba(238, 219, 241, 0.55);
    box-shadow:
        0 0 1vmin 0.2vmin rgba(238, 219, 241, 0.3),
        0 0 2.4vmin 0.5vmin rgba(124, 90, 255, 0.2);
    animation: cue-ring-breathe-look 2.6s ease-in-out infinite;
}

@keyframes cue-ring-breathe-look {

    0%,
    100% {
        border-color: rgba(238, 219, 241, 0.4);
        box-shadow:
            0 0 0.8vmin 0.15vmin rgba(238, 219, 241, 0.22),
            0 0 2vmin 0.4vmin rgba(124, 90, 255, 0.14);
    }

    50% {
        border-color: rgba(238, 219, 241, 0.75);
        box-shadow:
            0 0 1.3vmin 0.3vmin rgba(238, 219, 241, 0.4),
            0 0 3vmin 0.7vmin rgba(124, 90, 255, 0.28);
    }
}

/* Attack destination: the game's danger red, breathing faster. */
.cue-ring--target {
    border: 0.28vmin solid rgba(255, 128, 128, 0.95);
    box-shadow:
        0 0 1.2vmin 0.3vmin rgba(255, 107, 107, 0.55),
        0 0 3.2vmin 0.8vmin rgba(255, 107, 107, 0.3),
        inset 0 0 1.6vmin rgba(255, 140, 140, 0.3);
    animation: cue-ring-breathe-target 1.5s ease-in-out infinite;
}

@keyframes cue-ring-breathe-target {

    0%,
    100% {
        border-color: rgba(255, 118, 118, 0.75);
        box-shadow:
            0 0 1vmin 0.25vmin rgba(255, 107, 107, 0.4),
            0 0 2.6vmin 0.6vmin rgba(255, 107, 107, 0.22),
            inset 0 0 1.2vmin rgba(255, 140, 140, 0.2);
    }

    50% {
        border-color: rgba(255, 150, 150, 1);
        box-shadow:
            0 0 1.8vmin 0.5vmin rgba(255, 118, 118, 0.7),
            0 0 4.4vmin 1vmin rgba(255, 107, 107, 0.4),
            inset 0 0 2vmin rgba(255, 150, 150, 0.4);
    }
}

/* Bobbing chevron pointing at the click target. Rotation comes from JS
   via --cue-chevron-rot so the bob animation can own transform. */
.cue-chevron {
    position: absolute;
    overflow: visible;
    animation: cue-chevron-bob 1.4s ease-in-out infinite;
}

.cue-chevron path {
    stroke: #ffe9b0;
    filter: drop-shadow(0 0 0.6vmin rgba(255, 215, 130, 0.85));
}

@keyframes cue-chevron-bob {

    0%,
    100% {
        transform: rotate(var(--cue-chevron-rot, 0deg)) translateY(-0.55vmin);
    }

    50% {
        transform: rotate(var(--cue-chevron-rot, 0deg)) translateY(0.35vmin);
    }
}

/* Action word in the side-hint's voice: Cinzel, gold on deep violet. */
.cue-pill {
    position: absolute;
    font-family: 'Cinzel', 'Georgia', serif;
    font-weight: 700;
    font-size: 1.5vmin;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: #ffe9b0;
    padding: 0.5vmin 1.3vmin;
    background: linear-gradient(150deg, rgba(26, 16, 42, 0.95), rgba(46, 26, 58, 0.92));
    border: 0.1vmin solid rgba(255, 215, 130, 0.65);
    border-radius: 3vmin;
    box-shadow:
        0 0 1.6vmin rgba(124, 90, 255, 0.35),
        0 0.3vmin 1vmin rgba(0, 0, 0, 0.5);
    text-shadow: 0 0 0.8vmin rgba(255, 215, 130, 0.5);
    white-space: nowrap;
}

/* Staged escalation: the ring speaks first; the chevron and word only
   join after a few seconds of inaction (attract-mode convention). */
.cue--click .cue-chevron,
.cue--click .cue-pill,
.cue--drag .cue-pill {
    opacity: 0;
    transition: opacity 0.6s ease;
}

.cue--insist .cue-chevron,
.cue--insist .cue-pill {
    opacity: 1;
}

.cue--insist.cue--hidden>*,
.cue-layer--suspended .cue--insist>* {
    opacity: 0;
}

.cue--insist.cue--anchor-hovered>* {
    opacity: 0;
}

/* Drag guide: a comet arcing from the card to its destination. */
.cue-path {
    position: absolute;
    overflow: visible;
}

.cue-trail {
    stroke: rgba(255, 222, 150, 0.85);
    stroke-width: 0.32vmin;
    stroke-linecap: round;
    filter: drop-shadow(0 0 0.5vmin rgba(255, 215, 130, 0.7));
}

.cue-comet {
    position: absolute;
    left: 0;
    top: 0;
    width: 1.5vmin;
    height: 1.5vmin;
    border-radius: 50%;
    background: radial-gradient(circle,
            #fff8e0 0%,
            #ffd782 42%,
            rgba(255, 215, 130, 0) 72%);
    filter: drop-shadow(0 0 1vmin rgba(255, 215, 130, 0.9));
}

.cue-comet::after {
    content: '';
    position: absolute;
    right: 50%;
    top: 50%;
    width: 4.6vmin;
    height: 0.42vmin;
    transform: translateY(-50%);
    border-radius: 0.3vmin;
    background: linear-gradient(to left,
            rgba(255, 226, 160, 0.85),
            rgba(199, 158, 255, 0.35),
            rgba(199, 158, 255, 0));
}

/* Reduced motion: everything freezes at its brightest, the comet gives
   way to the full static guide path. Visibility must not degrade. */
.cue-layer--static .cue-ring,
.cue-layer--static .cue-chevron,
.cue-layer--static .cue-pill {
    animation: none;
}

.cue-layer--static .cue--click .cue-chevron,
.cue-layer--static .cue--click .cue-pill,
.cue-layer--static .cue--drag .cue-pill {
    opacity: 1;
}

.cue-layer--static .cue-comet {
    display: none;
}

.cue-layer--static .cue-trail {
    stroke-dasharray: 1.2vmin 0.9vmin;
}

@media (orientation: landscape) and (max-height: 560px) {
    /* Short landscape uses a wider proportional card so copy remains readable
       while preserving a real empty gutter beside the battle surfaces. */
    .tutorial-tip {
        width: min(62vmin, calc(100vw - 2vmin));
        font-size: max(13px, 3.4vmin);
    }

    .tutorial-tip-dismiss,
    .cue-pill {
        font-size: max(11px, 2.8vmin);
    }
}

/* Coarse tablet media does not match the short-phone query in mobile.css.
   Keep this secondary exit control honestly thumb-sized there as well; its
   measured positioner will reflow the whole End Turn command group around the
   new border box. */
@media (pointer: coarse) {
    #skip-tutorial-btn {
        min-width: 44px;
        min-height: 44px;
        padding: 8px 14px;
        font-size: max(13px, 1.45vmin);
        touch-action: manipulation;
    }
}

/* THE LANDSCAPE-PHONE SEAT: top-left, beside the story rail, on the chrome row.

   The base seat above is "one row below End Turn", and on a phone that row is
   the player's own summoner: the right rail is a solid column there (enemy
   crest, nameplate, End Turn, player crest, nameplate — top to bottom with a
   6px seam), so "below End Turn" lands squarely on the crest. Measured on the
   owner's iPhone 13 page box (844x280, 47px insets) during the guest tutorial:
   Skip at [650..766 x 168..212] over #player-portrait at [697..797 x 168..254]
   — 3027px² of the pill printed across the art, in Chrome, Firefox and WebKit
   alike, at the mulligan and on every turn. Reported: "Skip tutorial button is
   in the way ... weird place, it overlaps my summoner icon."

   No phone box has a free row on that rail, and the band inboard of it holds
   the countdown chip on the End Turn row and is narrower than this label on
   three of the six measured page boxes. The only lane that is free at every
   page box, in every phase, is the top-left: beside the story rail, level with
   the chrome menu in the opposite corner — so the tutorial's one piece of
   chrome sits on the chrome row, and the game keeps the whole right rail.

   `--mf-rail-top` is the row the menu sits on; `--story-rail-reach` is the
   story rail's true right edge INCLUDING its turn bar's overflowing arrow;
   `--board-row-left` is the centred slot row's left edge.

   THE WIDTH IS CAPPED TWICE, and both caps are load-bearing. The label wraps
   to a second line rather than reach the slot row (the band term), and it
   never grows past 124px even where the band is wider (the fixed term): a
   wrapped label does not shrink its box back to its longest line, so without
   the fixed cap a two-line German or Japanese pill would carry an empty
   142px slab into the mulligan header's centred text at 844x280. 124px is the
   longest single word any shipped locale puts on this button ("überspringen",
   ~85px at 13px bold with no tracking) plus the padding, with room for engine
   metrics. The band term binds only at 667x275, where it is 103px: the
   mulligan header's block starts at x 205 there and the story rail's reach
   ends at 90, so 6px of clearance on each side is all that box has to give —
   which is why the tracking `.game-command-btn` adds (0.035em, ~5px over
   twelve glyphs) is dropped here and the side padding is 6px. Two lines are
   still 44px tall and 13px text; the SE box wraps even English. Greedy
   wrapping on purpose — `text-wrap: balance` splits チュートリアル mid-word.
   Contract: tests/e2e-playwright/tutorial-skip-phone-seat.spec.js */
@media (orientation: landscape) and (max-height: 560px) and (max-width: 932px) {
    #skip-tutorial-btn {
        top: var(--mf-rail-top);
        right: auto;
        left: calc(var(--story-rail-reach) + 6px);
        max-width: min(
            calc(var(--board-row-left) - var(--story-rail-reach) - 21px),
            124px
        );
        min-width: 44px;
        min-height: 44px;
        padding: 6px;
        letter-spacing: 0;
        white-space: normal;
        overflow-wrap: normal;
        line-height: 1.15;
    }

    /* Korean breaks between syllable blocks by default, which put the break
       inside 건너뛰기 ("튜토리얼 건너 / 뛰기" at 667x275). `keep-all` holds
       Hangul together and breaks at the space instead. Japanese must NOT get
       this: チュートリアルをスキップ has no space, and keep-all would hold it
       on one 156px line through the cap. The label is UI copy in the
       document's own language, so `:lang()` is the honest scope. */
    #skip-tutorial-btn:lang(ko) {
        word-break: keep-all;
    }
}

@media (prefers-reduced-motion: reduce) {

    #end-turn-btn.tutorial-click-target,
    #end-turn-btn.tutorial-click-target::before,
    .cue-ring,
    .cue-chevron,
    .cue-pill {
        animation: none;
    }

    .tutorial-tip {
        animation: none;
    }

    .cue-veil-scrim {
        transition: none;
    }

    /* Defense-in-depth: the comet is JS-driven; if the runtime class lags
       the OS preference for a frame, the stylesheet still hides it. */
    .cue-comet {
        display: none;
    }
}

/* SKIP MUST OUTRANK THE MULLIGAN OVERLAY, ON EVERY SCREEN.
   `#mulligan-overlay` is z 9700 and covers the viewport; this button is 9450,
   so the overlay takes its click while the button stays `visible`, `flex` and a
   full 83-137x44 box — every geometry check passes and the screenshot looks
   right. The mulligan is the guest tutorial's OPENING step, so this is the one
   moment Skip most needs to work.

   This was first found on a phone and the lift was written inside
   `@media (orientation: landscape) and (max-height: 560px) and (max-width: 932px)`,
   which left the identical collision on every desktop viewport. Measured on
   main before this rule, Chromium at the mulligan: 1400x640, 1366x768 and
   1920x1080 all returned `mulligan-overlay` from `elementFromPoint` at Skip's
   own centre. The collision was never phone-only, so neither is the fix.

   Scoped to the phase, so ordering outside it is untouched, and still below the
   tutorial's own messages (10000).
   Contract: tests/e2e-playwright/tutorial-skip-reachable.spec.js */
body.game-active.mulligan-phase #skip-tutorial-btn {
    z-index: 9720;
}

/* ============================================================================
   ACTION INTENT LESSON — the tutorial teaching the intent step.
   ============================================================================
   The intent dialog is the topmost battle surface (`.action-intent-panel`,
   z 10009, over its own shield at 10008). Every tutorial layer lives below it:
   the coach mark at 10003, the cue layer at 9998, Skip at 9450, the veil at
   9420. So a ring drawn on one of the dialog's own option buttons, and a tip
   anchored to the dialog, both paint UNDERNEATH the thing they point at.

   These two rules lift exactly those two layers, and only while the dialog is
   showing a choice the tutorial is teaching. Both stay far below the story
   relationship stage (12000) and the drag clone (15000), so the ladder those
   surfaces rely on is unchanged; the cue layer is `pointer-events: none` in
   full, so raising it cannot capture input from the dialog beneath.

   The veil is deliberately NOT raised: at 9420 it dims the board while the
   dialog stays lit above it, which is the focus this moment wants.

   Written after the base declarations on purpose — `zLayerLadder.test.js`
   reads the FIRST z-index for each selector, and the base ladder is the
   contract.
   ========================================================================== */

body.tutorial-intent-choice #tutorial-cue-layer {
    z-index: 10011;
}

body.tutorial-intent-choice .tutorial-tip {
    z-index: 10012;
}

/* ON A SHORT LANDSCAPE BOX THE COACH MARK IS A BANNER, NOT A COLUMN.
   Measured on an iPhone 13 landscape page box (844x280): the dialog is 142px
   tall and the standard `min(62vmin, …)` tip is 173px wide and 164px tall —
   306px of content for 280px of page. No non-overlapping placement exists, so
   the tip solver falls back to least-overlap and parks the coach mark ON TOP of
   the dialog it is explaining, covering the open offer's own text.
   Matching the dialog's width turns five lines into two and buys back ~90px, so
   both fit with room to spare. Scoped to the intent lesson: every other coach
   mark points at a small target and wants the narrow card. */
@media (orientation: landscape) and (max-height: 560px) {
    body.tutorial-intent-choice .tutorial-tip {
        /* Wide enough to be two or three lines instead of five, NARROW enough
           to leave the right rail alone. Matching the dialog's own 820px was
           the obvious first move and was wrong: at 844x280 an 820px coach mark
           spans almost the whole width and — now that the lesson lifts it to
           z 10012 — covers Skip Tutorial and End Turn, trading one overlap for
           a worse one. 400px clears the rail at every landscape page box down
           to the iPhone SE's 667. */
        width: clamp(240px, 46vw, 400px);
        max-height: 40svh;
    }
}
