/* =========================================
   SHARED COLOR PALETTE
   Named here so future styles can reference one source of truth instead of
   retyping hex values. The app currently uses two blues in the wild -
   Bootstrap's default (--brand-blue) and a brighter accent (--accent-blue) -
   both are named as-is rather than unified, since picking one is a real
   design decision, not a refactor.
========================================= */
:root {
    --brand-blue: #0d6efd;
    --accent-blue: #3b82f6;
    --dark: #212529;
    --muted: #adb5bd;
    --success: #198754;
    --success-bright: #22c55e;
    --danger: #ef4444;
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}


.app-layout {
    display: flex;
    height: 100vh;
    width: 100vw;
    overflow: hidden;
}

.main-content {
    flex-grow: 1; /* Takes up the remaining horizontal space */
    overflow-y: auto; /* Adds a scrollbar to the content if it gets too long */
    background-color: #f8f9fa;
}

.content-wrapper {
    padding: 2rem;
}


/* The semi-transparent screen that covers the old page */
.global-loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 255, 255, 0.7); /* A slight white frost effect */
    z-index: 9999; /* Keeps it on top of everything else */
    /* Flexbox to center the spinner */
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    font-size: 1.2rem;
    color: #3b82f6; /* Matching your UI blue */
    font-weight: bold;
}

/* The animated spinning circle */
.spinner {
    border: 4px solid rgba(59, 130, 246, 0.2);
    border-top: 4px solid #3b82f6;
    border-radius: 50%;
    width: 50px;
    height: 50px;
    animation: spin 0.8s linear infinite;
    margin-bottom: 15px;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

/* =========================================
   SHARED FULLSCREEN IMAGE MODAL
   Used by WeekRemember, WeekTestPlay, and Memorial/Event's image zoom.
   Was previously copy-pasted with minor variations in each page's own
   .razor.css - one definition here now.
========================================= */
.image-modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(5px);
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;
    animation: fadeInModal 0.2s ease-out forwards;
}

.image-modal-content {
    max-width: 90vw;
    max-height: 90vh;
    border-radius: 12px;
    box-shadow: 0 10px 40px rgba(0,0,0,0.5);
    object-fit: contain;
    animation: zoomInImage 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards;
}

.image-modal-close {
    position: absolute;
    top: 20px;
    right: 30px;
    background: rgba(255, 255, 255, 0.2);
    border: none;
    color: white;
    font-size: 1.5rem;
    width: 45px;
    height: 45px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: background 0.2s;
}

    .image-modal-close:hover {
        background: rgba(255, 255, 255, 0.4);
    }

@keyframes fadeInModal {
    0% { opacity: 0; }
    100% { opacity: 1; }
}

@keyframes zoomInImage {
    0% { transform: scale(0.8); opacity: 0; }
    100% { transform: scale(1); opacity: 1; }
}

/* =========================================
   SIMPLE CONFIRMATION MODAL
   Used by WeekTest.razor's "Start Early?" prompt. This was missing
   entirely before - the .simple-modal-* classes were referenced in markup
   with no matching CSS anywhere, so the dialog rendered unstyled.
========================================= */
.simple-modal-backdrop {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 9999;
}

.simple-modal-content {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #ffffff;
    border-radius: 16px;
    padding: 2rem;
    max-width: 90vw;
    width: 420px;
    text-align: center;
    z-index: 10000;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}

/* =========================================
   "TAP TO RESPOND" HINT
   Used by Memorial/Event's timeline continue button, and now WeekRemember.
========================================= */
.bounce-hint {
    animation: bounceDown 2s infinite;
    opacity: 0.8;
}

@keyframes bounceDown {
    0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
    }

    40% {
        transform: translateY(-5px);
    }

    60% {
        transform: translateY(-3px);
    }
}

/* ---------------------------------------------------------------------------
   QR codes (event links, memorial and challenge share panels)

   Generated server-side by QrCode.ForUrl as inline SVG carrying a viewBox, so
   the code scales to whatever box it's given. That matters because a longer URL
   needs more modules - the svg's own dimensions grow with the link, and only
   the container decides how big it actually renders.

   Sizing lives on the wrapper rather than the svg so the white quiet zone a
   scanner needs stays inside the border.
   --------------------------------------------------------------------------- */
.qr-frame {
    /* These sit inside d-flex centering wrappers, and a flex item shrinks below its width by
       default - which collapsed the code to the width of its own padding while the height held.
       Never shrink; on a narrow phone fall back to the available width instead. */
    flex: 0 0 auto;
    max-width: 100%;
    aspect-ratio: 1 / 1;
}

.qr-frame svg {
    width: 100%;
    height: 100%;
    display: block;
}

/* Beside an event link - small enough that several sit in a row. */
.qr-frame-sm { width: 152px; height: 152px; }

/* The share panel's single code, meant to be scanned across a table. */
.qr-frame-lg { width: 200px; height: 200px; }

/* --- Occasion photo strip -------------------------------------------------
   A horizontal scroller rather than a grid: the day's photos arrive over time,
   from the host and from guests, so the count is unknown and a grid would keep
   reflowing the page underneath the header as people post.

   Snap points make it feel deliberate on a phone, which is where most of these
   are looked at - and where the guests posting them are standing. */
.event-gallery {
    display: flex;
    gap: .5rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-bottom: .35rem;
    /* Bleed to the container edge so a half-visible photo says "there is more",
       instead of the strip looking like it simply ends. */
    scrollbar-width: thin;
}

.event-gallery-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
    padding: 0;
    border: 0;
    background: none;
    border-radius: .5rem;
    overflow: hidden;
    cursor: zoom-in;
    line-height: 0;
}

    .event-gallery-item img {
        height: 190px;
        width: auto;
        max-width: 78vw;
        object-fit: cover;
        border-radius: .5rem;
        box-shadow: 0 .125rem .35rem rgba(0, 0, 0, .12);
    }

    .event-gallery-item:focus-visible {
        outline: 2px solid #0d6efd;
        outline-offset: 2px;
    }

@media (max-width: 575.98px) {
    .event-gallery-item img {
        height: 150px;
    }
}

/* ============================================================================
   Upload zone (MediaDropZone)

   Replaces the bare <InputFile class="form-control">, which on a phone was a
   small grey "Choose Files / No file chosen" - a tap target barely bigger than
   the text in it, with nothing to say what it wanted or whether anything was
   happening.
   ============================================================================ */

.upload-zone {
    border: 2px dashed #ced4da;
    border-radius: .75rem;
    padding: 1.5rem 1rem;
    text-align: center;
    background-color: #fbfbfc;
    transition: border-color .15s ease, background-color .15s ease;
}

    /* The zone is focusable so paste works, but a dashed box with a default focus
       ring on it looks broken. Tint instead. */
    .upload-zone:focus,
    .upload-zone:focus-visible {
        outline: none;
        border-color: #86b7fe;
        background-color: #f6f9ff;
    }

.upload-zone-compact {
    padding: 1rem .75rem;
}

.upload-zone-over {
    border-color: #0d6efd;
    background-color: #eef5ff;
}

.upload-zone-icon {
    display: block;
    font-size: 1.75rem;
    color: #6c757d;
    margin-bottom: .35rem;
}

/* ---- the strip of what is being uploaded ---- */

.upload-tiles {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    justify-content: center;
    margin-top: 1rem;
}

.upload-tile {
    position: relative;
    width: 72px;
    height: 72px;
    border-radius: .5rem;
    overflow: hidden;
    background-color: #e9ecef;
    flex: 0 0 auto;
}

    .upload-tile img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        display: block;
    }

.upload-tile-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: #6c757d;
    font-size: 1.5rem;
}

/* Sits along the bottom edge rather than covering the picture: the thumbnail is
   there so someone can see they picked the right photo, and a full-tile scrim
   would hide the one thing it is for. */
.upload-tile-overlay {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 5px;
    background-color: rgba(0, 0, 0, .35);
}

.upload-tile-bar {
    height: 100%;
    background-color: #0d6efd;
    transition: width .15s linear;
}

.upload-tile-failed {
    opacity: .55;
}

.upload-tile-error {
    height: auto;
    top: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(220, 53, 69, .75);
    color: #fff;
    font-size: 1.25rem;
}

/* ============================================================================
   Photo strip arrows (occasion page)

   The strip itself has always scrolled. On a phone that is the right and only
   affordance - a row of photos is a thing your thumb swipes. On a desktop it
   left a trackpad gesture and the native scrollbar, so five photos looked like
   three and a bit with no way to reach the rest.
   ============================================================================ */

.event-gallery-wrap {
    position: relative;
}

.event-gallery-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;

    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;

    border: 1px solid rgba(0, 0, 0, .08);
    border-radius: 50%;
    background-color: rgba(255, 255, 255, .92);
    color: #212529;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .18);

    cursor: pointer;
    transition: opacity .15s ease, background-color .15s ease;
}

    .event-gallery-nav:hover {
        background-color: #fff;
    }

    .event-gallery-nav.prev {
        left: 6px;
    }

    .event-gallery-nav.next {
        right: 6px;
    }

/* Hidden rather than disabled at each end: a chevron pointing at nothing is
   worse than no chevron, and the strip's own edge already says where it stops.
   visibility keeps the layout still so the photos do not shift as you scroll. */
.event-gallery-wrap.at-start .event-gallery-nav.prev,
.event-gallery-wrap.at-end .event-gallery-nav.next,
.event-gallery-wrap.no-overflow .event-gallery-nav {
    opacity: 0;
    visibility: hidden;
}

/* The actual question is "does this device have a mouse", not "is the window
   wide" - a phone held in landscape is wider than a small laptop window, and a
   touch laptop is both. Where the swipe is natural, two chevrons on top of the
   photos are just two photos you cannot see. */
@media (hover: none), (pointer: coarse) {
    .event-gallery-nav {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .event-gallery-nav {
        transition: none;
    }
}
