* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

a {
    color: #6fb8ff;
    text-decoration: underline;
}

a:hover {
    color: #a8d4ff;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background: #0a0a0a;
    color: #e0e0e0;
    overflow: hidden;
}

#canvas-container {
    width: 100%;
    height: 100vh;
    display: block;
}

canvas {
    display: block;
}

/* Info Panel */
#info-panel {
    position: fixed;
    top: 20px;
    left: 20px;
    background: rgba(20, 20, 30, 0.85);
    backdrop-filter: blur(10px);
    border: 1px solid rgba(100, 100, 120, 0.5);
    border-radius: 8px;
    padding: 15px 20px;
    z-index: 10;
    font-size: 14px;
    /* Explicit width, not shrink-to-fit: the pass image's natural width varies
       from 173px to 1470px and would otherwise resize the whole panel with it */
    width: 280px;
    max-width: calc(100vw - 40px);
}

#hints-box {
    position: relative;
    height: 1.6em;
    margin-bottom: 4px;
}

/* Rides along in the hints box, pinned right, so it fades away when the hints
   finish cycling and the box is hidden. Named for the language you'd switch to,
   not the one you're in. */
#lang-toggle {
    position: absolute;
    top: 0;
    right: 0;
    font-size: 12px;
    color: #888;
    text-decoration: none;
    border-bottom: 1px dotted rgba(136, 136, 136, 0.6);
    white-space: nowrap;
    transition: color 0.2s, border-color 0.2s;
}

#lang-toggle:hover {
    color: #d8d8d8;
    border-bottom-color: #d8d8d8;
}

/* ?review=1 only — marks French prose that is still a machine draft */
.fr-draft {
    border-left: 2px solid #c8a24a;
    padding-left: 8px;
    display: inline-block;
}

.hint-item {
    position: absolute;
    top: 0;
    left: 0;
    /* Keeps the longest hint clear of the language link in the same corner */
    right: 4.5em;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.8s ease;
    margin: 0;
}

.hint-item.active {
    opacity: 1;
    visibility: visible;
}

.scroll-hint {
    font-size: 12px;
    color: #888;
    margin-bottom: 5px;
}

.info-text {
    color: #b0b0b0;
    font-size: 13px;
}

/* The hints have finished cycling and the box is out of the panel. The divider
   it was separating itself from goes too — left alone it draws a line across
   the top edge of a panel that now starts at the year selector. */
#hints-box.gone {
    display: none;
}

#hints-box.gone ~ #year-selector {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

/* Year Selector */
#year-selector {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid rgba(100, 100, 120, 0.4);
}

/* An anchor to the about page, dressed as the caption it has always been:
   inline-block so the hit area is the words rather than the panel's full
   width, and no underline until you're over it */
#year-bus-label {
    display: inline-block;
    font-size: 10px;
    font-variant: small-caps;
    letter-spacing: 0.15em;
    color: #6fb8ff;
    text-decoration: none;
    margin-bottom: 4px;
    transition: color 0.2s;
}

#year-bus-label:hover {
    color: #a8d4ff;
    text-decoration: underline;
}

#year-display {
    font-size: 22px;
    font-weight: 600;
    color: #ffffff;
    text-align: center;
    margin-bottom: 6px;
    letter-spacing: 0.05em;
}

#year-slider {
    width: 100%;
    -webkit-appearance: none;
    appearance: none;
    height: 4px;
    border-radius: 2px;
    background: rgba(111, 184, 255, 0.25);
    outline: none;
    cursor: pointer;
}

#year-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #6fb8ff;
    cursor: pointer;
    box-shadow: 0 0 6px rgba(111, 184, 255, 0.6);
}

#year-slider::-moz-range-thumb {
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #6fb8ff;
    cursor: pointer;
    border: none;
    box-shadow: 0 0 6px rgba(111, 184, 255, 0.6);
}

/* Transit pass for the selected year: hidden until the ride starts, taken back
   once the bus makes its first stop.

   The slot animates grid-template-rows 0fr→1fr, which unlike height:auto is
   transitionable — so the panel opens and closes gradually rather than jumping,
   and overflow clips the pass as it slides away rather than popping it out. */
#year-pass-slot {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0;
    overflow: hidden;
    transition: grid-template-rows 1.2s ease, margin-top 1.2s ease;
}

#year-pass-slot.offered {
    grid-template-rows: 1fr;
    margin-top: 12px;
}

/* The passes range from 1.28:1 to 2.16:1, so a fixed box with object-fit keeps
   the panel from resizing as years change. */
#year-pass {
    width: calc(100% - 10px);
    margin: 0 auto;
    min-height: 0;   /* lets the grid row shrink past the image's own height */
    aspect-ratio: 1.6;
    object-fit: contain;
    border-radius: 4px;
    opacity: 0;
    transform: translateY(16px);
    transition: opacity 1.2s ease, transform 1.2s ease;
}

#year-pass-slot.held #year-pass {
    opacity: 1;
    transform: none;
}

/* Ride option: era music from a fellow passenger. Reads as a line of panel
   text with a checkbox rather than as another button, so it doesn't compete
   with the ride button directly below it. */
#bus-music-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 12px;
    color: #b0b0b0;
    font-size: 12px;
    line-height: 1.35;
    cursor: pointer;
    user-select: none;
    transition: color 0.2s;
}

#bus-music-toggle:hover {
    color: #d8d8d8;
}

#bus-music-toggle input {
    flex: none;   /* the label text wraps; the box must not shrink with it */
    width: 13px;
    height: 13px;
    margin: 0;
    accent-color: #6fb8ff;
    cursor: pointer;
}

/* Put away for the duration of the ride — you settle this before you board */
#bus-music-toggle.hidden {
    display: none;
}

/* No recording for the selected year — see BUS_TRACKS in scene.js */
#bus-music-toggle.unavailable,
#bus-music-toggle.unavailable:hover {
    opacity: 0.4;
    cursor: default;
}

#bus-music-toggle.unavailable input {
    cursor: default;
}

/* Night Bus Button */
#night-bus-btn {
    display: block;
    width: 100%;
    margin-top: 12px;
    padding: 8px 0;
    background: rgba(111, 184, 255, 0.12);
    border: 1px solid rgba(111, 184, 255, 0.4);
    border-radius: 6px;
    color: #6fb8ff;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s;
}

#night-bus-btn:hover {
    background: rgba(111, 184, 255, 0.22);
    border-color: rgba(111, 184, 255, 0.7);
}

/* Modal Popup */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    opacity: 1;
    transition: opacity 0.3s ease;
}

.modal.hidden {
    display: none;
    opacity: 0;
    pointer-events: none;
}

/* Column layout: only .modal-body scrolls, so the close and pause buttons stay
   put no matter how long the venue's description runs */
.modal-content {
    background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
    border: 2px solid rgba(100, 150, 200, 0.4);
    border-radius: 12px;
    padding: 30px;
    max-width: 500px;
    width: 90%;
    max-height: 80vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.7);
    position: relative;
}

/* z-index is load-bearing on iOS: .modal-body's -webkit-overflow-scrolling
   promotes it to its own compositing layer, which otherwise paints over this
   button and swallows taps on it. Same fix as #summary-content > .close-btn. */
.close-btn {
    position: absolute;
    z-index: 1;
    top: 15px;
    right: 15px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    color: #e0e0e0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
}

.close-btn:hover {
    background: rgba(255, 100, 100, 0.3);
    border-color: rgba(255, 100, 100, 0.5);
}

.pause-bus-btn {
    display: block;
    width: 100%;
    margin-top: 8px;
    padding: 8px 0;
    background: rgba(111, 184, 255, 0.12);
    border: 1px solid rgba(111, 184, 255, 0.4);
    border-radius: 6px;
    color: #6fb8ff;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s;
}

.pause-bus-btn:hover {
    background: rgba(111, 184, 255, 0.22);
    border-color: rgba(111, 184, 255, 0.7);
}

.pause-bus-btn.hidden {
    display: none;
}

/* The copy inside the popup sits below the scroll area, so it is always on
   screen without scrolling past a long description. */
.modal-content > .pause-bus-btn {
    flex: none;
    margin-top: 20px;
    padding: 12px 0;
    font-size: 14px;
}

/* Shown only while the guy's music is actually playing. Amber rather than the
   panel's blue, so it reads as switching something off instead of as another
   ride control. */
.mute-bus-btn {
    display: block;
    width: 100%;
    margin-top: 8px;
    padding: 8px 0;
    background: rgba(255, 192, 96, 0.12);
    border: 1px solid rgba(255, 192, 96, 0.4);
    border-radius: 6px;
    color: #ffc060;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s;
}

.mute-bus-btn:hover {
    background: rgba(255, 192, 96, 0.22);
    border-color: rgba(255, 192, 96, 0.7);
}

.mute-bus-btn.hidden {
    display: none;
}

.modal-content > .mute-bus-btn {
    flex: none;
    margin-top: 20px;
    padding: 12px 0;
    font-size: 14px;
}

/* Whichever comes first clears the description; the second just stacks under it */
.modal-content > .mute-bus-btn:not(.hidden) + .pause-bus-btn {
    margin-top: 8px;
}

.modal-body {
    text-align: center;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    min-height: 0;   /* without this a flex item won't shrink enough to scroll */
}

/* End-of-ride summary — covers the screen rather than sitting in a card.
   The id beats the .modal rules in the responsive blocks. */
#summary-modal {
    align-items: stretch;
    padding: 0;
}

#summary-content {
    position: relative;
    width: 100%;
    height: 100%;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 64px 20px 48px;
    background: linear-gradient(165deg, #090d1a 0%, #16213e 55%, #090d1a 100%);
}

/* Fixed, not absolute: the content itself scrolls, and an absolute button would
   scroll off the top with it */
#summary-content > .close-btn {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1;
}

#summary-title {
    color: #6fb8ff;
    text-align: center;
    font-size: 26px;
    margin-bottom: 6px;
}

/* Same size it was in the info panel, where the width works out to 230px */
#summary-pass {
    display: block;
    width: 230px;
    max-width: calc(100% - 20px);
    margin: 14px auto 16px;
    aspect-ratio: 1.6;
    object-fit: contain;
    border-radius: 4px;
}

#summary-subtitle {
    color: #8a90a6;
    text-align: center;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.15em;
    margin-bottom: 4px;
}

/* Plain text, not a link — the dialog gets shared as a single image */
#summary-url {
    color: #6fb8ff;
    text-align: center;
    font-size: 12px;
    letter-spacing: 0.08em;
    opacity: 0.8;
    margin-bottom: 28px;
}

/* The shareable block. Centred rather than left-aligned: the lines are of
   wildly different lengths (a title, two rows of dots, a stat line, a URL) and
   centring reads as a ticket stub instead of as a code listing. */
#share-block {
    max-width: 340px;
    margin: 28px auto 4px;
}

#share-text {
    font-family: ui-monospace, 'Cascadia Mono', Consolas, 'Courier New', monospace;
    font-size: 12px;
    line-height: 1.6;
    color: #d8d8e8;
    text-align: center;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(100, 150, 200, 0.25);
    border-radius: 8px;
    padding: 12px 14px;
    /* pre-wrap, not pre: a long ride wraps its dots rather than forcing the
       whole dialog to scroll sideways */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Same treatment as #comments-link and #night-bus-btn */
#share-copy {
    display: block;
    width: 100%;
    margin-top: 10px;
    padding: 8px 0;
    background: rgba(111, 184, 255, 0.12);
    border: 1px solid rgba(111, 184, 255, 0.4);
    border-radius: 6px;
    color: #6fb8ff;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background 0.2s, border-color 0.2s;
}

#share-copy:hover {
    background: rgba(111, 184, 255, 0.22);
    border-color: rgba(111, 184, 255, 0.7);
}

/* Held for a moment after a successful copy, so the button itself is the
   confirmation and the dialog doesn't need a toast */
#share-copy.done {
    background: rgba(120, 200, 140, 0.16);
    border-color: rgba(120, 200, 140, 0.6);
    color: #86d69b;
}

/* Stops mirror the street as seen heading Montreal-north: west side to the
   left, east side to the right, with the roadway running down the middle. */
#summary-stops {
    position: relative;
    max-width: 720px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

#summary-stops::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    border-left: 1px dashed rgba(111, 184, 255, 0.2);
}

.summary-stop {
    position: relative;
    width: 72%;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(100, 150, 200, 0.25);
    border-radius: 8px;
    padding: 10px 14px;
}

.summary-stop.west {
    margin-right: auto;
    border-left: 3px solid #ffb45a;
}

.summary-stop.east {
    margin-left: auto;
    border-right: 3px solid #6fb8ff;
    text-align: right;
}

.summary-name {
    color: #e8e8f0;
    font-size: 15px;
}

.summary-meta {
    color: #8a90a6;
    font-size: 12px;
    margin-top: 2px;
}

/* Bluesky replies, below the stop list so the shareable top of the dialog
   is unaffected. Populated by renderBlueskyComments() in scene.js. */
#summary-comments {
    max-width: 720px;
    margin: 36px auto 0;
    padding-top: 20px;
    border-top: 1px solid rgba(100, 150, 200, 0.2);
}

#comments-heading {
    color: #6fb8ff;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.15em;
    margin-bottom: 4px;
}

#comments-status {
    color: #8a90a6;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.15em;
    margin-bottom: 12px;
}

.comment {
    display: flex;
    gap: 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(100, 150, 200, 0.25);
    border-radius: 8px;
    padding: 10px 14px;
    margin-bottom: 10px;
}

.comment-avatar {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    object-fit: cover;
}

.comment-body {
    min-width: 0;   /* lets the flex child shrink so long text wraps */
    flex: 1;
}

.comment-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
}

.comment-name {
    color: #e8e8f0;
    font-size: 14px;
}

.comment-handle,
.comment-time {
    color: #8a90a6;
    font-size: 12px;
}

.comment-text {
    color: #b0b0b0;
    font-size: 14px;
    margin-top: 4px;
    white-space: pre-wrap;
    overflow-wrap: anywhere;   /* a bare URL must not widen the modal */
}

.comment-foot {
    display: flex;
    gap: 12px;
    color: #8a90a6;
    font-size: 12px;
    margin-top: 6px;
}

/* Same treatment as #night-bus-btn, so the call to action reads as a button */
#comments-link {
    display: block;
    max-width: 260px;
    margin: 14px auto 0;
    padding: 8px 0;
    background: rgba(111, 184, 255, 0.12);
    border: 1px solid rgba(111, 184, 255, 0.4);
    border-radius: 6px;
    color: #6fb8ff;
    font-size: 12px;
    font-variant: small-caps;
    letter-spacing: 0.12em;
    text-align: center;
    text-decoration: none;
    transition: background 0.2s, border-color 0.2s;
}

#comments-link:hover {
    background: rgba(111, 184, 255, 0.22);
    border-color: rgba(111, 184, 255, 0.7);
}

.modal-body h2 {
    color: #6fb8ff;
    margin-bottom: 15px;
    font-size: 24px;
}

/* The full width is always used; the height follows the photo's own shape,
   which setBuildingImage() writes in as an aspect-ratio at the moment it swaps
   the src. 16/9 is only the placeholder shape it holds while loading.
   A photo too tall for the cap is contained rather than cropped, so it stays
   in proportion and loses width instead of losing its top and bottom. */
#building-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    max-height: min(420px, 45vh);
    background: rgba(100, 100, 120, 0.3);
    border-radius: 8px;
    margin: 15px 0;
    object-fit: contain;
    transition: opacity 0.35s ease;
}

/* The starting point of the fade, not a state the slot rests in: `transition:
   none` lets setBuildingImage() snap a freshly swapped photo to invisible, and
   taking the class straight back off fades it up under the base rule. A slot
   still waiting on its download simply has no src, so it shows the placeholder
   background above rather than a hole in the card. */
#building-image.loading {
    opacity: 0;
    transition: none;
}

#building-years {
    color: #6fb8ff;
    font-size: 13px;
    letter-spacing: 0.05em;
    margin-top: -8px;
    margin-bottom: 10px;
    opacity: 0.85;
}

#building-description {
    color: #b0b0b0;
    line-height: 1.6;
    font-size: 14px;
}

.history-section {
    margin-top: 16px;
    text-align: left;
}

.history-label {
    font-size: 11px;
    font-variant: small-caps;
    letter-spacing: 0.15em;
    color: #6fb8ff;
    opacity: 0.7;
    margin-bottom: 10px;
}

.history-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0 0;
    font-size: 13px;
}

.history-table tr + tr td {
    border-top: 1px solid rgba(100, 100, 120, 0.2);
    padding-top: 8px;
}

.history-table td {
    padding-bottom: 8px;
    vertical-align: top;
}

.history-years {
    color: #6fb8ff;
    font-weight: bold;
    white-space: nowrap;
    padding-right: 14px;
    font-size: 12px;
    letter-spacing: 0.04em;
    padding-top: 2px;
}

.history-name {
    color: #e0e0e0;
    font-weight: 600;
}

.history-desc {
    color: #888;
    font-size: 12px;
    margin-top: 2px;
    line-height: 1.4;
}

[data-year] {
    cursor: pointer;
}

[data-year]:hover {
    text-decoration: underline;
    opacity: 1;
}

/* Responsive Design */
@media (max-width: 768px) {
    #info-panel {
        top: 10px;
        left: 10px;
        right: 10px;
        width: auto;      /* left/right stretch it instead; still not shrink-to-fit */
        max-width: none;
        padding: 12px 15px;
        font-size: 12px;
    }

    /* The panel goes full-bleed here, so cap the pass rather than letting it
       scale with the panel and swallow the screen below the slider */
    #year-pass {
        width: min(calc(100% - 10px), 170px);
    }

    #year-slider {
        height: 6px;
    }

    #year-slider::-webkit-slider-thumb {
        width: 32px;
        height: 32px;
    }

    #year-slider::-moz-range-thumb {
        width: 32px;
        height: 32px;
    }

    .modal {
        align-items: flex-start;
        padding: 20px 0;
    }

    .modal-content {
        max-width: 95%;
        padding: 20px;
        max-height: 85vh;
    }

    .modal-body h2 {
        font-size: 20px;
    }

    #summary-content {
        padding: 56px 12px 32px;
    }

    #summary-title {
        font-size: 20px;
    }

    #summary-pass {
        width: 170px;   /* matches the capped size in the info panel */
    }

    .summary-stop {
        width: 84%;
    }

    .comment {
        padding: 8px 10px;
    }

    .comment-avatar {
        width: 28px;
        height: 28px;
    }

    #building-image {
        max-height: min(300px, 40vh);
    }

    /* 32px is a small target for a thumb, and this is the only way out of a
       paused ride — go up to Apple's 44px minimum rather than down. */
    .close-btn {
        width: 44px;
        height: 44px;
        font-size: 26px;
    }
}

@media (max-width: 480px) {
    #info-panel {
        top: 5px;
        left: 5px;
        right: 5px;
        padding: 10px 12px;
        font-size: 11px;
    }

    .modal-content {
        max-width: 98%;
        padding: 15px;
    }

}
