/* ============================================================================
   mobile.css — Focal Comics
   ----------------------------------------------------------------------------
   HOW THIS FILE WORKS

   1. Every rule in here lives inside a @media (max-width: …) block. Nothing in
      this file can affect the desktop layout. If you add a rule, it goes inside
      a media query — no exceptions.

   2. It is linked LAST, after each page's inline <style>, so at equal
      specificity it wins. That's deliberate: it lets us correct the shared
      shell in one place instead of editing 30-odd files.

   3. NEW PAGES: add this one line before </head> and the page inherits every
      fix below — nav, tap targets, footer, tables, media:

          <link rel="stylesheet" href="mobile.css">

      (members/ pages use ../mobile.css)

   BREAKPOINTS — matched to the ones already used in the page shells:
     1280px  the nav collapses to the hamburger. Not 900: the desktop nav
             overflows the header by up to 107px between 900 and ~1100 and the
             links stay wrapped until ~1180, which is exactly where a 1080-wide
             monitor in portrait lands.
      860px  heroes.html character-select
      600px  phone-specific typography and spacing
   ========================================================================== */


/* --------------------------------------------------------------------------
   NAV — collapses at 1280px, not 900. Measured on index.html: the desktop nav
   overflows the header by 107px at 984 and 27px at 1064, and the links stay
   compressed and wrapping until ~1180. A monitor turned portrait is usually
   1080 wide, which is why "FOCAL POINT" was breaking onto two lines there.
   -------------------------------------------------------------------------- */
@media (max-width: 1280px) {

  /* --- the hamburger was landing in the middle of the header ---------------
     nav is `display:grid; grid-template-columns: 1fr auto 1fr` with four
     children: .nav-brand, .nav-links, .nav-right, .nav-toggle. On mobile
     .nav-right goes display:none and .nav-links goes position:fixed — both
     leave the grid flow. That left only the brand and the toggle, so the
     toggle occupied the middle `auto` track and `justify-self:end` pinned it
     to the right edge of *that track*, i.e. the centre of the header.
     Two tracks puts it back on the right edge of the header. */
  nav {
    grid-template-columns: 1fr auto;
  }

  /* The toggle was a ~34px target. Pad it out to a comfortable thumb size
     without moving the icon: negative margin cancels the extra padding so the
     bars stay optically aligned with the nav's 1.6rem gutter. */
  .nav-toggle {
    justify-self: end;
    padding: .6rem;
    margin-right: -.6rem;
    min-width: 44px;
    min-height: 44px;
  }
}


/* ==========================================================================
   1. SHARED SHELL — applies to every page using the standard nav/footer
   ========================================================================== */

@media (max-width: 900px) {

  /* --- FOOTER: link rows were 15px tall ---------------------------------- */
  .footer-nav {
    gap: .1rem 1.35rem;
  }
  .footer-nav a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 .2rem;
  }
  .footer-copy {
    font-size: .8rem;
    line-height: 1.7;
  }

  /* --- Nothing should be able to push the page sideways ------------------ */
  html,
  body {
    overflow-x: hidden;
  }

  /* Media and embeds stay inside their column. */
  img,
  video,
  iframe,
  svg {
    max-width: 100%;
  }

  /* ...except art layers, which set their own explicit width/height against a
     parent or the viewport. Clamping those doesn't scale the image, it squashes
     it — and for anything positioned with a negative offset it also knocks the
     image off-centre, because the offset assumed the full width.

     Both bugs showed up here: Patina rendered at a 0.398 ratio against a natural
     0.671, and the planet art (width:120%; left:-10% of a 58px orb) was clamped
     to 58x70 and sat 5.8px left of its own label and glow.

     RULE OF THUMB: any <img> the CSS sizes explicitly belongs in this list. */
  .hero-figure,
  .ha-slot img,
  .orb .w-art,
  .wv-orb img {
    max-width: none;
  }

  /* Long unbroken strings (URLs, emails, IDs) wrap instead of overflowing. */
  p,
  li,
  h1, h2, h3, h4,
  .lead, .pod-lead, .pod-sub, .eth-lead {
    overflow-wrap: break-word;
  }

  /* Wide content scrolls in its own box rather than stretching the page. */
  table,
  pre {
    display: block;
    max-width: 100%;
    overflow-x: auto;
  }

  /* --- Interactive chrome: give every control a real tap target ---------- */
  .lk,
  .chip,
  .tag,
  .c-more,
  .spot-link,
  .tier-mini-link,
  .btn-login,
  .btn-join {
    min-height: 38px;
    display: inline-flex;
    align-items: center;
  }

  /* Links sitting inside a sentence are deliberately NOT padded out — giving an
     inline link a 44px box breaks the line rhythm of the paragraph. Their tap
     area is the text line plus its leading, which is the accepted trade-off. */

  /* Buttons and inputs at >=16px so iOS Safari stops zooming the page on
     focus — this is the specific cause of the "form jumps when I tap it" bug. */
  input,
  select,
  textarea {
    font-size: 16px;
  }
  input,
  select,
  textarea,
  button[type="submit"],
  .form-submit {
    min-height: 44px;
  }
}


/* ==========================================================================
   2. PHONE-SPECIFIC TYPE AND SPACING
   Decorative Oswald kickers/badges keep their small tracked size — that is the
   brand voice. What gets raised here is body copy and anything you tap.
   ========================================================================== */

@media (max-width: 600px) {

  .pod-lead,
  .hub-lead,
  .collab-sub,
  .sp-blurb,
  .spot-blurb,
  .eth-lead {
    font-size: 1rem;
    line-height: 1.6;
  }

  .legal p,
  .legal li,
  .faq-a,
  .card p,
  .creative p {
    font-size: .95rem;
    line-height: 1.65;
  }

  /* Hero titles were sized off vw and could still overrun a 390px screen. */
  .pod-title {
    font-size: clamp(2rem, 11vw, 3rem);
    overflow-wrap: break-word;
  }

  /* Home, Socials and Store are dropped on mobile — Home and Socials are both
     reachable from the nav, and that leaves Contact / Privacy / Terms as a
     single tidy row instead of six links wrapping across two.
     Matched on href rather than position so reordering the footer won't
     silently hide the wrong links. */
  .footer-nav li:has(> a[href="index.html"]),
  .footer-nav li:has(> a[href="socials.html"]),
  .footer-nav li:has(> a[href="store.html"]) {
    display: none;
  }

  /* The three that remain, evenly spaced on one row. */
  .footer-nav {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .1rem .5rem;
    max-width: 20rem;
    margin-left: auto;
    margin-right: auto;
  }
  .footer-nav li {
    display: flex;
    justify-content: center;
  }

  /* Section padding tightened so content isn't squeezed into a narrow column. */
  .legal,
  .collab,
  .hub,
  section.sp {
    padding-left: 1.1rem;
    padding-right: 1.1rem;
  }

  /* Card grids: one clean column instead of cramped multi-column. */
  .hub-grid > .hub-card,
  .roster > .creative {
    flex: 1 1 100%;
  }
  .roster {
    grid-template-columns: 1fr;
  }

  /* Fixed-height roster cards clip their own content on a narrow screen. */
  .creative {
    height: auto;
    min-height: 0;
  }
}


/* ==========================================================================
   3. index.html — hero art was burying the copy (from 1080, not 900: below
      that width she stops sitting beside the copy and starts sitting on it)
   The mobile rule `.fig-right { opacity:.16 }` never took effect: .hero-figure
   runs `heroRise` with animation-fill-mode:both, and that keyframe ends at
   `opacity: 1`. CSS animations outrank normal declarations, so the animated
   value won and the art sat at full opacity behind the tagline.
   Cancelling the animation on mobile lets the intended .16 apply.
   ========================================================================== */

@media (max-width: 1080px) {

  .hero-figure {
    animation: none !important;
  }

  /* Placement. The page anchors her bottom:0 / right:-24%, carried over from the
     desktop composition where she stands to the right of the copy. As a
     full-bleed backdrop that read as floating and off-centre, for two reasons
     that only show up if you measure the artwork rather than its box:

       · patina.webp is 1476x2200 with its silhouette at 126,45 -> 1391,2119, so
         there are 81px of transparent padding under her boots. At the rendered
         size that put her feet 36px clear of the bottom edge.
       · her silhouette sits 20.5px right of the image's own centre, so centring
         the box does not centre her.

     bottom: -4.271% cancels the foot padding at any hero height — it is the
     81/2200 padding scaled by the 116% figure height. translateX(-51.389%)
     centres the silhouette rather than the box: -50% for the box, minus the
     20.5/1476 the artwork is off-centre by. Both are ratios, so they hold at
     every screen size.

     The left-fading mask goes too: it exists to blend her into the copy in the
     desktop layout, and on a centred backdrop it just makes her lopsided. */
  .fig-right {
    opacity: .16;
    top: auto;
    bottom: -4.271%;
    left: 50%;
    right: auto;
    transform: translateX(-51.389%);
    -webkit-mask-image: none;
    mask-image: none;
  }

  /* A scrim under the copy so the eyebrow and tagline hold up over the art. */
  .hero-content {
    position: relative;
    z-index: 2;
  }
  .hero-eyebrow,
  .hero-tagline {
    text-shadow: 0 2px 14px rgba(6, 7, 10, .95), 0 0 34px rgba(6, 7, 10, .8);
  }

  .hero-cta {
    gap: .7rem;
  }
  .hero-join {
    padding: .95rem 1.7rem;
  }
}


/* ==========================================================================
   4. heroes.html — the character page
   ----------------------------------------------------------------------------
   What was wrong: .topbar, .creator, .left-col, .right-col and .stats are all
   absolutely-positioned children of .screen, and .screen is
   `position:fixed; overflow:hidden` on a body locked to height:100vh. The
   existing 860px rule un-positioned only .left-col/.right-col, so the profile
   stacked to ~1215px inside a 844px fixed frame that could not scroll. Name,
   bio, lustre, info panel and associations were all unreachable, .stats stayed
   absolute and sat on top of the artwork, and .creator overlapped the Filter
   button.

   The fix: make .screen itself the scroll container and let its children flow
   in a deliberate order. body stays locked, so there's no double scrollbar.
   ========================================================================== */

@media (max-width: 860px) {

  .screen {
    position: fixed;
    inset: 0;
    bottom: 118px;                    /* clear the select bar */
    border: none;
    border-radius: 0;
    box-shadow: none;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    padding: 58px 0 2rem;             /* 58px clears the floating back pill */
  }

  /* Everything inside the frame joins the flow, in reading order. */
  .topbar,
  .creator,
  .left-col,
  .right-col,
  .stats {
    position: static;
    width: auto;
    max-width: none;
    inset: auto;
  }
  .topbar    { order: 1; }
  .right-col { order: 2; }
  .stats     { order: 3; }
  .left-col  { order: 4; }
  .creator   { order: 5; }

  .topbar {
    flex-wrap: wrap;
    gap: .55rem;
    padding: 0 1rem .8rem;
  }
  .tb-filter-btn {
    min-height: 38px;
  }

  .right-col {
    text-align: center;
    padding: 0 1rem;
    display: block;
  }

  /* .c-name was `white-space:nowrap` at up to 3.6rem — long names such as
     "Maiden Ireland" or "The Great White North" ran straight off the screen. */
  .c-name {
    white-space: normal;
    font-size: clamp(1.9rem, 9vw, 2.7rem);
    line-height: 1;
  }

  .c-actions {
    justify-content: center;
  }
  .c-btn {
    min-height: 40px;
    font-size: .66rem;
  }

  /* The bio scrolled in a 220px box inside a page that could not scroll.
     Now the frame scrolls, so let the bio simply be as long as it is. */
  .c-bio {
    flex: none;
    height: auto;
    max-height: none;
    overflow: visible;
    padding-right: 0;
    text-align: left;
    font-size: .96rem;
  }

  .lustre-frame,
  .info-frame {
    text-align: left;
  }

  /* .stats was width:min(560px,46vw) — 46vw is 179px on a 390px screen, which
     left roughly 40px for a 15-tick bar, so the bars rendered as slivers and
     the five legend labels ran together into "NULLUSTERRAASTRONOMNISMULTUS". */
  .stats {
    width: auto;
    padding: 1.4rem 1rem 0;
  }
  .stat-legend,
  .stat-row {
    /* value column carries the widest case, "10?" — see .stat-val in heroes.html */
    grid-template-columns: 74px 1fr 30px;
    gap: .5rem;
  }
  .stat-legend .lg {
    font-size: .52rem;
    letter-spacing: .02em;
  }
  .stat-label {
    font-size: .6rem;
    letter-spacing: .04em;
  }

  .left-col {
    padding: 1.4rem 1rem 0;
  }
  .assoc-list {
    overflow-y: visible;
  }
  .assoc-row {
    min-height: 40px;
  }

  .creator {
    text-align: center;
    padding: 1.4rem 1rem 0;
    opacity: .75;
  }

  /* Character art. .screen paints an opaque #0b0d11 at z-index:1, so anything
     placed behind it disappears completely. Make the frame transparent and let
     .bg-scene — which lives *inside* .screen, so it paints above the art — act
     as the legibility scrim instead. The art then reads clearly at the top and
     fades into the dark as you scroll into the copy. */
  .screen {
    background: transparent;
  }
  .bg-scene {
    background: linear-gradient(180deg,
      rgba(8, 9, 12, .40) 0%,
      rgba(8, 9, 12, .78) 44%,
      rgba(8, 9, 12, .93) 100%);
  }

  /* Top inset clears the back pill and the Heroes/Filter row, so the character's
     head isn't cropped behind the chrome. */
  .hero-art {
    position: fixed;
    inset: 96px 0 118px;
    z-index: 0;
    opacity: .62;
    align-items: flex-start;
  }
  .ha-slot {
    align-items: flex-start;
  }
  /* Sized past 100% on purpose: the figure is taller than the frame, so she
     crops slightly at the sides and runs behind the select bar the way a
     standing character should, instead of sitting as a small floating cutout. */
  .ha-slot img {
    height: 112%;
    width: auto;
    object-fit: contain;
    object-position: top center;
    animation: none;
  }

  /* The name and role sit over the figure — give them their own contrast. */
  .c-name,
  .c-role {
    text-shadow: 0 2px 12px rgba(6, 7, 10, .95), 0 0 30px rgba(6, 7, 10, .75);
  }

  /* The look-switcher was pinned at `right:480px` — 90px off the left edge of a
     390px screen. Rather than reposition it: on mobile the artwork is only a
     14%-opacity backdrop, so cycling it achieves nothing visible, and the
     control floated on top of the info panel. The Gallery button opens the same
     images properly, so hide the switcher here. */
  .art-swap-wrap,
  .art-swap-wrap.on {
    display: none;
  }

  /* A floating back pill, legible over whatever scrolls beneath it. */
  .back {
    top: 10px;
    left: 10px;
    z-index: 70;
    min-height: 40px;
    padding: 0 .95rem;
    border: 1px solid rgba(63, 198, 210, .28);
    border-radius: 100px;
    background: rgba(9, 11, 14, .88);
    backdrop-filter: blur(4px);
  }

  /* The select strip stays put and scrolls horizontally — good touch pattern. */
  .select-bar {
    height: 118px;
    scroll-snap-type: x proximity;
  }
  .sel-card {
    /* No fixed width: portraits are 1:1, and heroes.html gives the card
       aspect-ratio:1/1 with height:100%, so its width follows the bar height
       here as well. Pinning 96px would un-square it on phones. */
    scroll-snap-align: start;
  }
  .sc-role {
    font-size: .58rem;
  }
  .sc-name {
    font-size: .78rem;
  }

  .select-splash .sel-text {
    font-size: clamp(1.1rem, 6vw, 1.7rem);
    letter-spacing: .2em;
  }

  /* The select bar drifting on load is a viewport-units problem. On phones
     `100vh` is the LARGE viewport — the height with the browser's URL bar
     collapsed — so `body{height:100vh}` makes the page taller than what is
     actually on screen, and a `position:fixed; bottom:0` bar is anchored to a
     viewport that shifts while the browser chrome settles.

     `svh` is the SMALL viewport: the height with the URL bar showing. It does
     not change as the chrome moves, so nothing gets pushed below the fold and
     nothing animates back up. Pinning the frame to the body box instead of the
     viewport then removes the fixed-vs-visual-viewport quirk altogether.

     Two things this block is careful about:
       · it sits at the end of the section on purpose, because it has to come
         after the `position: fixed` declarations above to win the cascade;
       · everything is scoped to `body:has(.select-bar)`, i.e. heroes.html.
         `body` on its own would have applied a fixed height and a positioning
         context to all 33 pages at this width, and `.back` is not unique to
         this page — spotlight.html uses it too.
     Modals stay fixed — they are meant to cover the whole viewport. */
  @supports (height: 100svh) and (selector(:has(*))) {
    body:has(.select-bar) {
      height: 100svh;
      position: relative;
    }
    body:has(.select-bar) .screen,
    body:has(.select-bar) .hero-art,
    body:has(.select-bar) .select-bar,
    body:has(.select-bar) .back {
      position: absolute;
    }
  }
}


/* ==========================================================================
   5. heroes.html — the Full Bio overlay
   Nothing lives here any more, on purpose.

   This section used to carry align-items:flex-start, because .modal centres its
   card and a bio taller than the screen had its top edge pushed above the
   scroll origin — the card measured top:-1087px with scrollTop already at 0,
   so the first third of every long bio was unreachable.

   That whole class of bug is gone: the overlay is now exactly 100dvh with the
   bio text as the only scrolling element, so no part of the card can ever sit
   outside the scrollable area. The layout lives in heroes.html's own
   @media (max-width:840px) block, next to the desktop rules it overrides.
   ========================================================================== */


/* ==========================================================================
   6. multiverse.html — planets must stay clear of the copy
   The seven worlds are scattered across the full hero, which works on a wide
   screen but not a phone: .pod-content spans x 24–366 and y 321–589 at 390px,
   which buried Concursus, Exitus and Negativus. Re-lay them into two bands
   above the copy and one below it, so every world stays visible — and
   hit-testable, since .mv-worlds is pointer-events:none and the orbs are due
   to become links.

   The --x/--y/--sz values are inline style attributes, so overriding them from
   a stylesheet requires !important. Order below is DOM order, not world order.
   ========================================================================== */

@media (max-width: 700px) {

  .mv-worlds {
    z-index: 3;                       /* above .pod-content, which is z-index:2 */
  }
  /* .world gets pointer-events:auto from the page itself — the world-zoom
     feature needs it on desktop too, so it isn't a mobile-only concern. */

  /* Row 1 starts below the 66px fixed nav, row 2 clears row 1's label, and
     Primus sits below the copy. */
  .world:nth-child(1) { --x: 18% !important; --y: 16% !important; }   /* Secundus    */
  .world:nth-child(2) { --x: 50% !important; --y: 16% !important; }   /* Concursus   */
  .world:nth-child(3) { --x: 82% !important; --y: 16% !important; }   /* Futurus     */
  .world:nth-child(4) { --x: 18% !important; --y: 29% !important; }   /* Truculentus */
  .world:nth-child(5) { --x: 50% !important; --y: 29% !important; }   /* Exitus      */
  .world:nth-child(6) { --x: 82% !important; --y: 29% !important; }   /* Negativus   */
  .world:nth-child(7) { --x: 50% !important; --y: 80% !important; }   /* Primus      */

  /* The page sets width/height with !important at 760px, so --sz alone is
     ignored — these have to be set the same way. */
  .world {
    width: 58px !important;
    height: 58px !important;
  }
  .world:nth-child(7) {
    width: 74px !important;           /* Primus stays the anchor world */
    height: 74px !important;
  }

  /* Drop the orb box-shadows on mobile. They're tuned for an 86-92px desktop
     orb; at 58px a 30px blur with 5px spread throws a halo nearly as wide as
     the planet itself, which reads as a grey smudge and fights the glow baked
     into each world's art. The art carries the look on its own here.
     .w-glitch keeps its jitter but loses glitchGlow, which animates the shadow
     back in and would otherwise override this. */
  .orb,
  .w-teal .orb,
  .w-gold .orb,
  .w-prime .orb,
  .w-glitch .orb {
    box-shadow: none;
  }
  .w-glitch .orb {
    animation: worldGlitch 3.6s steps(1, end) infinite;
  }

  /* The page hides world labels below 760px, because the scattered desktop
     positions left no room for them. The banded layout above does have room —
     and an unlabelled orb is a poor thing to ask someone to tap, so bring the
     names back. */
  .w-label {
    display: block;
    font-size: .52rem;
    letter-spacing: .14em;
    bottom: -1.15rem;
  }
}


/* ==========================================================================
   7. store.html — drawer controls
   ========================================================================== */

@media (max-width: 900px) {

  .drawer .close {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-right: -.5rem;
  }
  .drawer .foot .btn {
    min-height: 48px;
  }
}


/* ==========================================================================
   8. Motion — respect the OS setting on the pages that animate heavily
   ========================================================================== */

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {

  .hero-rays,
  .rays,
  .ticker-track,
  .glint::after {
    animation: none !important;
  }
}
