/* ============================================================
   SHARED / MAP-LEVEL STYLES
   Used across the whole overworld map screen: landmark markers, the
   HUD, the featured-work strip, the travel transition, and the shared
   destination-panel shell every biome opens into. This file loads
   before all per-biome files below - if you're editing something that
   applies to more than one biome, it almost certainly belongs here,
   not in an individual biome file.
   ============================================================ */

  /* ---------- BIOME MARKERS ---------- */
  .biome-marker{
    position:absolute;
    cursor:pointer;
    display:flex;flex-direction:column;align-items:center;
    transform:translate(-50%,-50%);
    transition:transform .25s cubic-bezier(.2,.9,.3,1.3);
    outline:none;
    z-index:10;
  }
  .biome-marker:hover, .biome-marker:focus-visible{transform:translate(-50%,-56%) scale(1.06);}
  /* ---- per-biome cursors: replaces the removed mouse-parallax with a
     simpler, fully CSS-native effect. Each marker shows a small themed
     icon cursor on hover instead of the plain arrow. ---- */
  .biome-marker[data-id="forest"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M4%2022C4%2012%2010%204%2022%204C22%2016%2014%2022%204%2022Z%22%20fill%3D%22%238ee08a%22%20stroke%3D%22%23173a1c%22%20stroke-width%3D%221.5%22/%3E%3Cpath%20d%3D%22M5%2021C10%2015%2015%2010%2021%205%22%20stroke%3D%22%23173a1c%22%20stroke-width%3D%221.3%22%20fill%3D%22none%22/%3E%3C/svg%3E") 5 21, pointer; }
  .biome-marker[data-id="mountain"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M2%2022L10%208L14%2014L18%206L24%2022Z%22%20fill%3D%22%23dfe8ff%22%20stroke%3D%22%231c2130%22%20stroke-width%3D%221.4%22%20stroke-linejoin%3D%22round%22/%3E%3Cline%20x1%3D%2218%22%20y1%3D%226%22%20x2%3D%2218%22%20y2%3D%221%22%20stroke%3D%22%231c2130%22%20stroke-width%3D%221.4%22/%3E%3Cpath%20d%3D%22M18%201L23%203L18%205Z%22%20fill%3D%22%23ffb15c%22%20stroke%3D%22%231c2130%22%20stroke-width%3D%221%22/%3E%3C/svg%3E") 13 22, pointer; }
  .biome-marker[data-id="castle"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M13%202L23%208V14C23%2020%2018%2024%2013%2025C8%2024%203%2020%203%2014V8Z%22%20fill%3D%22%23ffb15c%22%20stroke%3D%22%232c1c14%22%20stroke-width%3D%221.4%22%20stroke-linejoin%3D%22round%22/%3E%3Cpath%20d%3D%22M13%208L16%2013L13%2018L10%2013Z%22%20fill%3D%22%232c1c14%22/%3E%3C/svg%3E") 13 3, pointer; }
  .biome-marker[data-id="village"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M22%202C22%202%2010%204%206%2012C3%2018%204%2022%204%2022C4%2022%208%2023%2014%2020C22%2016%2024%204%2024%204C24%204%2022%202%2022%202Z%22%20fill%3D%22%23ffd97a%22%20stroke%3D%22%232e2110%22%20stroke-width%3D%221.4%22%20stroke-linejoin%3D%22round%22/%3E%3Cline%20x1%3D%224%22%20y1%3D%2222%22%20x2%3D%2210%22%20y2%3D%2216%22%20stroke%3D%22%232e2110%22%20stroke-width%3D%221.4%22/%3E%3C/svg%3E") 4 22, pointer; }
  .biome-marker[data-id="lighthouse"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M11%2024H15L14%2012H12Z%22%20fill%3D%22%239fd8ff%22%20stroke%3D%22%23122630%22%20stroke-width%3D%221.2%22/%3E%3Cpath%20d%3D%22M10%2012H16L15%206H11Z%22%20fill%3D%22%23eaf6ff%22%20stroke%3D%22%23122630%22%20stroke-width%3D%221.2%22/%3E%3Crect%20x%3D%2211.5%22%20y%3D%222%22%20width%3D%223%22%20height%3D%224%22%20fill%3D%22%23ffb15c%22%20stroke%3D%22%23122630%22%20stroke-width%3D%221%22/%3E%3Cpath%20d%3D%22M16%204L23%202M16%206L24%206M16%208L23%2010%22%20stroke%3D%22%23ffd97a%22%20stroke-width%3D%221.2%22%20stroke-linecap%3D%22round%22/%3E%3C/svg%3E") 13 24, pointer; }
  .biome-marker[data-id="swamp"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Cpath%20d%3D%22M14%202L6%2015H12L10%2024L20%2010H14Z%22%20fill%3D%22%237cff9e%22%20stroke%3D%22%230f2c1c%22%20stroke-width%3D%221.4%22%20stroke-linejoin%3D%22round%22/%3E%3C/svg%3E") 13 2, pointer; }
  .biome-marker[data-id="siteseeing"]{ cursor:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2226%22%20height%3D%2226%22%20viewBox%3D%220%200%2026%2026%22%3E%3Crect%20x%3D%2211%22%20y%3D%226%22%20width%3D%223%22%20height%3D%2218%22%20fill%3D%22%237c5640%22%20stroke%3D%22%233a2418%22%20stroke-width%3D%220.8%22/%3E%3Crect%20x%3D%2214%22%20y%3D%228%22%20width%3D%2210%22%20height%3D%225%22%20rx%3D%220.5%22%20fill%3D%22%23ff9e6b%22%20stroke%3D%22%233a2418%22%20stroke-width%3D%220.8%22/%3E%3Cpolygon%20points%3D%2224%2C8%2026.5%2C10.5%2024%2C13%22%20fill%3D%22%23ff9e6b%22%20stroke%3D%22%233a2418%22%20stroke-width%3D%220.8%22/%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%229%22%20height%3D%224.5%22%20rx%3D%220.5%22%20fill%3D%22%23ffb18c%22%20stroke%3D%22%233a2418%22%20stroke-width%3D%220.8%22/%3E%3Cpolygon%20points%3D%222%2C13%20-0.5%2C15.25%202%2C17.5%22%20fill%3D%22%23ffb18c%22%20stroke%3D%22%233a2418%22%20stroke-width%3D%220.8%22/%3E%3C/svg%3E") 13 6, pointer; }
  .biome-marker:focus-visible .icon-wrap{ filter:drop-shadow(0 0 10px var(--gold-bright)); }
  .biome-marker .icon-wrap{ animation:idlebob 4.5s ease-in-out infinite; position:relative; }
  .biome-marker .glow{
    position:absolute;inset:-34px;border-radius:50%;
    opacity:0;transition:opacity .35s ease;
    pointer-events:none; filter:blur(16px);
  }
  .biome-marker:hover .glow, .biome-marker:focus-visible .glow{opacity:.6;}
  .ground-shadow{
    position:absolute; left:50%; bottom:-6px; width:56px; height:14px;
    transform:translateX(-50%);
    background:radial-gradient(ellipse, rgba(0,0,0,.45), transparent 70%);
    pointer-events:none;
  }
  .ring{
    position:absolute;left:50%;top:50%;
    width:40px;height:40px;margin:-20px 0 0 -20px;
    border:1px solid rgba(244,234,209,0);
    border-radius:50%;pointer-events:none;
  }
  .biome-marker:hover .ring, .biome-marker:focus-visible .ring{ animation:pulseRing 1.6s ease-out infinite; }
  @keyframes pulseRing{
    0%{opacity:.6;transform:scale(.6);border-color:rgba(244,234,209,.6);}
    100%{opacity:0;transform:scale(2.2);border-color:rgba(244,234,209,0);}
  }
  .biome-label{
    margin-top:6px; font-family:'Cinzel',serif; font-size:13px; letter-spacing:.12em;
    color:var(--parchment); text-shadow:0 2px 6px rgba(0,0,0,.8);
    /* Visible from first load (not just on hover) - a first-time visitor
       can't tell a decorative sprite from a clickable landmark otherwise.
       The short description stays in .biome-tooltip below, which is still
       hover-only, so labels don't get lost. Kept the transition so it can
       still animate in with the marker's own entrance without a hard cut. */
    opacity:1; transform:translateY(0);
    transition:opacity .25s ease, transform .25s ease;
    white-space:nowrap; display:flex;align-items:center;gap:6px;
    /* Flat solid background (no blur/backdrop-filter - cheap), since with
       every label on-screen at once now, a couple of markers sit close
       enough that their labels can pass behind each other; this keeps
       whichever one is on top legible instead of both blurring together. */
    background:rgba(6,4,12,.55); padding:2px 8px; border-radius:5px;
  }
  .visited-star{ color:var(--gold-bright); font-size:11px; }

  /* Plain-language subtitle under each landmark's fantasy name (e.g.
     "Mastery Peak" / "Skills") - a first-time visitor shouldn't have to
     click into a region just to find out what it actually contains.
     Deliberately smaller/dimmer than .biome-label so the fantasy name
     stays the primary read and this stays a quiet clarification. */
  .biome-subtitle{
    margin-top:2px; font-family:'Spectral',serif; font-size:10px; letter-spacing:.04em;
    color:var(--parchment-dim); text-shadow:0 2px 6px rgba(0,0,0,.8);
    text-align:center; white-space:nowrap;
    background:rgba(6,4,12,.55); padding:1px 7px; border-radius:4px;
  }

  /* short per-biome hover blurb - small enough to not compete with the
     label, tinted with each marker's own accent color for cohesion */
  .biome-marker{ --tip-shift-x: 0px; }
  .biome-tooltip{
    --tip-accent: var(--parchment);
    margin-top:5px; max-width:172px;
    font-family:'Spectral', serif; font-size:10.5px; line-height:1.45; text-align:center;
    color:var(--parchment-dim);
    background:rgba(8,8,12,.6); backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px);
    border:1px solid color-mix(in srgb, var(--tip-accent) 40%, transparent);
    border-radius:6px; padding:6px 10px;
    opacity:0; transform:translateY(3px) translateX(var(--tip-shift-x));
    pointer-events:none; white-space:normal;
    transition:opacity .2s ease .05s, transform .2s ease .05s;
  }
  .biome-marker:hover .biome-tooltip, .biome-marker:focus-visible .biome-tooltip{
    opacity:1; transform:translateY(0) translateX(var(--tip-shift-x));
  }
  /* markers close enough to the viewport edge that a centered tooltip
     would clip - nudge just those inward rather than shrinking everyone */
  .biome-marker[data-id="swamp"]{ --tip-shift-x: 26px; }
  @media (hover: none){
    .biome-tooltip{ display:none; } /* no hover on touch - tap still travels normally */
  }
  /* ---- TOUCH AFFORDANCE ----
     The name label being always-visible (see .biome-label above) is now
     the primary "this is tappable" cue on every device, so touch doesn't
     need its own extra affordance layer. It used to also get a permanent
     blurred glow (filter:blur, expensive to composite) and an infinite
     pulsing ring on all 7 markers at once - that was real, continuous
     GPU load stacked on top of everything else running on the map
     screen, and is what caused the flickering/glitching right when
     tapping something (the travel transition's own burst of work landed
     on top of an already-strained render budget). Keeping only the
     larger tap target, which is layout-only and effectively free. */
  @media (hover: none) and (pointer: coarse){
    /* padding (not margin) grows the tap target - transform:translate(-50%,-50%)
       always re-centers on the current box size, so padding alone keeps the
       visual icon exactly where it was while enlarging the hit area around it. */
    .biome-marker{ padding:16px; }
    /* slightly stronger shadow than the global label style, for legibility
       over the busier motion behind it on a small screen */
    .biome-marker .biome-label{
      text-shadow:0 2px 6px rgba(0,0,0,.9), 0 0 10px rgba(0,0,0,.7);
    }
  }
  @keyframes idlebob{ 0%,100%{transform:translateY(0);} 50%{transform:translateY(-6px);} }

  .beam{ position:absolute; left:50%; top:14px; width:2px; height:2px; transform-origin:50% 100%; animation:sweep 4s linear infinite; }
  .beam::before{
    content:'';position:absolute; left:-90px; top:-2px; width:180px; height:5px;
    background:linear-gradient(90deg, transparent, rgba(255,247,214,.6), transparent);
    filter:blur(2px);
  }
  @keyframes sweep{ from{transform:translateX(-50%) rotate(-52deg);} to{transform:translateX(-50%) rotate(52deg);} }

  .wave-line{ animation:waveMove 3s ease-in-out infinite; transform-origin:50% 50%; }
  @keyframes waveMove{ 0%,100%{transform:translateX(0);} 50%{transform:translateX(2px);} }

  .sway{ transform-origin:50% 100%; animation:sway 5s ease-in-out infinite; }
  @keyframes sway{ 0%,100%{transform:rotate(-1.6deg);} 50%{transform:rotate(1.6deg);} }

  .flicker-glow{ animation:flickerGlow 2.6s ease-in-out infinite; }
  @keyframes flickerGlow{ 0%,100%{opacity:.7;} 45%{opacity:1;} 55%{opacity:.55;} }

  .vine{ transform-origin:50% 0%; animation:vineSway 4.5s ease-in-out infinite; }
  @keyframes vineSway{ 0%,100%{transform:rotate(-4deg);} 50%{transform:rotate(4deg);} }

  .banner{ transform-origin:50% 0%; animation:bannerWave 2.2s ease-in-out infinite; }
  @keyframes bannerWave{ 0%,100%{transform:skewX(-6deg);} 50%{transform:skewX(6deg);} }

  /* ---------- HUD ---------- */
  .hud{
    position:absolute;top:0;left:0;width:100%;
    display:flex;justify-content:space-between;align-items:flex-start;
    padding:28px 36px;pointer-events:none; z-index:20;
    opacity:0; animation:hudIn 1s ease forwards 1s;
  }
  @keyframes hudIn{ from{opacity:0; transform:translateY(-6px);} to{opacity:1; transform:translateY(0);} }
  .hud-title{ margin:0; font-family:'Cinzel',serif;font-weight:700; font-size:22px;color:var(--parchment); letter-spacing:.15em; text-shadow:0 2px 10px rgba(0,0,0,.7); }
  .hud-title span{ display:block;font-family:'Share Tech Mono',monospace; font-size:11px;letter-spacing:.2em;color:var(--gold); margin-top:4px;opacity:.85; }
  .hud-player{ text-align:right;font-family:'Share Tech Mono',monospace; font-size:11px;color:var(--parchment);opacity:.85; letter-spacing:.05em; text-shadow:0 2px 8px rgba(0,0,0,.7); }
  .hud-player .badge{ display:inline-block;margin-top:4px;padding:3px 10px; border:1px solid var(--gold);border-radius:20px; color:var(--gold);font-size:10px;letter-spacing:.15em; }
  .hud-progress{ margin-top:10px; display:flex; gap:5px; justify-content:flex-end; }
  .hud-progress i{ width:6px;height:6px;border-radius:50%; background:rgba(244,234,209,.25); border:1px solid rgba(244,234,209,.4); transition:background .3s ease, box-shadow .3s ease; }
  .hud-progress i.filled{ background:var(--gold-bright); box-shadow:0 0 6px var(--gold-bright); border-color:var(--gold-bright); }

  /* The one deliberate escape hatch from the whole map concept: a plain,
     linear, no-exploration-required version of this portfolio for anyone
     who'd rather not click through 7 regions to get the facts. Kept quiet
     (small, muted) so it doesn't compete with the map for attention, but
     it's here, visible without hovering, from the very first screen. */
  .hud-linear-link{
    display:block; margin-top:10px; text-align:right;
    font-family:'Spectral', serif; font-size:10.5px; letter-spacing:.02em;
    color:var(--parchment-dim); text-decoration:none; opacity:.75;
    transition:opacity .2s ease, color .2s ease;
    /* .hud (the ancestor) sets pointer-events:none because everything else
       in it (title, badge, progress dots) is purely decorative/read-only -
       this is the first actually-clickable thing ever placed inside it, so
       it needs to explicitly opt back in or it's a link you can see but
       can't click. */
    pointer-events:auto;
  }
  .hud-linear-link:hover, .hud-linear-link:focus-visible{
    opacity:1; color:var(--gold-bright); text-decoration:underline;
  }

  /* ---------- FEATURED WORK STRIP ----------
     A fast lane for visitors who won't explore the whole map: a few direct
     links straight into specific projects, visible above the fold. */
  .featured-strip{
    position:absolute; top:94px; left:36px; z-index:20;
    display:flex; gap:10px; flex-wrap:wrap; max-width:440px;
    opacity:0; animation:hudIn 1s ease forwards 1.3s;
  }
  .featured-card{
    position:relative; width:86px; height:64px; border-radius:6px; overflow:hidden;
    border:1px solid rgba(224,180,92,.35); background:rgba(10,7,21,.5);
    cursor:pointer; padding:0; pointer-events:auto;
    transition:transform .15s ease, border-color .15s ease, box-shadow .15s ease;
    display:flex; align-items:flex-end;
    text-decoration:none; font:inherit;
  }
  .featured-card:hover, .featured-card:focus-visible{
    transform:translateY(-3px); border-color:var(--gold-bright);
    box-shadow:0 6px 16px rgba(0,0,0,.45);
    outline:none;
  }
  .featured-card img{
    position:absolute; inset:0; width:100%; height:100%; object-fit:cover; opacity:.55;
    transition:opacity .15s ease;
  }
  .featured-card:hover img, .featured-card:focus-visible img{ opacity:.8; }
  .featured-card::after{
    content:''; position:absolute; inset:0; pointer-events:none;
    background:linear-gradient(180deg, transparent 38%, rgba(6,4,12,.88) 100%);
  }
  .fc-label{
    position:relative; z-index:1; font-family:'Share Tech Mono',monospace; font-size:9.5px;
    color:var(--parchment); letter-spacing:.02em; padding:5px 7px; line-height:1.25;
    text-shadow:0 1px 3px rgba(0,0,0,.85);
  }
  .fc-badge{
    position:absolute; top:4px; right:4px; z-index:2;
    font-size:8px; background:rgba(224,180,92,.92); color:#1c1420;
    padding:1px 5px; border-radius:8px; font-family:'Share Tech Mono',monospace; letter-spacing:.02em;
  }
  .featured-resume{
    align-items:center; justify-content:center; flex-direction:column; gap:5px;
    background:rgba(224,180,92,.08); color:var(--gold);
  }
  .featured-resume .fc-label{ padding:0; }
  .featured-resume:hover{ color:var(--gold-bright); background:rgba(224,180,92,.14); }

  .compass-wrap{ position:absolute; bottom:24px; left:32px; z-index:25; }
  .compass{
    position:relative; width:46px;height:46px; opacity:0;
    animation:hudIn 1s ease forwards 1.2s;
    background:none; border:none; padding:0; margin:0; display:block; cursor:pointer;
    border-radius:50%; transition:transform .2s ease, filter .2s ease;
  }
  .compass:hover, .compass:focus-visible{ transform:scale(1.08); filter:drop-shadow(0 0 8px var(--gold-bright)); outline:none; }
  .compass svg{ width:100%;height:100%; }
  .compass-needle{ transform-origin:50% 50%; animation:needleDrift 9s ease-in-out infinite; }
  @keyframes needleDrift{ 0%,100%{transform:rotate(-4deg);} 50%{transform:rotate(4deg);} }

  .compass-menu{
    position:absolute; left:0; bottom:58px; min-width:196px;
    background:rgba(10,10,14,.88); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    border:1px solid rgba(244,234,209,.25); border-radius:8px;
    padding:6px; display:flex; flex-direction:column; gap:1px;
    opacity:0; visibility:hidden; transform:translateY(6px);
    transition:opacity .18s ease, transform .18s ease, visibility .18s;
    box-shadow:0 14px 34px rgba(0,0,0,.5);
  }
  .compass-menu.open{ opacity:1; visibility:visible; transform:translateY(0); }
  .compass-menu-item{
    display:flex; align-items:center; gap:9px;
    background:none; border:none; text-align:left; width:100%;
    padding:8px 10px; border-radius:5px; cursor:pointer;
    font-family:'Cinzel',serif; font-size:11.5px; letter-spacing:.03em; color:var(--parchment);
    transition:background .15s ease;
  }
  .compass-menu-item:hover, .compass-menu-item:focus-visible{ background:rgba(244,234,209,.1); outline:none; }
  .compass-menu-icon{ font-size:13px; width:16px; text-align:center; flex-shrink:0; }
  .compass-menu-dot{ width:5px;height:5px;border-radius:50%; background:rgba(244,234,209,.25); flex-shrink:0; margin-left:auto; }
  .compass-menu-item.visited .compass-menu-dot{ background:var(--gold-bright); box-shadow:0 0 5px var(--gold-bright); }

  .hint{
    position:absolute;bottom:22px;left:50%;transform:translateX(-50%);
    font-family:'Share Tech Mono',monospace;font-size:11px; color:var(--parchment);opacity:0;letter-spacing:.1em;
    animation:hintIn 1s ease forwards 1.6s, hintpulse 3s ease-in-out infinite 2.6s; z-index:20;
  }
  @keyframes hintIn{ to{opacity:.55;} }
  @keyframes hintpulse{0%,100%{opacity:.35;}50%{opacity:.75;}}

  /* Sticky mobile Contact CTA - desktop visitors already see every landmark
     (including Farpoint Lighthouse) at a glance, but on a phone the map is
     mostly scrolled/panned past, so a persistent shortcut to contact saves
     a hunt-and-tap. Hidden above the phone breakpoint. */
  .sticky-contact-cta{
    display:none;
  }
  @media (max-width: 640px) {
    .sticky-contact-cta{
      display:flex; align-items:center; gap:6px;
      position:fixed; bottom:20px; right:16px; z-index:30;
      font-family:'Cinzel',serif; font-size:11px; letter-spacing:.08em; text-transform:uppercase;
      color:var(--ink); background:var(--gold); border:none; border-radius:22px;
      padding:11px 18px; cursor:pointer;
      box-shadow:0 6px 18px rgba(0,0,0,.4);
      opacity:0; animation:hudIn 1s ease forwards 1.4s;
    }
    .sticky-contact-cta:active{ transform:scale(.95); background:var(--gold-bright); }
    .sticky-contact-cta svg{ flex-shrink:0; }
  }
  /* Note: no extra hide-when-modal-open logic needed - #destPanel.show
     covers the full viewport at z-index:110, well above this button's
     z-index:30, so it's already hidden underneath whenever a scene is open. */

  /* ---------- TRANSITION ---------- */
  #transitionOverlay{
    position:fixed;inset:0;z-index:100;pointer-events:none;
    background:radial-gradient(circle at var(--ox) var(--oy), var(--tint,#fff) 0%, var(--tint,#fff) 100%);
    clip-path:circle(0% at 50% 50%);
  }
  #transitionOverlay.wipe{ transition:clip-path .85s cubic-bezier(.6,0,.2,1); clip-path:circle(150% at var(--ox) var(--oy)); }

  /* Mobile's transition (see travelTo() in js/main.js): a plain opacity
     fade instead of the desktop iris-wipe. clip-path animation isn't
     reliably hardware-accelerated across mobile browsers the way opacity
     is - on some it falls back to full-viewport software rasterization
     every frame, which is a much worse cost than the effect is worth on a
     phone. This also skips the radial-gradient (both color stops are the
     same tint anyway, so it's really just a solid fill) in favor of a
     flat background - one less thing to compute per frame. */
  html.perf-mode #transitionOverlay{
    background:var(--tint,#111);
    clip-path:none;
    opacity:0;
    transition:opacity .32s ease;
  }
  html.perf-mode #transitionOverlay.wipe-mobile{ opacity:1; }


  /* ---------- DESTINATION PANEL ---------- */
  #destPanel{
    position:fixed;inset:0;z-index:110;
    display:flex;align-items:center;justify-content:center;
    background:radial-gradient(circle at 50% 50%, #2a1832 0%, #0d0812 100%);
    opacity:0;pointer-events:none; transition:opacity .4s ease;
  }
  #destPanel.show{opacity:1;pointer-events:all;}

