/**
 * Location globe.
 *
 * The section sits on Elementor's accent colour, a bright cyan (#00d8e8), so every value
 * here is chosen to read against that rather than against white.
 *
 * Every location is drawn on arrival - there is no default state to escape and no control
 * to find. Script only adds dragging and the detail panel. Nothing below assumes it ran.
 */

.op-globe {
    --op-globe-deep: #03151f;
    --op-globe-land-hi: #35c9dd;
    --op-globe-plate: rgba(3, 21, 31, .74);
    --op-globe-plate-edge: rgba(255, 255, 255, .22);
    --op-globe-live: #7dffb2;
    --op-globe-pin: #ffffff;
    --op-globe-pin-rest: #bdf0f9;

    position: relative;
    width: 100%;
    max-width: 1100px;
    margin-inline: auto;
    direction: rtl;
}

.op-globe__stage {
    position: relative;
    width: 100%;
    aspect-ratio: 1000 / 660;
}

.op-globe__svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* Only once script is driving it, and only on the sphere itself.

   touch-action: none gives us the entire gesture, so a finger turns the globe on both
   axes exactly as a mouse does. The cost is real and deliberate: a drag that starts on
   the globe no longer scrolls the page.

   What keeps that from trapping anyone is the size of this element, not a heuristic. The
   capture is confined to the svg - the heading, the subtitle and the section's padding are
   all outside it and scroll normally - and the stage is square, so its height is always
   the content width. On a portrait phone the width is the smaller dimension, so the globe
   can never fill the screen and there is always page above or below it to scroll from. */
.op-globe.is-interactive .op-globe__svg {
    touch-action: none;
    cursor: grab;
}

.op-globe.is-interactive.is-dragging .op-globe__svg {
    cursor: grabbing;
}

/* --- the sphere --------------------------------------------------------------- */

.op-globe__halo { fill: url(#opGlobeHalo); }
.op-globe__sphere { fill: url(#opGlobeOcean); }

/* Directional shade: clear at the lit point, heavy out at the far limb. It sits above the
   land, so land and ocean are lit by one light instead of each carrying its own. */
.op-globe__shade {
    fill: url(#opGlobeShade);
    pointer-events: none;
}

/* The specular. Small and weak, and bounded by its own falloff rather than by a clip path
   - its centre is 153 units from the middle and its radius 90, so it cannot reach the
   limb, let alone spill past it. */
.op-globe__sheen {
    fill: url(#opGlobeSpec);
    pointer-events: none;
}

/* Atmosphere: a lit edge hugging the inside of the limb. This is the modern half of the
   look, and it also does what the old flat vignette circle did, which is why that circle
   is gone instead of kept alongside it. */
.op-globe__atmosphere {
    fill: url(#opGlobeAtmo);
    pointer-events: none;
}

.op-globe__rim {
    fill: none;
    stroke: rgba(255, 255, 255, .28);
    stroke-width: 1;
}

.op-globe__graticule {
    fill: none;
    stroke: rgba(168, 232, 255, .10);
    stroke-width: .6;
}

/* Land takes a gradient in user space centred on the same light, so coastlines on the lit
   hemisphere are brighter than the ones turning away. The path is rebuilt every frame of a
   drag either way; a gradient fill costs a little more to rasterise than a flat one and
   nothing at all to recompute. */
.op-globe__land {
    fill: url(#opGlobeLandFill);
    stroke: rgba(255, 255, 255, .14);
    stroke-width: .55;
    stroke-linejoin: round;
}

.op-globe__land--hi {
    fill: var(--op-globe-land-hi);
    stroke: rgba(255, 255, 255, .85);
    stroke-width: .9;
}

.op-globe__orbit {
    fill: none;
    stroke: rgba(255, 255, 255, .26);
    stroke-width: 1;
    stroke-dasharray: 1.1 3.3;
    animation: op-globe-orbit 26s linear infinite;
}

.op-globe__orbit--front { stroke: rgba(255, 255, 255, .46); }

@keyframes op-globe-orbit {
    from { stroke-dashoffset: 0; }
    to { stroke-dashoffset: -100; }
}

/* --- arcs --------------------------------------------------------------------- */

.op-globe__arc {
    fill: none;
    stroke: rgba(255, 255, 255, .34);
    stroke-width: 1;
    stroke-linecap: round;
}

/* pathLength is fixed at 100 on every spark, so the dash pattern stays a percentage of
   the arc however much the length changes as the globe turns. */
.op-globe__spark {
    fill: none;
    stroke: #fff;
    stroke-width: 2.1;
    stroke-dasharray: 6 94;
    stroke-dashoffset: 100;
    filter: url(#opGlobeGlow);
    animation: op-globe-spark 5.2s linear infinite;
}

@keyframes op-globe-spark {
    from { stroke-dashoffset: 100; }
    to { stroke-dashoffset: 0; }
}

/* --- pins --------------------------------------------------------------------- */

.op-globe__hit {
    fill: transparent;
    stroke: none;
}

/* A pin that leads somewhere is a real link, so it gets link affordances whether or not
   script ran. A pin with no destination is deliberately flatter and keeps the default
   cursor: a dot that does nothing must never look like a dot that does something, which
   is the difference between "nothing happened" and "the map is broken". */
.op-globe__pin--link { cursor: pointer; }
.op-globe__pin--static { cursor: default; }

.op-globe__pin--static .op-globe__pin-dot { opacity: .55; }

.op-globe__pin--link:hover .op-globe__pin-dot {
    transform: scale(1.7);
    stroke: #fff;
    stroke-width: 2;
}

.op-globe__pin-dot {
    fill: var(--op-globe-pin-rest);
    stroke: var(--op-globe-deep);
    stroke-width: 1;
    transform-box: fill-box;
    transform-origin: center;
    transition: transform .18s ease-out;
}

.op-globe__pin--own .op-globe__pin-dot {
    fill: var(--op-globe-pin);
    filter: url(#opGlobeGlow);
}

.op-globe__pin-ring {
    fill: none;
    stroke: rgba(255, 255, 255, .85);
    stroke-width: 1.4;
    transform-box: fill-box;
    transform-origin: center;
    animation: op-globe-pulse 3.4s ease-out infinite;
}

@keyframes op-globe-pulse {
    0% { transform: scale(.3); opacity: .85; }
    70% { opacity: 0; }
    100% { transform: scale(2.4); opacity: 0; }
}

.op-globe__pin.is-selected .op-globe__pin-dot { transform: scale(1.7); }

.op-globe__pin:focus { outline: none; }

.op-globe__pin:focus-visible .op-globe__pin-dot {
    transform: scale(1.7);
    stroke: #fff;
    stroke-width: 2;
}

/* Anything on the far side of the sphere. visibility rather than opacity: opacity is what
   the flag backdrop animates, and two rules fighting over one property is how an element
   ends up half-visible. */
.is-behind { visibility: hidden; }

/* --- detail panel ------------------------------------------------------------- */

/* Now shown at every width, because a tap has to be able to name a location on a phone.
   It is absolutely positioned so it overlays the globe rather than pushing the page
   around when it opens and closes - but that means it has to be told to stay inside a
   330px stage, hence the max-width. min-width still wins on a wide screen. */
.op-globe__panel {
    position: absolute;
    inset-inline-start: 12px;
    top: 24px;
    z-index: 2;
    min-width: 210px;
    max-width: calc(100% - 24px);
    box-sizing: border-box;
    padding: 18px 20px;
    border: 1px solid var(--op-globe-plate-edge);
    border-radius: 16px;
    background: rgba(3, 21, 31, .9);
    color: #fff;
    text-align: start;
}

.op-globe__panel-close {
    position: absolute;
    inset-inline-end: 8px;
    top: 6px;
    padding: 4px 8px;
    border: 0;
    background: none;
    color: rgba(255, 255, 255, .6);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

.op-globe__panel-close:hover { color: #fff; }

.op-globe__panel-kind {
    margin: 0 0 6px;
    color: var(--op-globe-live);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
}

.op-globe__panel-city {
    margin: 0;
    font-size: 22px;
    font-weight: 700;
    line-height: 1.2;
}

.op-globe__panel-country {
    margin: 2px 0 0;
    color: rgba(255, 255, 255, .72);
    font-size: 14px;
}

.op-globe__panel-meta {
    margin: 10px 0 0;
    color: rgba(255, 255, 255, .6);
    font-size: 12px;
}

.op-globe__panel-meta:empty { display: none; }

/* The panel's only exit. It is a real anchor so it keeps link behaviour - middle-click,
   open in a new tab, the status bar showing where it goes - which a button would throw
   away. 44px tall because this one IS a finger target, and unlike a pin it has no
   neighbours to be confused with. */
.op-globe__panel-cta {
    display: block;
    margin-top: 14px;
    padding: 12px 18px;
    border-radius: 11px;
    background: var(--op-globe-live);
    color: var(--op-globe-deep);
    font-size: 14px;
    font-weight: 700;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

.op-globe__panel-cta:hover,
.op-globe__panel-cta:focus-visible {
    background: #fff;
    color: var(--op-globe-deep);
}

.op-globe__panel-cta:not([href]) { display: none; }

/* --- the flag backdrop --------------------------------------------------------- */

/* Behind everything, and it stays behind. The pins and arcs are the content;
   if this backdrop ever competes with them it is wrong however pretty it looks, which
   is why the opacity is low and set per flag rather than globally.

   Pointer events are off, so a drifting flag can never swallow a click meant for a pin
   underneath it. The container is aria-hidden in the markup, so none of it is announced.

   Only transform and opacity are animated. Both are composited, so twenty drifting
   elements cost no layout and no paint - which is what makes this affordable on a
   homepage. There is deliberately no will-change: twenty promoted layers would trade a
   cost the browser does not currently pay for one it would. */
.op-globe__flags {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    border-radius: 26px;
    pointer-events: none;
}

.op-globe__stage {
    position: relative;
    z-index: 1;
}

.op-globe__panel { z-index: 3; }

.op-globe__flag {
    position: absolute;
    width: var(--op-flag-size, 56px);
    aspect-ratio: 4 / 3;
    border-radius: 5px;
    background-size: cover;
    background-position: center;
    opacity: var(--op-flag-alpha, .13);
    animation-name: op-globe-flag-a;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

.op-globe__flag--a { animation-name: op-globe-flag-a; }
.op-globe__flag--b { animation-name: op-globe-flag-b; }
.op-globe__flag--c { animation-name: op-globe-flag-c; }
.op-globe__flag--d { animation-name: op-globe-flag-d; }
.op-globe__flag--e { animation-name: op-globe-flag-e; }
.op-globe__flag--f { animation-name: op-globe-flag-f; }

/* Six paths, each returning to where it started so the loop has no seam. The offsets are
   percentages of the flag's own box, not the container, so they scale with the flag and
   never need a media query to stay proportionate. Long durations and negative delays are
   set per flag in the markup: the negatives are what disperse them, so they are already
   scattered mid-drift on the first frame rather than setting off together. */
@keyframes op-globe-flag-a {
    0%, 100% { transform: translate(0, 0) rotate(-5deg); }
    25%      { transform: translate(340%, 150%) rotate(4deg); }
    50%      { transform: translate(560%, -170%) rotate(-3deg); }
    75%      { transform: translate(180%, -300%) rotate(6deg); }
}

@keyframes op-globe-flag-b {
    0%, 100% { transform: translate(0, 0) rotate(6deg); }
    25%      { transform: translate(-300%, 220%) rotate(-4deg); }
    50%      { transform: translate(-520%, -120%) rotate(3deg); }
    75%      { transform: translate(-160%, -290%) rotate(-6deg); }
}

@keyframes op-globe-flag-c {
    0%, 100% { transform: translate(0, 0) rotate(-3deg); }
    30%      { transform: translate(260%, -240%) rotate(5deg); }
    60%      { transform: translate(-190%, -380%) rotate(-5deg); }
    85%      { transform: translate(-320%, 90%) rotate(2deg); }
}

@keyframes op-globe-flag-d {
    0%, 100% { transform: translate(0, 0) rotate(4deg); }
    30%      { transform: translate(-280%, -200%) rotate(-6deg); }
    60%      { transform: translate(240%, -330%) rotate(3deg); }
    85%      { transform: translate(420%, -60%) rotate(-2deg); }
}

@keyframes op-globe-flag-e {
    0%, 100% { transform: translate(0, 0) rotate(-6deg); }
    35%      { transform: translate(-360%, 140%) rotate(5deg); }
    65%      { transform: translate(-540%, -210%) rotate(-3deg); }
    85%      { transform: translate(-240%, -340%) rotate(4deg); }
}

@keyframes op-globe-flag-f {
    0%, 100% { transform: translate(0, 0) rotate(5deg); }
    35%      { transform: translate(380%, -160%) rotate(-4deg); }
    65%      { transform: translate(600%, 120%) rotate(6deg); }
    85%      { transform: translate(260%, 280%) rotate(-5deg); }
}

/* Same rule as the globe's own rotation: if the visitor asked for stillness, it is still. */
@media (prefers-reduced-motion: reduce) {
    .op-globe__flag { animation: none; }
}

/* --- narrow ------------------------------------------------------------------- */

/* One breakpoint, and it is where the content stops working rather than where a device
   table says a tablet ends. The wide frame hands the globe half the stage width; a square
   stage covered by the SVG hands it three quarters. Below ~767px that difference is the
   difference between pins you can pick out and a smear, and above it a square stage would
   be taller than the viewport it sits in.

   The crop is safe by arithmetic, not by eye. Covering a square always shows x = +/-330
   user units whatever the viewport width, because the scale is set by the viewBox's
   660-unit height. The arcs reach 303 and the outermost pin with its tap circle 267, so
   nothing carrying information is ever cropped; what goes is the transparent fringe of
   the halo (375) and the last 15 units of the orbit ellipse (345), both decoration.

   overflow has to be hidden for covering to crop rather than paint outside the box. It
   stays visible above this width, where the halo's vertical bleed is wanted and nothing
   can escape sideways anyway (375 is inside the 500-unit half-width). */
@media (max-width: 767px) {
    .op-globe__stage { aspect-ratio: 1 / 1; }

    .op-globe__svg { overflow: hidden; }

    /* The globe is ~250px across on a 360px screen, which puts a partner dot under 4px.
       This enlarges the dot only. The tap circle underneath it is deliberately left at 12
       units: see the note on the hit circle - widening it would not buy a 44px target, it
       would only make a tap land on a neighbouring country's store page. */
    .op-globe__pin-dot { transform: scale(1.45); }

    /* display:none means no box is generated, and a background image on an element with
       no box is never fetched. That is the whole reason these are backgrounds and not
       <img>: phones download eight flags instead of twenty, decided by CSS alone, with
       no script and no server-side guess about the device. */
    .op-globe__flag--extra { display: none; }

    .op-globe__flag { width: calc(var(--op-flag-size, 56px) * .62); }
}

@media (prefers-reduced-motion: reduce) {
    .op-globe__spark,
    .op-globe__pin-ring,
    .op-globe__orbit { animation: none; }

    .op-globe__spark { opacity: 0; }
}
