/* Reset and Base Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* The colours and the schemes now live in public/css/schemes.css, which
   index.html loads before this file, so there is one definition of
   them. Everything below still reads the same custom properties. */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    line-height: 1.6;
    color: var(--text-color);
    background-color: var(--light-bg);
}

/* Header and Navigation */
header {
    background-color: var(--primary-color);
    color: var(--white);
    padding: 1.5rem 2rem;
    box-shadow: var(--shadow);
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
}

.header-content {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    max-width: 1200px;
    margin: 0 auto;
}

/* When someone is signed in, the identity block takes real space on the
   right instead of the brand staying dead-centre across the whole width -
   otherwise a long name/email (or the admin buttons) can reach far enough
   left to sit under the centred title. Logged-out visitors keep the
   original centred logo+title. */
.header-content:has(> .header-user:not([hidden])) {
    justify-content: space-between;
}

.header-brand {
    display: flex;
    align-items: center;
    /* Now a real flex sibling of .header-user rather than an absolute
       overlay, it can get squeezed on a narrow window. Keep the title on
       one line (letting it clip rather than wrap) so it can never grow
       the fixed-height header. */
    min-width: 0;
    flex-shrink: 1;
}

.header-brand h1 {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

header .header-logo {
    height: 3rem !important; /* Double size for better visibility */
    width: auto !important;
    max-width: 120px !important; /* Double width limit */
    margin-right: 1rem !important;
    object-fit: contain !important;
}

header h1 {
    font-size: 2rem;
    margin: 0;
}

/* The hamburger: opens and shuts the menu on a narrow screen, where
   the sidebar is not on show (the 768px rules below). Not there at all
   on a wide one. */
.nav-toggle {
    display: none;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    margin-right: 0.6rem;
    padding: 0.3rem;
    background: none;
    border: none;
    border-radius: 4px;
    color: inherit;
    cursor: pointer;
}

.nav-toggle:hover,
body.nav-open .nav-toggle {
    background-color: rgba(255, 255, 255, 0.12);
}

.nav-toggle svg {
    width: 100%;
    height: 100%;
}

/* Signed-in identity, top right of the header. A normal flex child (see
   .header-content:has(...) above) so it reserves its own space next to
   the title instead of overlaying it. The header's height is fixed at
   80px and the sidebar and main content are both offset by that; the
   identity block stays shorter than the title (name/address are
   ellipsised, not wrapped) so it never makes the header any deeper. */
.header-user {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 55%;
}

/* The hidden attribute only supplies a default display, and the
   display: flex above would win over it - leaving the Log Out button
   on screen after logging out. Spell the hidden state out. */
.header-user[hidden] {
    display: none;
}


/* The name and address are one button that opens "Your Details". Looks
   like text until hovered, so the header stays quiet. */
.header-user-identity {
    background: none;
    border: none;
    margin: 0;
    padding: 0.15rem 0.4rem;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.1rem;
    min-width: 0;
    overflow: hidden;
    line-height: 1.25;
    text-align: right;
    color: inherit;
    font-family: inherit;
    border-radius: 4px;
    cursor: pointer;
}

.header-user-identity:hover {
    background-color: rgba(255, 255, 255, 0.12);
}

/* Sits on the header grey, so the colours are the reverse of the other
   action buttons: white with grey text. */
.header-logout-btn {
    flex-shrink: 0;
    background-color: var(--white);
    color: var(--primary-color);
    font-weight: 700;
    border: none;
    padding: 0.4rem 0.9rem;
    font-size: 0.85rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.header-logout-btn:hover {
    background-color: #dfe6ed;
}


.header-user-name {
    font-size: 0.95rem;
    font-weight: 600;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.header-user-email {
    font-size: 0.78rem;
    opacity: 0.8;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}


/* Sidebar Navigation */
nav#mainNav {
    position: fixed;
    left: 0;
    top: calc(var(--header-height) + var(--header-gap));
    width: var(--sidebar-width);
    height: calc(100vh - var(--header-height) - var(--header-gap));
    background-color: var(--white);
    box-shadow: var(--shadow);
    overflow-y: auto;
    padding: 1rem 0;
    z-index: 50;
    /* A column, so the Welcome button can sit at the foot of the menu */
    display: flex;
    flex-direction: column;
}

/* Shows the Welcome window again (welcome.js): bottom left of the
   menu, after every item however many there are */
.nav-welcome-btn {
    order: 99;
    align-self: flex-start;
    margin: auto 0 0.25rem 1rem;
    padding: 0.35rem 0.75rem;
    background: none;
    border: 1px solid var(--accent-color);
    border-radius: 4px;
    color: var(--accent-color);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.nav-welcome-btn:hover,
.nav-welcome-btn:focus {
    background-color: var(--accent-color);
    color: var(--white);
}

.nav-btn {
    display: block;
    width: 100%;
    background-color: transparent;
    color: var(--text-color);
    border: none;
    border-left: 4px solid transparent;
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.3s ease;
    text-align: left;
}

.nav-btn:hover {
    background-color: var(--light-bg);
    border-left-color: var(--accent-color);
}

.nav-btn.active {
    background-color: var(--primary-color);
    border-left-color: var(--primary-color);
    color: var(--white);
    font-weight: 600;
}


/* Hierarchical menu styles */
.nav-item {
    margin: 0;
}

.nav-item.has-children > .nav-btn::after {
    content: '\25BC';
    float: right;
    font-size: 0.8rem;
    transition: transform 0.3s ease;
}

.nav-item.has-children.expanded > .nav-btn::after {
    transform: rotate(-180deg);
}

.nav-children {
    display: none;
    background-color: #f5f5f5;
}

.nav-item.expanded > .nav-children {
    display: block;
}

.nav-children .nav-btn {
    padding-left: 2.5rem;
    font-size: 0.95rem;
}

.nav-children .nav-children .nav-btn {
    padding-left: 3.5rem;
    font-size: 0.9rem;
}

/* Folders nest as deep as the manager makes them: each level below the
   third steps in a further 1rem */
.nav-children .nav-children .nav-children .nav-btn {
    padding-left: 4.5rem;
}

.nav-children .nav-children .nav-children .nav-children .nav-btn {
    padding-left: 5.5rem;
}

.nav-children .nav-children .nav-children .nav-children .nav-children .nav-btn {
    padding-left: 6.5rem;
}

.nav-separator {
    height: 3px;
    margin: 0.5rem 1.5rem;
    background-color: #999;
}

/* Main Content */
main {
    margin-left: var(--sidebar-width);
    margin-top: calc(var(--header-height) + var(--header-gap));
    padding: 2rem;
    min-height: calc(100vh - var(--header-height) - var(--header-gap));
}

.page {
    display: none;
}

.page.active {
    display: block;
}

/* Carousel Styles */
.carousel-container {
    position: relative;
    max-width: 1200px;
    margin: 0 auto;
    background-color: var(--white);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: var(--shadow-lg);
}

.carousel {
    position: relative;
    width: 100%;
    height: 600px;
    overflow: hidden;
}

.carousel-slide {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 0.5s ease-in-out;
}

.carousel-slide.active {
    opacity: 1;
}

.carousel-slide img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background-color: #000;
}

.carousel-slide .artist-name {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
    color: var(--white);
    padding: 2rem 1rem 1rem;
    font-size: 1.5rem;
    font-weight: 600;
}

/* Centred on the picture, not the container: the dots strip below
   the picture is about 2rem tall */
.carousel-arrow {
    position: absolute;
    top: calc(50% - 1rem);
    transform: translateY(-50%);
    background-color: rgba(0, 0, 0, 0.5);
    color: var(--white);
    border: none;
    font-size: 2rem;
    padding: 1rem 1.5rem;
    cursor: pointer;
    transition: background-color 0.3s ease;
    z-index: 10;
}

.carousel-arrow:hover {
    background-color: rgba(0, 0, 0, 0.8);
}

.carousel-arrow.prev {
    left: 1rem;
}

.carousel-arrow.next {
    right: 1rem;
}

.carousel-arrow:disabled {
    display: none;
}

/* The position dots sit in a strip under the picture, not over it,
   where a portrait picture's letterbox margins would otherwise put
   them on black */
.carousel-dots {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.55rem 0 0.65rem;
    background-color: var(--white);
}

/* No dots when the pictures all fit and nothing moves. display: flex
   above would otherwise outrank the hidden attribute. */
.carousel-dots[hidden] {
    display: none;
}

.carousel-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background-color: #c5cdd6;
    border: none;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.carousel-dot.active {
    background-color: var(--primary-color);
}

.carousel-dot:disabled {
    display: none;
}

/* Gallery Grid Styles */
.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 1rem;
    padding: 1rem 0;
}

.gallery-item {
    position: relative;
    background-color: var(--white);
    border-radius: 8px;
    overflow: hidden;
    box-shadow: var(--shadow);
    cursor: pointer;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    aspect-ratio: 1;
}

.gallery-item:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
}

.gallery-item img,
.gallery-item picture {
    width: 100%;
    height: 100%;
    display: block;
}

.gallery-item img {
    object-fit: contain;
    background-color: #f0f0f0;
}

.gallery-item picture img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    background-color: #f0f0f0;
}

/* The picture under the pointer, shown again three times the size
   (gallery.js). Over the page and the header (100), under the windows
   (200); it never takes the pointer, so the tile beneath keeps it. It
   does cover the edit pencil below, and the tick box Select mode puts
   in the opposite corner, which is why gallery.js keeps a dead zone
   around both corners. */
.gallery-preview {
    position: fixed;
    z-index: 150;
    object-fit: contain;
    border-radius: 8px;
    background-color: #f0f0f0;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45);
    pointer-events: none;
    animation: gallery-preview-in 0.15s ease-out;
}

@keyframes gallery-preview-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.gallery-item.loading {
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: loading 1.5s infinite;
}

/* Tree section only: filename caption under each image */
.gallery-item-captioned {
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
}

/* height: auto so the picture keeps its square when the row is taller
   than this tile (a video or PDF beside it): with height 100% it took
   the whole tile and pushed the caption out of sight. */
.gallery-item-captioned picture {
    aspect-ratio: 1;
    height: auto;
    flex-shrink: 0;
    position: relative;
}

/* ...and the picture laid over that square rather than inside it: a
   portrait picture would otherwise stretch the square it is meant to
   fit in (the video strip is held the same way) */
.gallery-item-captioned picture > img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.gallery-item-caption {
    padding: 0.5rem 0.75rem;
    font-size: 0.8rem;
    color: var(--secondary-color);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

@keyframes loading {
    0% {
        background-position: 200% 0;
    }
    100% {
        background-position: -200% 0;
    }
}

/* PDF tiles (Tree section only) - open in a new tab, no lightbox */
.gallery-item-pdf {
    cursor: default;
}

/* Video tiles: the still frame taken from the video, held in a strip of
   film - a dark band of sprocket holes down each edge, as Windows shows
   a video in a folder - with a play symbol over the middle
   (createVideoTile in gallery.js). --film-edge is the width of one
   band, and the frame is inset by the same on both sides so no part of
   the picture is hidden under it. */
.gallery-item-video .gallery-video-frame {
    --film-edge: 9%;
    position: relative;
    width: 100%;
    height: 100%;
    background-color: #17181a;
}

/* Captioned grids (the members' areas) let each tile take its own
   height, so the strip has to hold the square the pictures keep */
.gallery-item-captioned .gallery-video-frame {
    aspect-ratio: 1;
    height: auto;
    flex-shrink: 0;
}

/* The frame sits between the two perforated bands, and is laid over the
   strip rather than inside it: a tall frame would otherwise stretch the
   strip past the square the tile keeps. */
.gallery-item-video .gallery-video-frame > img,
.gallery-item-video .gallery-video-frame > .gallery-video-icon {
    position: absolute;
    top: 0;
    left: var(--film-edge);
    width: calc(100% - var(--film-edge) * 2);
    height: 100%;
    background-color: #17181a;
}

.gallery-item-video .gallery-video-frame > img {
    object-fit: contain;
    display: block;
}

/* The two perforated bands. The holes are one small rounded rectangle,
   drawn as an SVG and repeated down the band, so they stay crisp at
   any tile size rather than blurring as an image would. */
.gallery-item-video .gallery-video-frame::before,
.gallery-item-video .gallery-video-frame::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: var(--film-edge);
    background-color: #0d0e10;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='26'%3E%3Crect x='5' y='6.5' width='12' height='13' rx='3' fill='%23eceff2'/%3E%3C/svg%3E");
    background-size: 100% 26px;
    background-repeat: repeat-y;
    z-index: 1;
}

.gallery-item-video .gallery-video-frame::before {
    left: 0;
}

.gallery-item-video .gallery-video-frame::after {
    right: 0;
}

/* No frame could be taken from the video (an unusual format, or one the
   browser could not read): the strip carries the word instead */
.gallery-item-video .gallery-video-frame > .gallery-video-icon {
    display: flex;
    align-items: flex-end;      /* under the play symbol, not behind it */
    justify-content: center;
    padding-bottom: 1.25rem;
    box-sizing: border-box;
    color: #eceff2;
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.08em;
}

.gallery-video-play {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 3rem;
    height: 3rem;
    margin: -1.5rem 0 0 -1.5rem;
    border: 2px solid rgba(255, 255, 255, 0.9);
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 2;
    pointer-events: none;
}

/* The triangle inside it, a shade right of centre so it looks centred */
.gallery-video-play::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 54%;
    margin: -0.55rem 0 0 -0.45rem;
    border-style: solid;
    border-width: 0.55rem 0 0.55rem 0.9rem;
    border-color: transparent transparent transparent var(--white);
}

.gallery-pdf-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    padding: 1rem;
    box-sizing: border-box;
    background-color: #f0f0f0;
    color: var(--text-color);
    text-decoration: none;
    text-align: center;
}

.gallery-pdf-icon {
    background-color: #e74c3c;
    color: var(--white);
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
    padding: 0.4rem 0.75rem;
    border-radius: 4px;
    margin-bottom: 0.75rem;
}

.gallery-pdf-name {
    font-size: 0.85rem;
    color: var(--secondary-color);
    word-break: break-word;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
}

/* Lightbox Styles */
.lightbox {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.95);
    z-index: 1000;
    justify-content: center;
    align-items: center;
}

.lightbox.active {
    display: flex;
}

/* max-width/height account for .lightbox-comments sitting alongside
   the image (see further down) - .lightbox-content centres both. The
   video that plays in place of the image is held to the same size. */
.lightbox {
    /* The width of the comments beside the picture: moved by the
       divider (gallery.js initLightboxDivider), which sets it in
       pixels between 240 and half the window. */
    --lightbox-side: 320px;
}

.lightbox img,
.lightbox video {
    max-width: calc(95vw - var(--lightbox-side) - 3rem);
    max-height: 90vh;
    object-fit: contain;
}

/* With no comments beside it, the picture has the room they would have had */
.lightbox-content:has(> #lightboxComments[hidden]) img,
.lightbox-content:has(> #lightboxComments[hidden]) video {
    max-width: 90vw;
}

/* Holding the mouse down on the full image shows part of it enlarged
   (see initLightboxZoom in gallery.js), which the cursor gives away. */
#lightboxImage {
    cursor: zoom-in;
}

/* The zoom window itself. Its size, its place and the enlarged image
   behind it are all set by gallery.js; the ring is a shadow rather than
   a border so that the window's box is exactly the picture it shows.
   pointer-events: none so the mouse is still on the image beneath. */
.lightbox-zoom {
    position: fixed;
    z-index: 1001;
    border-radius: 8px;
    background-color: #000;
    background-repeat: no-repeat;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9), 0 8px 30px rgba(0, 0, 0, 0.6);
    pointer-events: none;
}

.lightbox-close {
    position: absolute;
    top: 2rem;
    right: 2rem;
    background-color: transparent;
    color: var(--white);
    border: none;
    font-size: 3rem;
    cursor: pointer;
    transition: opacity 0.3s ease;
    line-height: 1;
    padding: 0.5rem;
}

.lightbox-close:hover {
    opacity: 0.7;
}

/* Contact Form Styles */
.contact-container {
    max-width: 800px;
    margin: 0 auto;
    background-color: var(--white);
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
    /* Clips .contact-header's square corners to the container's round
       ones, the same trick every modal dialog uses for its title bar. */
    overflow: hidden;
}

/* Title bar in the header grey, so this reads as part of the same site
   as the banner above it and every modal window's title bar. */
.contact-header {
    padding: 1rem 1.5rem;
    background-color: var(--primary-color);
}

.contact-header h2 {
    color: var(--white);
    margin: 0;
}

.contact-body {
    padding: 1.25rem 1.5rem 1.5rem;
}

.contact-body > p {
    margin-bottom: 1.25rem;
    color: var(--secondary-color);
}

.form-group {
    margin-bottom: 1.5rem;
}

.form-group label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    color: var(--primary-color);
}

.form-group input,
.form-group textarea,
.form-group select {
    width: 100%;
    padding: 0.75rem;
    border: 2px solid #ddd;
    border-radius: 4px;
    font-size: 1rem;
    font-family: inherit;
    transition: border-color 0.3s ease;
}

.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
    outline: none;
    border-color: var(--accent-color);
}

/* Email (2/3) and phone (1/3) share a line - flex-basis:0 with the
   .form-row it sits in makes flex-grow split the row in an exact 2:1
   ratio, rather than by each field's own content width. Stacked full
   width again below 768px, with the rest of the site's mobile layout. */
.contact-email-phone-row .form-group:first-child {
    flex: 2 1 0;
    min-width: 0;
}

.contact-email-phone-row .form-group:last-child {
    flex: 1 1 0;
    min-width: 0;
}

/* The area field never needs a full line's width. */
.contact-area-field {
    max-width: 50%;
}

.form-group small {
    display: block;
    margin-top: 0.25rem;
    color: #666;
    font-size: 0.875rem;
}

/* The contact form's area field when there's only one valid area to
   pick, so there's nothing to actually choose - reads like a filled-in
   value rather than an editable control. */
.form-static-value {
    padding: 0.75rem;
    border: 2px solid #ddd;
    border-radius: 4px;
    background-color: var(--light-bg);
    color: var(--text-color);
    font-size: 1rem;
    margin: 0;
}

.submit-btn {
    background-color: var(--accent-color);
    color: var(--white);
    border: none;
    padding: 1rem 2rem;
    font-size: 1.1rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease, box-shadow 0.3s ease;
    width: 100%;
}

/* Hover: a shade darker than whichever accent the colour scheme has,
   rather than a fixed blue. The inset shadow tints the background
   only, so the white text stays crisp. */
.submit-btn:hover {
    box-shadow: inset 0 0 0 100vmax rgba(0, 0, 0, 0.18);
}

/* Get in Touch: the button is only as wide as its text and sits at the
   right, ending where the fields end (the login and password forms'
   buttons stay full width) */
#contactForm .submit-btn {
    display: block;
    width: auto;
    margin-left: auto;
}

.submit-btn:disabled {
    background-color: #95a5a6;
    cursor: not-allowed;
}

.form-feedback {
    margin-top: 1rem;
    padding: 1rem;
    border-radius: 4px;
    display: none;
}

.form-feedback.success {
    display: block;
    background-color: #d4edda;
    color: #155724;
    border: 1px solid #c3e6cb;
}

.form-feedback.error {
    display: block;
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
}

/* The invite window sits on top of Manage Users, which stays visible
   behind it so the list can be seen updating as invites are sent. */
/* A window opened on top of another (Send an Invite over Manage Users,
   the comments window over Site Admin, the folder manager over the
   sub-area list in Manage Areas). Both classes are named so this
   outranks .modal-overlay's own z-index further down the file - with
   one class each the later rule won, and a nested window sat level with
   its parent, relying on the markup order to stay on top. */
.modal-overlay.modal-overlay-nested {
    z-index: 210;
}

.tree-invite-actions {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.tree-username {
    color: var(--secondary-color);
    font-size: 0.95rem;
    font-weight: 600;
}



/* Tree Page Styles */
.tree-container {
    max-width: 800px;
    margin: 0 auto;
}

.tree-login {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
}

/* On a wide screen the sign-in panels take half the width they would
   otherwise have (half of .tree-container's 800px), centred.

   They are short, single-column forms - an address, a password, a few
   buttons - and at the container's full width the labels sit a long way
   from the fields they belong to, which reads as a page rather than as
   something to fill in. Narrower is also how a login is expected to
   look, so it is recognised before it is read.

   All four panels, not just the login: they share this class, and
   leaving the others wide would make the card jump size on the way to
   "Forgot password?" and back. Request an invite has the most in it and
   is still a single column, so it takes the same width comfortably.

   Only above 1024px, where there is width to give away. Below it the
   panel keeps the container's width, which on a phone is the screen. */
@media (min-width: 1024px) {
    .tree-login {
        max-width: 400px;
        margin-left: auto;
        margin-right: auto;
    }
}

/* Title row with a close control, for the panels that have one. */
.tree-login-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.tree-login-header h2 {
    margin-bottom: 1rem;
}

.tree-login h2 {
    color: var(--primary-color);
    margin-bottom: 1rem;
}

.tree-login > p {
    margin-bottom: 2rem;
    color: var(--secondary-color);
}

.tree-login-actions {
    display: flex;
    gap: 0.75rem;
    align-items: stretch;
}

/* The login actions share their row out between the buttons.
   Elsewhere - a Cancel beside a form's Save, the page toolbar, the
   select/reorder bar - a button is only as wide as its text, like
   Post comment. */
.tree-login-actions .submit-btn,
.tree-login-actions .tree-secondary-btn {
    flex: 1 1 0;
    width: auto;
}

.tree-secondary-btn {
    background-color: var(--primary-color);
    color: var(--white);
    border: none;
    padding: 1rem 1.25rem;
    font-size: 1.1rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.tree-secondary-btn:hover {
    background-color: var(--secondary-color);
}

.tree-secondary-btn:disabled {
    background-color: #a9b4bf;
    cursor: not-allowed;
}

.tree-secondary-btn:disabled:hover {
    background-color: #a9b4bf;
}

/* The login form's ways in, one under the other with "or" between
   them: Use Passkey / or / Use Password / or / Request an invite. A
   column rather than a row because they are alternatives of equal
   standing - a row of equal halves reads as a choice between opposites
   instead, which these are not. */
.tree-login-stack {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* display: flex above would otherwise beat [hidden], and tree.js keeps
   a stack back until its step is reached (showLoginStep). The same is
   true of .tree-login-actions, which is display: flex further up. */
.tree-login-stack[hidden],
.tree-login-actions[hidden] {
    display: none;
}

/* The buttons take their own height here. The rule further up gives
   buttons in a login ROW "flex: 1 1 0" so they share the width out;
   turned into a column that becomes a share of the HEIGHT, stretching
   them to match each other rather than to their text. */
.tree-login-stack .submit-btn,
.tree-login-stack .tree-secondary-btn {
    flex: 0 0 auto;
    width: 100%;
}

/* "Passkey failed", with a round ? beside it that opens the
   explanation (tree.js showPasskeyFailure). The fact goes on the
   screen; the reasons wait for whoever wants them, because most people
   meeting this are about to type a password instead and do not need
   three sentences about Windows first. */
.passkey-failed-line {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.passkey-help-toggle {
    flex: 0 0 auto;
    width: 1.35rem;
    height: 1.35rem;
    border-radius: 50%;
    /* currentColor throughout, so it follows the feedback box's own
       colour and stays legible in every scheme. */
    border: 1px solid currentColor;
    background: transparent;
    color: inherit;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}

/* No background fill on hover. The button takes its colour from the
   feedback box it sits in, so filling it with currentColor would paint
   the "?" out in its own colour. Fading and an outline read clearly
   whatever scheme the box is drawn in. */
.passkey-help-toggle:hover {
    opacity: 0.7;
}

.passkey-help-toggle:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

.passkey-help-detail:not([hidden]) {
    display: block;
    margin-top: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid currentColor;
}

.passkey-help-detail p {
    margin: 0 0 0.5rem;
    font-size: 0.95rem;
}

.passkey-help-detail p:last-child {
    margin-bottom: 0;
    font-weight: 600;
}

.tree-login-or {
    margin: 0.25rem 0;
    text-align: center;
    color: var(--secondary-color);
    font-size: 0.95rem;
}

.tree-content {
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
}

.tree-content-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1.5rem;
}

.tree-content-header h2 {
    color: var(--primary-color);
}

.tree-content-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
}

.tree-placeholder {
    color: var(--secondary-color);
}

.logout-btn {
    background-color: var(--primary-color);
    color: var(--white);
    border: none;
    padding: 0.5rem 1.25rem;
    font-size: 0.95rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.logout-btn:hover {
    background-color: var(--secondary-color);
    color: var(--white);
}

.logout-btn:disabled,
.logout-btn:disabled:hover {
    background-color: #a9b4bf;
    cursor: not-allowed;
}

.link-btn {
    display: block;
    background: none;
    border: none;
    color: var(--accent-color);
    font-size: 0.9rem;
    cursor: pointer;
    margin-top: 0.75rem;
    padding: 0;
    text-decoration: underline;
}

/* Hover: the accent, a shade darker, whatever the scheme */
.link-btn:hover {
    filter: brightness(0.8);
}

@media (max-width: 480px) {
    .tree-login-actions {
        flex-direction: column;
    }
}

.form-group-checkbox label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: normal;
    cursor: pointer;
}

.form-group-checkbox input[type="checkbox"] {
    width: auto;
}

/* Manage Users modal */
.modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 200;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    box-sizing: border-box;
}

.modal-overlay:not([hidden]) {
    display: flex;
}

/* Every modal window keeps its title bar and footer (where it has one)
   fixed, scrolling only the body between them - otherwise long content
   (a long user list, an area's images, etc.) pushes the title out of
   view at the top and any footer button past the bottom of the screen
   where it can't be reached. */
.modal-dialog {
    background-color: var(--white);
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 500px;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Title bar in the header grey, so every window reads as part of the
   same site as the banner above it. */
.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.5rem;
    background-color: var(--primary-color);
    color: var(--white);
    border-radius: 8px 8px 0 0;
}

.modal-header h3 {
    color: var(--white);
}


.modal-close {
    background: none;
    border: none;
    font-size: 1.75rem;
    line-height: 1;
    padding: 0;
    cursor: pointer;
    color: var(--secondary-color);
}

.modal-close:hover {
    color: var(--text-color);
}

/* On the dark title bar the close control has to be light. The same
   class is also used on white panels, hence the scoping. */
.modal-header .modal-close {
    color: rgba(255, 255, 255, 0.85);
}

.modal-header .modal-close:hover {
    color: var(--white);
}

.modal-body {
    padding: 1.5rem;
    flex: 1 1 auto;
    overflow-y: auto;
    /* Without this a flex child refuses to shrink below its content,
       and the body would grow the dialog instead of scrolling. */
    min-height: 0;
}

.tree-user-list {
    list-style: none;
    margin-bottom: 1.5rem;
}

.tree-user-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.6rem 0.75rem;
    background-color: var(--light-bg);
    border-radius: 4px;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
}

.tree-user-details {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

.tree-user-name {
    word-break: break-word;
}

/* Signed in right now. The highlight is the whole signal when no
   sign-in time was recorded, so it has to read on its own - the
   ".tree-user-presence" line below is only there when we know a time. */
.tree-user-name.logged-in {
    background-color: #d8f3dc;
    border-radius: 3px;
    padding: 0.1rem 0.35rem;
    margin: -0.1rem -0.35rem;
}

.tree-user-presence {
    font-size: 0.8rem;
    color: #2d6a4f;
}

.tree-user-status {
    font-size: 0.8rem;
}

.tree-user-status.pending {
    color: var(--secondary-color);
}

.tree-user-status.expired {
    color: #a94442;
    font-weight: 600;
}

/* The invite's past, above the line saying where it stands now: the
   same small type, held back so the current state still reads first */
.tree-user-invite-history {
    opacity: 0.75;
}

.tree-resend-btn {
    flex-shrink: 0;
    background-color: var(--primary-color);
    color: var(--white);
    border: none;
    padding: 0.4rem 0.8rem;
    font-size: 0.8rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.tree-resend-btn:hover:not(:disabled) {
    background-color: var(--secondary-color);
}

.tree-resend-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

/* The buttons beside a member: Access, Make admin, Request Approval,
   History, Delete. There can be five of them, which is wider than the
   card on any ordinary screen - so they wrap onto a second line instead
   of running off the right-hand edge, and the block is allowed to
   shrink for that wrapping to take effect (it was flex-shrink: 0, which
   held it at full width and pushed Delete outside the card). */
.tree-user-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    min-width: 0;
}

/* Manage Users only: the buttons always start a line of their own,
   left justified under the member's details, instead of sitting to
   their right and dropping down only when they run out of room. A row
   there carries up to five buttons and several lines of detail, so
   where they landed depended on how much text was above them - which
   put them in a different place on every row of the list. The other
   lists built from .tree-user-list (Manage Areas, an area's folders and
   members, Your Details) have one or two buttons and keep them on the
   right. */
#treeUserList > li > .tree-user-details,
#treeUserList > li > .tree-user-actions {
    flex: 1 1 100%;
}

#treeUserList > li > .tree-user-actions {
    justify-content: flex-start;
}

.tree-delete-btn {
    flex-shrink: 0;
    background-color: transparent;
    color: #a94442;
    border: 1px solid #a94442;
    padding: 0.4rem 0.8rem;
    font-size: 0.8rem;
    border-radius: 4px;
    cursor: pointer;
    transition: background-color 0.3s ease, color 0.3s ease;
}

.tree-delete-btn:hover:not(:disabled) {
    background-color: #a94442;
    color: var(--white);
}

.tree-delete-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

.tree-drop-zone {
    border: 2px dashed #ccc;
    border-radius: 4px;
    padding: 0.5rem;
    transition: border-color 0.2s ease, background-color 0.2s ease;
}

.tree-drop-zone.dragging {
    border-color: var(--accent-color);
    background-color: #eaf4fb;
}

.tree-drop-zone textarea {
    width: 100%;
    border: none;
    background: transparent;
    padding: 0.25rem;
    font-family: inherit;
    font-size: 0.85rem;
    resize: vertical;
}

.tree-drop-zone textarea:focus {
    outline: none;
}

.tree-drop-feedback {
    margin-top: 0.35rem;
    font-size: 0.8rem;
    min-height: 1rem;
}

.tree-drop-feedback.success {
    color: #2d6a4f;
}

.tree-drop-feedback.error {
    color: #a94442;
}

.tree-invite-note {
    margin-bottom: 1rem;
    font-size: 0.8rem;
    color: var(--secondary-color);
}

/* Footer */
footer#siteFooter {
    margin-left: var(--sidebar-width);
    padding: 1.5rem 2rem;
    text-align: center;
    color: var(--white);
    font-size: 0.85rem;
    background-color: var(--primary-color);
}

.footer-content {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
}

.footer-copyright {
    color: var(--white);
    margin: 0;
}

.footer-links {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.footer-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--white);
    font-family: inherit;
    font-size: inherit;
    text-decoration: underline;
    cursor: pointer;
    transition: opacity 0.3s ease;
}

.footer-link:hover {
    opacity: 0.75;
}

.footer-link-sep {
    color: rgba(255, 255, 255, 0.5);
}

/* Privacy / Cookies Policy Pages */
.legal-container {
    max-width: 800px;
    margin: 0 auto;
    background-color: var(--white);
    padding: 2rem;
    border-radius: 8px;
    box-shadow: var(--shadow-lg);
}

/* The statistics page is a row of boxes rather than a column of prose,
   so it gets more room than a policy page's comfortable reading width. */
.legal-container-wide {
    max-width: 1100px;
}

.legal-container h2 {
    color: var(--primary-color);
    margin-bottom: 0.25rem;
}

.legal-container h3 {
    color: var(--primary-color);
    font-size: 1.15rem;
    margin-top: 2rem;
    margin-bottom: 0.5rem;
}

.legal-container p {
    margin-bottom: 1rem;
}

.legal-container ul {
    margin-bottom: 1rem;
    padding-left: 1.5rem;
}

.legal-container li {
    margin-bottom: 0.5rem;
}

.legal-updated {
    color: #666;
    font-size: 0.85rem;
    margin-bottom: 1.5rem;
}

.legal-inline-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--accent-color);
    font-family: inherit;
    font-size: inherit;
    text-decoration: underline;
    cursor: pointer;
}

.legal-inline-link:hover {
    filter: brightness(0.8);
}

.legal-table-wrap {
    overflow-x: auto;
    margin-bottom: 1rem;
}

.legal-table {
    width: 100%;
    min-width: 480px;
    border-collapse: collapse;
    font-size: 0.95rem;
}

.legal-table th,
.legal-table td {
    border: 1px solid #ddd;
    padding: 0.75rem;
    text-align: left;
    vertical-align: top;
}

.legal-table th {
    background-color: var(--light-bg);
    color: var(--primary-color);
}


/* The body above this scrolls under it, so without a rule and a gap of
   its own the footer button sits directly against whatever row the list
   happens to be cut off at mid-scroll, and reads as part of the list. */
.modal-footer {
    flex-shrink: 0;
    padding: 1rem 1.5rem 1.5rem;
    border-top: 1px solid #e0e0e0;
}

/* Where a list is the whole of a modal body (the Manage Users list, an
   area's members) the body's own padding is the gap below it, and the
   list's usual bottom margin only adds dead space above the footer. */
.modal-body > .tree-user-list:last-child {
    margin-bottom: 0;
}

.modal-footer .form-feedback {
    margin-top: 0;
    margin-bottom: 1rem;
}

/* Two fields side by side where there is room, stacked where there
   isn't - the basis is the width below which they stop fitting. */
.form-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0 1rem;
}

.form-row .form-group {
    flex: 1 1 180px;
    min-width: 0;
}

/* Tighter than the site's forms: this window has a lot of fields in a
   small dialog. The drop box is deliberately left alone - it is a
   target to drag onto, so it needs to stay big. */
#treeInviteModal .form-group,
#treeProfileModal .form-group {
    margin-bottom: 0.85rem;
}

#treeInviteModal .form-group label,
#treeProfileModal .form-group label {
    margin-bottom: 0.25rem;
}

#treeInviteModal .form-group input,
#treeProfileModal .form-group input {
    padding: 0.4rem 0.55rem;
    border-width: 1px;
    font-size: 0.95rem;
}

#treeInviteModal .tree-invite-note,
#treeProfileModal .tree-invite-note {
    margin-bottom: 0.85rem;
}

/* Honeypot field - hidden from users */
.honeypot-field {
    position: absolute;
    left: -9999px;
}

/* reCAPTCHA v3 (public/js/recaptcha.js) - the invisible check behind
   the contact form, log in, forgot password and request an invite.

   Google's script drops a floating badge into the bottom corner of the
   page and never takes it away again, so on a single-page site like
   this one it sits there for the rest of the visit, long after the
   form that triggered it. Google's terms allow hiding the badge only
   where the reCAPTCHA wording and its two links appear in the form
   instead - that is .recaptcha-notice below, on all four forms.

   This has to be visibility: hidden, not display: none. display: none
   stops reCAPTCHA scoring the visitor and the check then fails. */
.grecaptcha-badge {
    visibility: hidden;
}

/* The wording that stands in for the hidden badge. Quiet, but a real
   part of the form: it must stay legible, since it is the only thing
   keeping the hidden badge within Google's terms. */
.recaptcha-notice {
    margin-top: 1rem;
    font-size: 0.75rem;
    line-height: 1.5;
    color: var(--secondary-color);
}

/* Google's own reCAPTCHA mark, served from this site rather than from
   Google, so no page contacts them before a form is actually submitted
   (the privacy policy says reCAPTCHA is involved in form submissions,
   and hot-linking the image here would quietly make that untrue).
   Decorative: the wording beside it already names reCAPTCHA. */
.recaptcha-notice-logo {
    width: 16px;
    height: 16px;
    margin-right: 0.4rem;
    vertical-align: -3px;
}

.recaptcha-notice a {
    color: inherit;
    text-decoration: underline;
}

.recaptcha-notice a:hover,
.recaptcha-notice a:focus-visible {
    color: var(--accent-color);
}

/* The idle-timeout window (public/index.html) is built from the modal
   classes above and needs no rules of its own; only its one line of
   text, which reads as a sentence rather than a form label. */
#sessionTimeoutText,
#idleWarningText {
    line-height: 1.5;
}

/* The warning before it can arrive over anything - another window, a
   window on top of that, a photograph open full screen - and has to be
   in front of all of them to be answered. */
#idleWarningModal {
    z-index: 1100;
}

/* Responsive Design */
@media (max-width: 768px) {
    body {
        /* Body padding to account for the fixed header, plus the same
           gap the desktop layout leaves below it. */
        padding-top: calc(var(--header-height-mobile) + var(--header-gap));
    }

    header {
        padding: 0.75rem 1rem;
        position: fixed; /* Ensure header stays fixed */
        height: 60px; /* Set fixed height for mobile */
        z-index: 100;
    }

    /* Mobile header is 60px, not 80px - keep the sticky bar flush under it */
    .manage-actionbar {
        top: calc(var(--header-height-mobile) + var(--header-gap));
    }

    .header-content {
        /* Title on the left on mobile even when logged out (unlike
           desktop, which keeps it centred). With a single flex child
           (logged out) space-between behaves like flex-start, and with
           two children (logged in) it puts the identity block at the
           right edge instead of packing it right next to the title. */
        justify-content: space-between;
    }
 
    header .header-logo {
        height: 2rem !important; /* Double size for mobile */
        max-width: 80px !important; /* Double width limit on mobile */
        margin-right: 0.75rem !important;
        object-fit: contain !important;
        width: auto !important;
    }
 
    header h1 {
        font-size: 1.2rem;
    }

    /* The menu is out of sight until the header's hamburger opens it
       (initializeNavToggle in menu.js puts nav-open on the body): then
       it drops down from under the header, over the page, as tall as
       its items need and no taller than the screen below the header. */
    .nav-toggle {
        display: flex;
    }

    nav#mainNav {
        display: none;
        top: var(--header-height-mobile);
        width: 100%;
        height: auto;
        max-height: calc(100vh - var(--header-height-mobile));
        max-height: calc(100dvh - var(--header-height-mobile));
        overflow-x: hidden;
        border-bottom: 2px solid var(--primary-color);
        -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
        z-index: 95; /* over the page and the colour scheme toggle, under the header */
    }

    body.nav-open nav#mainNav {
        display: flex;
    }

    main {
        margin-left: 0;
        margin-top: 0;
        padding: 1rem;
    }

    footer#siteFooter {
        margin-left: 0;
        padding: 1rem;
    }

    /* Narrow screens put the title on the left, so the identity block
       has to be compact to stay clear of it: the address line goes
       (it is one tap away in "Your Details"), and the name and button
       shrink. */
    .header-user {
        max-width: 50%;
        gap: 0.3rem;
    }

    .header-user-email {
        display: none;
    }

    .header-user-name {
        font-size: 0.78rem;
    }

    .header-user-identity {
        padding: 0.1rem 0.15rem;
    }

    .header-logout-btn {
        padding: 0.25rem 0.5rem;
        font-size: 0.72rem;
    }



    .legal-container {
        padding: 1.5rem;
    }


    .carousel {
        height: 400px;
    }

    .carousel-arrow {
        font-size: 1.5rem;
        padding: 0.5rem 1rem;
    }

    .carousel-arrow.prev {
        left: 0.5rem;
    }

    .carousel-arrow.next {
        right: 0.5rem;
    }

    .gallery-grid {
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    }

    .lightbox-close {
        top: 1rem;
        right: 1rem;
        font-size: 2rem;
    }
}

@media (max-width: 480px) {
    /* Make Home button even more prominent on narrow screens */
    .nav-item:first-child .nav-btn {
        font-weight: 700;
        background-color: var(--light-bg);
        border-left-color: var(--accent-color);
    }

    /* The prominence rule above outranks .nav-btn.active, so without
       this the first item would drop its active grey on phones. */
    .nav-item:first-child .nav-btn.active {
        background-color: var(--primary-color);
        border-left-color: var(--primary-color);
        color: var(--white);
    }


    .carousel {
        height: 300px;
    }

    .gallery-grid {
        grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
        gap: 0.5rem;
    }

    .contact-body {
        padding: 1rem;
    }

    .contact-email-phone-row .form-group:first-child,
    .contact-email-phone-row .form-group:last-child {
        flex: 1 1 100%;
    }

    .contact-area-field {
        max-width: 100%;
    }
}

/* Additional breakpoint for very narrow screens */
@media (max-width: 360px) {
    /* Ensure nav items are compact on very small screens */
    .nav-btn {
        padding: 0.5rem 1rem;
        font-size: 0.9rem;
    }
 
    .nav-children .nav-btn {
        padding-left: 2rem;
        font-size: 0.85rem;
    }
 
    .nav-children .nav-children .nav-btn {
        padding-left: 2.5rem;
        font-size: 0.8rem;
    }

    .nav-children .nav-children .nav-children .nav-btn {
        padding-left: 3rem;
    }

    .nav-children .nav-children .nav-children .nav-children .nav-btn {
        padding-left: 3.5rem;
    }

    .nav-children .nav-children .nav-children .nav-children .nav-children .nav-btn {
        padding-left: 4rem;
    }
}

/* ------------------------------------------------------------------
   Area pages (Phase 8): generated per area from /api/areas.php - the
   area name, the manager-written headed text, and either the root
   folder's images or a "pick a folder" placeholder. Folder pages carry
   headed text of their own, the same way.
   ------------------------------------------------------------------ */
.area-page-title {
    margin-bottom: 1rem;
    color: var(--primary-color);
}

/* A folder page's title is its breadcrumb (menu.js appendBreadcrumb):
   "Gallery > Aliya Conway > Layering", the names above the folder being
   links to those pages. They read as part of the title until pointed at. */
.area-crumb {
    color: inherit;
    text-decoration: none;
}

.area-crumb:hover,
.area-crumb:focus-visible {
    text-decoration: underline;
}

.area-crumb-separator {
    margin: 0 0.4em;
    opacity: 0.6;
}

/* "Managed by ..." under a folder page's title (menu.js) */
.area-managed-by {
    margin: -0.5rem 0 1rem;
}

.area-front-heading {
    margin: 0 0 0.75rem;
    color: var(--primary-color);
    font-weight: 600;
}

/* The heading with the logo mark at its left (menu.js
   appendPageSections). The heading's height is pinned (font size x
   line height) and the mark is given exactly that, so the two are the
   same height at any zoom. A long heading wraps under itself; the mark
   stays level with its first line. The first square is white in the
   header, which sits on the primary colour; on the page it is primary. */
.area-front-title {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    margin: 0 0 0.75rem;
}

.area-front-title .area-front-heading {
    margin: 0;
    font-size: 1.4rem;
    line-height: 1.3;
}

.area-front-logo {
    flex: 0 0 auto;
    width: calc(1.4rem * 1.3);
    height: calc(1.4rem * 1.3);
}

.area-front-logo > g:first-child path {
    fill: var(--primary-color);
}

.area-front-text {
    max-width: 800px;
    margin: 0 0 1.5rem;
    color: #555;
    line-height: 1.6;
    white-space: pre-line;
}

/* The headed text of a page (menu.js appendPageSections): sections in
   order, each a heading and/or a paragraph. Every heading after the
   first stands a little clear of the text above it; the first, on an
   area's front page, is the logo-marked title above. */
.area-page-text {
    max-width: 800px;
    margin: 0 0 1.5rem;
}

.area-page-text .area-front-heading {
    margin: 1.25rem 0 0.5rem;
    font-size: 1.2rem;
}

.area-page-text .area-front-title .area-front-heading {
    margin: 0;
    font-size: 1.4rem;
}

.area-page-text > :first-child {
    margin-top: 0;
}

.area-page-text .area-front-text {
    margin: 0 0 0.75rem;
}

/* ------------------------------------------------------------------
   Area access and area management (Phase 8): the access line and
   inline editor in Manage Users, the area checkboxes on the invite
   form, the Manage Areas form, and the manager's front-page button.
   ------------------------------------------------------------------ */
.tree-user-access {
    display: block;
    font-size: 0.85rem;
    color: #666;
    margin-top: 0.15rem;
}

.tree-area-checks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.25rem;
    margin: 0.35rem 0;
}

.tree-check-inline {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-weight: normal;
    cursor: pointer;
}

/* Send an Invite: one row per area the sender may give - its name,
   Access (members-only areas) and Manager (one per area) */
.tree-invite-area-rows {
    display: flex;
    flex-direction: column;
    margin: 0.35rem 0 0.6rem;
}

.tree-invite-area-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.25rem;
    padding: 0.4rem 0;
    border-bottom: 1px solid #eee;
}

.tree-invite-area-name {
    flex: 1 1 10rem;
    min-width: 0;
    font-weight: 600;
    color: var(--primary-color);
}

.tree-invite-area-row small {
    font-weight: normal;
    color: #666;
}

.tree-invite-area-row label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-weight: normal;
    color: var(--text-color);
    cursor: pointer;
}

.tree-invite-area-row input[type="checkbox"] {
    width: auto;
}

.tree-invite-area-row input[type="checkbox"]:disabled + span,
.tree-invite-area-row label.is-disabled {
    color: #999;
    cursor: default;
}

.tree-invite-area-public {
    font-size: 0.85rem;
    color: #666;
}

/* Your Details: the member's own areas, each with Leave */
.tree-profile-areas {
    margin-top: 1.5rem;
}

.tree-profile-areas h4 {
    margin: 0 0 0.35rem;
    color: var(--primary-color);
}

.tree-access-editor {
    flex-basis: 100%;
    margin-top: 0.75rem;
    padding: 0.75rem 1rem;
    background: #f5f7fa;
    border-radius: 6px;
}

.tree-user-list li {
    flex-wrap: wrap;
}

/* What a move of a folder will do (move.js requestMove), on top of
   the Content panel */
.move-confirm-list {
    margin: 0;
    padding-left: 1.25rem;
}

.move-confirm-list li {
    margin-bottom: 0.6rem;
}

.move-confirm-list li:last-child {
    margin-bottom: 0;
}

.home-front-page {
    max-width: 1000px;
    margin: 0 auto 1rem;
}

/* ------------------------------------------------------------------
   Invite workflow (Phase 9): the request reason, per-area approval
   lines, the email history under a member, and self-deletion in
   Your Details.
   ------------------------------------------------------------------ */
.tree-user-reason {
    display: block;
    font-size: 0.85rem;
    color: #555;
    font-style: italic;
    margin-top: 0.15rem;
    white-space: pre-line;
}

.tree-user-approval {
    display: block;
    font-size: 0.85rem;
    color: #7a5a00;
    margin-top: 0.15rem;
}

.tree-user-approval.approved {
    color: #1e7e34;
}

.tree-user-approval.declined {
    color: #b02a37;
}

/* "Waiting on you" (Manage Users and Your Details): the decisions the
   viewer may answer now, set apart from the list below them. */
.tree-waiting {
    margin-bottom: 1rem;
    padding: 0.5rem 0.75rem;
    border-left: 4px solid var(--secondary-color);
    background: #fffaf0;
    border-radius: 4px;
}

.tree-waiting h4 {
    margin: 0 0 0.5rem;
}

/* Refer: a heading over each kind of thing to decide */
.tree-access-editor h5 {
    margin: 0.75rem 0 0.25rem;
    font-size: 0.85rem;
}

.tree-access-editor select {
    max-width: 14rem;
}

.tree-approval-record {
    margin-left: 0.75rem;
    font-size: 0.8rem;
}

.tree-user-history {
    flex-basis: 100%;
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0.5rem 0.75rem;
    background: #f5f7fa;
    border-radius: 6px;
    font-size: 0.8rem;
    color: #444;
}

.tree-user-history li {
    display: block;
    padding: 0.15rem 0;
}

.tree-profile-danger {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid #e0e0e0;
}

/* ------------------------------------------------------------------
   Image uploads (Phase 10): the "Upload Images" button, the Upload Images
   window (drop zone, per-file rows with progress), and the lightbox
   caption carrying the image's details.
   ------------------------------------------------------------------ */
.area-upload-btn {
    margin: 0 0 1.5rem;
}

.upload-drop-zone {
    border: 2px dashed #b8c2cc;
    border-radius: 8px;
    padding: 1.25rem;
    text-align: center;
    background: #f8fafc;
    margin-bottom: 1rem;
}

.upload-drop-zone.dragging {
    border-color: var(--primary-color);
    background: #eef4fa;
}

.upload-drop-zone p {
    margin: 0.25rem 0;
}

.upload-list {
    list-style: none;
    margin: 0 0 1rem;
    padding: 0;
}

.upload-item {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.6rem 0;
    border-bottom: 1px solid #e6e6e6;
    position: relative;
}

.upload-item.done {
    opacity: 0.7;
}

.upload-item.failed .upload-status {
    color: #b02a37;
}

.upload-preview {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: 4px;
    flex-shrink: 0;
    background: #eee;
}

/* A PDF has no picture to preview, so it gets the same red badge the
   gallery tile uses, at the size of the image previews beside it. A
   video gets the same in the dark of its film strip, for the moment it
   takes the browser to hand back the first frame - once it has, the
   frame itself replaces the badge (prepareVideoUpload in upload.js). */
.upload-preview-pdf,
.upload-preview-video {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #e74c3c;
    color: var(--white);
    font-weight: 700;
    font-size: 0.85rem;
    letter-spacing: 0.05em;
}

.upload-preview-video {
    background: #17181a;
    color: #eceff2;
    font-size: 0.7rem;
}

.upload-fields {
    flex: 1;
    min-width: 0;
}

.upload-name {
    font-size: 0.9rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.upload-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0.35rem 0;
    align-items: center;
}

.upload-year {
    width: 6rem;
}

.upload-comment {
    flex: 1;
    min-width: 8rem;
}

.upload-owner {
    font-size: 0.8rem;
    color: #666;
}

.upload-progress {
    height: 6px;
    background: #e6e6e6;
    border-radius: 3px;
    overflow: hidden;
}

.upload-progress-bar {
    height: 100%;
    width: 0;
    background: var(--primary-color);
    transition: width 0.2s ease;
}

/* Beside the Upload button: "Uploading photo.jpg" with dots counting
   1, 2, 3 while it works, then "Done". The Close button that appears
   afterwards sits at the far end of the row. */
.upload-progress-note {
    align-self: center;
    min-width: 0;
    font-size: 0.95rem;
    color: var(--secondary-color);
    overflow-wrap: anywhere;
}

.upload-progress-note:empty {
    display: none;
}

.upload-progress-note.success {
    color: #155724;
}

.upload-progress-note.error {
    color: #b02a37;
}

/* Wide enough for all three dots, so the note keeps its width */
.upload-progress-dots {
    display: inline-block;
    min-width: 1em;
}

#uploadDoneBtn {
    margin-left: auto;
}

.upload-status {
    font-size: 0.8rem;
    color: #666;
    margin-top: 0.25rem;
}

.upload-remove {
    flex-shrink: 0;
    font-size: 1.4rem;
}

/* ------------------------------------------------------------------
   The Welcome window (welcome.js): its header and every section's
   heading in the logo scheme's accent colour, the logo mark beside
   each section - to the right of the first, the left of the second,
   and so on.
   ------------------------------------------------------------------ */
.welcome-dialog {
    max-width: 720px;
}

.welcome-dialog .modal-header {
    background-color: var(--accent-color);
}

.welcome-section {
    display: flex;
    align-items: flex-start;
    gap: 1.25rem;
    margin-bottom: 1.5rem;
}

.welcome-section:last-child {
    margin-bottom: 0;
}

.welcome-section-flip {
    flex-direction: row-reverse;
}

.welcome-section-body {
    flex: 1 1 auto;
    min-width: 0;
}

.welcome-heading {
    margin: 0 0 0.5rem;
    color: var(--accent-color);
    font-size: 1.2rem;
    font-weight: 600;
}

.welcome-text {
    margin: 0;
    color: #444;
    line-height: 1.6;
    white-space: pre-line;
}

.welcome-logo {
    flex: 0 0 auto;
    width: 72px;
    height: 72px;
}

/* The first square is white in the header, which sits on the primary
   colour; on the white dialog it is primary */
.welcome-logo > g:first-child path {
    fill: var(--primary-color);
}

.welcome-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* display: flex would otherwise beat [hidden]. The footer is hidden when
   an admin opens the window from Welcome Window / View, where neither
   answer it asks for is theirs to give (welcome.js
   applyWelcomeReviewMode) - the × in the header closes it instead. */
.welcome-actions[hidden] {
    display: none;
}

@media (max-width: 480px) {
    .welcome-logo {
        width: 48px;
        height: 48px;
    }
}

/* ------------------------------------------------------------------
   Page text editor (tree.js renderSectionsEditor - the Manage Areas
   form and the Edit Page Text modal): one block per section, a title
   bar (the heading as typed, with move up / down and remove) over the
   heading and the text.
   ------------------------------------------------------------------ */
.page-section-rows {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

.page-section-row {
    border: 1px solid #ddd;
    border-radius: 4px;
    padding-bottom: 0.6rem;
}

.page-section-title {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.4rem;
    background-color: var(--light-bg);
    border-bottom: 1px solid #ddd;
    border-radius: 4px 4px 0 0;
    font-weight: 600;
    color: var(--primary-color);
}

.page-section-title-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.page-section-move {
    flex: 0 0 auto;
    padding: 0.1rem 0.45rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: var(--white);
    color: var(--primary-color);
    font-size: 0.75rem;
    line-height: 1.5;
    cursor: pointer;
}

.page-section-move:hover,
.page-section-move:focus {
    background-color: var(--primary-color);
    color: var(--white);
}

.page-section-remove {
    flex: 0 0 auto;
    font-size: 1.3rem;
    line-height: 1;
}

.page-section-fields {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0 0.6rem;
}

/* The dialog's own label rule (block, with a margin) would break the
   field up: this is more specific and wins. */
.page-section-row .page-section-fields label {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 0;
}

/* ...and so beats [hidden] too: the "Shown to" field is hidden on a
   page where the audience is not the editor's to choose. */
.page-section-row .page-section-fields label[hidden] {
    display: none;
}

.page-section-row .page-section-fields label > span {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--primary-color);
}

.page-section-row .page-section-fields input,
.page-section-row .page-section-fields textarea {
    width: 100%;
    min-width: 0;
}

.page-section-row .page-section-fields textarea {
    resize: vertical;
}

.page-section-row .page-section-fields select {
    width: auto;
    max-width: 100%;
    padding: 0.4rem 0.6rem;
}

/* ------------------------------------------------------------------
   Per-area tooltip fields: the field editor rows (Manage Areas and the
   manager's area modal) and the "edit details" pencil on gallery tiles.
   ------------------------------------------------------------------ */
.tooltip-field-rows {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
}

/* One field: a title bar (the prompt, in the page background so it
   stands out from the white dialog) then its settings, each a small
   title over its control - Prompt on a line of its own, then Source and
   Order, then Type and Length. Type and Length only apply to a value the
   member types in, and Length only to text, so tree.js marks the field
   .tooltip-field-automatic / .tooltip-field-number and they are hidden. */
.tooltip-field-row {
    border: 1px solid #ddd;
    border-radius: 4px;
    padding-bottom: 0.5rem;
}

.tooltip-field-title {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.4rem 0.6rem;
    margin-bottom: 0.4rem;
    background-color: var(--light-bg);
    border-bottom: 1px solid #ddd;
    border-radius: 4px 4px 0 0;
    font-weight: 600;
    color: var(--primary-color);
}

.tooltip-field-title-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tooltip-field-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    padding: 0 0.6rem;
}

/* The dialog's own label rule (block, with a margin) would break the
   cell up: this is more specific and wins. Two cells to a line; the
   prompt's cell takes the whole line. */
.tooltip-field-row .tooltip-field-cell {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1 1 40%;
    min-width: 0;
    margin: 0;
}

.tooltip-field-row .tooltip-field-cell-wide {
    flex-basis: 100%;
}

.tooltip-field-row .tooltip-field-cell > span {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--primary-color);
}

.tooltip-field-row .tooltip-field-cell input,
.tooltip-field-row .tooltip-field-cell select {
    width: 100%;
    min-width: 0;
}

.tooltip-field-row.tooltip-field-automatic .tooltip-field-entered,
.tooltip-field-row.tooltip-field-number .tooltip-field-text {
    display: none;
}

.tooltip-field-remove {
    font-size: 1.3rem;
    line-height: 1;
}

.gallery-item {
    position: relative;
}

/* Always at least partly visible - opacity:0-until-hover made this
   undiscoverable (no hover state exists on touch devices at all, and
   even with a mouse it's easy to miss). Full strength on hover/focus/
   touch so it still reads as an accent the rest of the time. The hover
   preview would sit over it, so gallery.js holds the preview back while
   the pointer is in this corner (inEditCorner). */
.gallery-edit-btn {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    width: 2rem;
    height: 2rem;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: var(--primary-color);
    font-size: 1rem;
    cursor: pointer;
    opacity: 0.85;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
    transition: opacity 0.2s ease, transform 0.2s ease;
    z-index: 2;
}

.gallery-item:hover .gallery-edit-btn,
.gallery-edit-btn:focus,
.gallery-edit-btn:active {
    opacity: 1;
    transform: scale(1.08);
}

/* ------------------------------------------------------------------
   Phase 11: carousels on a sliding track (side-by-side slides when
   they fit, half height inside folders), the page's Edit button,
   selection and reorder modes, and the carousel editor.
   ------------------------------------------------------------------ */
.carousel-host {
    margin-bottom: 1.5rem;
}

.carousel-track {
    display: flex;
    height: 100%;
    transition: transform 0.5s ease-in-out;
    will-change: transform;
}

.carousel-track > .carousel-slide {
    position: relative;
    flex: 0 0 auto;
    height: 100%;
    opacity: 1;
    transition: none;
}

/* Pictures that do not all fit (carousel.js layoutCarousel). Two:
   they sit on top of each other and the new one fades in over the old.
   Three or more: a stack, the current picture large in the centre and
   its neighbours smaller, dimmer and behind it; carousel.js sets each
   slide's transform, opacity, brightness and stacking order. */
.carousel-stack .carousel-track,
.carousel-fade .carousel-track {
    display: block;
    position: relative;
    transition: none;
}

.carousel-stack .carousel-track > .carousel-slide,
.carousel-fade .carousel-track > .carousel-slide {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
}

.carousel-fade .carousel-track > .carousel-slide {
    opacity: 0;
    transition: opacity 0.8s ease-in-out;
}

.carousel-stack .carousel-track > .carousel-slide {
    cursor: pointer;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
    transition: transform 0.6s ease-in-out, opacity 0.6s ease-in-out, filter 0.6s ease-in-out;
}

.carousel-stack .carousel-track > .carousel-slide.is-current {
    cursor: default;
}

/* Only the picture in front is named */
.carousel-stack .carousel-slide:not(.is-current) .artist-name {
    display: none;
}

@media (prefers-reduced-motion: reduce) {
    .carousel-stack .carousel-track > .carousel-slide {
        transition: opacity 0.4s;
    }
}

.carousel-half .carousel {
    height: 300px;
}

@media (max-width: 768px) {
    .carousel-half .carousel {
        height: 200px;
    }
}

/* Both bars stay in view while the grid scrolls underneath them, rather
   than scrolling away with the images. `top` matches the fixed header's
   height (see the 768px override below for the shorter mobile header) so
   they sit right under it; an opaque background stops grid tiles showing
   through as they scroll past. Consecutive sticky siblings with the same
   `top` stack correctly on their own - each stays in flow, so when both
   the toolbar and an action bar (select/reorder mode) are present, the
   action bar sticks just below the toolbar rather than on top of it. */
/* "Edit this page" at the top of an area's or folder's page (menu.js
   appendEditButton): the one button a page carries, for a member. It
   opens the Content panel at the page, and goes while the panel is open
   (the panel is then the way). */
.area-edit-btn {
    margin: 0 0 1.25rem;
}

body.content-open .area-edit-btn {
    display: none;
}

.manage-actionbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
    align-items: center;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    background: #eef4fa;
    border-radius: 8px;
    position: sticky;
    top: calc(var(--header-height) + var(--header-gap));
    z-index: 40;
}

.manage-count {
    font-weight: 600;
    color: var(--primary-color);
    margin-right: 0.5rem;
}

.manage-message {
    flex-basis: 100%;
}

.manage-move {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.manage-move-target {
    padding: 0.4rem 0.6rem;
}

.manage-move-btn:disabled,
.manage-unlink-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

/* An image that is a copy in this folder (it also lives in another
   folder of the area): a small tag in the tile's corner.

   Hidden until the folder is in Select mode. The tag exists to say
   that "Remove copy" applies to this image rather than Delete, which
   only matters while images are being chosen; the rest of the time it
   is a label sitting over somebody's photograph. */
.gallery-copy-tag {
    display: none;
    position: absolute;
    top: 0.4rem;
    /* Right, not left: Select mode is the only time this shows, and the
       tick box it would otherwise sit on top of is in the left corner.
       The pencil that normally holds this corner is hidden meanwhile. */
    right: 0.4rem;
    z-index: 2;
    padding: 0.1rem 0.45rem;
    border-radius: 3px;
    background: rgba(0, 0, 0, 0.55);
    color: var(--white);
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    pointer-events: none;
}

.selecting .gallery-copy-tag {
    display: block;
}

/* The Bin (deleted.js), in the Content panel */
.deleted-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.deleted-row {
    display: flex;
    gap: 1rem;
    align-items: center;
    flex-wrap: wrap;
    padding: 0.75rem 0;
    border-bottom: 1px solid #eee;
}

.deleted-thumb {
    flex: 0 0 72px;
    height: 72px;
    border-radius: 4px;
    background: #ddd;
    overflow: hidden;
}

.deleted-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.deleted-info {
    flex: 1 1 220px;
    min-width: 0;
    overflow-wrap: anywhere;
}

.deleted-meta {
    font-size: 0.85rem;
    color: #666;
}

.deleted-actions {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
}

.deleted-target {
    max-width: 16rem;
}

.deleted-section {
    margin: 1.25rem 0 0.25rem;
    font-weight: 600;
    color: var(--primary-color);
    border-bottom: 2px solid var(--primary-color);
    padding-bottom: 0.25rem;
}

.deleted-section:first-child {
    margin-top: 0;
}

.site-admin-deleted-btn {
    margin-top: 0.75rem;
}

.deleted-empty {
    color: #666;
    font-style: italic;
}

/* A copy of a picture deleted from its own area is marked whenever it is
   shown. Top left, where the pencil would be: such a copy cannot be edited. */
.gallery-copy-tag.gallery-deleted-tag {
    display: block;
    left: 0.4rem;
    right: auto;
    background: rgba(160, 20, 20, 0.85);
}

.selecting .gallery-item {
    cursor: pointer;
}

/* The tick box on each tile. Drawn here rather than added to the tile
   so entering and leaving Select mode touches no markup; gallery.js
   reads its place and size from this rule to keep the hover preview off
   it (inSelectCorner). */
.selecting .gallery-item::after {
    content: '';
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    width: 1.5rem;
    height: 1.5rem;
    border: 2px solid var(--white);
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.35);
    z-index: 2;
}

.selecting .gallery-item.selected {
    outline: 4px solid var(--primary-color);
    outline-offset: -4px;
}

.selecting .gallery-item.selected::after {
    content: '\2713';
    color: var(--white);
    background: var(--primary-color);
    border-color: var(--primary-color);
    text-align: center;
    line-height: 1.5rem;
    font-weight: 700;
}

.selecting .gallery-edit-btn,
.reordering .gallery-edit-btn {
    display: none;
}

.reordering .gallery-item {
    cursor: grab;
}

/* Picked with a click, Ctrl/Cmd+click or Shift+click to be dragged
   together (manage.js) */
.reordering .gallery-item.selected {
    outline: 4px solid var(--primary-color);
    outline-offset: -4px;
}

/* Shift+click picks a run of tiles; it must not highlight their
   captions as text as well */
.selecting .gallery-item,
.reordering .gallery-item {
    -webkit-user-select: none;
    user-select: none;
}

.reordering .gallery-item.dragging {
    opacity: 0.4;
}

.modal-dialog-wide {
    max-width: 1000px;
    width: 95%;
}

.carousel-pick-outside .carousel-pick-name {
    color: #b23b3b;
}

.carousel-editor {
    display: flex;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.carousel-editor-column {
    flex: 1 1 18rem;
    min-width: 0;
    /* The list fills the column, and the columns are as tall as each
       other: the white space under a short carousel takes a drop too */
    display: flex;
    flex-direction: column;
}

.carousel-editor-column .carousel-pick-list {
    flex: 1 1 auto;
    min-height: 8rem; /* room to drop into when the columns are stacked */
}

.carousel-editor-column h4 {
    margin: 0 0 0.5rem;
    color: var(--primary-color);
}

.carousel-pick-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 50vh;
    overflow-y: auto;
    /* A little air at the foot, so the last row is not cut off by the
       list's own edge when scrolled to the end */
    padding-bottom: 0.5rem;
}

.carousel-pick {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.35rem 0;
    border-bottom: 1px solid #eee;
    position: relative; /* for the drop-position line */
}

/* Drag and drop in the editor (manage.js makeCarouselPickDraggable):
   the row being dragged fades, the carousel list gets a dashed frame
   while a drop is possible, and a line shows where the drop will land.
   The line is a pseudo-element over the row's own padding, so nothing
   moves while dragging. */
.carousel-pick[draggable="true"] {
    cursor: grab;
}

.carousel-pick.dragging {
    opacity: 0.4;
}

.carousel-pick-list.carousel-drop-target,
.carousel-pick-list.carousel-drop-remove {
    outline: 2px dashed var(--primary-color);
    outline-offset: 2px;
    border-radius: 4px;
}

/* A carousel row held over the right-hand list: letting go takes it out */
.carousel-pick-list.carousel-drop-remove {
    outline-color: #a33a2a;
}

.carousel-pick.carousel-drop-before::before,
.carousel-pick.carousel-drop-after::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 3px;
    border-radius: 2px;
    background: var(--primary-color);
    pointer-events: none;
}

.carousel-pick.carousel-drop-before::before {
    top: 0;
}

.carousel-pick.carousel-drop-after::after {
    bottom: 0;
}

.carousel-editor-hint {
    flex: 1 1 100%;
    margin: 0;
    font-size: 0.85rem;
    color: #666;
}

/* The carousel's size: Large (full height) or Small (half height) */
.carousel-size-row {
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
}

.carousel-size-row label {
    margin: 0;
    font-weight: 600;
    color: var(--primary-color);
}

.carousel-size-row select {
    width: auto;
    padding: 0.4rem 0.6rem;
}

.carousel-size-note {
    font-size: 0.85rem;
    color: #666;
}

.carousel-pick img {
    width: 56px;
    height: 42px;
    object-fit: cover;
    border-radius: 4px;
    flex-shrink: 0;
    background: #eee;
}

/* The four-times-size hover copy of a thumbnail (see manage.js
   showCarouselPreview). Fixed so it sits over the lists without
   moving anything; pointer-events off so the thumbnail underneath
   keeps receiving mouseleave. */
.carousel-pick-preview {
    position: fixed;
    z-index: 300; /* above the modal overlay (200) */
    object-fit: cover;
    border-radius: 4px;
    background: #eee;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
    pointer-events: none;
}

.carousel-pick-name {
    flex: 1;
    min-width: 0;
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.carousel-pick-controls .link-btn {
    margin: 0 0.15rem;
    font-size: 0.9rem;
}

.carousel-candidate-folder {
    font-size: 0.8rem;
    font-weight: 600;
    color: #666;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 0.6rem 0 0.2rem;
}

/* ------------------------------------------------------------------
   Comments - on an image (in the lightbox) or a page (an area's front
   page or a folder's page). One shared look, built by comments.js.
   ------------------------------------------------------------------ */

/* The lightbox grows a side panel for the image's comment thread,
   stacking below the image instead on a narrow screen. */
.lightbox-content {
    display: flex;
    align-items: stretch;
    gap: 1.5rem;
    max-width: 95vw;
    max-height: 90vh;
}

/* A document read in the lightbox: the frame takes the room the image
   would have had, with the way out to a new tab under it. An iframe has
   no shape of its own, so unlike the image it is given one. */
.lightbox-pdf {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    width: calc(95vw - var(--lightbox-side) - 3rem);
    max-height: 90vh;
}

/* display: flex above would otherwise beat [hidden], leaving an empty
   frame and its "Open in a new tab" link under every picture and video */
.lightbox-pdf[hidden] {
    display: none;
}

.lightbox-pdf iframe {
    flex: 1;
    min-height: 0;
    height: 85vh;
    width: 100%;
    border: none;
    border-radius: 8px;
    background-color: var(--white);
}

.lightbox-pdf-link {
    flex-shrink: 0;
    align-self: flex-start;
    color: var(--white);
    font-size: 0.95rem;
    text-decoration: underline;
}

.lightbox-pdf-link:hover,
.lightbox-pdf-link:focus {
    opacity: 0.8;
}

/* The panel is its own light surface over the darkened lightbox, so it
   states both its background and the colour of the text on it. Without
   the second of those, a scheme whose text colour is nearly black (the
   Neon Noir heading colour, for one) would be read against the black
   backdrop rather than against this panel. */
.lightbox-comments {
    width: var(--lightbox-side);
    flex-shrink: 0;
    max-height: 90vh;
    overflow-y: auto;
    background: #000;
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 8px;
    padding: 1rem 1.25rem;
    box-sizing: border-box;
}

/* The boundary between the picture and its comments, dragged to give
   one or the other more room (gallery.js). A slim bar with a grip,
   wide enough to catch with a finger. */
.lightbox-divider {
    flex: 0 0 10px;
    align-self: stretch;
    cursor: col-resize;
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.25);
    touch-action: none;
}

.lightbox-divider:hover,
.lightbox-divider:focus,
.lightbox-divider.dragging {
    background: rgba(255, 255, 255, 0.7);
    outline: none;
}

.lightbox-divider[hidden] {
    display: none;
}

/* The comments in the lightbox are white on black, the reverse of a
   page's thread: every colour the thread sets for a light surface is
   restated here for a dark one. */
.lightbox-comments .comment-thread-heading,
.lightbox-comments .comment-text,
.lightbox-comments .comment-login-prompt,
.lightbox-comments .comment-filter {
    color: #fff;
}

.lightbox-comments .comment-author {
    color: #8ec5ff;
}

.lightbox-comments .comment-meta,
.lightbox-comments .comment-empty {
    color: #bbb;
}

.lightbox-comments .comment {
    border-bottom-color: rgba(255, 255, 255, 0.22);
}

.lightbox-comments .comment-deleted .comment-line,
.lightbox-comments .comment-deleted .comment-text {
    color: #999;
}

.lightbox-comments .link-btn {
    color: #8ec5ff;
}

.lightbox-comments .comment-new-box,
.lightbox-comments .comment-edit-box,
.lightbox-comments .comment-filter-select {
    color: #fff;
    background: #1a1a1a;
    border-color: #666;
}

.lightbox-comments .comment-new-box:focus,
.lightbox-comments .comment-edit-box:focus {
    border-color: #8ec5ff;
}

.lightbox-comments .form-feedback.error {
    color: #ff8a8a;
}

@media (max-width: 900px) {
    .lightbox-content {
        flex-direction: column;
        max-height: 95vh;
        overflow-y: auto;
    }

    /* Stacked, there is no side to move the boundary along */
    .lightbox-divider {
        display: none;
    }

    .lightbox img,
    .lightbox video {
        max-width: 90vw;
        max-height: 50vh;
    }

    .lightbox-pdf {
        width: 90vw;
        max-height: 60vh;
    }

    .lightbox-pdf iframe {
        height: 55vh;
    }

    .lightbox-comments {
        width: 90vw;
        max-height: none;
    }
}

.comment-filter label {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-right: 1rem;
}

.comment-filter label[hidden] {
    display: none;
}

/* On a page (not the lightbox), the thread sits in the page's own
   colours rather than a white panel. */
.page > .comment-thread {
    max-width: 800px;
    margin: 2rem auto 0;
}

.comment-thread-heading {
    color: var(--primary-color);
    margin-bottom: 0.75rem;
}

.comment-list {
    margin-bottom: 1rem;
}

.comment-empty {
    color: var(--secondary-color);
    font-style: italic;
}

.comment {
    padding: 0.6rem 0;
    /* The scheme's own colour at low strength, so the rule between
       comments sits in the same family as everything around it
       whichever scheme is on, instead of a fixed grey. The plain grey
       first is for browsers without color-mix(), which drop the line
       below and keep this one. */
    border-bottom: 1px solid #eee;
    border-bottom: 1px solid color-mix(in srgb, var(--secondary-color) 22%, transparent);
}

.comment-line {
    margin: 0;
    line-height: 1.5;
    overflow-wrap: break-word;
}

/* The name in a different colour from the message, as one line:
   "Firstname Surname: the message" (spec). */
.comment-author {
    color: var(--primary-color);
    font-weight: 600;
}

.comment-text {
    color: var(--text-color);
}

/* Quieter than the comment itself, but still the scheme's colour
   rather than a grey that belongs to no scheme at all. */
.comment-meta {
    margin: 0.15rem 0 0;
    font-size: 0.78rem;
    color: #888;
    color: color-mix(in srgb, var(--secondary-color) 75%, transparent);
}

/* Struck through, manager/admin view only - regular members never
   receive a deleted comment from the API at all. */
.comment-deleted .comment-line,
.comment-deleted .comment-text {
    text-decoration: line-through;
    color: #999;
    color: color-mix(in srgb, var(--secondary-color) 60%, transparent);
}

.comment-actions {
    display: flex;
    gap: 0.9rem;
    margin-top: 0.3rem;
}

.comment-actions .link-btn {
    display: inline;
    margin-top: 0;
    font-size: 0.82rem;
}

.comment-new-box,
.comment-edit-box {
    width: 100%;
    min-height: 4.5rem;
    padding: 0.6rem;
    border: 2px solid #ddd;
    border-radius: 4px;
    font-size: 0.95rem;
    font-family: inherit;
    resize: vertical;
    box-sizing: border-box;
}

.comment-new-box:focus,
.comment-edit-box:focus {
    outline: none;
    border-color: var(--accent-color);
}

.comment-post-btn {
    margin-top: 0.5rem;
}

/* The compact secondary button: Post comment, and every Cancel beside
   a form or in an action bar - all the same size */
.comment-post-btn,
.tree-btn-compact {
    padding: 0.5rem 1.1rem;
    font-size: 0.9rem;
}

.comment-login-prompt {
    color: var(--secondary-color);
    margin: 0;
}

.comment-login-prompt .link-btn {
    display: inline;
    margin-top: 0;
    font-size: inherit;
}

/* ------------------------------------------------------------------
   Site Admin (admin only): the stats page and its Users/Areas/
   comments-browser daughter windows.
   ------------------------------------------------------------------ */

/* The figures, a box per subject. auto-fit measures the container, not
   the window, so Registered users and Areas sit side by side as soon as
   two 280px columns will fit - which is what the sidebar leaves on a
   desktop - and fall into one column on a phone without a breakpoint
   having to guess where that happens. Server spans whatever number of
   columns there turn out to be: its values are labels, not counts, and
   read badly in a narrow one. */
.site-admin-boxes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
    margin-top: 1.25rem;
}

.site-admin-box {
    padding: 1rem 1.15rem;
    background-color: var(--light-bg);
    border: 1px solid #e2e2e2;
    border-radius: 6px;
}

.site-admin-box-wide {
    grid-column: 1 / -1;
}

/* These headings sit inside a box, so they drop the 2rem of air
   .legal-container gives an h3 that starts a new section of a policy. */
.legal-container .site-admin-box h3 {
    margin: 0 0 0.6rem;
    font-size: 1rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--primary-color);
    padding-bottom: 0.4rem;
    border-bottom: 1px solid #dcdcdc;
}

/* Label left, value right, tight enough to read as one block. The dl
   carries the grid, so the dt/dd pairs need no wrapper. */
.site-admin-figures {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.1rem 1rem;
    margin: 0;
    font-size: 0.9rem;
}

.site-admin-figures dt {
    color: #555;
}

.site-admin-figures dd {
    margin: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
    color: var(--text-color, #333);
}

/* The total is the line the eye goes to, so it is ruled off above and
   set in bold - in both columns, since the grid has no row to style. */
.site-admin-figures .site-admin-total {
    margin-top: 0.35rem;
    padding-top: 0.35rem;
    border-top: 1px solid #dcdcdc;
    font-weight: 600;
    color: var(--primary-color);
}

/* OPcache off, or no AVIF: the value is a sentence saying what to do
   about it, so it is allowed to wrap and left-aligned to read as one. */
.site-admin-figures .site-admin-warn {
    color: #a94442;
    font-weight: 600;
}

.site-admin-figures dd.site-admin-warn {
    text-align: left;
}

.site-admin-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* What a manager's Users or Areas list is a list of, at the head of it */
.site-admin-scope-note {
    margin-bottom: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: var(--light-bg);
    border-radius: 4px;
    font-size: 0.85rem;
    color: #555;
}

.site-admin-item {
    padding: 0.75rem 0;
    border-bottom: 1px solid #eee;
}

.site-admin-item-title {
    margin: 0 0 0.2rem;
    font-weight: 600;
    color: var(--primary-color);
}

.site-admin-item-details {
    margin: 0.15rem 0;
    font-size: 0.85rem;
    color: #555;
}

.site-admin-item-details .link-btn {
    display: inline;
    margin-top: 0;
    font-size: inherit;
}

.site-admin-subtree {
    list-style: none;
    margin: 0.3rem 0 0.3rem 1.25rem;
    padding: 0.4rem 0 0.4rem 0.75rem;
    border-left: 2px solid #eee;
}

.site-admin-toggle {
    display: inline-block;
    margin-top: 0.3rem;
    font-size: 0.82rem;
}

.site-admin-comment-group {
    color: var(--primary-color);
    margin: 1rem 0 0.4rem;
}

.site-admin-comment-group-area {
    font-size: 1rem;
    border-bottom: 2px solid var(--primary-color);
    padding-bottom: 0.2rem;
}

.site-admin-comment-group-directory {
    font-size: 0.9rem;
    padding-left: 0.5rem;
}

.site-admin-comment-group-image {
    font-size: 0.82rem;
    font-weight: 600;
    color: #666;
    padding-left: 1rem;
}

/* Colour scheme toggle: a small circle in the bottom-right corner of
   the sidebar itself (fixed to the viewport, not a scrolling child of
   nav#mainNav, so a long area/folder list scrolling underneath it
   never carries it away). Its ground is primary-color, the same as
   the header - not accent-color, which the mark itself uses for two
   of its four squares and would disappear into a same-coloured circle.
   Below the 768px breakpoint the sidebar is no longer a fixed column
   (it becomes part of the normal page flow), so there the button
   falls back to the corner of the screen instead. */
.scheme-toggle {
    position: fixed;
    bottom: 1.25rem;
    left: calc(var(--sidebar-width) - 46px - 1.25rem);
    z-index: 90;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    border: 2px solid var(--white);
    background-color: var(--primary-color);
    box-shadow: var(--shadow-lg);
    padding: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: transform 0.15s ease;
}

.scheme-toggle svg {
    width: 100%;
    height: 100%;
}

.scheme-toggle:hover {
    transform: scale(1.08);
}

.scheme-toggle:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 2px;
}

@media (max-width: 768px) {
    .scheme-toggle {
        bottom: 1rem;
        left: auto;
        right: 1rem;
        width: 42px;
        height: 42px;
        padding: 8px;
    }
}

/* "More about this picture" - the opposite corner from the pencil, and
   wider, because it carries a word rather than a glyph. Semi-opaque so
   the picture still reads through it, and like the pencil it comes
   fully forward on hover. Everyone who can see the image sees this;
   the pencil is only for whoever may edit it. */
.gallery-detail-btn {
    position: absolute;
    bottom: 0.4rem;
    right: 0.4rem;
    padding: 0.25rem 0.6rem;
    border-radius: 1rem;
    background: rgba(255, 255, 255, 0.82);
    color: var(--primary-color);
    font-size: 0.8rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    opacity: 0.85;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
    transition: opacity 0.2s ease, transform 0.2s ease;
    z-index: 2;
}
.gallery-item:hover .gallery-detail-btn,
.gallery-detail-btn:focus,
.gallery-detail-btn:active {
    opacity: 1;
    transform: scale(1.06);
}
/* The All / Public only / Private only choice on a private area's comments */
.comment-filter {
    margin: 0 0 0.5rem;
    font-size: 0.9rem;
}
.comment-filter-select {
    margin-left: 0.25rem;
}

/* ------------------------------------------------------------------
   The site's own message boxes (dialogs.js): the title bar of every
   other window, with a coloured symbol that says what kind of message
   it is - green for done, amber for a warning or a question, red for
   something that went wrong. Above everything else on the page,
   the lightbox included.
   ------------------------------------------------------------------ */
.site-dialog-overlay {
    z-index: 3000;
}

.site-dialog {
    max-width: 460px;
}

.site-dialog .modal-header {
    justify-content: flex-start;
    gap: 0.75rem;
}

.site-dialog .modal-header h3 {
    margin: 0;
    font-size: 1.1rem;
}

.site-dialog-symbol {
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    font-size: 1.1rem;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    border: 2px solid #fff;
}

.site-dialog-success .site-dialog-symbol {
    background: #2e9e5b;
}

.site-dialog-warning .site-dialog-symbol {
    background: #f0a500;
    color: #3a2a00;
}

.site-dialog-error .site-dialog-symbol {
    background: #d03a3a;
}

.site-dialog-body {
    overflow-y: auto;
}

.site-dialog-text {
    margin: 0;
    line-height: 1.5;
    white-space: pre-line;
    overflow-wrap: anywhere;
}

.site-dialog-input {
    display: block;
    width: 100%;
    margin-top: 0.9rem;
    padding: 0.55rem 0.7rem;
    font: inherit;
    border: 2px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
}

.site-dialog-input:focus {
    outline: none;
    border-color: var(--accent-color);
}

.site-dialog-footer {
    display: flex;
    gap: 0.75rem;
    justify-content: flex-end;
}

/* ---------------------------------------------------------------------
 * Frames (a folder's manager chooses: Page > Frames)
 *
 * The same tiles and slides, hung like pictures in a gallery: a frame
 * - black, walnut or gilt, picked from the picture by frames.js - round
 * a cream mat, and the first line of the picture's tooltip on a small
 * label beneath. Styling only: gallery.js and carousel.js build what
 * they always did, so the larger preview on hover, the lightbox with
 * its details and comments, the edit and detail buttons, Select and
 * Reorder all carry on as they were.
 * ------------------------------------------------------------------- */
.gallery-grid.framed,
.carousel-framed {
    --wall: #ece7df;
    --mat: #f7f4ee;
    --frame: linear-gradient(135deg, #4a3320, #2e1f13);
}

.gallery-grid.framed [data-frame="black"],
.carousel-framed [data-frame="black"] { --frame: linear-gradient(135deg, #2a2a2a, #111); }
.gallery-grid.framed [data-frame="walnut"],
.carousel-framed [data-frame="walnut"] { --frame: linear-gradient(135deg, #5a4028, #2e1f13); }
.gallery-grid.framed [data-frame="gilt"],
.carousel-framed [data-frame="gilt"] { --frame: linear-gradient(135deg, #c3a05a, #8a6a2f); }

.gallery-grid.framed {
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 3.5rem 3rem;
    padding: 3rem 2.5rem 4rem;
    background: var(--wall);
    border-radius: 4px;
    box-shadow: inset 0 0 60px rgba(43, 33, 24, 0.12);
    align-items: start;
}

.gallery-grid.framed .gallery-item {
    /* The frame: a gradient border, with the mat inside it */
    border: 10px solid transparent;
    background: linear-gradient(var(--mat), var(--mat)) padding-box,
                var(--frame) border-box;
    border-radius: 0;
    overflow: visible;
    box-shadow: 0 18px 28px -12px rgba(43, 33, 24, 0.45),
                0 3px 6px rgba(43, 33, 24, 0.25);
}

.gallery-grid.framed .gallery-item:hover {
    transform: translateY(-3px);
    box-shadow: 0 24px 34px -12px rgba(43, 33, 24, 0.5),
                0 4px 8px rgba(43, 33, 24, 0.25);
}

/* The mat: the picture sits inside a cream margin of its own */
.gallery-grid.framed .gallery-item picture {
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12) inset;
    background: transparent;
}

.gallery-grid.framed .gallery-item picture img {
    box-sizing: border-box;
    padding: 9%;
    background-color: transparent;
}

.gallery-grid.framed .gallery-item.loading {
    animation: none;
    background: linear-gradient(var(--mat), var(--mat)) padding-box,
                var(--frame) border-box;
}

/* The label hangs just below the frame, like a gallery's placard, and
   takes up no room in the tile - the grid's gap holds it. A picture
   with nothing in its first line has no label (gallery.js). */
.gallery-grid.framed .gallery-item-labelled {
    margin-bottom: 2.6rem;
}

.gallery-grid.framed .gallery-item-caption {
    position: absolute;
    top: calc(100% + 10px + 0.8rem);
    left: 50%;
    transform: translateX(-50%);
    max-width: 92%;
    padding: 0.35rem 1rem;
    background: var(--mat);
    border: 1px solid #d6cfc2;
    box-shadow: 0 1px 2px rgba(43, 33, 24, 0.15);
    color: #4a3d2c;
    font-family: Georgia, 'Times New Roman', serif;
    font-style: italic;
    font-size: 0.95rem;
    letter-spacing: 0.02em;
}

/* The carousel: each slide framed the same way. The picture is held
   inside the mat by the padding of its own box, so the layout of the
   slides (carousel.js sizes each to its picture) is untouched. */
.carousel-framed .carousel-slide {
    box-sizing: border-box;
    border: 8px solid transparent;
    background: linear-gradient(var(--mat), var(--mat)) padding-box,
                var(--frame) border-box;
}

.carousel-framed .carousel-slide img {
    box-sizing: border-box;
    padding: 5%;
    background-color: transparent;
}

/* The caption, as a label across the foot of the mat rather than a
   shadow over the picture */
.carousel-framed .carousel-slide .artist-name {
    background: var(--mat);
    color: #4a3d2c;
    padding: 0.35rem 1rem;
    font-family: Georgia, 'Times New Roman', serif;
    font-style: italic;
    font-size: 1rem;
    font-weight: 400;
    text-align: center;
    border-top: 1px solid #d6cfc2;
}

/* The Frames dialog's choices */
.frames-choices {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    margin-top: 0.75rem;
}

.frames-choice {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
}

/* A framed picture in the lightbox: the same mat and frame as its tile,
   made of shadow rings round the picture - which add nothing to its box,
   so the zoom (gallery.js) still measures the picture itself. The
   picture is centred rather than stretched to the comments' height, so
   the frame closes round it. */
.lightbox.framed { --lightbox-frame: #4a3320; }
.lightbox.framed[data-frame="black"] { --lightbox-frame: #1c1c1c; }
.lightbox.framed[data-frame="walnut"] { --lightbox-frame: #4a3320; }
.lightbox.framed[data-frame="gilt"] { --lightbox-frame: #b8964f; }

.lightbox.framed #lightboxImage {
    align-self: center;
    margin: 2.5rem;
    max-width: calc(95vw - var(--lightbox-side) - 8rem);
    max-height: calc(90vh - 9rem);
    box-shadow: 0 0 0 22px #f7f4ee,
                0 0 0 32px var(--lightbox-frame),
                0 24px 60px 32px rgba(0, 0, 0, 0.6);
}

.lightbox.framed .lightbox-content:has(> #lightboxComments[hidden]) #lightboxImage {
    max-width: calc(90vw - 8rem);
}

.lightbox-label {
    position: fixed;
    z-index: 1001;
    transform: translateX(-50%);
    max-width: 60vw;
    padding: 0.4rem 1.2rem;
    background: #f7f4ee;
    border: 1px solid #d6cfc2;
    color: #4a3d2c;
    font-family: Georgia, 'Times New Roman', serif;
    font-style: italic;
    font-size: 1.05rem;
    text-align: center;
    pointer-events: none;
}

/* ------------------------------------------------------------------
   The Content panel (content.js): docked to the left of the window,
   over the sidebar, with the page beside it. Its width is the custom
   property content.js sets as the divider is dragged; the page's main
   and footer start where the panel ends. Folded to the rail the panel
   is a strip down the edge; taking the whole window it hides the page
   behind it; previewing, it is out of the way altogether and the
   sidebar is back, with a bar above the page to return by.
   ------------------------------------------------------------------ */
:root {
    --content-panel-width: 640px;
    --content-rail-width: 36px;
    --content-divider-width: 18px;
    --content-preview-bar-height: 44px;
}

.content-panel {
    position: fixed;
    /* --content-panel-top is set by content.js from where the header
       really ends, with a gap below it */
    top: var(--content-panel-top, calc(var(--header-height) + var(--header-gap)));
    left: 0;
    width: var(--content-panel-width);
    height: calc(100vh - var(--content-panel-top, calc(var(--header-height) + var(--header-gap))));
    display: flex;
    background-color: var(--white);
    box-shadow: var(--shadow-lg);
    /* A line between the site's header and the top of the panel, so the
       two read as separate things rather than the panel's title bar
       running on from the header */
    border-top: 3px solid var(--accent-color);
    /* Over the sidebar (50) and the page's sticky bars (40); under the
       header (100) and every window (200) */
    z-index: 60;
}

.content-panel[hidden] {
    display: none;
}

.content-panel-main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* Title bar in the header grey, as every window's is */
.content-panel-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 1rem 0.7rem 1.25rem;
    background-color: var(--primary-color);
    color: var(--white);
}

.content-panel-header h3 {
    flex: 1 1 auto;
    margin: 0;
    font-size: 1.1rem;
    color: var(--white);
}

.content-panel-header .modal-close {
    color: rgba(255, 255, 255, 0.85);
}

.content-panel-header .modal-close:hover {
    color: var(--white);
}

.content-view-btn {
    background: none;
    border: 1px solid rgba(255, 255, 255, 0.7);
    border-radius: 4px;
    padding: 0.35rem 0.75rem;
    color: var(--white);
    font-size: 0.85rem;
    cursor: pointer;
}

.content-view-btn:hover {
    background-color: rgba(255, 255, 255, 0.12);
}

.content-panel-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
}

/* The tree: the site's menu, with handles for dragging */
.content-tree-pane {
    width: 240px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid #e0e0e0;
    background-color: var(--light-bg);
}

.content-tree {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: 0.5rem 0.4rem;
}

.content-tree-row {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    min-height: 2rem;
    border-radius: 4px;
    font-size: 0.92rem;
}

.content-tree-row.selected {
    background-color: var(--primary-color);
    color: var(--white);
}

.content-tree-area {
    margin-top: 0.15rem;
    font-weight: 600;
}

.content-tree-row[draggable="true"] {
    cursor: grab;
}

.content-tree-handle {
    width: 1rem;
    flex-shrink: 0;
    text-align: center;
    color: #999;
}

.content-tree-row.selected .content-tree-handle {
    color: rgba(255, 255, 255, 0.7);
}

/* A row this member may not drag keeps its handle's room, so the names
   line up, and loses the handle */
.content-tree-handle-still {
    visibility: hidden;
}

.content-tree-toggle {
    width: 1.4rem;
    height: 1.8rem;
    flex-shrink: 0;
    border: none;
    background: none;
    color: inherit;
    font-size: 0.8rem;
    cursor: pointer;
}

.content-tree-toggle:disabled {
    cursor: default;
}

.content-tree-name {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.35rem 0.3rem;
    border: none;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}

.content-tree-row:not(.selected) .content-tree-name:hover {
    text-decoration: underline;
}

.content-tree-badge {
    flex-shrink: 0;
    margin-right: 0.4rem;
    padding: 0 0.4rem;
    border: 1px solid currentColor;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 400;
    opacity: 0.75;
}

/* The Welcome window and the Bin: no handle, no toggle */
.content-tree-plain {
    padding-left: 1.65rem;
}

.content-tree-rule {
    height: 1px;
    margin: 0.5rem;
    background-color: #d0d0d0;
}

/* Dragging (move.js): the row in hand fades; the row it would go into
   is framed; a drop between two rows shows as a line along the edge of
   the row it would land beside */
.content-tree-row.dragging {
    opacity: 0.4;
}

.content-tree-row.move-drop {
    outline: 2px dashed var(--primary-color);
    outline-offset: -2px;
}

.content-tree-row.move-drop-before {
    box-shadow: inset 0 3px 0 var(--primary-color);
}

.content-tree-row.move-drop-after {
    box-shadow: inset 0 -3px 0 var(--primary-color);
}

.content-tree-foot {
    padding: 0.6rem 0.75rem;
    border-top: 1px solid #e0e0e0;
}

.content-tree-foot .tree-secondary-btn {
    width: 100%;
}

.content-tree-foot .tree-invite-note {
    margin: 0.5rem 0 0;
}

.content-tree-foot .form-feedback {
    margin-top: 0.5rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
}

/* The right-hand pane: the chosen row's name, its tabs, the open tab */
.content-pane {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.content-pane-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem 0.75rem;
    padding: 0.9rem 1.25rem 0;
}

.content-crumbs {
    font-size: 0.9rem;
    color: #666;
}

.content-crumb-sep {
    margin: 0 0.35rem;
}

.content-crumb-here {
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-color);
}

.content-chip {
    padding: 0.1rem 0.6rem;
    border-radius: 10px;
    background-color: #e8edf3;
    color: var(--primary-color);
    font-size: 0.75rem;
}

.content-tabs {
    display: flex;
    gap: 0.25rem;
    padding: 0.5rem 1.25rem 0;
    border-bottom: 1px solid #e0e0e0;
}

.content-tabs:empty {
    display: none;
}

.content-tab {
    padding: 0.55rem 0.9rem;
    border: none;
    border-bottom: 3px solid transparent;
    background: none;
    color: #555;
    font: inherit;
    font-size: 0.95rem;
    cursor: pointer;
}

.content-tab:hover {
    color: var(--text-color);
}

.content-tab.active {
    border-bottom-color: var(--accent-color);
    color: var(--primary-color);
    font-weight: 600;
}

.content-pane-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    padding: 1rem 1.25rem 1.5rem;
}

/* A boxed group in a tab */
.content-card {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 0.85rem 1rem;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    background-color: var(--white);
}

.content-card h4 {
    margin: 0;
    font-size: 0.95rem;
    color: var(--primary-color);
}

.content-card-note {
    margin: 0;
    font-size: 0.82rem;
    color: #666;
}

/* Two cards side by side where there is room, one above the other
   where there is not */
.content-card-pair {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0.9rem;
}

.content-card .tree-secondary-btn,
.content-card .logout-btn,
.content-card .tree-delete-btn,
.content-actions .tree-secondary-btn,
.content-actions .logout-btn {
    margin: 0;
    align-self: flex-start;
}

.content-card .link-btn {
    margin: 0;
}

/* A field: its small title over its control */
.content-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    flex: 1 1 auto;
    min-width: 0;
}

.content-field > span {
    font-size: 0.8rem;
    color: #666;
}

.content-field input[type="text"],
.content-field input[type="email"],
.content-field select,
.content-inline select,
.content-pane-body .page-sections-editor input,
.content-pane-body .page-sections-editor select,
.content-pane-body .page-sections-editor textarea,
.content-pane-body .tooltip-fields-editor input,
.content-pane-body .tooltip-fields-editor select {
    width: 100%;
    padding: 0.5rem 0.6rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font: inherit;
    font-size: 0.95rem;
}

.content-field input:focus,
.content-field select:focus,
.content-inline select:focus {
    outline: none;
    border-color: var(--accent-color);
}

/* A field with its button beside it: Name and Rename, Manager and Save */
.content-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
}

.content-inline .content-field {
    flex: 1 1 160px;
}

.content-inline select {
    flex: 1 1 160px;
    width: auto;
}

.content-radios {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 1.25rem;
}

.content-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.content-member-list {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 0;
    padding: 0;
}

.content-member-list li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.4rem 0;
    border-top: 1px solid #eee;
    font-size: 0.9rem;
}

.content-member-list li:first-child {
    border-top: none;
}

.content-member-list .tree-delete-btn {
    margin: 0;
    padding: 0.3rem 0.7rem;
    font-size: 0.8rem;
}

.content-sub-link {
    display: inline;
    margin: 0;
    font-size: 0.9rem;
}

/* The red strip at the foot of a tab, for the thing not done lightly */
.content-danger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 1rem;
    border: 1px solid #e3b9b5;
    border-radius: 8px;
    background-color: #fbf1f0;
}

.content-danger strong {
    color: #8e2a23;
}

.content-danger .tree-delete-btn {
    margin: 0;
    white-space: nowrap;
}

/* Save, at the foot of a tab, with what there is to say beside it */
.content-save-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: auto;
    padding-top: 0.75rem;
    border-top: 1px solid #e0e0e0;
}

.content-save-row .form-feedback {
    flex: 1 1 auto;
    margin: 0;
    padding: 0.5rem 0.75rem;
    font-size: 0.85rem;
}

.content-save-row .logout-btn {
    margin: 0 0 0 auto;
}

.content-unsaved {
    margin: 0;
    color: #9a6b00;
}

.content-pane-body .frames-choices {
    margin-top: 0;
}

/* The folder's pictures in the Pictures tab, where Select and Reorder
   happen: smaller tiles than a page's, and the action bar held at the
   top of the pane as it scrolls rather than under the site header */
.content-pictures .gallery-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: 0.75rem;
    padding: 0;
}

.content-pictures .manage-actionbar {
    top: -1rem;
}

/* The page beside the panel only shows: its pencils go while the panel
   is open, as its Edit button does - everything is done in the panel */
body.content-open main .gallery-edit-btn {
    display: none;
}

/* The divider down the panel's right edge, dragged to show more or
   less of the page (content.js initContentDivider) */
.content-divider {
    position: relative;
    width: var(--content-divider-width);
    flex-shrink: 0;
    border-left: 1px solid #b9b5ab;
    background-color: #d9d5cc;
    cursor: col-resize;
}

/* Where a press takes hold of it: wider than the bar it looks like,
   reaching 14px into the panel, so it is caught without having to find
   its exact edge - against the window's right edge most of all */
.content-divider::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: -14px;
    right: 0;
    cursor: col-resize;
}

/* The grip: a raised tab in the middle of the bar, with arrows saying
   which way it goes */
.content-divider::after {
    content: '\25C2\25B8';
    position: absolute;
    top: 50%;
    left: 1px;
    width: 15px;
    height: 64px;
    margin-top: -32px;
    border-radius: 4px;
    background-color: var(--primary-color);
    color: var(--white);
    font-size: 9px;
    line-height: 64px;
    letter-spacing: -1px;
    text-align: center;
}

.content-divider:hover,
.content-divider.dragging,
.content-divider:focus-visible {
    background-color: #c4ccd6;
    outline: none;
}

.content-divider[hidden] {
    display: none;
}

/* While the divider is being dragged, nothing on the page is selected
   by the pointer passing over it */
body.content-resizing {
    cursor: col-resize;
    user-select: none;
}

/* The rail: the panel folded away to a strip down the edge */
.content-rail {
    display: none;
}

body.content-collapsed .content-panel-main {
    display: none;
}

body.content-collapsed .content-rail {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.75rem;
    width: var(--content-rail-width);
    flex-shrink: 0;
    padding: 0.6rem 0;
    background-color: var(--primary-color);
    color: var(--white);
}

.content-rail-open {
    width: 26px;
    height: 26px;
    border: none;
    border-radius: 4px;
    background-color: rgba(255, 255, 255, 0.15);
    color: var(--white);
    font-size: 0.9rem;
    cursor: pointer;
}

.content-rail-open:hover {
    background-color: rgba(255, 255, 255, 0.3);
}

.content-rail-label {
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    max-height: 60vh;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

.content-rail-dirty {
    width: 8px;
    height: 8px;
    border-radius: 4px;
    background-color: #f4c542;
}

.content-rail-dirty[hidden] {
    display: none;
}

/* The page beside the panel: the sidebar is under the panel (the tree
   stands in for it), and the page and the footer begin where the panel
   ends. Taking the whole window, the panel hides the page rather than
   pushing it off to the right. */
body.content-open:not(.content-previewing) nav#mainNav {
    display: none;
}

body.content-open:not(.content-previewing) main,
body.content-open:not(.content-previewing) footer#siteFooter {
    margin-left: var(--content-panel-width);
}

/* Out of the layout altogether, not just out of sight: a hidden page
   still had its height, and so a scrollbar beside the panel with
   nothing to scroll, sitting over the divider */
body.content-open.content-full:not(.content-previewing) main,
body.content-open.content-full:not(.content-previewing) footer#siteFooter {
    display: none;
}

/* Previewing: the bar across the top of the page, under the header,
   and the page moved down to make room for it */
.content-preview-bar {
    position: fixed;
    top: calc(var(--header-height) + var(--header-gap));
    left: 0;
    right: 0;
    z-index: 90;
    display: flex;
    align-items: center;
    gap: 1rem;
    height: var(--content-preview-bar-height);
    padding: 0 1.25rem;
    background-color: var(--primary-color);
    color: var(--white);
    font-size: 0.9rem;
}

.content-preview-bar[hidden] {
    display: none;
}

.content-preview-bar span {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    opacity: 0.9;
}

.content-preview-back {
    flex-shrink: 0;
    padding: 0.35rem 0.8rem;
    border: none;
    border-radius: 4px;
    background-color: var(--white);
    color: var(--primary-color);
    font-weight: 600;
    cursor: pointer;
}

.content-preview-back:hover {
    background-color: #dfe6ed;
}

body.content-previewing main {
    margin-top: calc(var(--header-height) + var(--header-gap) + var(--content-preview-bar-height));
}

body.content-previewing nav#mainNav {
    top: calc(var(--header-height) + var(--header-gap) + var(--content-preview-bar-height));
    height: calc(100vh - var(--header-height) - var(--header-gap) - var(--content-preview-bar-height));
}

/* A narrow screen has no room for the page beside the panel: the panel
   is the screen (content.js keeps it in its "full" state there), the
   tree takes a share of it, and "View this page" is the way to the page */
@media (max-width: 768px) {
    .content-panel {
        top: var(--header-height-mobile);
        height: calc(100vh - var(--header-height-mobile));
        height: calc(100dvh - var(--header-height-mobile));
    }

    .content-tree-pane {
        width: 42%;
        min-width: 150px;
    }

    .content-pane-head,
    .content-tabs,
    .content-pane-body {
        padding-left: 0.75rem;
        padding-right: 0.75rem;
    }

    .content-preview-bar {
        top: var(--header-height-mobile);
        padding: 0 0.75rem;
    }

    body.content-previewing main {
        margin-top: var(--content-preview-bar-height);
    }

    body.content-previewing nav#mainNav {
        top: calc(var(--header-height-mobile) + var(--content-preview-bar-height));
        max-height: calc(100vh - var(--header-height-mobile) - var(--content-preview-bar-height));
        max-height: calc(100dvh - var(--header-height-mobile) - var(--content-preview-bar-height));
    }
}
