/* ═══════════════════════════════════════════════════════════════════
   PORTAL EMBED OVERRIDES (styles/embed.css)

   Everything in this file applies ONLY inside the Tools Portal iframe, under
   html.framed. Each page's init() adds that class:

     if (window.self !== window.top) document.documentElement.classList.add('framed');

   ⚠ WITHOUT THAT LINE EVERY RULE HERE IS DEAD CSS — it reviews as fixed and
   still misbehaves in production. index.html sets it in app.js init();
   tutorial.html sets it in tutorial.js. Both set it BEFORE the first
   rvo-height is posted (auth.js completeSignIn runs init() → signalRendered()),
   so the corrected layout is what gets measured.

   Linked LAST by BOTH index.html and tutorial.html. It cannot live in app.css:
   tutorial.html does not load app.css, and duplicating these rules into a
   second stylesheet would guarantee drift.

   ── WHY ANY OF THIS IS NEEDED ──
   The portal frames each tool with scrolling="no" and sets the iframe height
   from the tool's OWN rvo-height message. Two consequences drive every rule
   below:
     1. the portal page is the only scroll surface — the frame has no local
        viewport of its own, so position:fixed and sticky do not work;
     2. inside the frame, 100vh EQUALS THE HEIGHT WE LAST REPORTED. Viewport
        units are self-referential.

   Nothing here is observable outside the portal. Standalone rendering is
   byte-identical to not shipping this file at all.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 1. THE VIEWPORT-HEIGHT RATCHET ─────────────────────────────────
   A min-height:100vh on a CHILD of <body> makes the frame grow forever:

     reported  = nav + max(child content, 100vh) = nav + H
     portal sets frame = nav + H   ⇒   100vh becomes nav + H
     reported  = nav + (nav + H)   ⇒   ResizeObserver fires ⇒ repeat

   Every pass adds the nav's height. Unbounded. The symptom is dead space
   below the report that KEEPS GROWING, and it only shows on the results page.

   Fix by OVERRIDING, never by capping the posted height — a numeric cap clips
   a legitimately tall report (two letter pages ≈ 2300px) and hides the bug.

   Every offending selector has to be covered; one override does not cover a
   second offender, and the symptom is identical, so a miss reads as "the fix
   didn't work". As of this file the full set is:
     hfg-tool-shell.css:66   body
     hfg-tool-shell.css:189  #mv .mapp.dk
     hfg-tool-shell.css:193  #mv .sub
     hfg-tool-shell.css:284  #pg2
     hfg-tutorial-v2.css:31  #tut-app                                        */
html.framed body{min-height:0;}
html.framed #pg2{min-height:0;}
html.framed #tut-app{min-height:0;}
/* The mobile live screen is a body child too, and both of these sit inside it.
   A phone opening the portal would otherwise ratchet the same way. */
html.framed #mv .mapp.dk,
html.framed #mv .sub{min-height:0;}

/* ⚠ THE AUTH GATE IS THE EXCEPTION, AND IT DEPENDS ON THE RULE ABOVE.
   .auth-gate is position:fixed, and a fixed element contributes NOTHING to
   scrollHeight. While the gate is up every other region is display:none, so
   with body's min-height zeroed the measured height collapses to ~0, the portal
   sizes the frame to nothing, and the officer sees an empty tab instead of a
   sign-in button. (Before this file, body's 100vh was accidentally holding the
   frame open.) So framed, the gate joins the flow and carries its own height —
   which is also more honest: we report the height of what we are actually
   showing. inset:0 goes inert once it is static; the flex centering still
   centres the contents inside this box. */
html.framed .auth-gate{position:static;min-height:420px;}

/* ── 2. position:fixed AND sticky DO NOT WORK FRAMED ────────────────
   sticky has no scroll viewport to stick to, so it is inert (harmless).
   fixed resolves against the iframe viewport, which EQUALS the full content
   height — so a bottom:0 bar pins to the bottom of the entire tall frame
   instead of floating over the reader. Flatten both, and drop any padding
   that was reserving space for the fixed bar. */
html.framed .site-nav{position:static;}

/* ⚠ SPECIFICITY: #tut-app .site-nav is (1,1,0) and BEATS a bare
   html.framed .site-nav at (0,2,0). The ID has to be repeated or this
   silently does nothing — writing the obvious selector and having it not
   apply is the whole failure mode here. */
html.framed #tut-app .site-nav{position:static;}

/* Prev/Next go into the flow directly under the step content, which is a
   better embedded experience than faking sticky anyway — no scrolling to
   reach them. The 104px was reserving room for the fixed bar. */
html.framed #tut-app .tut-ctrl{position:static;box-shadow:none;}
html.framed #tut-app{padding-bottom:30px;}

/* ── 3. THE BACK-TO-PORTAL LINK ─────────────────────────────────────
   app.js sets portalLink.hidden when framed (iframe touchpoint 3) — a
   redundant escape hatch out of the portal's own workspace. That only works
   because .nav-logo-link[hidden]{display:none} is restated in
   hfg-tool-shell.css:77; the display:flex on that anchor outspecifies the UA
   [hidden] rule. Noted here because the two halves live in different files
   and the failure is silent. */

/* ── 4. SCROLL-TO-TOP IS DONE WITH FOCUS, NOT CSS ───────────────────
   window.scrollTo is a silent no-op framed (a cross-origin child cannot
   scroll its parent), so app.js showPage() and tutorial-kit's step change
   focus a tabindex="-1" heading instead: moving focus is a user-agent action,
   and the UA scrolls EVERY ancestor scroll container to reveal the target,
   iframe boundary included. These suppress the ring for that programmatic
   case only — a real keyboard user keeps their focus indicator. */
#pg2Heading:focus:not(:focus-visible),
#pg1Heading:focus:not(:focus-visible),
#tut-app .tut-headline:focus:not(:focus-visible){outline:none;}
