/*
Theme Name: Sidebar Collapse Theme
Theme URI: https://example.com
Description: A modern WordPress theme with collapsible sidebar navigation, light/dark mode, and download functionality
Version: 1.0
Author: Your Name
Author URI: https://example.com
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: sidebar-theme
*/

/* CSS Variables for Theme Colors */
:root {
    --bg-primary: #3a3a3a;   /* body + sidebar */
    --bg-secondary: #4a4a4a; /* buttons, hovers */
    --bg-content: #e0e0e0;   /* content card */
    --accent: #c49a3d;       /* brand gold — active menu item */
    --text-primary: #e0e0e0;
    --text-heading: #f0f0f0;
    --text-link: #fff;
    --text-meta: #aaa;
    --text-placeholder: #666;
    --icon-color: #2a2a2a;
    --border-color: rgba(255,255,255,0.1);
    --shadow-color: rgba(0,0,0,0.3);
    --menu-item-color: #ecf0f1;
    --menu-icon-color: #ecf0f1;
    --transition-logo-color: #fff;
    --transition-logo-opacity: 0.5;

    /* Optical centring. Flexbox centres the em box, not the marks inside it, so
       a ✕ or a chevron never sits where the ring around it looks centred. Both
       values are signed and move the mark only — negative is up, positive down —
       and every ring gets its glyph in a .glyph span so the ring stays put.
       Dial these two numbers; never re-centre a button by hand. */
    --glyph-nudge: -2px;     /* symbols inside a circle: ✕ ‹ › */
    --caps-nudge: 2px;       /* uppercase labels in a pill */

    /* One knob for the whole desktop sidebar — width, type, logo, padding and
       spacing all shrink together, exactly like resizing it in a design tool.
       Only the space it takes from the content changes. 1 = original size. */
    --sidebar-scale: 0.82;
}

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

body {
    font-family: var(--brand-font, 'Epilogue'), -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    line-height: 1.6;
    color: var(--text-primary);
    background: var(--bg-primary);
    transition: background-color 0.3s ease, color 0.3s ease;
}

/* Sidebar Styles */
.sidebar {
    position: fixed;
    left: 0;
    top: 0;
    height: 100vh;
    width: 60px;
    background: var(--bg-primary);
    transition: width 0.3s ease, background-color 0.3s ease;
    overflow: hidden;
    z-index: 1000;
    box-shadow: 0 2px 10px var(--shadow-color);
    display: flex;
    flex-direction: column;
}

:is(.sidebar:hover, .sidebar.pinned) {
    width: 280px;
}

/* Arrow indicator */
.sidebar::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 23px;
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border-left: 6px solid transparent;
    border-right: 6px solid transparent;
    border-top: 8px solid #fff;
    opacity: 1;
    transition: opacity 0.3s ease;
    pointer-events: none;
}

:is(.sidebar:hover, .sidebar.pinned)::after {
    opacity: 0;
}

.sidebar-header {
    width: 60px;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    transition: width 0.3s ease, height 0.3s ease;
}

:is(.sidebar:hover, .sidebar.pinned) .sidebar-header {
    width: 280px;
    height: 280px;
}

.logo-placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.logo-placeholder svg {
    width: 100%;
    height: 100%;
}

.custom-logo-link {
    display: flex;
    width: 100%;
    height: 100%;
    align-items: center;
    justify-content: center;
}

.logo-placeholder img,
.custom-logo {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.sidebar-menu {
    list-style: none;
    padding: 30px 0;
}

.sidebar-menu li {
    margin: 0 0 2px 0;
}

.sidebar-menu > li > a .menu-text {
    font-weight: 700;
}

.sidebar-menu a {
    display: flex;
    align-items: center;
    padding: 9px 20px 9px 25px;
    color: var(--menu-item-color);
    text-decoration: none;
    transition: color 0.2s ease;
    white-space: nowrap;
    position: relative;
}

.sidebar-menu a:hover {
    color: #ffffff;
}

.menu-text {
    opacity: 0;
    transition: opacity 0.3s ease;
}

:is(.sidebar:hover, .sidebar.pinned) .menu-text {
    opacity: 1;
}

/* Sidebar Footer Logo */
.sidebar-footer {
    width: 60px;
    height: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-top: 1px solid var(--border-color);
    transition: width 0.3s ease, height 0.3s ease;
    padding: 20px 10px;       /* collapsed: 20px above and below the monogram */
    margin-top: auto;
}

:is(.sidebar:hover, .sidebar.pinned) .sidebar-footer {
    width: 280px;
    height: 280px;
    padding: 30px 20px;
}

.sidebar-footer-logo {
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: width 0.3s ease, height 0.3s ease;
}

:is(.sidebar:hover, .sidebar.pinned) .sidebar-footer-logo {
    width: 30px;
    height: 30px;
}

.sidebar-footer-logo svg {
    width: 100%;
    height: 100%;
}

.sidebar-footer-contact {
    display: none;
    text-align: center;
    margin-top: 20px;
}

:is(.sidebar:hover, .sidebar.pinned) .sidebar-footer-contact {
    display: block;
}

.sidebar-footer-contact a {
    display: block;
    color: var(--text-placeholder);
    text-decoration: none;
    font-size: 0.765rem;
    line-height: 1.6;
    margin: 4px 0;
}

.sidebar-footer-contact a:hover {
    color: var(--text-placeholder);
}

/* Menu Icons */
.menu-icon {
    width: 24px;
    height: 24px;
    opacity: 1;
    transition: opacity 0.3s ease;
    flex-shrink: 0;
}

:is(.sidebar:hover, .sidebar.pinned) .menu-icon {
    opacity: 0;
    width: 0;
}

.menu-icon svg {
    width: 100%;
    height: 100%;
    stroke: var(--menu-icon-color);
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Active menu item */
.sidebar-menu .menu-item.active > a {
    opacity: 1;
}

/* Submenu styles */
/* Collapsed until you are inside that section — animated open */
.sub-menu {
    list-style: none;
    padding: 0;
    margin: 0;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    /* Open state is max-height:none — a measured pixel cap can go stale and clip
       a long list with no way to reach the entries below it. Fade only.        */
    transition: opacity 0.35s ease;
}

:is(.sidebar:hover, .sidebar.pinned) .menu-item.active > .sub-menu {
    max-height: none;
    opacity: 1;
}

.sub-menu li {
    margin: 0;
}

/* Hide icons in submenu */
.sub-menu .menu-icon {
    display: none;
}

/* Same white as the main entries — anything clickable reads the same */
.sub-menu a {
    padding: 7px 20px 7px 29px;
    font-size: 0.855rem;
    opacity: 1;
}

.sub-menu .menu-text { font-weight: 500; }

.sub-menu a:hover {
    opacity: 1;
}

.sub-menu .menu-item.active a {
    opacity: 1;
}

.menu-item-has-children > a .menu-text::after {
    content: '';
    display: inline-block;
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--menu-item-color);
    margin-left: 8px;
    opacity: 0;
    transition: opacity 0.3s ease;
}

:is(.sidebar:hover, .sidebar.pinned) .menu-item-has-children > a .menu-text::after {
    opacity: 1;
}

/* Main Content */
.main-content {
    margin-left: 60px;
    /* Extra top gutter is the strip the language switch sits in, clear of the
       card's rounded corner. Sides stay tight — that width belongs to content. */
    padding: 34px 20px 20px;
    min-height: 100vh;
    transition: margin-left 0.3s ease;
}

.content-wrapper {
    position: relative;
    z-index: 2;               /* stays above the page-change swash */
    --card-pad: 40px;         /* full-bleed elements cancel this out */
    /* The gutter is .main-content's padding alone. Anything extra here would
       spend the width the narrower sidebar just freed on background instead. */
    max-width: 100%;
    margin: 0 auto;
    background: var(--bg-content);
    padding: var(--card-pad);
    border-radius: 16px;
    box-shadow: 0 2px 10px var(--shadow-color);
    color: var(--text-primary);
    transition: background-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
}

/* Typography */
h1, h2, h3, h4, h5, h6 {
    margin-bottom: 20px;
    line-height: 1.05;
    color: var(--text-heading);
}

h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }

p {
    margin-bottom: 15px;
}

a {
    color: var(--text-link);
}

a:hover {
    color: #ddd;
}

.entry-meta {
    color: var(--text-meta);
}

article {
    /* No border below content */
}

/* Image Grid */
.image-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 30px;
    margin: 40px 0;
}

.image-placeholder {
    background: #3a3a3a;
    aspect-ratio: 1/1;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-placeholder);
    font-size: 0.9rem;
}

.image-caption {
    margin-top: 10px;
    font-size: 0.9rem;
    color: var(--text-meta);
    text-align: center;
}

/* Page Navigation */
.page-navigation {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 60px;
    padding-top: 30px;
    border-top: 1px solid var(--border-color);
}

.nav-previous,
.nav-next {
    display: flex;
    flex-direction: column;
    max-width: 45%;
}

.nav-previous {
    text-align: left;
}

.nav-next {
    text-align: right;
}

.nav-label {
    font-size: 0.8rem;
    color: #888;
    margin-bottom: 5px;
}

.nav-title {
    font-size: 1rem;
    color: var(--text-link);
    text-decoration: none;
}

.nav-title:hover {
    color: #ddd;
}

/* Responsive */
/* Desktop: sidebar permanently expanded — no hover-collapse */
@media (min-width: 769px) {
    .sidebar { width: 280px; }
    .sidebar-header { width: 280px; height: 280px; }
    .menu-text { opacity: 1; }
    .menu-icon { opacity: 0; width: 0; }
    .menu-item.active > .sub-menu { max-height: none; opacity: 1; }
    .menu-item-has-children > a .menu-text::after { opacity: 1; }
    /* Footer idle: logomark only, compact; only hovering the footer itself reveals contact */
    .sidebar-footer,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer { width: 280px; height: 82px; padding: 20px; transition: height 0.3s ease; }
    .sidebar-footer-logo,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer-logo { width: 30px; height: 30px; }
    .sidebar-footer-contact,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer-contact { display: none; }
    .sidebar .sidebar-footer:hover { height: 230px; }
    .sidebar .sidebar-footer:hover .sidebar-footer-contact { display: block; }
    .sidebar::after { opacity: 0; }            /* hide the "hover to expand" arrow */
    .sidebar { transition: none; }             /* no width animation, nothing to animate */

    /* Scale the sidebar as one object (see --sidebar-scale). Its height must not
       come from vh here: browsers disagree on whether a viewport unit inside a
       zoomed box is scaled, and the disagreement pushes the footer — logo and
       contacts — off the bottom of the screen. top/bottom insets are 0 either
       way, so the box is exactly the viewport and the footer stays put. */
    .sidebar {
        zoom: var(--sidebar-scale);
        height: auto;
        top: 0;
        bottom: 0;
    }
    .main-content { margin-left: calc(280px * var(--sidebar-scale)); }
}

@media (max-width: 768px) {
    .sidebar {
        position: fixed;
        width: 100%;
        height: 60px;
        top: 0;
        left: 0;
        right: 0;
        bottom: auto;
        overflow: visible;
        background: var(--bg-primary);
        z-index: 2000;
    }
    
    .sidebar.menu-open {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        height: 100vh;
        width: 100vw;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }

    /* Show header logo, hide footer by default */
    .sidebar-header {
        display: none;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: auto;
        padding: 20px 0 0;
        border-bottom: none;
        flex-shrink: 0;
    }

    .sidebar.menu-open .sidebar-header {
        display: flex;
    }

    .sidebar-footer {
        display: none;
        position: relative;
        width: 100%;
        height: auto;
        padding: 15px 10px 20px;
        margin-top: auto;
        flex-shrink: 0;
    }

    .sidebar.menu-open .sidebar-footer {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    }

    .sidebar-footer-logo,
    .sidebar.menu-open .sidebar-footer-logo {
        width: 40px;
        height: 40px;
    }

    .sidebar-footer-contact {
        display: block;
        margin-top: 10px;
    }

    .logo-placeholder,
    .sidebar.menu-open .logo-placeholder {
        width: 110px;
        height: 110px;
    }

    /* Show only arrow, hide icons */
    .menu-icon {
        display: none;
    }

    /* Arrow positioned for top bar - always visible */
    .sidebar::after {
        left: auto;
        right: 20px;
        top: 26px;
        border-left: 6px solid transparent;
        border-right: 6px solid transparent;
        border-top: 8px solid #fff;
        border-bottom: 0;
        transform: none;
        opacity: 1;
    }

    .sidebar.menu-open::after {
        transform: rotate(180deg);
        opacity: 0;
    }

    /* Menu hidden by default */
    .sidebar nav {
        display: none;
        flex: none;
        padding: 10px 0;
    }

    .sidebar.menu-open nav {
        display: block;
        margin: 0;
        width: 100%;
    }

    .sidebar-menu {
        padding: 0;
        background: transparent;
        text-align: center;
    }

    .sidebar-menu > li {
        margin: 0 0 4px 0;
    }

    .sidebar-menu a {
        padding: 9px 16px;
        justify-content: center;
        background: transparent;
        font-size: 1rem;
    }

    .sidebar-menu a:hover {
        background: transparent;
        padding-left: 16px;
    }

    .sidebar-menu .menu-text {
        opacity: 1;
    }

    /* Open menu: three centered zones — logo top, menu middle, contact bottom */
    .sidebar.menu-open {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: space-between;
        padding: 10px 0;
    }

    .sidebar.menu-open .sidebar-header {
        width: 100%;
        justify-content: center;
        align-items: center;
        margin: 0;
    }

    .sidebar.menu-open .sidebar-footer {
        width: 100%;
        justify-content: center;
        align-items: center;
        text-align: center;
        margin: 0;
    }

    /* Single column grid on mobile */
    .image-grid {
        grid-template-columns: 1fr;
    }
    
    .main-content {
        margin-left: 0;
        margin-top: 60px;
        padding: 10px !important;
    }
    
    .content-wrapper {
        --card-pad: 15px;
        padding: 15px !important;
        max-width: 100% !important;
    }
    
    article {
        padding: 0 !important;
    }
    
    .entry-content {
        padding: 0 !important;
    }
    
    h1, h2, h3, h4, h5, h6, p {
        padding-left: 0 !important;
        padding-right: 0 !important;
    }
}


/* Download All Button */
/* Same pill as the per-format buttons, left aligned under its heading.
   .link-btn is the same pill for a plain link — it goes somewhere and does
   nothing else. Keep the two classes apart: the engine binds "zip this page"
   to .download-all-btn, and a link must never trigger that. */
.download-all-btn,
.link-btn {
    display: inline-block;
    margin: 0 0 12px;
    padding: 6px 16px;
    font-family: inherit;
    line-height: 1.6;         /* keeps <button> and <a> the same height */
    vertical-align: top;
    background: transparent;
    color: var(--text-meta);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    text-decoration: none;
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.2s ease;
}

.download-all-btn:hover,
.link-btn:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-color: var(--text-meta);
}

/* Individual Download Button */
.download-btn {
    display: inline-block;
    margin-top: 8px;
    padding: 6px 16px;
    background: transparent;
    color: var(--text-meta);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    text-decoration: none;
    font-size: 0.8rem;
    cursor: pointer;
    transition: all 0.2s ease;
}

.download-btn:hover {
    background: var(--bg-secondary);
    color: var(--text-primary);
    border-color: var(--text-meta);
}

/* Lightbox */
.lightbox {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.9);
    z-index: 9999;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.lightbox.active {
    display: flex;
    opacity: 1;
}

.lightbox-content {
    max-width: 90%;
    max-height: 90%;
    position: relative;
}

.lightbox-content img {
    max-width: 100%;
    max-height: 90vh;
    object-fit: contain;
}

.lightbox-close {
    position: absolute;
    top: 20px;
    right: 20px;
    width: 40px;
    height: 40px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 24px;
    line-height: 1;
    transition: all 0.2s ease;
}

/* The mark inside any ring — one rule, one number, both directions. */
.lightbox-close .glyph,
.lightbox-nav .glyph,
.caption-clear .glyph,
.undo-close .glyph,
.tag-chip-x .glyph {
    display: block;
    transform: translateY(var(--glyph-nudge));
}

.lightbox-close:hover {
    background: rgba(255, 255, 255, 0.2);
    transform: scale(1.1);
}

.wp-block-image img,
.image-placeholder {
    cursor: pointer;
    transition: opacity 0.2s ease;
}


/* Force all images in content to be square */
.entry-content img,
.wp-block-image img {
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: contain;
    display: block;
    padding: 10%;
    box-sizing: border-box;
}

.wp-block-image {
    margin-bottom: 20px;
}

/* Image captions in Gutenberg */
.wp-block-image figcaption {
    margin-top: 10px;
    font-size: 0.9rem;
    color: var(--text-meta);
    text-align: center;
}

/* Transition Logo */
/* Page-change swash. One 1.7s keyframe animation, played by adding .show —
   js/main.js removes the class on animationend, so the state can't get stuck. */
.transition-logo {
    position: fixed;
    top: 50%;
    left: 50%;
    /* wider than the viewport: the artwork runs off both edges */
    width: 160vw;
    aspect-ratio: 524.63 / 314.45;
    transform: translate(-50%, -50%);
    opacity: 0;
    pointer-events: none;
    z-index: 0;
}

.transition-logo.show {
    animation: swash 1.7s ease-out both;
}

.transition-logo img {
    display: block;
    width: 100%;
    height: 100%;
}

@keyframes swash {
    0% {
        opacity: 0;
        transform: translate(-50%, -50%) translate(-4vw, 2vw);
    }
    35%, 65% {
        opacity: var(--transition-logo-opacity);
    }
    100% {
        opacity: 0;
        transform: translate(-50%, -50%) translate(4vw, -2vw);
    }
}

@media (prefers-reduced-motion: reduce) {
    .transition-logo.show {
        animation: swash-still 1.7s ease-out both;
    }

    @keyframes swash-still {
        0%, 100% { opacity: 0; }
        25%, 75% { opacity: var(--transition-logo-opacity); }
    }
}

/* Content wrapper transition state */
.content-wrapper.transitioning {
    background: transparent;
    box-shadow: none;
}

/* Page fade animations */
@keyframes fadeOut {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10%);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Mobile menu open state */
@media (max-width: 768px) {
    .sidebar.menu-open {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        height: 100vh;
        width: 100vw;
        overflow-y: auto;
    }
    
    .sidebar.menu-open .sidebar-header {
        opacity: 1;
        pointer-events: auto;
    }
    
    .sidebar.menu-open .sidebar-footer {
        display: flex;
        justify-content: center;
        align-items: center;
    }
    
    .sidebar.menu-open nav {
        display: block;
    }
}

/* ===== Brand asset figures ===== */
/* Transparent PNGs sit directly on the card, no tile background */
.wp-block-image.tile-light img,
.wp-block-image.tile-dark img {
    padding: 12%;
    width: 100%;
}

/* Row of per-format download buttons under an asset */
.download-row {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    justify-content: center;
    margin-top: 10px;
}

.download-row .download-btn {
    margin-top: 0;
}

/* Active menu item — darker grey */
.sidebar-menu .menu-item.active > a > .menu-text {
    color: #9a9a9a;
}

/* Light content card — dark text inside */
.content-wrapper {
    color: #3a3a3a;
}

.content-wrapper h1, .content-wrapper h2, .content-wrapper h3,
.content-wrapper h4, .content-wrapper h5, .content-wrapper h6 {
    color: #3d3d3d;
    font-weight: 700;
    margin-top: 40px;
}

.content-wrapper h1 { font-size: 3rem; margin-top: 0; letter-spacing: -0.06em; }
.content-wrapper h2 { font-size: 2.2rem; letter-spacing: -0.04em; }

/* Roomier paragraphs */
.content-wrapper p,
.content-wrapper ul {
    margin-bottom: 28px;
}

.content-wrapper a {
    color: #2e2e2e;
}

.content-wrapper .wp-block-image figcaption,
.content-wrapper .image-caption {
    color: #777;
}

.content-wrapper .download-btn,
.content-wrapper .download-all-btn,
.content-wrapper .link-btn {
    color: #555;
    border-color: rgba(0, 0, 0, 0.25);
}

.content-wrapper .download-all-btn:hover,
.content-wrapper .download-btn:hover,
.content-wrapper .link-btn:hover {
    background: rgba(0, 0, 0, 0.08);
    color: #2e2e2e;
    border-color: rgba(0, 0, 0, 0.4);
}

/* ===== Asset + sample split =====
   Reusable page layout: the asset on the left with its download row, a cropped
   square sample photo on the right with its label set over the image.

   <section class="asset-group">
     <button class="download-all-btn">Download all …</button>
     <div class="split">
       <figure class="wp-block-image asset-square no-lightbox">
         <img …><div class="download-row">…</div>
       </figure>
       <figure class="wp-block-image sample-photo">
         <img … style="object-position: 55% 35%;">
         <figcaption class="sample-label">Sample</figcaption>
       </figure>
     </div>
   </section>

   Both columns are square, so they stand exactly the same height; the pair
   stacks to one column once a column would drop below 340px.
   Add `no-lightbox` to any figure that should not open the lightbox.        */
.entry-content .split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
    gap: 40px;
    align-items: start;
    margin: 12px 0 40px;
}

.entry-content .split .wp-block-image { margin-bottom: 0; }
.entry-content .split .download-row { margin-top: 5px; }
.entry-content .no-lightbox img { cursor: default; }

.entry-content .asset-square img {
    aspect-ratio: 1 / 1;
    padding: 6%;
}

/* A column of samples running alongside the main column, independent of it */
.entry-content .sample-stack {
    display: grid;
    gap: 40px;
    align-content: start;
}

.entry-content .sample-photo { position: relative; }

/* The last column of samples runs out to the card edge, cancelling its padding */
.entry-content .split > .sample-photo:last-child,
.entry-content .split > .sample-stack:last-child {
    margin-right: calc(-1 * var(--card-pad, 40px));
}

.entry-content .sample-photo img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    padding: 0;
    border-radius: 16px 0 0 16px;
    display: block;
}

.entry-content .sample-photo .sample-label {
    position: absolute;
    left: 24px;
    top: 18px;
    margin: 0;
    color: #fff;
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    text-shadow: 0 2px 10px rgba(0, 0, 0, 0.55);
}

/* ===== Language switch =====
   Tiny text, top right, sitting on the dark ground beside the content card.   */
.lang-switch {
    position: fixed;
    top: 9px;
    right: 24px;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.68rem;
    letter-spacing: 0.18em;
    color: #6f6f6f;
}

.lang-switch button {
    background: none;
    border: 0;
    padding: 2px;
    font: inherit;
    letter-spacing: inherit;
    color: #8a8a8a;
    cursor: pointer;
    transition: color 0.2s ease;
}

.lang-switch button:hover { color: #fff; }

/* Only the language you can switch to is shown */
.lang-switch button.active,
.lang-switch span { display: none; }

@media (max-width: 768px) {
    /* sits in the mobile menu bar, top left */
    .lang-switch {
        top: 20px;
        left: 20px;
        right: auto;
        z-index: 2001;
    }
}

/* ===== Front page =====
   js/main.js puts .is-home on <body> before the transition starts, so none of
   this arrives late. No content card, and the swash is frozen where the
   page-change animation leaves it.                                          */
body.is-home .content-wrapper {
    background: transparent;
    box-shadow: none;
    color: var(--text-primary);
}

body.is-home .content-wrapper h1,
body.is-home .content-wrapper h2,
body.is-home .content-wrapper h3 { color: var(--text-heading); }

body.is-home .content-wrapper .kicker { color: #b8b8b8; }
body.is-home .content-wrapper a { color: var(--text-link); }

body.is-home .transition-logo {
    animation: none !important;
    opacity: 0.1;
    transform: translate(-50%, -50%) translate(4vw, -2vw);
}

/* ===== Prev / next footer ===== */
.content-wrapper .page-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 64px;
}

.content-wrapper .page-nav-btn {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1 1 260px;
    padding: 18px 24px;
    border: 1px solid rgba(0, 0, 0, 0.25);
    border-radius: 16px;
    text-decoration: none;
    color: #8a8a8a;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.content-wrapper .page-nav-btn:hover {
    background: rgba(0, 0, 0, 0.06);
    border-color: rgba(0, 0, 0, 0.5);
    color: #5a5a5a;
}

.content-wrapper .page-nav-btn.next {
    justify-content: flex-end;
    text-align: right;
    margin-left: auto;
}

.content-wrapper .page-nav-arrow {
    font-size: 1.7rem;
    line-height: 1;
}

.content-wrapper .page-nav-label {
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.3;
}

/* Kicker line above page title */
.content-wrapper .kicker {
    font-weight: 300;
    letter-spacing: 0.35em;
    text-transform: uppercase;
    font-size: 0.85rem;
    color: #6a6a6a;
    margin-bottom: 12px;
}

/* Full-card image — the image IS the content card, text overlaid */
.entry-content .full-card {
    margin: 0;
    position: relative;
}

.entry-content .full-card img {
    width: 100%;
    display: block;
    aspect-ratio: auto;
    border-radius: 16px;
}

.entry-content .full-card-text {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 60px 40px 30px;
    border-radius: 0 0 8px 8px;
    background: linear-gradient(transparent, rgba(0, 0, 0, 0.55));
}

.entry-content .full-card-text h1,
.entry-content .full-card-text p {
    color: #fff;
    margin: 0;
}

.entry-content .full-card-text p {
    opacity: 0.85;
}

@media (max-width: 768px) {
    .entry-content .full-card-text {
        padding: 40px 15px 15px;
    }
}

/* ===== Touch: neutralise sticky :hover =====
   Safari applies :hover after a tap and keeps it, so the desktop hover states
   would override the mobile bar. These come last, so they win.              */
@media (max-width: 768px) {
    :is(.sidebar:hover, .sidebar.pinned) { width: 100%; }

    :is(.sidebar:hover, .sidebar.pinned) .sidebar-header {
        width: 100%;
        height: auto;
        padding: 20px 0 0;
    }

    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer {
        width: 100%;
        height: auto;
        padding: 15px 10px 20px;
    }

    :is(.sidebar:hover, .sidebar.pinned) .menu-icon { display: none; }
    :is(.sidebar:hover, .sidebar.pinned) .menu-text { opacity: 1; }
    :is(.sidebar:hover, .sidebar.pinned)::after { opacity: 1; }
    .sidebar.menu-open:hover::after { opacity: 0; }

    /* Submenus follow the open menu on touch, not hover */
    :is(.sidebar:hover, .sidebar.pinned) .menu-item.active > .sub-menu { max-height: 0; opacity: 0; }
    .sidebar.menu-open .menu-item.active > .sub-menu {
        max-height: none;
        opacity: 1;
    }
}

/* ===== Phone: keep everything inside the card ===== */
@media (max-width: 768px) {
    /* grids declare min column widths wider than the screen — collapse them all */
    .entry-content .asset-grid,
    .entry-content .rule-grid,
    .entry-content .alt-grid,
    .entry-content .photo-grid,
    .entry-content .split {
        grid-template-columns: 1fr !important;
        column-count: 1;
    }

    /* nothing bleeds off the card edge on a narrow screen */
    .entry-content .split > .sample-photo:last-child,
    .entry-content .split > .sample-stack:last-child { margin-right: 0; }
    .entry-content .sample-photo img { border-radius: 8px; }

    /* open menu: logo bigger and higher, list closer, footer reachable */
    .sidebar.menu-open { justify-content: flex-start; padding: 4px 0 20px; }
    .sidebar.menu-open .sidebar-header { padding: 6px 0 0; }
    .logo-placeholder,
    .sidebar.menu-open .logo-placeholder { width: 126px; height: 126px; }
    .sidebar.menu-open nav { margin-top: 4px; }
    .sidebar.menu-open .sidebar-footer { margin-top: 16px; padding: 10px 10px 16px; }

    /* a tapped parent shows its children */
    .sidebar.menu-open .menu-item.open > .sub-menu {
        max-height: none;
        opacity: 1;
    }
    .sub-menu a { padding: 8px 16px; }
}

/* ===== Lightbox navigation ===== */
.lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 52px;
    height: 52px;
    border: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    font-size: 2rem;
    line-height: 1;
    /* Was baseline-aligned: the chevron sat near the top of a 52px circle. */
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 2;
    transition: background 0.2s ease;
}

.lightbox-nav:hover { background: rgba(0, 0, 0, 0.75); }
.lightbox-nav.prev { left: 18px; }
.lightbox-nav.next { right: 18px; }

@media (max-width: 768px) {
    /* thumb-reachable, out of the way of the image */
    .lightbox-nav { top: auto; bottom: 24px; transform: none; width: 46px; height: 46px; }
    .lightbox-nav.prev { left: 24px; }
    .lightbox-nav.next { right: 24px; }

    /* main entries read as headings, sub items clearly secondary */
    .sidebar-menu > .menu-item > a .menu-text {
        font-size: 1.25rem;
        font-weight: 700;
        letter-spacing: -0.01em;
    }
    .sub-menu .menu-text {
        font-size: 0.92rem;
        font-weight: 400;
    }
}

/* Lightbox caption — number and where the file came from */
.lightbox-caption {
    margin-top: 14px;
    text-align: center;
    font-size: 0.78rem;
    letter-spacing: 0.04em;
    color: rgba(255, 255, 255, 0.85);
}

.lightbox-caption:empty { display: none; }
.lightbox-caption strong { margin-right: 8px; }
.lightbox-caption .when { opacity: 0.65; }

/* ===== Sidebar always fits =====
   The menu is the only part that may scroll; the logo block and footer shrink
   with the viewport height so short screens (small laptops, iPad landscape)
   never cut entries off, however long a submenu gets.                        */
.sidebar nav {
    flex: 1 1 auto;
    min-height: 0;              /* without this a flex child refuses to shrink */
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 14px 0;            /* on the scroller, not the list: a scroll box
                                   does not count the last child's padding */
}

.sidebar-menu { padding: 0; }

.sidebar nav::-webkit-scrollbar { width: 6px; }
.sidebar nav::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.22);
    border-radius: 3px;
}
.sidebar nav { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.22) transparent; }

/* Under ~900px tall: give the menu the room the logo was taking */
@media (min-width: 769px) and (max-height: 900px) {
    .sidebar-header,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-header { height: clamp(96px, 15vh, 200px); }
    .sidebar nav { padding: 10px 0; }
    .sidebar-menu li { margin-bottom: 2px; }
    .sidebar-menu a { padding: 7px 20px 7px 25px; }
    .sub-menu a { padding: 6px 20px 6px 29px; font-size: 0.855rem; }
    .sidebar-footer,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer { height: auto; padding: 12px 20px; }
}

/* Netbook / iPad landscape territory */
@media (min-width: 769px) and (max-height: 720px) {
    .sidebar-header,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-header { height: clamp(84px, 15vh, 150px); }
    .sidebar-menu a { padding: 6px 20px 6px 25px; font-size: 0.95rem; }
    .sub-menu a { padding: 5px 20px 5px 29px; font-size: 0.8075rem; }
    .sidebar-footer-logo,
    :is(.sidebar:hover, .sidebar.pinned) .sidebar-footer-logo { width: 22px; height: 22px; }
    .sidebar-footer-contact a { font-size: 0.7rem; margin: 2px 0; }
}

/* Set by main.js when it lays a gallery out itself (see layoutMasonry). The
   figures are positioned, so the grid needs to be their containing block and to
   carry the height they add up to. */
.entry-content .photo-grid.masonry-js {
    position: relative;
    margin-bottom: 48px;
}

/* ===== Mirror gallery =====
   Previews of a whole Drive shoot folder — no originals, so no download
   controls. Shared by every pages/ph-*.html that build-gallery-pages.py
   writes, which is why it lives here and not in a page-scoped <style>.
   Masonry by CSS columns: nothing is cropped, tall frames stay tall. */
.entry-content .mirror-grid {
    column-count: 3;
    column-gap: 20px;
    margin: 12px 0 48px;
}
@media (min-width: 2100px) { .entry-content .mirror-grid { column-count: 4; } }
@media (max-width: 1400px) { .entry-content .mirror-grid { column-count: 2; } }
@media (max-width: 900px)  { .entry-content .mirror-grid { column-count: 1; } }

.entry-content .mirror-grid .photo {
    position: relative;
    break-inside: avoid;
    margin: 0 0 20px;
}
.entry-content .mirror-grid .photo img {
    width: 100%;
    height: auto;
    /* cancels the site-wide square box: these keep their own proportions */
    aspect-ratio: auto;
    object-fit: fill;
    padding: 0;
    display: block;
    border-radius: 8px;
    cursor: zoom-in;
}
.entry-content .mirror-grid .num {
    position: absolute;
    top: 12px;
    left: 12px;
    min-width: 26px;
    height: 26px;
    padding: 0 7px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
}
.entry-content .mirror-grid .src {
    position: absolute;
    right: 14px;
    bottom: 44px;              /* clear of the OG button in the corner below */
    /* Never reaches the centred caption pill (108px wide) whatever the column
       width: half the photo, less the pill's half and a gap. Wraps to three or
       four short lines instead of running across the photo. */
    max-width: calc(50% - 70px);
    text-align: right;
    font-size: 0.62rem;
    line-height: 1.35;
    color: rgba(255, 255, 255, 0.92);
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.75);
    word-break: break-word;
}
.entry-content .mirror-grid .src .where { opacity: 0.75; }
.entry-content .mirror-grid .src .when { opacity: 0.75; }

/* ===== Downloads =====
   Two buttons, bottom right of a tile and again above the gallery: S is the
   optimised copy this site serves, OG the original on Drive. One pill shape for
   both, so they read as a pair — the word carries the meaning, the arrow says
   what happens. On a tile they sit on their own layer with their own click, so
   neither ever reaches the photo underneath and opens the lightbox. */
.og-link {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    min-height: 28px;
    padding: 0 9px;
    border: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.62);
    color: #fff;
    font: inherit;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    backdrop-filter: blur(2px);
    transition: background 0.2s ease;
}
/* .content-wrapper a paints links dark for the card; on this plate that made
   the letters unreadable, so the pill states its own colour with equal weight. */
.content-wrapper .og-link,
.og-link { color: #fff; }
.og-link:hover { background: rgba(0, 0, 0, 0.85); }
.og-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.og-link[disabled] { opacity: 0.65; cursor: default; }

/* The arrow rides with the letters, not with the ring symbols: --glyph-nudge
   lifts a ✕ inside a circle, and used here it parked the ↓ above the word. */
.og-arrow { font-size: 0.8rem; transform: translateY(calc(var(--caps-nudge) - 2px)); }
.og-word { transform: translateY(calc(var(--caps-nudge) - 2px)); }

.entry-content .photo-grid .tile-dl {
    position: absolute;
    z-index: 3;
    right: 10px;
    bottom: 10px;
    display: flex;
    gap: 5px;
}

/* Above the grid: the same two buttons, each with the sentence that says what
   it does — the tile corner has no room to explain itself. */
/* .entry-content p sets the card's reading leading and outweighs a lone class,
   so these two rows say it again with the same reach. */
.entry-content .gallery-tools { margin: 0 0 18px; }
.entry-content .gallery-tool { margin: 0 0 6px; line-height: 1.3; }
.gallery-tool {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 5px;
    /* The card's paragraph leading is set for reading; two one-line rows next
       to a pill only need the pill's own height. */
    line-height: 1.3;
    font-size: 0.85rem;
    /* The card's own text colour: this is an instruction, not a footnote. */
    color: #2e2e2e;
}
/* Both pills the same width, so the two lines of text start on one edge. */
.gallery-tool .og-link {
    flex: 0 0 auto;
    align-self: flex-start;
    min-width: 78px;
    justify-content: center;
}
.gallery-tool-lead { font-weight: 700; letter-spacing: 0.04em; }

/* The download row is one button wearing the same clothes as the line below it. */
.entry-content .gallery-tool-btn {
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    font-size: 0.85rem;
    text-align: left;
    cursor: pointer;
}
.gallery-tool-btn:hover .og-link { background: rgba(0, 0, 0, 0.85); }
.gallery-tool-btn:hover .gallery-tool-text { text-decoration: underline; }
.gallery-tool-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.gallery-tool-btn[disabled] { cursor: default; }
.gallery-tool-btn[disabled] .og-link { opacity: 0.65; }
.gallery-tool-btn[disabled]:hover .gallery-tool-text { text-decoration: none; }
.gallery-tool-text { flex: 1 1 auto; min-width: 0; }
.og-legend { cursor: default; opacity: 0.75; }
.og-legend:hover { background: rgba(0, 0, 0, 0.62); }

@media (max-width: 768px) {
    /* A finger target. The caption and the tracking text move up in the phone
       block at the end of this file — they are set again further down, and the
       last rule of equal weight is the one that lands. */
    .entry-content .photo-grid .og-link { min-height: 36px; padding: 0 12px; }
}

/* ===== Photo-bank tagging =====
   Field top right, the tags it makes bottom left. Both sit on the photo, so
   both carry their own dark plate — a caption over an unknown image is
   otherwise unreadable half the time. */
.tag-bar { margin: 0 0 20px; }
.tag-status { font-size: 0.85rem; color: #8a2b2b; }

/* ===== Sign in =====
   First entry of the Photostock submenu, above the folders: signing in is what
   adds the rest of that list. Collapsed to one button until asked for, and only
   visible while the sidebar is expanded, like the menu labels themselves.
   While the form is open the sidebar carries .pinned, which holds it — and this
   submenu — open no matter where the pointer goes, so the field cannot vanish
   mid-password. */
/* No visibility rule of its own, deliberately. It is a row of the submenu and
   shows exactly when the submenu does — the moment it had its own opacity and
   its own display switch, there were widths and states where the menu was open
   and LOG IN was not there. The one control that unlocks the site must never be
   able to go missing while the sidebar is out. */
.sidebar-signin {
    padding: 6px 20px 10px 29px;
}

/* Full column width, same box as the field under it: this is the one thing to
   press on a locked menu, so it reads as a control, not as a footnote. */
.signin-btn {
    display: block;
    width: 100%;
    padding: 9px 12px;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.12);
    color: var(--menu-item-color);
    font: inherit;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-align: center;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease;
}
.signin-btn:hover {
    background: rgba(255, 255, 255, 0.22);
    border-color: rgba(255, 255, 255, 0.6);
}
.signin-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.signin-form { display: none; margin-top: 10px; }
.sidebar-signin.open .signin-form { display: block; }
.signin-label {
    display: block;
    margin-bottom: 4px;
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    color: var(--text-meta);
}
.signin-input {
    width: 100%;
    padding: 6px 9px;
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-link);
    font: inherit;
    font-size: 0.8rem;
}
.signin-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.signin-error { margin: 6px 0 0; font-size: 0.72rem; color: #e0a0a0; }

@media (max-width: 768px) {
    /* The mobile bar collapses the sidebar; the control rides with the menu,
       like every other row in it — no display switch of its own. */
    .sidebar-signin { padding: 6px 16px 14px; }
    /* 16px or Safari zooms the page in when the field takes focus, which reads
       as the form running away from you. */
    .signin-input { font-size: 16px; padding: 9px 12px; }
    .signin-btn { padding: 11px 18px; font-size: 0.8rem; }
}

@media (prefers-reduced-motion: reduce) {
    .sidebar-signin, .signin-btn { transition: none; }
}

.entry-content .photo-grid .tag-field {
    position: absolute;
    top: 10px;
    right: 10px;
    max-width: calc(100% - 60px);
}
/* The field and the chips are the same object on a tile and in the lightbox —
   only where they sit differs. Defined once so an alignment fix lands in both. */
.tag-input {
    width: 108px;
    padding: 5px 9px;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.8);
    color: #1a1a1a;
    font: inherit;
    font-size: 0.75rem;
    transition: width 0.2s ease, background 0.2s ease;
}
.tag-input::placeholder { color: rgba(0, 0, 0, 0.55); }
.tag-input:focus {
    width: 168px;
    background: #fff;
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 6px 4px 10px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.9);
    color: #1a1a1a;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}
/* Uppercase: the line box keeps descender room the letters never use, which
   parks the word above the middle of its pill. */
.tag-chip-label { transform: translateY(calc(var(--caps-nudge) - 1.5px)); }
.tag-chip-x {
    /* Its own nudge: this ✕ is smaller than the lightbox's and sits high in the
       ring at the shared value. The token is inherited, so the shared .glyph
       rule picks this up — the ring itself does not move. */
    --glyph-nudge: -0.5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.14);
    color: #1a1a1a;
    font-size: 0.6rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease;
}
.tag-chip-x:hover { background: #8a2b2b; color: #fff; }
.tag-chip-x:disabled { opacity: 0.5; cursor: default; }

/* Caption — the client's own line about the photo. Centred along the bottom
   edge, the tag pill's width at rest so the tile stays quiet, and it opens to
   the width of the photo the moment you click into it — over the source label,
   which is why it sits on a higher layer. */
/* Chips sit above the resting caption so their ✕ stays clickable; an open
   caption has to sit above the chips, or it types underneath them. */
.entry-content .photo-grid .caption-field:focus-within {
    justify-content: flex-start;
    padding-right: 112px;      /* clear of the two download buttons */
    z-index: 4;
}
/* At rest the pill is centred on the photo. It only has to dodge the S and OG
   pair once it is open and takes the width, which is what the padding does. */
.entry-content .photo-grid .caption-field { padding-right: 0; }
.entry-content .photo-grid .caption-field {
    position: absolute;
    z-index: 2;
    /* The box spans the whole tile but only the field inside it is real. Left
       solid it lies across the chips on the same bottom line and swallows every
       click on a chip's ✕ — the tag then looks unremovable in the grid, while
       the lightbox, laid out differently, works fine. */
    pointer-events: none;
    left: 50%;
    bottom: 10px;
    transform: translateX(-50%);
    width: calc(100% - 20px);
    display: flex;
    justify-content: center;
}
.caption-field > * { pointer-events: auto; }

.caption-input {
    width: 108px;              /* the tag pill's width at rest */
    text-align: center;
    padding: 5px 10px;
    border: 0;
    border-radius: 999px;
    /* Quieter than the chips: at rest this is an offer, not a statement. It
       goes solid the moment it is being typed into. */
    background: rgba(255, 255, 255, 0.56);
    color: #1a1a1a;
    font: inherit;
    font-size: 0.75rem;
    transition: width 0.2s ease, background 0.2s ease;
}
.caption-input::placeholder { color: rgba(0, 0, 0, 0.55); }
.caption-input:focus {
    background: #fff;
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.entry-content .photo-grid .caption-input:focus {
    /* The field's own ✕ is the only thing it has to leave room for: the
       download pair is kept out by the wrapper's padding, at rest and open
       alike. Left-aligned when open — centring a field this wide would push its
       right end back under the buttons. */
    width: calc(100% - 32px);
    text-align: left;
}
.caption-input.saving { opacity: 0.6; }

/* Signed out the caption is a line of text, not a field — same pill, same
   corner, so the tile reads the same to a visitor as it does to the client. */
.caption-static {
    max-width: 100%;
    padding: 5px 10px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.8);
    color: #1a1a1a;
    font-size: 0.75rem;
    text-align: center;
    overflow-wrap: anywhere;
}

/* Remove the caption. Only offered once there is one to remove —
   :placeholder-shown is exactly "this field is empty". */
.caption-field { align-items: center; gap: 4px; }
.caption-clear {
    --glyph-nudge: -1px;       /* measured: the ✕ ink sat a pixel low */
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.8);
    color: #1a1a1a;
    font-size: 0.7rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease;
}
.caption-clear:hover { background: #fff; }
.caption-clear:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.caption-input:placeholder-shown + .caption-clear { display: none; }
.entry-content .photo-grid .caption-field:has(.caption-static) { pointer-events: none; }

/* ===== Undo =====
   Every tag and caption change offers its inverse here. One slot, over
   everything including the lightbox, and it stays until the next change: a
   countdown would mean spotting the mistake and then racing a bar. */
.undo-bar {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    z-index: 10000;            /* the lightbox is 9999, and tagging happens in it */
    display: flex;
    align-items: center;
    gap: 12px;
    width: max-content;
    max-width: min(560px, calc(100vw - 32px));
    padding: 12px 12px 12px 20px;
    border-radius: 10px;
    background: #232323;
    color: #f4f4f4;
    font-size: 0.85rem;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
    animation: undo-in 0.25s ease;
}
@keyframes undo-in {
    from { opacity: 0; transform: translate(-50%, 12px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
.undo-text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.undo-btn {
    flex: 0 0 auto;
    padding: 8px 18px;
    border: 0;
    border-radius: 6px;
    background: var(--accent);
    color: #1a1a1a;
    font: inherit;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    cursor: pointer;
}
.undo-btn:hover { filter: brightness(1.08); }
.undo-btn[disabled] { opacity: 0.6; cursor: default; }
.undo-close {
    --glyph-nudge: -0.75px;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: none;
    color: #cfcfcf;
    font-size: 0.85rem;
    cursor: pointer;
}
.undo-close:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
.undo-btn:focus-visible,
.undo-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 768px) {
    /* Full width on a phone, and clear of the mobile bar at the bottom. */
    .undo-bar {
        left: 16px;
        right: 16px;
        bottom: 16px;
        width: auto;
        max-width: none;
        transform: none;
        animation: none;
    }
    .undo-btn { padding: 10px 16px; }
}

@media (prefers-reduced-motion: reduce) {
    .undo-bar { animation: none; }
}

.entry-content .photo-grid .tag-list {
    position: absolute;
    z-index: 3;                /* above the caption, which opens to full width */
    left: 10px;
    bottom: 10px;              /* the same bottom line as the caption */
    max-width: calc(50% - 70px);   /* never reaches the centred caption pill */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 5px;
    max-width: calc(100% - 20px);
}

/* Tagging from inside the lightbox. Same corners as a tile — field top right,
   chips bottom left — laid over the photo so it costs no height. */
.lightbox-frame {
    position: relative;
    display: inline-block;
    line-height: 0;               /* no descender gap under the image */
}
.lightbox-tags .tag-field {
    position: absolute;
    top: 12px;
    right: 12px;
    line-height: normal;
}
.lightbox-tags .caption-field {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 12px;
    display: flex;
    justify-content: center;
}
.lightbox-tags .caption-input {
    /* Empty, it is just an offer — no reason to lay a bar across the photo. It
       takes the full width once there is something to read, or to type.
       (:placeholder-shown is exactly "the field is empty".) */
    width: 150px;
    font-size: 0.8rem;
}
.lightbox-tags .caption-input:focus,
.lightbox-tags .caption-input:not(:placeholder-shown) {
    /* Not a percentage: the frame measures zero while the photo is still
       loading, and a percentage of nothing is nothing. */
    width: min(60vw, 640px);
    text-align: left;
}
/* The same corner as on a tile: bottom right, over the photo, on its own
   layer. The caption box spans the photo but only its field is real — left
   solid it swallows the clicks meant for these buttons. */
.lightbox-tags .tile-dl {
    position: absolute;
    z-index: 3;
    right: 12px;
    bottom: 12px;
    display: flex;
    gap: 5px;
    line-height: normal;
}
.lightbox-tags .caption-field { pointer-events: none; }
.lightbox-tags .caption-field:focus-within { padding-right: 118px; z-index: 4; }

.lightbox-tags .tag-list {
    position: absolute;
    left: 12px;
    bottom: 52px;              /* above the caption running along the bottom */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    max-width: calc(100% - 24px);
    line-height: normal;
}
.lightbox-tags .tag-input { width: 128px; }
.lightbox-tags .tag-input:focus { width: 188px; }

/* The number badge shares the top edge with the field; keep them apart. */
@media (max-width: 480px) {
    .entry-content .photo-grid .tag-input { width: 88px; font-size: 0.7rem; }
    .entry-content .photo-grid .tag-input:focus { width: 140px; }
}

@media (prefers-reduced-motion: reduce) {
    .tag-input,
    .caption-input,
    .tag-chip-x { transition: none; }
}

/* ===== Tag galleries in the sidebar =====
   Appended under the mirrored folders at runtime, because the tags are the
   client's, not ours. The divider is a line, not an item — it has no target. */
/* Needs .sidebar-menu in front: `.sidebar-menu li` is more specific than a
   lone class and was silently winning the margin. */
.sidebar-menu .sub-menu-divider {
    height: 1px;
    margin: 32px 20px 32px 24px;
    background: rgba(255, 255, 255, 0.16);
}
.sub-menu .tag-menu-item .menu-text {
    display: flex;
    align-items: baseline;
    letter-spacing: 0.02em;
}
/* The count carries its own gap, because it is appended to a plain label on a
   folder entry as well — where there is no flex gap to lean on, and the number
   ran straight into the year: "Vanalinn, June 2025 84", not "...202584". */
.tag-menu-count {
    margin-left: 0.85em;
    font-size: 0.72em;
    font-weight: 400;
    opacity: 0.55;
}

/* ===== Phone: the tile overlay stacks =====
   One column is too narrow to fit the chips, the caption and both download
   buttons on one line, so the caption takes a line of its own above them and
   the tracking text moves up again. Last in the file on purpose: the rules it
   overrides live further down than the download section. */
@media (max-width: 768px) {
    .entry-content .photo-grid .caption-field,
    .entry-content .photo-grid .caption-field:focus-within { padding-right: 0; }
    .entry-content .photo-grid .caption-field { bottom: 54px; }
    .entry-content .mirror-grid .src { bottom: 92px; }

    /* The open photo is just as narrow — same stacking under it. */
    .lightbox-tags .caption-field,
    .lightbox-tags .caption-field:focus-within { padding-right: 0; }
    .lightbox-tags .caption-field { bottom: 58px; }
}
