/* ============================================================================
   map-shell.css — the map container shared by every map app
   ============================================================================

   Kort Udforsker, Havnatur, Punktsky-fremviser and (when it exists) Syd for
   Solen all render into this. The point is that the chrome is *identical*
   across them: the same status chip in the same corner, the same zoom control,
   the same coordinate readout, the same optional selection panel. An app brings
   its own controls and its own map engine; it does not bring its own layout.

   Desktop:  300px control panel | map filling the rest, 620px tall.
   Mobile:   full-bleed map, with the panel as a tabbed bottom sheet over it.

   Requires tokens.css and shell.css. Class prefix `ms-`.
   Behaviour (tabs, sheet drag, chrome updates) is assets/js/map-shell.js.
   ========================================================================= */


/* ── Shell ───────────────────────────────────────────────────────────────── */

.ms-shell {
    display: grid;
    grid-template-columns: 300px 1fr;
    height: 620px;
    background: var(--surface);
}


/* ── Left: control panel ─────────────────────────────────────────────────── */

.ms-panel {
    display: flex;
    flex-direction: column;
    gap: var(--gap-sections);
    padding: var(--space-panel);
    background: var(--surface);
    border-right: 1px solid var(--border);
    /* The panel itself must NOT scroll: the footer is pinned to its bottom, and a
       scrolling panel lets tall content slide underneath it. The scroller inside
       does the scrolling instead. */
    overflow: hidden;
}

/* The scroller carries the section rhythm, so it works the same whether it is a
   plain column (desktop) or the scrolling body of the sheet (mobile). */
.ms-panel__scroll {
    display: flex;
    flex-direction: column;
    gap: var(--gap-sections);
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* A section is a micro-label and the controls under it. The rhythm is fixed so
   four different apps stack their controls at the same cadence. */
.ms-section > .ms-section__label {
    display: block;
    margin-bottom: 12px;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: var(--text-micro);
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--faint);
}

/* The tab bar exists only on mobile; on desktop every section is visible. */
.ms-tabs { display: none; }

.ms-panel__footer {
    margin-top: auto;
    padding-top: 16px;
    border-top: 1px solid var(--hairline);
    font-size: 11px;
    line-height: 1.5;
    color: var(--faint);
}
.ms-panel__footer a { color: var(--green-600); }
.ms-panel__footer a:hover { color: var(--orange); }


/* ── Right: map ──────────────────────────────────────────────────────────── */

.ms-map {
    position: relative;
    overflow: hidden;
    background: var(--sand-400);
}

/* The map engine's own element fills the area absolutely. */
.ms-map__canvas {
    position: absolute;
    inset: 0;
}

/* Leaflet paints its container #ddd by default, which shows as a grey slab
   wherever a tile is missing, still loading, or 403-ing. Sand keeps the map
   looking like this design's map rather than a broken one. */
.ms-map .leaflet-container { background: var(--sand-400); }

/* The graticule. The alpha is load-bearing: at the originally drafted .045 the
   lines were invisible against sand. Used by apps whose map is a stand-in, and
   as the backdrop behind a tiled map while it loads. */
.ms-graticule {
    background-color: var(--sand-400);
    background-image:
        repeating-linear-gradient(0deg,  transparent 0 47px, rgba(76,122,90,.26) 47px 48px),
        repeating-linear-gradient(90deg, transparent 0 47px, rgba(76,122,90,.26) 47px 48px);
}


/* ── Map chrome ──────────────────────────────────────────────────────────
   Four elements at fixed positions on every map. Above Leaflet's panes (400-700)
   and its control container (800).                                            */

.ms-chrome { position: absolute; z-index: 1000; }

.ms-chip {
    font-family: var(--font-mono);
    font-size: var(--text-data);
    letter-spacing: .06em;
    color: var(--green-900);
    background: rgba(255, 255, 255, .92);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 6px 11px;
    white-space: nowrap;
}

.ms-status { top: 16px; left: 16px; max-width: calc(100% - 120px); overflow: hidden; text-overflow: ellipsis; }
.ms-coords { bottom: 16px; left: 16px; color: var(--green-600); }

.ms-zoom {
    top: 16px;
    right: 16px;
    display: flex;
    flex-direction: column;
}
.ms-zoom button {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--green-600);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    transition: background var(--ease-state);
}
.ms-zoom button:first-child { border-radius: var(--radius-chip) var(--radius-chip) 0 0; }
.ms-zoom button:last-child  { border-radius: 0 0 var(--radius-chip) var(--radius-chip); border-top: 0; }
.ms-zoom button:hover       { background: var(--sand-100); }
.ms-zoom button:disabled    { opacity: .45; cursor: default; background: var(--surface); }

/* Optional floating selection panel. Enters with a fade and an 8px rise. */
.ms-select {
    top: 16px;
    right: 16px;
    bottom: 16px;
    width: 320px;
    overflow: auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-panel);
    padding: var(--space-panel);
    animation: ms-select-in 160ms ease both;
}
@keyframes ms-select-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}
.ms-select[hidden] { display: none; }

/* When the selection panel is open it would sit under the zoom control, so the
   zoom control steps left by the panel's width plus its inset. */
.ms-map.has-selection .ms-zoom { right: calc(320px + 16px + 12px); }

.ms-select__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 6px;
}
.ms-select__close {
    width: 30px;
    height: 30px;
    flex: none;
    border: 1px solid var(--border);
    border-radius: var(--radius-chip);
    background: var(--surface);
    color: var(--green-600);
    cursor: pointer;
}

/* Leaflet's own attribution, folded into the system rather than left as-is.
   Kept (not disabled) because the data licences require it. */
.ms-map .leaflet-control-attribution {
    background: rgba(255, 255, 255, .88);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin: 0 16px 16px 0;
    padding: 3px 8px;
    font-family: var(--font-body);
    font-size: 10px;
    color: var(--faint);
}
.ms-map .leaflet-control-attribution a { color: var(--green-600); }


/* ── Dark variant ────────────────────────────────────────────────────────
   The Punktsky viewport is dark; the same chrome inverts rather than becoming
   a different set of components.                                             */

.ms-shell--dark .ms-map { background: var(--green-900); }

.ms-shell--dark .ms-graticule {
    background-color: var(--green-900);
    background-image:
        repeating-linear-gradient(0deg,  transparent 0 59px, rgba(244,241,234,.07) 59px 60px),
        repeating-linear-gradient(90deg, transparent 0 59px, rgba(244,241,234,.07) 59px 60px);
}

.ms-shell--dark .ms-chip {
    background: var(--invert-fill);
    border-color: var(--invert-border);
    color: var(--invert-text);
}
.ms-shell--dark .ms-coords { color: rgba(244, 241, 234, .72); }

.ms-shell--dark .ms-zoom button {
    background: var(--invert-fill);
    border-color: var(--invert-border);
    color: var(--invert-text);
}
.ms-shell--dark .ms-zoom button:hover { background: rgba(31, 61, 43, .8); }

.ms-shell--dark .ms-map__hint {
    position: absolute;
    left: 50%;
    bottom: 16px;
    transform: translateX(-50%);
    z-index: 1000;
    font-family: var(--font-mono);
    font-size: var(--text-data);
    letter-spacing: .06em;
    color: rgba(244, 241, 234, .55);
    white-space: nowrap;
}


/* ── Loading ─────────────────────────────────────────────────────────────── */

.ms-loading {
    position: absolute;
    inset: 0;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    background: var(--sand-400);
    font-size: var(--text-small);
    color: var(--green-600);
    transition: opacity 200ms ease;
}
.ms-loading[hidden] { display: none; }

.ms-spinner {
    width: 26px;
    height: 26px;
    border: 2px solid var(--border);
    border-top-color: var(--green-600);
    border-radius: 50%;
    animation: ms-spin .7s linear infinite;
}
@keyframes ms-spin { to { transform: rotate(360deg); } }


/* ── Mobile: the panel becomes a bottom sheet ────────────────────────────
   The map takes the full area above; the sheet is pulled 18px up over it and
   carries a grab handle. Sections stop stacking and become tabs — one visible
   at a time — because a 300px column of stacked sections does not survive being
   40% of a phone screen.                                                      */

@media (max-width: 760px) {
    .ms-shell {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr auto;
        height: calc(100dvh - 56px);   /* full screen below the top bar */
    }

    .ms-map { grid-row: 1; }

    /* Chrome tightens to 12px insets but keeps its positions. */
    .ms-status { top: 12px; left: 12px; }
    .ms-coords { bottom: 12px; left: 12px; }
    .ms-zoom   { top: 12px; right: 12px; }

    /* Attribution stacks above the coordinate readout instead of sharing the
       bottom line with it — at 390px a long source credit and a coordinate pair
       do not both fit, and they overlapped. */
    .ms-map .leaflet-control-attribution {
        margin: 0 12px 46px 0;
        max-width: calc(100% - 24px);
    }

    /* The selection panel becomes a card over the map rather than a side rail. */
    .ms-select {
        top: auto;
        left: 12px;
        right: 12px;
        bottom: 12px;
        width: auto;
        max-height: 46%;
    }
    .ms-map.has-selection .ms-zoom { right: 12px; }

    .ms-panel {
        grid-row: 2;
        position: relative;
        z-index: 1100;
        margin-top: -18px;
        padding: 0;
        gap: 0;
        border-right: 0;
        border-top: 1px solid var(--border);
        border-radius: 18px 18px 0 0;
        box-shadow: var(--shadow-sheet);
        overflow: hidden;              /* the scroller is inside, not here */
        transition: height 200ms ease;
    }

    /* Grab handle. */
    .ms-panel::before {
        content: '';
        display: block;
        width: 38px;
        height: 4px;
        flex: none;
        margin: 10px auto 6px;
        border-radius: var(--radius-pill);
        background: var(--border);
    }

    /* While dragging, drop the transition so the sheet tracks the finger. */
    .ms-panel.is-dragging { transition: none; }

    .ms-tabs {
        display: flex;
        gap: 6px;
        flex: none;
        padding: 0 12px 10px;
        overflow-x: auto;
        scrollbar-width: none;
    }
    .ms-tabs::-webkit-scrollbar { display: none; }

    .ms-tabs button {
        flex: 1 0 auto;
        min-height: var(--hit);
        padding: 10px 14px;
        border: 1px solid var(--border);
        border-radius: 10px;
        background: var(--surface);
        color: var(--green-600);
        font-size: var(--text-row);
        white-space: nowrap;
        cursor: pointer;
        transition: background var(--ease-state), border-color var(--ease-state), color var(--ease-state);
    }
    .ms-tabs button[aria-selected="true"] {
        background: var(--yellow-tint);
        border-color: var(--yellow);
        color: var(--green-900);
        font-weight: 500;
    }

    /* Only the scroller scrolls. The map never does. */
    .ms-panel__scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        padding: 0 16px 20px;
    }

    /* One section at a time. */
    .ms-section { display: none; }
    .ms-section.is-current { display: block; }

    /* The label is redundant once the tab above says the same word. */
    .ms-section > .ms-section__label { display: none; }

    /* Collapsed: tabs only, no scroller. */
    .ms-panel.is-collapsed .ms-panel__scroll { display: none; }

    .ms-panel__footer { padding: 12px 16px; margin-top: 0; }

    /* Rows grow for the thumb. */
    .ms-panel .kg-toggle-row { padding: 12px 0; font-size: var(--text-body); }
}
