/* ==================================================
   RESET / BASE
   ================================================== */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 0;
    min-height: 100vh;

    font-family: Arial, sans-serif;
}


/* ==================================================
   PAGE / SCROLL CONTAINER
   The page scrolls through .scroll-space while .scene
   stays pinned to the viewport (sticky), which is what
   lets script.js drive the animation purely off scrollY.
   ================================================== */

.scroll-space {
    height: 900vh;
}

html {
    scrollbar-width: none; /* Firefox */
}

body {
    -ms-overflow-style: none; /* older Edge */
}

html::-webkit-scrollbar,
body::-webkit-scrollbar {
    display: none; /* Chrome, Safari */
}

.scene {
    position: sticky;
    top: 0;

    width: 100%;
    height: 100vh;
    /* Falls back silently on browsers that don't support dvh;
       dvh accounts for mobile browser chrome (address bar, etc)
       so the sticky stage doesn't jump as it shows/hides. */
    height: 100dvh;

    overflow: hidden;
}


/* ==================================================
   VISUALISATION STAGE
   ================================================== */

#visualisation {
    position: relative;
    width: 100%;
    height: 100vh;
    overflow: hidden;
}


/* ==================================================
   MAP
   ================================================== */

#map-background {
    position: absolute;

    left: 0;
    top: 0;

    width: 100%;
    height: 100%;

    object-fit: contain;

    opacity: 0;

    z-index: 1;

    pointer-events: none;
}


/* ==================================================
   DOTS
   ================================================== */

#dots {
    position: absolute;
    left: 0;
    top: 0;

    width: 100%;
    height: 100%;

    pointer-events: none;
    z-index: 3;
}

.data-dot {
    position: absolute;

    width: 8px;
    height: 8px;

    border-radius: 50%;
    box-sizing: border-box;

    opacity: 0;

    transform: translate(-50%, -50%) scale(0);
}

.data-dot.missing {
    background: #9ac9fe;
}

.data-dot.killed {
    background: #FFB5A7;
}

.data-dot.affected {
    background: rgb(221, 221, 221);
}

#country-labels {
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
}

.country-label {
    position: absolute;
    transform: translate(-50%, -100%);
    width: 135px;

    font-size: 12px;
    font-weight: 600;
    color: #222;
    opacity: 0;
    transition: opacity 0.4s ease;
    white-space: normal;
    text-align: center;
    line-height: 1.15;
}


/* ==================================================
   CANVAS
   The affected-dots canvas sits above the map and
   below the HTML dot layer / labels.
   ================================================== */

#affected-canvas {
    position: absolute;
    inset: 0;

    width: 100%;
    height: 100%;

    z-index: 2;

    pointer-events: none;
}


/* ==================================================
   INTRO
   ================================================== */

.intro-person {
    position: absolute;

    width: 12px;
    height: 20px;

    background: #9ac9fe;

    border-radius: 6px 6px 4px 4px;
}

.intro-person::before {
    content: "";

    position: absolute;

    width: 10px;
    height: 10px;

    border-radius: 50%;
    background: #9ac9fe;

    left: 50%;
    top: -10px;

    transform: translateX(-50%);
}

#intro-title {
    position: absolute;

    left: 50%;
    top: 50%;

    transform: translate(-50%, -50%);

    width: min(900px, 85vw);

    text-align: center;

    z-index: 30;
    pointer-events: none;

    opacity: 1;
}

#intro-title h1 {
    margin: 0;

    font-size: clamp(52px, 7vw, 96px);
    font-weight: 700;
    line-height: 0.95;

    letter-spacing: -0.04em;
}

.intro-subtitle {
    max-width: 780px;

    margin: 28px auto 0;

    font-size: 20px;
    line-height: 1.45;

    color: #555;
}

.scroll-hint {
    margin-top: 70px;

    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;

    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.12em;

    color: #777;
}

.scroll-arrow {
    font-size: 24px;

    animation: scrollArrow 1.5s ease-in-out infinite;
}

@keyframes scrollArrow {

    0%,
    100% {
        transform: translateY(0);
    }

    50% {
        transform: translateY(8px);
    }
}


/* ==================================================
   STORY TEXT
   Each scene has its own #text-... panel, absolutely
   positioned relative to the viewport center. Only one
   panel is visible (opacity 1) at a time; script.js
   controls which one based on scroll progress.
   ================================================== */

#story-text {
    position: absolute;
    left: 50%;
    top: 55%;
    z-index: 10;
    pointer-events: none;
}

.story-step {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 320px;
    padding: 24px 32px;
    text-align: left;
    background: rgba(255, 255, 255, 0.75);
    border-radius: 12px;
    opacity: 0;
    transition: opacity 0.5s ease;
}

.story-number {
    font-size: 48px;
    font-weight: 700;
}

.story-description {
    margin-top: 8px;
    font-size: 18px;
    line-height: 1.4;
}

.story-stat {
    margin-top: 18px;

    font-size: 32px;
    font-weight: 700;

    color: #000000;
}

#text-one-person {
    position: absolute;

    left: calc(50% + 150px);
    top: calc(50% - 70px);

    width: 280px;

    transform: translateY(-50%);

    text-align: left;
}

#text-missing {
    /* Right of the 114-figure group (which scatters up to ~220px
       around viewport center), forming one horizontal composition
       — matches #text-one-person's pattern, just offset further out
       to clear the wider group. Width uses vw so it can't run off
       the right edge on narrower desktop/tablet widths. */
    position: absolute;

    left: calc(50% + 250px);
    top: calc(50% - 70px);

    width: min(30vw, 320px);

    transform: translateY(-50%);

    text-align: left;
}

#text-dot-value {
    position: absolute;

    left: calc(50% - 150px);
    top: calc(50% - 70px);

    width: min(90vw, 640px);
    min-width: 480px;

    transform: translateY(-50%);

    text-align: left;
}

/* "1 dot ≈ 100 people" must stay on one line, not wrap into
   "1 dot ≈" / "100 people". */
#text-dot-value .story-number {
    white-space: nowrap;
}

#text-map {
    /* Sits below the map's bottom edge (see geoToScreen's mapBottom
       = 0.74 in script.js). Bottom-anchored so it can't overflow
       past the viewport edge on shorter screens. Wider than before
       so the description doesn't wrap awkwardly. */
    position: absolute;

    left: calc(50% + 0px);
    top: calc(50% + 320px);

    min-width: 580px;

    transform: translate(-50%, -100%);

    text-align: left;
}

#text-ranking-fiji {
    /* left/top here are only a fallback (script.js sets these
       inline, positioned relative to the actual Fiji circle, for
       desktop/tablet). Width matters either way — kept narrow
       enough to not run off the right edge of the screen. */
    position: fixed;
    left: calc(50% + 100px);
    top: calc(50% - 100px);
    width: min(34vw, 380px);
    transform: translateY(-50%);
    text-align: left;
}

#text-ranking-png {
    /* Same as Fiji above — left/top are a fallback only. */
    position: fixed;
    left: calc(50% + 100px);
    top: calc(50% - 20px);
    width: min(34vw, 380px);
    transform: translateY(-50%);
    text-align: left;
}

#text-table {
    /* Sits in the band reserved below the table's last country row
       (see getTableLayout's tableBottomMargin in script.js). */
    position: absolute;


    left: 50%;
    top: calc(50% - 120px);

    min-width: 680px;

    transform: translate(-50%, 0);

    text-align: left;
}

#text-table .story-description {
    text-align: center;
}

#text-table .story-number {
    text-align: center;
}


/* ==================================================
   DISASTER CASE STUDIES
   Winston (2016) and Harold (2020) reuse the story-step
   panel styles above, plus their own copy formatting.
   ================================================== */

.disaster-category {
    margin-top: 8px;
    font-size: 18px;
    font-weight: 700;
}

.disaster-title {
    margin-top: 10px;
    font-size: 32px;
    font-weight: 700;
}

.disaster-copy {
    margin-top: 20px;
    font-size: 18px;
    line-height: 1.5;
}

#text-winston {
    /* left/top here are a fallback only — script.js positions this
       directly below the actual highlighted Winston dot on
       desktop/tablet. */
    position: fixed;

    left: calc(50% + 0px);
    top: calc(50% - 0px);

    min-width: 480px;
    max-height: 32vh;
    overflow-y: auto;

    transform: translateY(-50%);
    text-align: left;
}

#text-harold {
    /* Same as Winston above — left/top are a fallback only. */
    position: fixed;

    left: calc(50% + 120px);
    top: calc(50% - 100px);

    min-width: 480px;
    max-height: 42vh;
    overflow-y: auto;

    transform: translateY(-50%);
    text-align: left;
}


/* ==================================================
   TABLE / YEAR LABELS
   ================================================== */

.year-label {
    position: absolute;

    font-size: 11px;
    white-space: nowrap;

    transform: translate(-50%, -50%);

    opacity: 0;

    pointer-events: none;
}


/* ==================================================
   TABLET
   1024px and below: the desktop side-by-side composition still
   holds (story-text beside the visualization), but panels shrink
   and pull inward so they don't run past the (narrower) viewport
   edge or the visualization's dot clouds.
   ================================================== */

@media (max-width: 1023px) {

    .story-step {
        width: min(80vw, 320px);
        padding: 20px 26px;
    }

    .story-number {
        font-size: clamp(32px, 5vw, 48px);
    }

    .story-description {
        font-size: clamp(15px, 2vw, 18px);
    }

    /* Width only — each scene's own left/top/transform (set per-id
       above, or inline by script.js for the ranking/cyclone panels)
       still applies on tablet. Forcing a shared centered box here
       would undo the intentional "next to the visualization"
       positioning these panels rely on. */
    #text-one-person,
    #text-missing {
        width: min(34vw, 280px);
    }

    #text-dot-value {
        /* Wider than the other tablet panels — needs to keep
           "1 dot ≈ 100 people" on one line even as the story-number
           font-size shrinks less than proportionally on tablet. */
        width: min(88vw, 560px);
    }

    #text-map,
    #text-ranking-fiji,
    #text-ranking-png,
    #text-table,
    #text-winston,
    #text-harold {
        width: min(80vw, 420px);
    }

    .disaster-copy,
    .story-description {
        text-align: left;
    }

    .country-label {
        font-size: clamp(9px, 1.1vw, 12px);
        width: 100px;
    }
}


/* ==================================================
   MOBILE
   Below 768px, the composition changes rather than just shrinking:
   the visualization (map/dots/canvas) occupies the top portion of
   the viewport (MOBILE_VISUALIZATION_HEIGHT_RATIO in script.js —
   keep these two values in sync), and every story-text panel
   becomes a single shared bottom-anchored card instead of sitting
   beside the visualization. One rule block covers every scene's
   panel, rather than repositioning each #text-... id separately.
   ================================================== */

@media (max-width: 767px) {

    /* Longer scroll distance gives mobile readers more time per
       scene without changing the normalized 0-1 TIMELINE progress
       script.js animates against (progress is scrollY relative to
       this length, whatever it is). */
    .scroll-space {
        height: 1400vh;
    }

    /* Map image is capped to the same top fraction of the viewport
       that script.js confines dot/label math to (getResponsiveLayout
       -> visualizationHeight), so the map graphic and the dots stay
       aligned instead of the map bleeding under the text card. */
    #map-background {
        height: 58%;
        object-position: top center;
    }

    .data-dot {
        width: 5px;
        height: 5px;
    }

    .country-label {
        font-size: 9px;
        width: 84px;
    }

    .year-label {
        font-size: 9px;
    }

    #intro-title {
        width: min(92vw, 640px);
    }

    .intro-subtitle {
        font-size: clamp(15px, 4vw, 18px);
    }

    /* Shared bottom card for every scene's story text. `fixed`
       (not `absolute`) so it anchors to the real viewport edges
       regardless of #story-text's own (zero-height, since all its
       children are absolutely positioned) box. */
    .story-step,
    #text-one-person,
    #text-missing,
    #text-dot-value,
    #text-killed,
    #text-affected,
    #text-map,
    #text-ranking-fiji,
    #text-ranking-png,
    #text-table,
    #text-winston,
    #text-harold {
        position: fixed;
        left: 50%;
        top: auto;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
        transform: translateX(-50%);

        width: min(92vw, 440px);
        max-height: 38dvh;
        overflow-y: auto;

        padding: 16px 20px;
        text-align: left;
    }

    .story-number {
        font-size: clamp(26px, 8vw, 40px);
    }

    .story-description {
        font-size: clamp(14px, 3.6vw, 17px);
    }

    .story-stat {
        font-size: clamp(22px, 6vw, 28px);
    }

    /* Winston/Harold have the longest copy of any scene, so their
       card is allowed more vertical room than the others. */
    #text-winston,
    #text-harold {
        max-height: 46dvh;
    }

    .disaster-title {
        font-size: clamp(20px, 6vw, 26px);
    }

    .disaster-copy {
        font-size: clamp(14px, 3.6vw, 16px);
        line-height: 1.4;
    }
}


/* ==================================================
   SMALL MOBILE
   Below 480px: same stacked composition as MOBILE, tightened
   further so text stays comfortably within a very narrow viewport.
   ================================================== */

@media (max-width: 479px) {

    .story-step,
    #text-one-person,
    #text-missing,
    #text-dot-value,
    #text-killed,
    #text-affected,
    #text-map,
    #text-ranking-fiji,
    #text-ranking-png,
    #text-table,
    #text-winston,
    #text-harold {
        width: calc(100vw - 24px);
        padding: 14px 16px;
    }

    .country-label {
        font-size: 8px;
        width: 68px;
    }

    .data-dot {
        width: 4px;
        height: 4px;
    }
}


/* ==================================================
   MOBILE LANDSCAPE / SHORT SCREENS
   A phone on its side (script.js: getViewportMode() ->
   "mobile-landscape") is short as well as modestly narrow, so the
   bottom text card is capped tighter to leave visible room for the
   visualization above it, and side safe-area insets are respected
   (rounded corners / camera housings on some devices).
   ================================================== */

@media (max-height: 500px) and (max-width: 900px) {

    #map-background {
        height: 58%;
    }

    .story-step,
    #text-one-person,
    #text-missing,
    #text-dot-value,
    #text-killed,
    #text-affected,
    #text-map,
    #text-ranking-fiji,
    #text-ranking-png,
    #text-table,
    #text-winston,
    #text-harold {
        position: fixed;
        left: calc(50% + env(safe-area-inset-left, 0px) / 2 - env(safe-area-inset-right, 0px) / 2);
        top: auto;
        bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
        transform: translateX(-50%);

        width: min(70vw, 380px);
        max-height: 34dvh;
        padding: 10px 16px;
    }

    #text-winston,
    #text-harold {
        max-height: 34dvh;
    }

    .story-number {
        font-size: clamp(20px, 6vw, 28px);
    }

    .story-description {
        font-size: clamp(12px, 2.6vw, 14px);
    }

    #intro-title {
        top: 46%;
    }

    .scroll-hint {
        margin-top: 24px;
    }
}
