    :root { color-scheme: light dark; }
    body {
      font-family: -apple-system, system-ui, sans-serif;
      max-width: 1200px;
      margin: 2rem auto;
      padding: 0 1rem;
      line-height: 1.5;
    }
    nav { margin-bottom: 1.5rem; display: flex; gap: 1rem; align-items: baseline; }
    nav a { text-decoration: none; }
    .site-nav {
      justify-content: space-between;
      border-bottom: 1px solid rgba(127,127,127,0.2);
      padding-bottom: 1rem;
    }
    .brand-link { font-weight: 700; letter-spacing: 0; }
    .landing-nav { margin-bottom: 0; }
    .landing-page {
      min-height: calc(100vh - 8rem);
      display: grid;
      align-items: center;
    }
    .landing-hero {
      max-width: 42rem;
      padding: 12vh 0 16vh;
    }
    .landing-kicker {
      margin: 0 0 0.75rem;
      color: rgba(127,127,127,1);
      font-size: 0.78rem;
      font-weight: 700;
      letter-spacing: 0.08em;
      text-transform: uppercase;
    }
    .landing-hero h1 {
      margin: 0;
      font-size: clamp(3rem, 9vw, 7rem);
      line-height: 0.95;
      font-weight: 700;
    }
    .landing-copy {
      max-width: 32rem;
      margin: 1.5rem 0 0;
      color: rgba(127,127,127,1);
      font-size: 1.08rem;
    }
    nav .nav-inline { margin: 0; display: inline; padding: 0; gap: 0; }
    nav .link-button {
      background: none; border: 0; padding: 0; margin: 0;
      color: inherit; font: inherit; cursor: pointer;
      text-decoration: underline;
    }
    nav .link-button:hover { color: rgb(190,40,40); }
    h1 { margin-top: 0; }
    form { margin: 1rem 0; display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
    form.stacked { flex-direction: column; align-items: stretch; max-width: 32rem; gap: 1rem; }
    form.stacked label { display: flex; flex-direction: column; gap: 0.25rem; }
    form.stacked label.checkbox,
    form.stacked label.radio {
      flex-direction: row; align-items: flex-start; gap: 0.55rem;
    }
    form.stacked label.checkbox input,
    form.stacked label.radio input { margin-top: 0.25rem; flex: 0 0 auto; }
    form.stacked label.checkbox > span,
    form.stacked label.radio > span { display: block; }
    form.stacked label.checkbox .meta,
    form.stacked label.radio .meta { display: block; margin-top: 0.1rem; }
    form.stacked .actions { display: flex; gap: 1rem; align-items: center; }
    /* Disclosure inside the create-gallery form — wraps optional fields
       so the simple "name + button" path stays one input. */
    details.create-advanced {
      border: 1px solid rgba(127,127,127,0.25);
      border-radius: 6px;
      padding: 0.6rem 0.85rem;
      max-width: 32rem;
    }
    details.create-advanced[open] {
      padding-bottom: 1rem;
    }
    details.create-advanced summary {
      cursor: pointer;
      font-size: 0.92rem;
      color: rgba(127,127,127,1);
    }
    details.create-advanced label {
      display: flex; flex-direction: column; gap: 0.25rem;
      margin-top: 0.85rem;
    }
    details.create-advanced label.checkbox {
      flex-direction: row; align-items: flex-start; gap: 0.55rem;
    }
    details.create-advanced label.checkbox input {
      margin-top: 0.25rem; flex: 0 0 auto;
    }
    details.create-advanced select {
      padding: 0.5rem; font-size: 1rem; font-family: inherit;
    }
    /* Visual grouping for the admin gallery edit page. Each top-level form
       wraps its inputs in a fieldset+legend; the page reads as a stack of
       labelled cards instead of a series of free-floating <form>s. */
    fieldset.edit-section {
      max-width: 32rem;
      margin: 0 0 1.25rem;
      padding: 0.5rem 1.1rem 1.1rem;
      border: 1px solid rgba(127,127,127,0.3);
      border-radius: 6px;
      display: flex; flex-direction: column; gap: 1rem;
    }
    fieldset.edit-section legend {
      padding: 0 0.4rem;
      font-size: 0.78rem; font-weight: 700;
      letter-spacing: 0.08em; text-transform: uppercase;
      color: rgba(127,127,127,1);
    }
    fieldset.edit-section.danger {
      border-color: rgba(190,40,40,0.35);
    }
    fieldset.edit-section.danger > legend {
      color: rgb(190,40,40);
    }
    form.pin-form, form.pin-clear-form {
      /* The PIN-save and PIN-clear forms are visually their own sections;
         drop the default form margin so the fieldset spacing controls
         everything. Without this, two stacked fieldsets get an extra 1rem
         gap between them. */
      margin: 0;
    }
    form.stacked button.destructive { color: rgb(190,40,40); }
    input[type=text], input[type=file], input[type=date], textarea { padding: 0.5rem; font-size: 1rem; font-family: inherit; }
    textarea { resize: vertical; }
    .gallery-description { white-space: pre-line; margin: 0.5rem 0 1.5rem; }
    .gallery-shoot-date { color: rgba(127,127,127,1); font-size: 0.95em; margin-bottom: 0.25rem; }
    button { padding: 0.5rem 1rem; font-size: 1rem; cursor: pointer; }
    code { background: rgba(127,127,127,0.15); padding: 0.1em 0.3em; border-radius: 3px; }
    .client-link { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                   font-size: 0.92em;
                   background: rgba(127,127,127,0.15);
                   padding: 0.1em 0.4em; border-radius: 3px;
                   text-decoration: none; word-break: break-all; }
    .client-link:hover { background: rgba(127,127,127,0.25); }
    .pin-reveal { display: flex; flex-wrap: wrap; gap: 0.5rem 0.75rem; align-items: baseline; }
    .pin-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                 font-size: 1.05em; font-weight: 600;
                 background: rgba(127,127,127,0.15);
                 padding: 0.1em 0.5em; border-radius: 3px;
                 user-select: all; }
    ul.galleries { list-style: none; padding: 0; }
    .gallery-row { padding: 0.75rem 0; border-bottom: 1px solid rgba(127,127,127,0.2); display: flex; gap: 1rem; align-items: baseline; flex-wrap: wrap; }
    .gallery-row a { font-weight: 600; }
    .meta { color: rgba(127,127,127,1); font-size: 0.9em; }
    .client-chip {
      display: inline-flex; align-items: center; flex-wrap: wrap;
      gap: 0.5rem;
      background: rgba(127,127,127,0.12);
      padding: 0.35rem 0.85rem;
      border-radius: 999px;
      font-size: 0.85rem;
      margin: 0.25rem 0 1.25rem;
      max-width: 100%;
    }
    .client-chip-label {
      text-transform: uppercase;
      letter-spacing: 0.06em;
      font-size: 0.72rem;
      color: rgba(127,127,127,1);
    }
    .client-chip-email {
      font-weight: 600;
      word-break: break-all;
    }
    .client-chip-form { display: inline-flex; margin: 0; padding: 0; gap: 0; }
    .client-chip-logout {
      background: none; border: 0; padding: 0; margin: 0;
      color: inherit; font: inherit; cursor: pointer;
      text-decoration: underline;
      opacity: 0.65;
    }
    .client-chip-logout:hover { opacity: 1; color: rgb(190,40,40); }
    .client-prompt { margin: 0.5rem 0 1.25rem; }
    /* Workflow status callout (admin detail page + client gallery page).
       Both surfaces use the same shape so the photographer and the client
       recognize the same visual language across their views. */
    .workflow-banner {
      margin: 0.5rem 0 1.25rem;
      padding: 0.75rem 1rem;
      border-radius: 6px;
      background: rgba(127,127,127,0.12);
      border-left: 4px solid rgba(127,127,127,0.6);
      line-height: 1.45;
    }
    .workflow-banner.culling {
      background: rgba(60,90,180,0.10);
      border-left-color: rgba(60,90,180,0.7);
    }
    .workflow-banner.culling-submitted {
      background: rgba(60,140,80,0.10);
      border-left-color: rgba(60,140,80,0.7);
    }
    .workflow-banner form.inline-form {
      display: inline; margin: 0; padding: 0;
    }
    .link-button {
      background: none; border: 0; padding: 0; margin: 0;
      color: inherit; font: inherit; cursor: pointer;
      text-decoration: underline;
    }
    /* "Olen valmis valitsemaan" button — visually distinct because it's
       the only durable action a culling-stage client can take after
       favoriting; not a destructive call so green, not red. */
    .culling-submit-form { margin: 1.5rem 0 1rem; }
    .culling-submit-form button {
      background: rgb(60,140,80); color: white;
      border: 0; border-radius: 6px;
      padding: 0.65rem 1.25rem;
      font-size: 1rem; font-weight: 600;
      cursor: pointer;
    }
    .culling-submit-form button:hover { background: rgb(45,115,65); }
    .culling-submit-form button:disabled {
      background: rgba(127,127,127,0.35); cursor: default;
    }
    /* Sticky selection pill — bottom-center on mobile, bottom-right on
       desktop. Always visible so the client can glance at "what have I
       picked so far?" while scrolling. The pill IS the filter toggle:
       a tap flips the grid between Kaikki and Suosikit ("keräilykori")
       without changing the page. The tab strip above the grid is the
       same control in a more conventional UI shape — both reflect the
       same `data-active` state. */
    .selection-summary {
      position: fixed;
      bottom: 1.25rem;
      left: 50%;
      transform: translateX(-50%);
      z-index: 50;
      display: inline-flex; align-items: baseline; gap: 0.55rem;
      padding: 0.55rem 1rem;
      border: none;
      border-radius: 999px;
      background: rgba(0,0,0,0.78);
      color: white;
      font-family: inherit;
      font-size: 0.9rem;
      box-shadow: 0 4px 14px rgba(0,0,0,0.25);
      cursor: pointer;
      transition: background 0.2s ease-out, transform 0.1s ease-out;
    }
    .selection-summary:active { transform: translateX(-50%) scale(0.97); }
    .selection-summary.has-picks {
      background: rgba(220,40,80,0.92);
    }
    .selection-summary.filter-active {
      /* When favorites filter is on, switch to a blue/indigo tint so the
         user gets a clear "I am in cart view" cue distinct from the
         pink picked-count fill. Outline ring reinforces "this is the
         current mode" affordance. */
      background: rgba(60,90,180,0.92);
      box-shadow: 0 0 0 3px rgba(60,90,180,0.25), 0 4px 14px rgba(0,0,0,0.25);
    }
    .selection-summary-label {
      text-transform: uppercase;
      letter-spacing: 0.06em;
      font-size: 0.72rem;
      opacity: 0.85;
    }
    .selection-summary-count strong {
      font-variant-numeric: tabular-nums;
      font-size: 1.05rem;
    }
    .selection-summary-sep { opacity: 0.6; padding: 0 0.15rem; }
    @media (min-width: 800px) {
      .selection-summary {
        left: auto; right: 1.5rem;
        transform: none;
      }
      .selection-summary:active { transform: scale(0.97); }
    }
    /* Client-side filter tabs — Kaikki / Suosikit. Same visual language
       as the admin filter strip but client-friendly copy (no
       "Ei valittu" — the client isn't curating, only picking). */
    nav.client-filter {
      display: flex; flex-wrap: wrap; gap: 0.4rem;
      margin: 0.5rem 0 1rem;
    }
    .client-filter-tab {
      display: inline-flex; align-items: center; gap: 0.4rem;
      padding: 0.45rem 0.95rem;
      border: 1px solid rgba(127,127,127,0.35);
      border-radius: 999px;
      background: transparent;
      color: inherit;
      font: inherit;
      font-size: 0.92rem;
      cursor: pointer;
    }
    .client-filter-tab:hover { background: rgba(127,127,127,0.12); }
    .client-filter-tab.active {
      background: rgba(60,90,180,0.92);
      border-color: rgba(60,90,180,0.92);
      color: white;
      font-weight: 600;
    }
    .client-filter-count {
      font-variant-numeric: tabular-nums;
      font-size: 0.82rem;
      background: rgba(255,255,255,0.18);
      padding: 0.05rem 0.45rem;
      border-radius: 999px;
    }
    .client-filter-tab:not(.active) .client-filter-count {
      background: rgba(127,127,127,0.18);
    }
    /* In favorites-only view, hide non-favorited figures. The grid layout
       (and CSS Masonry where supported) reflows around the missing
       items. The toggle is instant — no animation — because the user is
       making a deliberate switch and a transition would just delay it. */
    .grid.show-favorites figure:not(.is-favorite) { display: none; }
    /* Empty-state hint for the favorites view when the user hasn't
       picked anything yet — without this, "Suosikit" tab shows a blank
       page and the user wonders if something broke. */
    .filter-empty {
      color: rgba(127,127,127,1);
      font-style: italic;
      margin: 1.5rem 0;
    }
    /* Row-major thumb grid with natural image aspect ratios.
       CSS Grid (not Columns) so the visual reading order — left to right
       then down — matches the DOM order the lightbox iterates.
       Modern browsers (Safari 17.4+, Chrome 140+) get native Masonry,
       which packs each item into the shortest column → no ragged rows,
       no JS. Older browsers fall back to plain Grid with
       `align-items: start`, leaving acceptable gaps below shorter cells. */
    .grid {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
      gap: 0.5rem;
      margin-top: 1rem;
      align-items: start;
    }
    @supports (grid-template-rows: masonry) {
      .grid {
        grid-template-rows: masonry;
        align-items: stretch;
      }
    }
    @media (min-width: 600px) { .grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); } }
    .grid figure { margin: 0; position: relative; }
    .grid a { display: block; }
    .grid img { width: 100%; height: auto; display: block; border-radius: 4px; }
    .empty { color: rgba(127,127,127,1); font-style: italic; }
    .fav {
      position: absolute; top: 0.5rem; right: 0.5rem;
      background: rgba(0,0,0,0.55); color: white;
      border: none; border-radius: 999px;
      width: 2rem; height: 2rem;
      font-size: 1.1rem; line-height: 1; cursor: pointer;
      display: flex; align-items: center; justify-content: center;
    }
    .fav-on { background: rgba(220,40,80,0.92); }
    .fav-count {
      position: absolute; top: 0.5rem; right: 0.5rem;
      background: rgba(0,0,0,0.65); color: white;
      border-radius: 999px; padding: 0.1rem 0.5rem;
      font-size: 0.85rem;
    }
    /* Admin photo-filter tab strip (Kaikki / Suosikit / Ei valittu). */
    nav.filter-tabs {
      display: flex; flex-wrap: wrap; gap: 0.4rem;
      margin: 0.75rem 0 0.75rem;
      padding-bottom: 0.4rem;
      border-bottom: 1px solid rgba(127,127,127,0.25);
    }
    .filter-tab {
      display: inline-flex; align-items: center; gap: 0.35rem;
      padding: 0.4rem 0.85rem;
      border-radius: 6px 6px 0 0;
      text-decoration: none;
      color: inherit;
      font-size: 0.92rem;
      border: 1px solid transparent;
      border-bottom: none;
    }
    .filter-tab:hover { background: rgba(127,127,127,0.12); }
    .filter-tab.active {
      background: rgba(127,127,127,0.18);
      border-color: rgba(127,127,127,0.3);
      font-weight: 600;
    }
    .filter-tab-count {
      font-variant-numeric: tabular-nums;
      opacity: 0.7;
      font-size: 0.82rem;
      background: rgba(127,127,127,0.18);
      padding: 0.05rem 0.45rem;
      border-radius: 999px;
    }
    /* When viewing "Kaikki" on a gallery whose client has clicked valmis,
       non-favorited photos fade to half opacity. Mikko can still see and
       delete them, but the visual hierarchy points at the picks. The
       "favorites only" filter is the more emphatic version of the same
       intent — this is the default for browsing while staying contextual. */
    .grid.dim-unpicked figure:not(.is-favorite) > a img {
      opacity: 0.35;
      transition: opacity 0.15s ease-out;
    }
    .grid.dim-unpicked figure:not(.is-favorite):hover > a img {
      opacity: 0.85;
    }
    /* Per-photo picker emails — shown under the thumbnail in the admin
       grid when at least one authenticated client picked the photo.
       Anonymous-only photos show just the count badge. */
    .picker-list {
      margin: 0.25rem 0 0;
      display: flex; flex-wrap: wrap; gap: 0.25rem;
      font-size: 0.78rem;
      line-height: 1.3;
    }
    .picker-email {
      display: inline-block;
      padding: 0.05rem 0.45rem;
      border-radius: 999px;
      background: rgba(220,40,80,0.15);
      color: rgba(220,40,80,0.95);
      word-break: break-all;
    }
    .flash {
      padding: 0.75rem 1rem; border-radius: 4px;
      background: rgba(60,140,80,0.15); border: 1px solid rgba(60,140,80,0.4);
      margin: 0 0 1rem 0;
    }
    .flash.error {
      background: rgba(190,40,40,0.12); border-color: rgba(190,40,40,0.4);
      color: rgb(190,40,40);
    }
    .photo-delete {
      position: absolute; bottom: 0.5rem; right: 0.5rem;
      background: rgba(0,0,0,0.55); color: white;
      border: none; border-radius: 999px;
      width: 1.8rem; height: 1.8rem;
      font-size: 0.9rem; line-height: 1; cursor: pointer;
      padding: 0;
    }
    .photo-delete:hover { background: rgba(190,40,40,0.92); }
    .dropzone {
      border: 2px dashed rgba(127,127,127,0.4);
      border-radius: 6px;
      padding: 1.5rem; margin: 1rem 0;
      text-align: center; color: rgba(127,127,127,1);
      transition: background 0.15s, border-color 0.15s;
    }
    .dropzone.hover { background: rgba(60,140,80,0.08); border-color: rgba(60,140,80,0.6); color: inherit; }
    .upload-queue {
      list-style: none; padding: 0; margin: 0.5rem 0 0;
      display: flex; flex-direction: column; gap: 0.25rem;
    }
    .upload-item {
      display: grid; grid-template-columns: 1fr 10rem 6rem; gap: 0.75rem;
      align-items: center;
      padding: 0.25rem 0.5rem; border-radius: 4px;
      background: rgba(127,127,127,0.08);
      font-size: 0.9rem;
    }
    .upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .upload-item progress { width: 100%; height: 0.5rem; }
    .upload-status { color: rgba(127,127,127,1); font-size: 0.85rem; }
    .upload-item.ok { background: rgba(60,140,80,0.12); }
    .upload-item.ok .upload-status { color: rgb(40,120,60); }
    .upload-item.failed { background: rgba(190,40,40,0.12); }
    .upload-item.failed .upload-status { color: rgb(190,40,40); }
    .upload-item.processing .upload-status {
      color: rgb(100,100,180);
      font-style: italic;
    }
    .upload-summary {
      display: flex; gap: 1rem; align-items: center;
      padding: 0.6rem 0.8rem; margin-top: 0.5rem;
      border-radius: 4px;
      background: rgba(190,40,40,0.12); color: rgb(150,30,30);
      border: 1px solid rgba(190,40,40,0.3);
    }
    .upload-summary button { padding: 0.3rem 0.7rem; font-size: 0.9rem; }
    .lightbox {
      position: fixed; inset: 0;
      background: rgba(0,0,0,0.95);
      z-index: 1000;
      display: none;
      flex-direction: column;
    }
    .lightbox.open { display: flex; }
    /* touch-action: none on the stage so iOS Safari doesn't claim the
       touch as a page-scroll before JS can read it. `pan-y pinch-zoom`
       on the parent caused horizontal swipes to be lost — Safari
       commits to scroll-or-not earlier than other browsers. The image
       itself keeps pinch-zoom so two-finger zoom still works. */
    .lightbox-stage {
      flex: 1; min-height: 0;
      position: relative;
      overflow: hidden;
      padding: 0.5rem;
      touch-action: none;
    }
    /* Two stacked images with "stretch and centre" positioning — both
       fill the stage at natural aspect, anchored to centre. JS toggles
       the active one via the data-role attribute. */
    .lightbox-image {
      position: absolute;
      inset: 0;
      margin: auto;
      max-width: 100%; max-height: 100%;
      object-fit: contain;
      user-select: none; -webkit-user-drag: none;
      transition: transform 0.18s ease-out;
      will-change: transform;
      touch-action: pinch-zoom;
    }
    .lightbox-image[data-role="secondary"] {
      opacity: 0;
      pointer-events: none;
    }
    .lightbox-image.dragging { transition: none; }
    .lightbox-close, .lightbox-nav {
      position: absolute;
      background: rgba(0,0,0,0.55); color: white;
      border: none; border-radius: 999px;
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-family: inherit;
      z-index: 1;
    }
    .lightbox-close {
      top: 0.75rem; right: 0.75rem;
      width: 2.75rem; height: 2.75rem;
      font-size: 1.5rem; line-height: 1;
    }
    .lightbox-download {
      position: absolute;
      top: 0.75rem; right: 4.25rem;
      width: 2.75rem; height: 2.75rem;
      background: rgba(0,0,0,0.55); color: white;
      border-radius: 999px;
      display: flex; align-items: center; justify-content: center;
      font-size: 1.5rem; line-height: 1;
      text-decoration: none;
      z-index: 1;
    }
    .lightbox-download:hover { background: rgba(0,0,0,0.8); }
    /* Lightbox heart sits between close (top-right) and download. When
       download is hidden (culling galleries), the heart slides into its
       slot via the workflow-conditional class on the lightbox-fav
       element — see the `.lightbox:not(:has(.lightbox-download))` rule
       below for the no-download layout. */
    .lightbox-fav {
      position: absolute;
      top: 0.75rem; right: 4.25rem;
      width: 2.75rem; height: 2.75rem;
      background: rgba(0,0,0,0.55); color: white;
      border: none; border-radius: 999px;
      cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      font-size: 1.5rem; line-height: 1;
      z-index: 1;
    }
    .lightbox-fav.fav-on { background: rgba(220,40,80,0.92); }
    .lightbox-fav:hover { background: rgba(0,0,0,0.8); }
    .lightbox-fav.fav-on:hover { background: rgba(200,30,70,0.95); }
    /* When the download button is present, push the heart left of it. */
    .lightbox:has(.lightbox-download) .lightbox-fav { right: 7.75rem; }
    .lightbox-nav {
      top: 50%; transform: translateY(-50%);
      width: 3rem; height: 3rem;
      font-size: 2rem; line-height: 1;
    }
    .lightbox-prev { left: 0.75rem; }
    .lightbox-next { right: 0.75rem; }
    .lightbox-nav:disabled { opacity: 0.3; cursor: default; }
    .lightbox-meta {
      flex: 0 0 auto;
      padding: 0.75rem 1rem;
      color: rgba(255,255,255,0.85);
      font-size: 0.9rem;
      display: flex; gap: 1rem; flex-wrap: wrap;
      align-items: baseline;
      transition: opacity 0.18s ease-out;
    }
    .lightbox-hint {
      position: absolute;
      bottom: 4.5rem; left: 50%;
      transform: translateX(-50%);
      max-width: calc(100vw - 2rem);
      padding: 0.55rem 0.9rem;
      background: rgba(0,0,0,0.7);
      color: rgba(255,255,255,0.95);
      font-size: 0.85rem;
      border-radius: 999px;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.35s ease-out;
      z-index: 1;
      text-align: center;
      white-space: nowrap;
    }
    .lightbox-hint.visible { opacity: 1; }
    .lightbox.meta-hidden .lightbox-meta { opacity: 0; pointer-events: none; }
    .lightbox-counter { font-variant-numeric: tabular-nums; opacity: 0.7; }
    .lightbox-filename { opacity: 0.7; word-break: break-all; }
    @media (hover: hover) {
      .lightbox-close:hover, .lightbox-nav:not(:disabled):hover {
        background: rgba(0,0,0,0.8);
      }
    }
    @media (max-width: 600px) {
      .lightbox-nav { display: none; }
      .lightbox-stage { padding: 0; }
    }
    /* Auth modal — overlay above lightbox for the email+PIN gate. */
    .auth-modal {
      position: fixed; inset: 0;
      z-index: 1100;
      display: none;
      align-items: center; justify-content: center;
      padding: 1rem;
    }
    .auth-modal.open { display: flex; }
    .auth-modal-backdrop {
      position: absolute; inset: 0;
      background: rgba(0,0,0,0.55);
    }
    .auth-modal-card {
      position: relative;
      background: #fff; color: #111;
      max-width: 28rem; width: 100%;
      padding: 1.75rem 1.5rem 1.5rem;
      border-radius: 6px;
      box-shadow: 0 25px 60px rgba(0,0,0,0.5);
      box-sizing: border-box;
    }
    @media (prefers-color-scheme: dark) {
      .auth-modal-card { background: #1f1f1f; color: #f5f5f5; }
    }
    .auth-modal-card h2 {
      margin: 0 0 0.5rem;
      font-size: 1rem; font-weight: 700;
      letter-spacing: 0.08em; text-transform: uppercase;
    }
    .auth-modal-desc {
      margin: 0 0 1.25rem;
      font-size: 0.92rem; line-height: 1.5;
      color: rgba(0,0,0,0.7);
    }
    @media (prefers-color-scheme: dark) {
      .auth-modal-desc { color: rgba(255,255,255,0.75); }
    }
    .auth-modal-close {
      position: absolute;
      top: 0.4rem; right: 0.5rem;
      background: none; border: 0;
      width: 2rem; height: 2rem;
      font-size: 1.5rem; line-height: 1;
      cursor: pointer; color: inherit;
      padding: 0;
    }
    .auth-modal-form {
      display: flex; flex-direction: column;
      gap: 0.85rem; margin: 0;
    }
    .auth-modal-form label {
      display: flex; flex-direction: column;
      gap: 0.25rem; margin: 0;
    }
    .auth-modal-form label > span {
      font-size: 0.8rem;
      letter-spacing: 0.04em;
      color: rgba(0,0,0,0.6);
    }
    @media (prefers-color-scheme: dark) {
      .auth-modal-form label > span { color: rgba(255,255,255,0.65); }
    }
    .auth-modal-form input[type=email],
    .auth-modal-form input[type=text] {
      padding: 0.55rem 0.7rem;
      font-size: 1rem; font-family: inherit;
      border: 1px solid rgba(127,127,127,0.45);
      border-radius: 4px;
      background: transparent; color: inherit;
    }
    .auth-modal-form input:focus {
      outline: 2px solid rgba(120,140,200,0.55);
      outline-offset: 1px;
      border-color: transparent;
    }
    .auth-modal-form label.has-error > span {
      color: rgb(190,40,40);
    }
    .auth-modal-form label.has-error input {
      border-color: rgb(190,40,40);
    }
    .auth-modal-form label.has-error input:focus {
      outline-color: rgba(190,40,40,0.55);
    }
    .auth-modal-error {
      margin: 0; padding: 0.55rem 0.75rem;
      background: rgba(190,40,40,0.12);
      border: 1px solid rgba(190,40,40,0.4);
      color: rgb(190,40,40);
      border-radius: 4px;
      font-size: 0.92rem;
      display: flex; align-items: flex-start;
      gap: 0.5rem;
    }
    .auth-modal-error::before {
      content: "⚠";
      font-size: 1rem; line-height: 1.2;
      flex: 0 0 auto;
    }
    @keyframes auth-modal-shake {
      0%, 100% { transform: translateX(0); }
      20% { transform: translateX(-5px); }
      40% { transform: translateX(5px); }
      60% { transform: translateX(-3px); }
      80% { transform: translateX(3px); }
    }
    .auth-modal-card.shake {
      animation: auth-modal-shake 0.36s ease-in-out;
    }
    @media (prefers-reduced-motion: reduce) {
      .auth-modal-card.shake { animation: none; }
    }
    .auth-modal-submit {
      padding: 0.6rem 1rem;
      font-size: 0.95rem; font-weight: 600;
      background: #222; color: #fff;
      border: 0; border-radius: 4px;
      cursor: pointer;
      margin-top: 0.25rem;
    }
    .auth-modal-submit:disabled {
      opacity: 0.6; cursor: not-allowed;
    }
    .auth-modal-submit:hover:not(:disabled) {
      background: #000;
    }
    .auth-modal-footnote {
      margin: 1rem 0 0;
      font-size: 0.78rem; line-height: 1.45;
      color: rgba(0,0,0,0.55);
    }
    @media (prefers-color-scheme: dark) {
      .auth-modal-footnote { color: rgba(255,255,255,0.55); }
    }
    .privacy-footnote {
      margin-top: 1.5rem;
      font-size: 0.85rem; line-height: 1.5;
      color: rgba(0,0,0,0.6);
      max-width: 32rem;
    }
    @media (prefers-color-scheme: dark) {
      .privacy-footnote { color: rgba(255,255,255,0.65); }
    }
    /* Download-start toast — fixed top-center pill that flashes "Lataus
       alkaa…" on every download click so the user knows the request
       reached the server. The actual byte transfer is invisible to JS
       (Content-Disposition hands off to the browser's download manager
       and the stream's total size is unknown until the last byte), so
       this is intentionally a fire-and-forget liveness cue, not a real
       progress bar. z-index above the lightbox (1000) and auth modal
       (1100) so it's visible from any surface. */
    .download-toast {
      position: fixed;
      top: 1rem; left: 50%;
      transform: translateX(-50%);
      z-index: 1200;
      padding: 0.6rem 1.1rem;
      border-radius: 999px;
      background: rgba(0,0,0,0.85);
      color: white;
      font-size: 0.9rem;
      box-shadow: 0 4px 14px rgba(0,0,0,0.4);
      display: inline-flex; align-items: center; gap: 0.55rem;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.18s ease-out;
      max-width: calc(100vw - 2rem);
    }
    .download-toast.visible { opacity: 1; }
    .download-toast::before {
      content: "";
      width: 0.85rem; height: 0.85rem;
      border: 2px solid rgba(255,255,255,0.35);
      border-top-color: white;
      border-radius: 50%;
      animation: download-toast-spin 0.75s linear infinite;
      flex: 0 0 auto;
    }
    @keyframes download-toast-spin {
      to { transform: rotate(360deg); }
    }
    @media (prefers-reduced-motion: reduce) {
      .download-toast::before { animation: none; opacity: 0.6; }
    }

    /* Admin "Deleted galleries" view — warning chip color. Pulled out of
       inline style="color: rgb(190,40,40)" on admin_deleted.html so the
       template stays CSP-clean (no inline style attrs). */
    .meta-deleted-warn {
      color: rgb(190, 40, 40);
    }
