/* ===========================================================================
   PhotoGalaxy · core component styles
   Loads after theme.css. Every color reference is a CSS variable.
   =========================================================================== */

/* Document-level horizontal clipping. Prevents any single element (cover image,
   masonry tile measured pre-layout, fixed overlay) from making the page wider than
   the viewport — which on mobile manifests as photos appearing pushed to the right
   with extra whitespace on the left. The trade-off is that legitimate horizontal
   scroll (e.g. a wide data table) would be clipped; we have no such surfaces, so
   the clip is safe. */
html, body {
  overflow-x: hidden;
  max-width: 100vw;
}

/* ---------- TOP NAV ---------- */
.top-nav {
  position: sticky; top: 0; z-index: 20;
  background: rgba(251, 250, 248, 0.85);
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid var(--line);
}
.nav-inner {
  max-width: 1280px; margin: 0 auto;
  padding: 16px 32px;
  display: flex; align-items: center; gap: 24px;
}
.nav-inner .brand { flex-shrink: 0; }
.nav-inner .nav-links {
  margin-left: 0;     /* search bar handles the spacing now */
  flex-shrink: 0;
}

/* ---------- Top-nav search ---------- */
.nav-search {
  flex: 1 1 auto;       /* grow to fill, shrink if needed */
  min-width: 0;         /* allow flex shrink past content width */
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 18px;
  height: 40px;
  background: var(--surface-alt);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  transition:
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.nav-search:focus-within {
  background: var(--surface);
  border-color: var(--line-strong);
  box-shadow: var(--ring-focus);
}
.nav-search-icon {
  display: inline-flex;
  color: var(--text-muted);
  flex-shrink: 0;
}
.nav-search-input {
  flex: 1;
  border: none;
  background: transparent;
  font: 400 14px var(--font-ui);
  color: var(--text);
  outline: none;
  min-width: 0;
  padding: 0;
}
.nav-search-input::placeholder {
  color: var(--text-muted);
}
.nav-search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* Spinner — sits on the right side of the search input while a query is in flight. */
.nav-search-spinner {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1.5px solid var(--line);
  border-top-color: var(--text-secondary);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
  flex-shrink: 0;
}
.nav-search-spinner.active {
  opacity: 1;
  animation: navSearchSpin .8s linear infinite;
}
@keyframes navSearchSpin { to { transform: rotate(360deg); } }

/* ============================================================
   Search results dropdown — appended to <body> and positioned
   absolutely via JS so it can overflow the nav.
   ============================================================ */
.nav-search-dropdown {
  position: absolute;
  z-index: 999;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, .08), 0 4px 12px rgba(0, 0, 0, .04);
  overflow: hidden;
  max-height: 70vh;
  overflow-y: auto;
  animation: navSearchDropdownIn 160ms var(--ease);
}
.nav-search-dropdown[hidden] { display: none; }
@keyframes navSearchDropdownIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.search-group + .search-group {
  border-top: 1px solid var(--line-soft);
}
.search-group-title {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
  padding: 12px 16px 6px;
}

.search-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: transparent;
  border: none;
  text-align: left;
  padding: 10px 16px;
  cursor: pointer;
  font: inherit;
  color: var(--text);
  transition: background var(--dur-fast) var(--ease);
}
.search-row:hover,
.search-row.active {
  background: var(--surface-alt);
}

.search-row-pill {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
  width: 56px;
  justify-content: center;
}
/* Two pill variants to visually distinguish the two result types. */
.search-row-pill-client {
  background: var(--accent-soft);
  color: var(--text);
}
.search-row-pill-gallery {
  background: rgba(56, 55, 58, 0.08);
  color: var(--text-secondary);
}

.search-row-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.search-row-label {
  display: inline-flex;
  align-items: center;
  min-width: 0;            /* allow inner text span to shrink and ellipsize */
  max-width: 100%;
}
.search-row-label-text {
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Private-gallery indicator. Sits inline immediately after the gallery name so it
   reads as part of the title rather than as a separate trailing element. */
.search-row-lock-badge {
  flex-shrink: 0;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fef3e8;
  color: #b87333;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 8px;
  vertical-align: middle;
}
.search-row-sub {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.search-empty {
  padding: 24px 16px;
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
}

/* Plain text links for Login / Register / Sign-out — no chrome, just hover affordance */
.nav-text-link {
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  padding: 6px 4px;
  position: relative;
  transition: color var(--dur-fast) var(--ease);
}
.nav-text-link:hover {
  color: var(--text);
}
.nav-text-link:hover::after {
  transform: scaleX(1);
}
.nav-text-link::after {
  content: "";
  position: absolute;
  left: 4px; right: 4px; bottom: 2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}

@media (max-width: 720px) {
  .nav-search { display: none; }
}
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--text); }
.brand:hover { color: var(--text); }
.brand-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  color: var(--text);
  flex-shrink: 0;
}
.brand-mark img,
.brand-mark svg {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
}
.brand-name {
  font-family: var(--font-display);
  font-weight: 500; font-size: 18px;
  letter-spacing: -.005em;
  color: var(--text);
}
.nav-links {
  display: flex; align-items: center; gap: 22px;
  font-size: 14px;
}
.nav-links a {
  color: var(--text-secondary);
  font-weight: 500;
}
.nav-links a:hover { color: var(--text); }
.inline-form { display: inline; margin: 0; }
.link-button {
  background: none; border: none; cursor: pointer;
  color: var(--text-secondary);
  font: inherit; font-weight: 500; padding: 0;
  transition: color var(--dur-fast) var(--ease);
}
.link-button:hover { color: var(--text); }

/* ---------- BUTTONS ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 8px;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  background: var(--surface);
  color: var(--text);
  font: 500 14px/1 var(--font-ui);
  cursor: pointer;
  transition:
    background var(--dur) var(--ease),
    color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: none; box-shadow: var(--ring-focus); }

/* Default — light fill, soft border */
.btn:not(.btn-primary):not(.btn-ghost) {
  background: var(--surface);
  border-color: var(--line);
}
.btn:not(.btn-primary):not(.btn-ghost):hover {
  background: var(--surface-alt);
  border-color: var(--line-strong);
}

/* Primary / selected — dark grey */
.btn-primary {
  background: var(--accent);
  color: var(--text-on-accent);
  border-color: var(--accent);
}
.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

/* Outline — soft alternative to primary. Used for paired CTAs like Login/Register
   where one action is the recommended path and the other should still feel deliberate. */
.btn-outline {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--text);
}
.btn-outline:hover {
  background: var(--surface-alt);
  border-color: var(--accent);
  color: var(--text);
}

/* Ghost — borderless, used for secondary actions */
.btn-ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-secondary);
}
.btn-ghost:hover {
  background: var(--surface-alt);
  color: var(--text);
}

.btn-sm { padding: 7px 12px; font-size: 13px; }
.btn-lg { padding: 14px 24px; font-size: 15px; }
.btn-block { width: 100%; }

.btn[disabled], .btn:disabled {
  opacity: .55; cursor: not-allowed;
  background: var(--surface-sunken);
  color: var(--text-muted);
  border-color: var(--line);
}

.linklike {
  background: none; border: none; cursor: pointer;
  color: var(--text);
  font: inherit; padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--line-strong);
  transition: text-decoration-color var(--dur-fast) var(--ease);
}
.linklike:hover { text-decoration-color: var(--accent); }

/* ---------- LANDING ---------- */
.page-main { min-height: calc(100vh - 140px); }

/* Full-bleed background wrapper for the hero. The .hero itself stays max-width:1280
   centered so the grid layout doesn't change. Background intentionally white per spec —
   keep the bottom hairline as the only visual indicator that this is a discrete zone. */
.hero-wrap {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.hero {
  max-width: 1280px; margin: 0 auto;
  padding: 60px 32px 32px;
  display: grid; gap: 48px;
  grid-template-columns: 1.1fr 1fr;
  align-items: center;
}
.eyebrow {
  text-transform: uppercase;
  letter-spacing: .24em;
  font-size: 11px;
  /* Weight 500 (medium) rather than 600 (semibold). Reference shows the eyebrow as a
     quiet tracked label, not a strong header — semibold here was overpowering the
     subsequent display headline. */
  font-weight: 500;
  color: var(--text-muted);
  margin: 0 0 18px;
}
.hero-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(36px, 5vw, 64px);
  line-height: 1.05;
  letter-spacing: -.02em;
  margin: 0 0 22px;
  color: var(--text);
}
.hero-title em {
  font-style: italic;
  color: var(--text-secondary);
}
.hero-sub {
  font-size: 17px;
  color: var(--text-secondary);
  max-width: 52ch;
  margin: 0 0 32px;
}
.hero-cta { display: flex; gap: 12px; flex-wrap: wrap; }

.hero-collage {
  /* 8-column × 5-row grid gives us the proportions in the reference:
       - Row 1-2: two wide tiles (portrait + landscape)
       - Row 3-4: narrow / wide-center / tall-narrow trio (still life sequence)
       - Row 5:   narrow / wide footer (print + beach)
     Total height tuned so the collage roughly matches the hero text column height. */
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: repeat(5, 1fr);
  height: 560px;
}
.tile {
  border-radius: var(--radius);
  background: var(--surface-alt) center/cover no-repeat;
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  position: relative;
  /* Subtle float animation — each tile uses a different animation-delay so they don't
     move in lockstep. The transform also enables hover zoom via a child rule below.
     `will-change: transform` hints the compositor; performance-friendly at this small
     set of moving elements. */
  animation: tile-float 6s ease-in-out infinite;
  will-change: transform;
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.tile:hover {
  transform: scale(1.025);
  box-shadow: var(--shadow);
  /* On hover, lift this tile above its neighbors so the box-shadow isn't clipped by the
     adjacent grid items' rounded corners. */
  z-index: 2;
}
.tile::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(135deg, rgba(56, 55, 58, .04), rgba(56, 55, 58, .12));
  /* Pointer-events none so the hover above triggers from the tile itself, not blocked
     by the gradient overlay sitting on top. */
  pointer-events: none;
}
/* When the tile has a real sample-gallery cover (data-driven), the inline
   `style="background-image:url(...)"` set by Razor supplies the image. We DON'T want
   the per-tile gradient fallback (t1/t2/etc.) to show through, so reset background-image
   on .has-cover and let the inline style win. */
.tile.has-cover {
  background-image: none;
}

/* Per-tile placement + themed fallback gradient. The fallback gradients are designed
   to evoke each tile's content category (the reference shows a couple, mountains,
   vase still life, camera centerpiece, family in golden hour, framed print, beach),
   so a fresh install reads as "intentional warm photography" rather than "missing
   images". Each tile gets a unique animation-delay so the floating motion is desynced. */

/* t1 — top-left wide. Portrait/couple slot. Warm sunset tones. */
.t1 {
  grid-column: 1 / 5;
  grid-row: 1 / 3;
  background-image: linear-gradient(135deg, #d6a98a 0%, #8b6249 40%, #6b4a37 100%);
  animation-delay: 0s;
}
/* t2 — top-right wide. Landscape/mountain slot. Cool dusk blues + warm horizon. */
.t2 {
  grid-column: 5 / 9;
  grid-row: 1 / 3;
  background-image: linear-gradient(180deg, #c9b896 0%, #8a7a6e 35%, #4a4a55 75%, #2e2e3a 100%);
  animation-delay: -1.0s;
}
/* t3 — mid-left narrow tall. Still life slot. Soft cream + sand. */
.t3 {
  grid-column: 1 / 3;
  grid-row: 3 / 5;
  background-image: linear-gradient(135deg, #f1ead8 0%, #d9c8a8 100%);
  animation-delay: -2.0s;
}
/* t4 — mid-center wide. Camera centerpiece. Warm bedding tones with darker camera area. */
.t4 {
  grid-column: 3 / 7;
  grid-row: 3 / 5;
  background-image: linear-gradient(120deg, #d8c6ad 0%, #8c7560 40%, #3c322a 75%, #5a4a3e 100%);
  animation-delay: -3.0s;
}
/* t5 — right tall column. Family/golden hour. Spans rows 3-5. */
.t5 {
  grid-column: 7 / 9;
  grid-row: 3 / 6;
  background-image: linear-gradient(180deg, #e8c9a1 0%, #b58e62 40%, #6e533b 100%);
  animation-delay: -4.0s;
}
/* t6 — bottom-left small. Photo print slot. Soft warm. */
.t6 {
  grid-column: 1 / 3;
  grid-row: 5 / 6;
  background-image: linear-gradient(135deg, #e8dcc6 0%, #b8a482 100%);
  animation-delay: -2.5s;
}
/* t7 — bottom-center wide. Beach/ocean slot. Sand to sea blue. */
.t7 {
  grid-column: 3 / 7;
  grid-row: 5 / 6;
  background-image: linear-gradient(180deg, #d8cba7 0%, #6e8a92 60%, #3f5e6d 100%);
  animation-delay: -1.5s;
}

@keyframes tile-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}

/* Respect prefers-reduced-motion: users who opt out of motion shouldn't see ANY of the
   floating animation. We disable the animation entirely. The hover zoom is fine to keep
   since it's a direct response to user input. */
@media (prefers-reduced-motion: reduce) {
  .tile { animation: none; }
}

/* ---------- TRUST STRIP ----------
   Three-up icon+label row sitting beneath the hero. Conveys "real product, used by
   real people" without competing with the hero's primary CTA. Three short items so
   the row reads as one breath, not a wall of marketing copy. */
.trust-strip {
  border-top: 1px solid var(--line);
  background: var(--surface);
  padding: 18px 32px;
}
.trust-strip-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.trust-strip-heading {
  margin: 0;
  color: var(--text-secondary);
  font-size: 14px;
  letter-spacing: .01em;
}
.trust-strip-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: 32px;
  align-items: center;
  flex-wrap: wrap;
}
.trust-item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text-secondary);
  font-size: 13px;
  letter-spacing: .01em;
}
.trust-item-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--text);
}
.trust-item-label { font-weight: 500; color: var(--text); }

@media (max-width: 720px) {
  /* Stack on phones — the heading sits above the list, each trust item sits on its
     own line so labels never wrap mid-phrase. */
  .trust-strip-inner { flex-direction: column; align-items: flex-start; gap: 14px; }
  .trust-strip-list { flex-direction: column; gap: 10px; align-items: flex-start; }
}

/* ---------- FEATURES ---------- */
.features {
  max-width: 1280px; margin: 0 auto;
  padding: 60px 32px 100px;
}
.features-grid {
  display: grid; gap: 20px;
  grid-template-columns: repeat(4, 1fr);
}
.feature {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px 22px;
  box-shadow: var(--shadow-xs);
  transition:
    transform var(--dur-slow) var(--ease),
    box-shadow var(--dur-slow) var(--ease),
    border-color var(--dur) var(--ease);
}
.feature:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: var(--line-strong);
}
.feature-num {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--text-muted);
  letter-spacing: .12em;
  margin-bottom: 16px;
}
.feature h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -.01em;
  margin: 0 0 8px;
  color: var(--text);
}
.feature p {
  color: var(--text-secondary);
  font-size: 14px;
  margin: 0;
}

/* Feature cards inside the value-section have a distinct visual treatment:
   - Flush against each other (gap:0) with shared single-pixel borders
   - Square corners (no border-radius)
   - Animated bottom-line on hover (slides in from center) + light background tint
   The non-scoped .feature rules above remain as the default for any other usage; these
   overrides apply only within the value-section wrapper. */

/* Grid: zero gap so cards sit flush. The container gets the outer top+left borders;
   each card draws only its right+bottom. Result: every shared edge is one pixel, no
   doubled lines, no gaps. */
.value-section .features-grid {
  gap: 0;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}
.value-section .feature {
  /* Override the parent .feature: no individual border, no radius, no shadow. The card's
     own right + bottom borders combine with neighbors' left + top to form a single grid. */
  border: 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: var(--surface);
  box-shadow: none;
  padding: 40px 32px 36px;
  position: relative;     /* anchors the absolute hover underline pseudo-element */
  overflow: hidden;       /* keeps the underline from poking out at corners */
  transition: background var(--dur) var(--ease);
}

/* Hover state: light fill + animated bottom underline.
   The underline is a ::after pseudo-element anchored to the bottom of the card. It starts
   at scaleX(0) from center; on hover scales to 1 so it sweeps outward in both directions.
   When the mouse leaves, the same transition runs in reverse — animated in BOTH directions
   per the user's spec. */
.value-section .feature::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--text);
  /* scaleX(0) hides the line; transform-origin: center makes it grow from middle outward.
     The same transition runs in reverse on mouse-out — no JS needed. */
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 360ms var(--ease);
}
.value-section .feature:hover {
  /* Soft warm fill matching the project palette — distinct from the white default so the
     hovered card is unmistakably "active" without being loud. Same color used elsewhere
     for hover states (surface-alt is a touch too warm here; this is a hair lighter). */
  background: #faf7f1;
  transform: none;       /* override the .feature:hover lift we don't want here */
  box-shadow: none;
  border-color: var(--line);
}
.value-section .feature:hover::after {
  transform: scaleX(1);
}

.value-section .feature-num {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 14px;
  color: rgba(56, 55, 58, .35);
  letter-spacing: 0;
  margin-bottom: 28px;
}
.value-section .feature h3 {
  font-family: var(--font-display);
  /* Regular weight (400) per spec — not bold. The display serif at this weight reads as
     a confident title without competing with the headline above. */
  font-weight: 400;
  font-size: 22px;
  letter-spacing: -.01em;
  margin: 0 0 12px;
  color: var(--text);
}
.value-section .feature p {
  font-size: 14px;
  line-height: 1.65;
  color: var(--text-secondary);
}

/* ---------- MARQUEE STRIP ----------
   Dark band that separates the Featured Galleries section from the value-prop sections
   below. Auto-scrolling text. The track contains the phrase block duplicated twice; CSS
   animates it from translateX(0) to -50% so when the first copy fully scrolls off the
   second copy is exactly where the first was, giving a seamless infinite loop. */
.marquee-strip {
  background: #0f0f10;
  color: rgba(232, 230, 225, .82);
  padding: 14px 0;
  overflow: hidden;
  /* The marquee is decorative; it should never be wider than the viewport (which would
     create horizontal page scroll). Belt-and-braces against any sub-pixel rounding
     making the inner track measure slightly wider than its parent. */
  position: relative;
}
.marquee-track {
  display: inline-flex;
  align-items: center;
  gap: 28px;
  white-space: nowrap;
  /* The track is wider than the viewport — that's the point. animation transforms it
     left to create the scroll. 70s/loop is slow enough to be readable without being
     soporific; lighter weight + smaller size makes for a quiet tape rather than a
     dominant band. */
  animation: marquee-scroll 70s linear infinite;
  will-change: transform;
}
.marquee-item {
  /* Body sans-serif at small uppercase-tracked — the previous version used the display
     serif at 22px which felt heavy. The reference design wants a quiet divider, not a
     statement. */
  font-family: inherit;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: .26em;
  text-transform: uppercase;
}
.marquee-bullet {
  color: rgba(232, 230, 225, .35);
  font-size: 11px;
  /* Pull the diamond down a hair so it reads as centered between adjacent uppercase
     items (uppercase letters sit higher than their x-height). */
  transform: translateY(-1px);
}
@keyframes marquee-scroll {
  /* From flush left → translate by half the track. Half because we duplicated content
     twice; at -50% the second copy aligns with the start position of the first. */
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  /* Honor reduced-motion users — pause the scroll so the text is readable but not
     moving. Showing static text is more accessible than removing the section entirely. */
  .marquee-track { animation-play-state: paused; }
}

/* ---------- VALUE SECTION (What we offer) ----------
   Matches reference: large two-column head (title left, supporting blurb right at the
   same baseline), then 4-card feature grid below. Backgrounds and spacing tuned to
   read as one beat, not as competing zones. */
.value-section {
  background: var(--surface);
  padding: 72px 32px 80px;
}
.value-section-inner {
  max-width: 1280px;
  margin: 0 auto;
}
.value-section-head {
  /* Two-column intro. The 1.45fr / 1fr split leaves enough room for the long display
     title on the left while giving the right-column blurb generous reading width.
     Aligned to end so the blurb's baseline meets the last line of the title. */
  display: grid;
  grid-template-columns: 1.45fr 1fr;
  gap: 80px;
  align-items: end;
  margin-bottom: 56px;
}
.value-section-intro .eyebrow {
  margin-bottom: 32px;
}
.value-section-title {
  font-family: var(--font-display);
  /* Weight 300 (Fraunces Light) rather than 400. The display serif renders quite heavy
     at large sizes — 400 reads as semi-bold relative to the rest of the page. 300 gives
     the thin elegant look matching the reference's airy headline treatment. */
  font-weight: 300;
  /* Reference shows large but not enormous — capping around 56px on the widest
     viewports keeps the title in proportion with the body grid below. */
  font-size: clamp(34px, 4.6vw, 56px);
  line-height: 1.08;
  letter-spacing: -.02em;
  color: var(--text);
  margin: 0;
}
.value-section-title em {
  font-style: italic;
  /* Italic emphasis a touch lighter than roman so it reads as accent rather than
     equally-weighted text. Matches "intentional" treatment on hero. */
  color: rgba(56, 55, 58, .55);
}
.value-section-blurb {
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 1.7;
  margin: 0;
  /* Slight bottom padding so the right-column blurb's baseline aligns with the last
     line of the headline on the left. Tuned by eye against the rendered output. */
  padding-bottom: 6px;
  max-width: 380px;
}

/* Eyebrow rule — short underscore preceding the uppercase label. Matches the reference
   images' "— WHAT WE OFFER" pattern. Inline span so it sits next to the text rather
   than as a pseudo-element (which would be harder to control on wrapping eyebrows). */
.eyebrow-rule {
  display: inline-block;
  width: 28px;
  height: 1px;
  background: currentColor;
  vertical-align: middle;
  margin-right: 12px;
  opacity: .55;
}

@media (max-width: 880px) {
  /* On tablets and below, stack the intro vertically; the side-by-side becomes one
     column with the blurb following the title. */
  .value-section-head {
    grid-template-columns: 1fr;
    gap: 24px;
    align-items: stretch;
    margin-bottom: 40px;
  }
}

/* ---------- HOW IT WORKS ----------
   Background is a very soft warm cream that distinguishes from the white "What we offer"
   above and the white CTA below. Matches reference's restrained palette. */
.how-section {
  /* Slightly brighter than the previous #f4f1ec — keeps the warm undertone but reads
     closer to white. Differentiation from the surrounding white sections still comes
     through, but the section feels less like a separate "chapter" and more like a
     subtle pause in rhythm. */
  background: #faf8f4;
  padding: 80px 32px 90px;
  position: relative;
  overflow: hidden;
}
.how-section-inner {
  max-width: 1280px;
  margin: 0 auto;
  position: relative;     /* anchors the absolute watermark inside the inner wrapper */
}
.how-section-head { margin-bottom: 56px; }

/* Giant "N5" watermark behind the steps. Currently hidden per design direction —
   the section reads cleaner without it. Markup retained so this is a one-line revert
   (delete the display:none below) if the watermark is wanted back. */
.how-watermark {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -42%);
  font-family: var(--font-display);
  font-weight: 300;
  font-style: normal;
  font-size: clamp(280px, 38vw, 520px);
  line-height: 1;
  color: rgba(0, 0, 0, .045);
  letter-spacing: 0;
  z-index: 0;
  pointer-events: none;
  user-select: none;
}

/* Three step columns sit above the watermark. */
.how-steps {
  position: relative;
  z-index: 1;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 56px;
}
.how-step {
  padding: 0 8px;
  position: relative;
}
.how-step + .how-step {
  /* Vertical divider — thin, very faint. Matches reference's hairline separator
     between adjacent step columns. */
  border-left: 1px solid rgba(0, 0, 0, .08);
  padding-left: 40px;
}
.how-step-num {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 44px;
  line-height: 1;
  /* Quiet — sits as a numeric marker, not a heavy label. Reference shows the numbers
     at very low contrast so the step title is the visual entry point. */
  color: rgba(0, 0, 0, .22);
  letter-spacing: -.02em;
  margin-bottom: 36px;
}
.how-step-title {
  font-family: var(--font-display);
  font-weight: 400;     /* lighter for the airier reference look */
  font-size: clamp(22px, 2.2vw, 28px);
  letter-spacing: -.015em;
  color: var(--text);
  margin: 0 0 14px;
}
.how-step-desc {
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.7;
  margin: 0;
  max-width: 300px;
}

@media (max-width: 880px) {
  .how-steps {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .how-step + .how-step {
    border-left: none;
    border-top: 1px solid rgba(0, 0, 0, .08);
    padding-top: 32px;
  }
  /* Watermark on phones — shrink so it doesn't overflow the section bounds and create
     ghost horizontal scrolling. */
  .how-watermark { font-size: 200px; }
}

/* ---------- GET STARTED (CTA + plans) ---------- */
.cta-section {
  background: var(--surface);
  padding: 80px 32px 90px;
}
.cta-section-inner {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: 80px;
  align-items: start;
  /* Align-start (not center) so the eyebrow and the first plan card start at the same
     visual y-position. Reference shows them flush at the top. */
}
.cta-pitch .eyebrow { margin-bottom: 32px; }
.cta-title {
  font-family: var(--font-display);
  /* Weight 300 — same reasoning as value-section-title. Display serif at 400 reads
     as semi-bold at this size; 300 is the thin elegant look the reference shows. */
  font-weight: 300;
  font-size: clamp(34px, 4.6vw, 56px);
  line-height: 1.08;
  letter-spacing: -.02em;
  color: var(--text);
  margin: 0 0 24px;
}
.cta-title em {
  font-style: italic;
  color: rgba(56, 55, 58, .55);
  /* Match the roman weight so the italic emphasis doesn't read heavier than the rest. */
  font-weight: 300;
}
.cta-sub {
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 1.7;
  margin: 0 0 40px;
  max-width: 380px;
}
.cta-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  /* Uppercase tracked label per reference's "CREATE YOUR GALLERY →" treatment. */
  text-transform: uppercase;
  letter-spacing: .12em;
  font-size: 13px;
  font-weight: 500;
  padding: 18px 28px;
  /* Square corners per spec — no border-radius. We use !important to win over the
     base .btn rule which sets border-radius to the pill token. */
  border-radius: 0 !important;
}

/* Plan cards — three stacked rows. Each is a clickable link with hover state that
   nudges the arrow right. Per spec: transparent background, square corners, hairline
   outline (otherwise the cards would be invisible against the white section). Hover
   reveals a soft warm tint so users see the click affordance without the heavy fill. */
.plans-list {
  display: flex;
  flex-direction: column;
  gap: 0;     /* flush so the cards read as a stacked list, not individual chips */
  border-top: 1px solid var(--line);
}
.plan-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 28px 32px;
  background: transparent;
  /* Bottom border on each card; combined with the .plans-list top border this gives a
     clean stacked-row appearance with a single hairline between adjacent cards. */
  border-bottom: 1px solid var(--line);
  /* Square corners per spec. */
  border-radius: 0;
  text-decoration: none;
  color: inherit;
  transition:
    background var(--dur) var(--ease),
    padding-left var(--dur) var(--ease);
}
.plan-card:hover {
  /* Soft warm fill on hover — matches the value-section card hover for consistency.
     Padding-left bump nudges content rightward as visual feedback (replaces the
     translateX which would have shifted the borders too). */
  background: #faf7f1;
  padding-left: 38px;
}
.plan-card-text { display: flex; flex-direction: column; gap: 6px; }
.plan-card-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 20px;
  letter-spacing: -.005em;
  color: var(--text);
  margin: 0;
}
.plan-card-desc {
  color: var(--text-muted);
  font-size: 13px;
  letter-spacing: .01em;
  margin: 0;
}
.plan-card-arrow {
  font-size: 18px;
  color: var(--text-muted);
  transition: transform var(--dur) var(--ease), color var(--dur) var(--ease);
}
.plan-card:hover .plan-card-arrow {
  color: var(--text);
  transform: translateX(4px);
}

@media (max-width: 880px) {
  .cta-section-inner {
    grid-template-columns: 1fr;
    gap: 48px;
  }
}

/* ---------- FEATURED SAMPLE GALLERIES ----------
   Section showing client galleries marked as IsSampleGallery. Premium card grid
   modeled on photography portfolio sites (Pic-Time, Shootproof, etc.):
   large cover image dominates the card, name + author sit underneath. */
.featured-samples {
  background: var(--surface);
  padding: 48px 0 56px;
  border-top: 1px solid var(--line);
}
.featured-samples-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 32px;
}
.featured-samples-head {
  text-align: center;
  margin-bottom: 32px;
}
.featured-samples-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(28px, 4vw, 44px);
  letter-spacing: -.02em;
  color: var(--text);
  margin: 8px 0 12px;
}
.featured-samples-sub {
  color: var(--text-secondary);
  font-size: 15px;
  max-width: 600px;
  margin: 0 auto;
}

.featured-samples-grid {
  display: grid;
  gap: 24px;
  grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 1100px) {
  .featured-samples-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 820px) {
  .featured-samples-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  .featured-samples-grid { grid-template-columns: 1fr; gap: 18px; }
}

.sample-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}
.sample-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
  border-color: var(--line-strong);
}
.sample-card-disabled {
  /* Non-clickable card (no subdomain/slug). Slightly faded; no hover lift. */
  opacity: .75;
  cursor: default;
}
.sample-card-disabled:hover {
  transform: none;
  box-shadow: var(--shadow-xs);
  border-color: var(--line);
}

.sample-card-cover {
  /* Fixed aspect — 3:2 reads as photographic. background-image gets set inline by Razor.
     Slight zoom on hover for a premium feel without being distracting. */
  aspect-ratio: 3 / 2;
  background: var(--surface-alt) center/cover no-repeat;
  transition: transform var(--dur-slow) var(--ease);
}
.sample-card:hover .sample-card-cover {
  transform: scale(1.03);
}
.sample-card-body {
  padding: 18px 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.sample-card-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -.01em;
  color: var(--text);
  margin: 0;
  /* Cap to 2 lines so cards stay uniform height even with long names. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sample-card-desc {
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.5;
  margin: 0;
  /* Same line-clamp pattern for descriptions. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.sample-card-meta {
  margin-top: auto;
  padding-top: 8px;
  color: var(--text-muted);
  font-size: 12px;
  letter-spacing: .04em;
}
.sample-card-author { font-style: italic; }

.featured-samples-foot {
  margin-top: 48px;
  text-align: center;
}
.featured-samples-empty {
  text-align: center;
  padding: 60px 20px;
  color: var(--text-secondary);
}
.featured-samples-empty p { margin: 0 0 20px; }

/* Standalone /Galleries/Sample listing page: slightly tighter top padding since the
   site header is the only chrome above. */
.featured-samples-page { padding-top: 60px; }

/* ---------- FOOTER ----------
   Fixed at the bottom of the viewport — stays visible while the user scrolls.
   The main content area gets bottom padding so the last bit of content isn't covered.
*/
.site-footer {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 15;                 /* above page content, below modals (which use z >= 50) */
  border-top: 1px solid var(--line);
  padding: 12px 32px;
  color: var(--text-muted);
  font-size: 13px;
  /* Slightly translucent so cover images / colored backgrounds peek through;
     backdrop-filter keeps text legible. Falls back to solid surface in browsers
     that don't support backdrop-filter. */
  background: rgba(251, 250, 248, 0.92);
  backdrop-filter: saturate(1.2) blur(10px);
  -webkit-backdrop-filter: saturate(1.2) blur(10px);
}
/* Reserve bottom room on the main content area so it isn't hidden behind the fixed footer.
   Applies to every layout (_Layout, _DashboardLayout, _AdminLayout). With document-level
   scrolling, the last 56px of any main content would otherwise sit under the fixed footer. */
main { padding-bottom: 56px; }
@media (max-width: 640px) {
  main { padding-bottom: 68px; }   /* footer wraps to 2 lines on phones */
}

.footer-inner {
  max-width: 1680px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.footer-left {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.footer-right {
  display: flex;
  align-items: center;
}
.footer-sep { opacity: .5; }
.footer-version {
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  letter-spacing: .03em;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--text-secondary);
  border: 1px solid var(--line);
}
/* Build timestamp pill. Same shape as the version pill but slightly quieter so the
   eye lands on the version first (timestamp is operator-facing detail; version is the
   product identifier). */
.footer-build {
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  letter-spacing: .02em;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--line);
}
@media (max-width: 640px) {
  /* Hide the build timestamp on phones — the footer is already cramped at this width
     and the version pill is the more important identifier. The timestamp is still
     visible on tablet+. */
  .footer-build, .footer-build + .footer-sep { display: none; }
}

@media (max-width: 640px) {
  .site-footer { padding: 10px 14px; font-size: 12px; }
  .footer-inner { gap: 6px; justify-content: center; text-align: center; }
  .footer-left  { justify-content: center; gap: 8px; }
}

/* ---------- AUTH ---------- */
.page-auth .page-main {
  display: flex; align-items: center; justify-content: center;
  padding: 60px 24px;
}
.auth-shell { width: 100%; max-width: 420px; }
.auth-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 40px;
  box-shadow: var(--shadow);
}
/* When .login-card is the auth content, the stepper provides its own padding —
   so collapse the outer auth-card padding to avoid doubling up. */
.auth-card.login-card { padding: 0; }
.auth-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 28px;
  letter-spacing: -.01em;
  margin: 0 0 6px;
  color: var(--text);
}
.auth-sub {
  color: var(--text-secondary);
  margin: 0 0 28px;
}
.auth-switch {
  text-align: center;
  margin-top: 20px;
  color: var(--text-secondary);
  font-size: 14px;
}
.auth-switch a {
  color: var(--text);
  font-weight: 500;
  border-bottom: 1px solid var(--line-strong);
}

/* ---------- FORMS ---------- */
.field { display: block; margin-bottom: 16px; }
.field > span {
  display: block;
  font-size: 13px; font-weight: 500;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font: 400 15px var(--font-ui);
  color: var(--text);
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--text-muted); }
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--line-strong); }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring-focus);
}
.field-inline {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px;
  color: var(--text-secondary);
  margin: 4px 0 20px;
}

/* Required-field marker — small red asterisk after the label text. aria-hidden in the
   markup because screen readers already get the requirement signal via aria-required
   on the input itself; visual indicator is for sighted users only. */
.required-marker {
  color: var(--danger);
  font-weight: 600;
  margin-left: 2px;
}

/* Optional-field indicator — the inverse: when a field is NOT required, we say so
   explicitly rather than leaving the user to guess from the absence of the asterisk.
   Muted color, small size — doesn't compete with the label. */
.field-optional {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-muted);
  margin-left: 4px;
  letter-spacing: .01em;
  /* Inherit "normal" case from the label rather than being uppercase like other muted
     UI labels in the app — reads more naturally inline. */
  text-transform: none;
}

/* Field-level validation error message. Sits directly under the input. The
   asp-validation-for tag-helper renders an empty span when there are no errors, so
   we collapse the box to zero height in that case (no layout jump until an error
   actually appears). */
/* Field-level message slot. Used for both:
   - Server-side validation errors (asp-validation-for populates the text)
   - Client-side availability hints (JS sets textContent + toggles .is-success)
   Default = red error state (most common usage). The .is-success modifier flips it to
   green for affirmative messages like "✓ Email available". One slot, one source of
   truth — never stacks error + success at the same time. */
.field-error {
  display: block;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--danger);
  margin-top: 6px;
  min-height: 0;
}
.field-error:empty { margin-top: 0; }
.field-error.is-success {
  /* Project doesn't have a dedicated "success" token; we use a muted green that
     reads positive but not alarming. The check-mark glyph in the message body
     does most of the affirmative work. */
  color: #2d7a3e;
}
/* .is-error modifier is a no-op (default style is already red) but is set by the JS
   for symmetry with .is-success; keeping it explicit means the rule still works if a
   future change separates the default from the explicit error state. */
.field-error.is-error { color: var(--danger); }

/* Legacy hint span — kept in CSS for any other surface that uses .field-hint. The
   registration form has been collapsed to use .field-error exclusively. */
.field-hint {
  display: none;
  font-size: 12px;
  line-height: 1.5;
  margin-top: 6px;
}
.field-hint.is-active { display: block; }
.field-hint.is-error  { color: var(--danger); }
.field-hint.is-success { color: #2d7a3e; }

.alert {
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  margin-bottom: 16px;
  border: 1px solid var(--line);
  background: var(--surface-alt);
  color: var(--text);
}
.alert-error {
  background: var(--danger-soft);
  color: var(--danger);
  border-color: var(--danger-soft);
}
.alert-info {
  background: var(--surface-alt);
  color: var(--text);
  border-color: var(--line);
}

/* ============================================================
   Login stepper — three steps in a sliding track.

   Layout rules:
     - Card has its own padding (no padding inside steps)
     - All three steps share the same internal layout: title → subtitle → fields → button
     - The track translates left/right to reveal the active step
     - Off-screen steps are aria-hidden + visually clipped so they don't
       affect height or take focus
   ============================================================ */
.login-card {
  /* As a modal: the parent card already provides bg/radius/shadow. As a standalone
     auth page: this class adds those properties via .auth-card. */
  overflow: hidden;
}

.login-stepper {
  /* Single source of truth for inner padding — every step uses this surface. */
  padding: 32px 32px 28px;
  /* Track children stack vertically: alert (optional) → track → switch link */
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.login-stepper .alert { margin: 0; }

/* The sliding track: 600% wide, six equal slots (3 login + 3 forgot password) */
.login-track {
  display: flex;
  width: 600%;
  transition: transform var(--dur-slow, 420ms) cubic-bezier(0.4, 0, 0.2, 1);
  /* Avoid the track contributing extra height when steps differ. The track's
     height is whatever the visible step needs. */
  align-items: stretch;
}

.login-step {
  flex: 0 0 16.6666%;
  width: 16.6666%;
  /* Each step's internal layout — fields, button — uses a vertical flex. */
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Padding 0 here; .login-stepper handles outer spacing */
  padding: 0;
  box-sizing: border-box;
}

.login-stepper[data-step="email"]        .login-track { transform: translateX(0); }
.login-stepper[data-step="password"]     .login-track { transform: translateX(-16.6666%); }
.login-stepper[data-step="otp"]          .login-track { transform: translateX(-33.3333%); }
.login-stepper[data-step="forgotEmail"]  .login-track { transform: translateX(-50%); }
.login-stepper[data-step="forgotOtp"]    .login-track { transform: translateX(-66.6666%); }
.login-stepper[data-step="forgotReset"]  .login-track { transform: translateX(-83.3333%); }

/* Forgot password link below the password input */
.login-forgot-form {
  margin: 0;
  text-align: center;
}
.login-forgot-form .linklike {
  font-size: 13px;
  color: var(--text-secondary);
}
.login-forgot-form .linklike:hover { color: var(--text); }

/* Step footer link ("Back to sign in", "Use a different email") */
.login-step-foot {
  text-align: center;
  margin: 0;
  font-size: 13px;
}
.login-step-foot a {
  color: var(--text-secondary);
  font-weight: 500;
}
.login-step-foot a:hover { color: var(--text); }

/*
  Off-screen steps must not:
    - take tab focus (hidden inputs would still be reachable otherwise)
    - contribute to height (we want the visible step's height to drive the card)
  Trick: clip the track height to the visible step using a CSS trick — only the
  active step's max-height is unbounded, others collapse.
*/
.login-step[aria-hidden="true"] {
  visibility: hidden;
  pointer-events: none;
  /* Keep the slot in the layout for the slide animation, but at zero functional height. */
  max-height: 0;
  overflow: hidden;
}
/* Visible step takes its natural height */
.login-step[aria-hidden="false"] {
  max-height: none;
  overflow: visible;
}

/* ---------- Step heading ---------- */
.login-step-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--text);
  margin: 0;
}
.login-step-sub {
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.5;
  margin: 0;
}
.login-step-sub strong {
  color: var(--text);
  font-weight: 500;
  word-break: break-word;
}

/* ---------- Back button (Step 2 + 3) ---------- */
.login-back {
  background: transparent;
  border: none;
  cursor: pointer;
  align-self: flex-start;     /* don't stretch to row width */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 500 13px var(--font-ui);
  color: var(--text-secondary);
  padding: 4px 8px;
  margin: 0 0 -8px -8px;       /* visually align with title baseline */
  border-radius: var(--radius-sm);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.login-back:hover {
  color: var(--text);
  background: var(--surface-alt);
}

/* ---------- Form fields inside steps ---------- */
.login-step .field {
  margin: 0;          /* gap on .login-step handles spacing */
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.login-step .field > span {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  letter-spacing: .005em;
}
.login-step .field input {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  font: 400 15px var(--font-ui);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  outline: none;
  box-sizing: border-box;
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.login-step .field input:focus {
  border-color: var(--accent);
  box-shadow: var(--ring-focus);
}

/* OTP field — bigger, centered, tracking */
.login-step .otp-input {
  font-variant-numeric: tabular-nums;
  letter-spacing: .5em;
  text-align: center;
  font-size: 20px !important;
  font-weight: 500;
  height: 52px !important;
  /* The letter-spacing creates a trailing gap; pull text left to re-center visually */
  padding-left: 1.4em !important;
  padding-right: .9em !important;
}

/* Primary submit button inside steps — full width, consistent height */
.login-step .btn-primary {
  width: 100%;
  height: 44px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  margin-top: 4px;        /* tiny breathing space above the CTA */
}

/* The form inside each step is the action wrapper — make it stack like the step itself */
.login-step form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0;
}

/* Resend form — text-link styling, sits below the verify button */
.login-resend-form {
  text-align: center;
  margin: 0;
}
.login-resend-form .linklike {
  font-size: 13px;
  color: var(--text-secondary);
}
.login-resend-form .linklike:hover {
  color: var(--text);
}

.login-switch {
  text-align: center;
  margin: 0;
  font-size: 13px;
  color: var(--text-muted);
  /* Pin to the bottom of the stepper visually with a thin separator above */
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.login-switch a { color: var(--text); font-weight: 500; }

@media (max-width: 540px) {
  .login-stepper { padding: 28px 22px 22px; gap: 16px; }
  .login-step-title { font-size: 20px; }
  .login-step .field input { height: 42px; }
  .login-step .btn-primary { height: 42px; }
}

/* ============================================================
   Register form — single-step variant of the login layout.
   Reuses the same field/button styling so the two modals feel paired.
   ============================================================ */
.register-card { overflow: hidden; }
.register-form {
  padding: 32px 32px 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.register-header { display: flex; flex-direction: column; gap: 6px; }
.register-form .alert { margin: 0; }
.register-form form {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin: 0;
}
.register-form .field {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.register-form .field > span {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}
.register-form .field input {
  width: 100%;
  height: 44px;
  padding: 0 14px;
  font: 400 15px var(--font-ui);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  outline: none;
  box-sizing: border-box;
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.register-form .field input:focus {
  border-color: var(--accent);
  box-shadow: var(--ring-focus);
}
.register-form .btn-primary {
  width: 100%;
  height: 44px;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  margin-top: 4px;
}
.register-form .login-switch {
  text-align: center;
  margin: 0;
  font-size: 13px;
  color: var(--text-muted);
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.register-form .login-switch a { color: var(--text); font-weight: 500; }

@media (max-width: 540px) {
  .register-form { padding: 28px 22px 22px; gap: 16px; }
  .register-form .field input { height: 42px; }
  .register-form .btn-primary { height: 42px; }
}

/* When the standalone register page wraps the form in .auth-card, kill the outer padding
   the same way login does so we don't double up. */
.auth-card.register-card { padding: 0; }

/* ============================================================
   Login modal — overlay shell that hosts the login stepper.

   Architecture:
     .login-modal            fixed full-viewport flex container, scrolls if content
                             exceeds viewport (rare). z-index above ALL site chrome.
     .login-modal-backdrop   absolute fill, dims the page
     .login-modal-card       centered card with fixed max-width, hosts everything

   The inner stepper has its own scroll if any single step is taller than the card.
   ============================================================ */
body.modal-open { overflow: hidden; }

.login-modal {
  position: fixed;
  inset: 0;
  /* Above sticky nav (z:20), below browser-native dialogs (~2147xxx) */
  z-index: 9999;
  /* Flexbox centering — works even when card height < viewport */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* If the card is taller than the viewport, allow the modal itself to scroll
     so the user can still reach the close button and the form's submit. */
  overflow-y: auto;
  animation: loginModalIn var(--dur) var(--ease);
}
.login-modal[hidden] { display: none; }

.login-modal-backdrop {
  position: fixed;       /* fixed, not absolute — covers everything regardless of overflow */
  inset: 0;
  background: rgba(20, 18, 15, .58);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.login-modal-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
  /* Important: the card is the bounded container. Its height adapts to content. */
  margin: auto;
  /* Stack: close button (absolute) + body */
  display: flex;
  flex-direction: column;
  animation: loginModalCardIn var(--dur-slow) cubic-bezier(0.22, 1, 0.36, 1);
}

.login-modal-close {
  position: absolute;
  top: 12px; right: 12px;
  z-index: 2;
  background: transparent;
  border: none;
  width: 32px; height: 32px;
  border-radius: 50%;
  cursor: pointer;
  font-size: 22px;
  line-height: 1;
  color: var(--text-muted);
  display: inline-flex; align-items: center; justify-content: center;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.login-modal-close:hover {
  background: var(--surface-alt);
  color: var(--text);
}

.login-modal-body {
  /* Hide overflow so the off-screen steps in the slider don't escape the card. */
  overflow: hidden;
  /* Inherit corner radius from the card */
  border-radius: inherit;
}

.login-modal-loading {
  padding: 80px 24px;
  text-align: center;
  color: var(--text-muted);
  font-size: 14px;
}

@keyframes loginModalIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes loginModalCardIn {
  from { transform: translateY(12px) scale(.98); opacity: 0; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

@media (max-width: 540px) {
  .login-modal { padding: 12px; align-items: flex-start; padding-top: 32px; }
  .login-modal-card { max-width: none; border-radius: 12px; }
}

.field-help {
  margin: -6px 0 18px;
  font-size: 12px;
  color: var(--text-muted);
}

/* When .field-help follows the clientid-input wrapper (suffix-attached input),
   the wrapper's bottom edge doesn't align with a plain input baseline, so the
   default -6px top margin pulls the help text up to visually touch the input.
   Reset it to a positive value so there's clear breathing room. */
.clientid-input + .field-help {
  margin-top: 8px;
}

/* ClientId input — text field on the left, fixed suffix on the right */
.clientid-input,
.subdomain-input {  /* keep .subdomain-input alias to avoid breaking any stale templates */
  display: flex;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  transition:
    border-color var(--dur-fast) var(--ease),
    box-shadow var(--dur-fast) var(--ease);
}
.clientid-input:focus-within,
.subdomain-input:focus-within {
  border-color: var(--accent);
  box-shadow: var(--ring-focus);
}
.clientid-input input,
.subdomain-input input {
  flex: 1;
  border: none;
  background: transparent;
  padding: 11px 14px;
  font: 400 15px var(--font-ui);
  color: var(--text);
  outline: none;
}
.clientid-suffix,
.subdomain-suffix {
  display: inline-flex; align-items: center;
  padding: 0 14px;
  background: var(--surface-alt);
  border-left: 1px solid var(--line);
  color: var(--text-secondary);
  font-size: 14px;
  white-space: nowrap;
}

/* Profile page layout. Now a single-column form. The main card has a max-width so it
   doesn't stretch across very wide displays. (Earlier this was a 2fr/1fr grid with a
   side card; that's been removed in favor of consolidating Client ID into the main
   panel — see Profile.cshtml.) */
.profile-layout {
  display: block;
  max-width: 720px;
}
.profile-main {
  display: flex; flex-direction: column;
}
.profile-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: var(--shadow-xs);
}
.profile-clientid {
  margin-top: auto; /* belt-and-suspenders for the bottom anchor */
}
.profile-card-help {
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0 0 18px;
  line-height: 1.5;
}
.profile-card-help strong {
  color: var(--text);
  font-weight: 500;
}

/* Current Client ID display block */
.clientid-display {
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 6px;
}
.clientid-display.is-empty {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.clientid-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.clientid-value {
  font: 600 18px var(--font-display);
  letter-spacing: -.005em;
  color: var(--text);
  flex: 1;
  min-width: 0;
  word-break: break-all;
}
.clientid-value-empty {
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
}
.clientid-help {
  color: var(--text-muted);
  font-size: 12px;
}
.clientid-url {
  display: block;
  color: var(--text-secondary);
  font-size: 12px;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}
.clientid-copy {
  flex-shrink: 0;
  background: transparent;
  border: 1px solid var(--line);
  width: 28px; height: 28px;
  border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  color: var(--text-secondary);
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.clientid-copy:hover {
  background: var(--surface);
  color: var(--text);
  border-color: var(--accent);
}

/* Checkbox-style field row — used for Vanity toggle and similar boolean fields. */
.field-checkbox {
  display: flex;
  align-items: flex-start;  /* changed from center → checkbox tops out with first label line when the label includes a description */
  gap: 10px;
  padding: 10px 0;
  cursor: pointer;
}
.field-checkbox input[type="checkbox"] {
  width: 16px; height: 16px;
  flex-shrink: 0;
  accent-color: var(--accent);
  cursor: pointer;
  /* Small top offset so the checkbox vertically centers on the first label line, not
     on the entire flex item (which would be off-center when a description is present). */
  margin-top: 2px;
}
.field-checkbox-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 14px;
  color: var(--text);
  font-weight: 500;
}
.field-checkbox-desc {
  font-size: 12px;
  font-weight: 400;
  color: var(--text-muted);
  line-height: 1.5;
}

/* Form-level action area — used on Profile to host the single "Save changes" button
   below all profile-card sections. Padding pulls it slightly away from the last card. */
.profile-actions {
  margin-top: 16px;
}

.profile-divider {
  border: none;
  border-top: 1px solid var(--line);
  margin: 18px 0;
}

/* Visibility radio group used on the cover editor */
.visibility-group {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin-bottom: 16px;
}
.visibility-option {
  display: block;
  cursor: pointer;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  transition:
    border-color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease);
}
.visibility-option:hover {
  border-color: var(--line-strong);
}
.visibility-option input[type="radio"] {
  position: absolute; opacity: 0; pointer-events: none;
}
.visibility-option:has(input:checked) {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.visibility-option-title {
  display: block;
  font-weight: 500;
  font-size: 14px;
  color: var(--text);
  margin-bottom: 2px;
}
.visibility-option-desc {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.4;
}

/* Sidebar separator + private-password sub-panel on the cover editor */
.side-divider {
  border: none;
  border-top: 1px solid var(--line);
  margin: 24px 0 18px;
}
.password-panel {
  margin-top: 6px;
  transition:
    opacity var(--dur) var(--ease),
    max-height var(--dur) var(--ease);
}
.password-panel.is-hidden {
  display: none;
}
.clear-pw-btn {
  font-size: 12px;
  margin-top: -8px;
}

/* Form-actions footer at the bottom of the cover editor's left column. Top border + spacing
   creates a clear visual separation between the editable fields above and the commit
   action below, so the user always knows which button "applies everything they just changed". */
.cover-form-actions {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

/* Input with a trailing action button (e.g. password show/hide eye toggle).
   Wraps the input + button so the button overlays the right edge. */
.input-with-action {
  position: relative;
  display: block;
}
.input-with-action input {
  width: 100%;
  padding-right: 44px;     /* leave room for the eye button so text doesn't overlap */
}
.input-action-btn {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  background: transparent;
  border: none;
  width: 32px; height: 32px;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text-muted);
  display: inline-flex; align-items: center; justify-content: center;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.input-action-btn:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.input-action-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Visual cues for required vs informational labels */
.field-required {
  color: var(--accent, #c0392b);
  font-weight: 600;
  margin-left: 2px;
}
.field-help-inline {
  color: var(--text-muted);
  font-weight: 400;
  font-size: 12px;
  margin-left: 4px;
}

/* Briefly flash an input red on validation error */
.input-error {
  border-color: #d04848 !important;
  box-shadow: 0 0 0 3px rgba(208, 72, 72, .15) !important;
  animation: shake .22s cubic-bezier(.36, .07, .19, .97);
}
@keyframes shake {
  10%, 90% { transform: translateX(-1px); }
  20%, 80% { transform: translateX(2px); }
  30%, 50%, 70% { transform: translateX(-3px); }
  40%, 60% { transform: translateX(3px); }
}

/* Visible-on-card "Private" badge for the dashboard gallery list */
.gallery-private-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-secondary);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  margin-left: 8px;
  vertical-align: middle;
}

/* OTP-specific bits */
.otp-target {
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 13px;
  display: flex; align-items: center; gap: 8px;
}
.otp-target-label {
  color: var(--text-muted);
}
.otp-target strong {
  color: var(--text);
  font-weight: 500;
}
.otp-input {
  font-variant-numeric: tabular-nums;
  letter-spacing: .5em !important;
  text-align: center;
  font-size: 22px !important;
  font-weight: 500;
  padding: 14px 14px !important;
  /* The letter-spacing makes a trailing gap; pull text left to re-center visually */
  padding-left: 1.3em !important;
}
.otp-secondary {
  margin-top: 14px;
  text-align: center;
  font-size: 13px;
  color: var(--text-muted);
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.dot-sep { color: var(--line-strong); }

/* ---------- DASHBOARD APP SHELL ---------- */
/*
  The dashboard uses a different chrome than the public pages:
  a full-height grey sidebar pinned to the left, and a white canvas
  for all page content. The root layout's top nav is hidden via
  ViewData["HideChrome"] = true on the dashboard.
*/
.page-dashboard { background: var(--surface-alt); }
.page-dashboard .page-main { min-height: 100vh; }

.dashboard-main { padding: 0; max-width: none; }

/* ===== App shell =====
   Two-column grid: 260px sidebar + flexible main. The shell is min-height:100vh so it
   always fills at least the viewport, and grows taller when main content is long. The
   document itself scrolls (no overflow:hidden on the shell) so position:sticky and
   position:fixed elements inside main content (e.g., cover-editor-side, slideshow
   pills) work naturally relative to the document.

   Earlier iteration tried scroll-confined-to-main (.app-main { overflow-y: auto })
   which broke sticky positioning inside main and caused the cover page's save button
   to render off-screen at the bottom of its sticky aside container. */
.app-shell {
  display: grid;
  grid-template-columns: 260px 1fr;
  min-height: 100vh;
}

/* ===== Sidebar =====
   `position: sticky` keeps the sidebar visible while the document scrolls.
   `align-self: start` is the key bit: without it, the grid cell stretches the sidebar
   to match the main column's height (because grid items default to stretch). That makes
   the sticky behavior moot — the element would be taller than the viewport and never
   need to stick. With align-self:start, the sidebar shrinks to its own content height
   and sticky positioning takes over for scroll.

   The min-height ensures the sidebar's gradient/border visually extends to the bottom
   of the viewport on short content while not stretching infinitely tall. */
.app-side {
  background: var(--surface-sidebar);
  border-right: 1px solid rgba(0, 0, 0, .08);
  padding: 24px 24px 20px;
  display: flex; flex-direction: column;
  gap: 18px;
  position: sticky;
  top: 0;
  align-self: start;
  min-height: calc(100vh - 56px);
  max-height: calc(100vh - 56px);
  overflow-y: auto;
  /* The 56px reserves space for the fixed site-footer at the bottom of the viewport. */
}

.side-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  margin-bottom: 6px;
}
.side-brand:hover { color: var(--text); }
.side-brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  color: var(--text);
  flex-shrink: 0;
}
.side-brand-mark img,
.side-brand-mark svg { width: 100%; height: 100%; display: block; object-fit: contain; }
.side-brand-name {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 15px;
  letter-spacing: -.005em;
  color: var(--text);
}

.side-back {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .22em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin-bottom: 4px;
}
.side-back:hover { color: var(--text); }

.side-gallery-title {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: .14em;
  color: var(--text);
  line-height: 1.2;
  padding-right: 24px;
}
.side-gallery-title--muted {
  color: var(--text-secondary);
  font-size: 14px;
  letter-spacing: .18em;
}

.side-status {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--text-secondary);
  font-size: 12px;
  letter-spacing: .04em;
}
.side-status-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--text-secondary);
  display: inline-block;
}

.side-nav {
  display: flex; flex-direction: column;
  gap: 2px;
  margin-top: 10px;
}
.nav-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  margin: 0 -12px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: 14px;
  font-weight: 500;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
  position: relative;
  text-align: left;
}
.nav-link .nav-icon {
  width: 18px; height: 18px;
  flex-shrink: 0;
  color: currentColor;
  opacity: .85;
}
.nav-link span { flex: 1; }
.nav-link:hover {
  background: rgba(255, 255, 255, .35);
  color: var(--text);
}
.nav-link.active {
  color: var(--text);
  background: rgba(255, 255, 255, .55);
}
.nav-link.active .nav-icon { opacity: 1; }
.nav-link.active::before {
  content: "";
  position: absolute;
  left: -24px; top: 10px; bottom: 10px;
  width: 2px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}

/* Profile link tooltip — shows the logged-in user's name on hover/focus. Pure CSS, no JS.
   Positioned to the RIGHT of the link (the link sits at the bottom-left of the sidebar;
   showing above would clip on short viewports, and below would clip off the bottom of
   the screen). The native title attribute is also set as a fallback so the info is still
   accessible if CSS doesn't load. */
.nav-profile {
  position: relative;
}
.nav-profile::after {
  content: attr(data-user-tooltip);
  position: absolute;
  left: calc(100% + 12px);
  top: 50%;
  transform: translateY(-50%) translateX(-4px);
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: var(--radius-xs);
  background: var(--text);
  color: var(--surface);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .02em;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
  z-index: 50;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .14);
}
.nav-profile:hover::after,
.nav-profile:focus-visible::after {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}
/* Suppress the native browser tooltip (title attribute) while our custom one is visible —
   otherwise the user gets two tooltips, one delayed and looking jankier. The title stays
   for screen readers (which use accessibility tree, not the visible bubble) and for
   no-CSS fallback. */
.nav-profile:hover { /* keeps title attribute for a11y but our ::after takes over visually */ }

.nav-separator {
  height: 1px;
  background: rgba(0, 0, 0, .12);
  margin: 10px -12px;
}

/* Bottom-anchored footer */
.side-footer {
  margin-top: auto;
  padding-top: 20px;
  display: flex; flex-direction: column;
  gap: 4px;
}
.side-cta {
  letter-spacing: .12em;
  text-transform: uppercase;
  font-size: 12px;
  padding: 14px 18px;
}
.side-cta + .side-cta { margin-top: 6px; }
.side-cta + .nav-separator { margin-top: 14px; }

/* Sign-out styled as a nav-link button — same width, padding, icon alignment */
.side-signout-form { margin: 0; }
.side-signout-link {
  background: none;
  border: none;
  cursor: pointer;
  width: 100%;
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  /* Re-derives all .nav-link styling via the class on the button */
}

/* ===== Main canvas ===== */
.app-main {
  background: var(--surface);
  padding: 44px 48px 80px;
  min-width: 0;            /* prevents grid blowout when main content is wider than column */
}

.page-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; margin-bottom: 32px;
}
.page-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 32px;
  letter-spacing: -.02em;
  margin: 0 0 4px;
  color: var(--text);
}
.page-sub {
  color: var(--text-secondary);
  margin: 0; font-size: 15px;
}

.section-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .18em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin: 0 0 16px;
}

.muted { color: var(--text-muted); font-size: 13px; }

/* ---------- GALLERY CARDS (overview) ---------- */
.gallery-grid {
  display: grid; gap: 22px;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.gallery-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-xs);
  position: relative;        /* anchor for the drag handle overlay */
  transition:
    transform var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}
.gallery-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow);
  border-color: var(--line-strong);
}

/* Drag handle overlay on gallery cards. Hidden at rest (zero opacity), revealed on hover —
   keeps the cards looking clean when the user isn't actively reorganizing, but is a tap
   target on touch devices too (touchstart triggers the same hover-equivalent state via
   the :focus-within fallback below). */
.gallery-drag-handle {
  position: absolute;
  top: 8px; left: 8px;
  z-index: 2;
  width: 32px; height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  border-radius: var(--radius-pill);
  cursor: grab;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  /* iOS Safari needs this hint to prevent text selection during drag */
  -webkit-user-select: none;
  user-select: none;
  touch-action: none;
}
.gallery-card:hover .gallery-drag-handle,
.gallery-card:focus-within .gallery-drag-handle {
  opacity: 1;
}
.gallery-drag-handle:hover { background: rgba(0, 0, 0, .75); }
.gallery-drag-handle:active { cursor: grabbing; }

/* On touch devices there's no hover — fall back to always-visible-but-subtle. The media
   query catches devices without fine-pointer hover, which is the standard way to detect
   touch-primary devices. */
@media (hover: none) {
  .gallery-drag-handle { opacity: .85; }
}

/* While dragging a gallery card. SortableJS adds these classes during the drag. */
.gallery-sortable-ghost {
  opacity: .35;
  background: var(--accent-soft);
}
.gallery-sortable-chosen { cursor: grabbing; }
.gallery-sortable-drag {
  cursor: grabbing;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .18);
  transform: scale(1.02);
}

/* Delete button on gallery card. Mirrors the drag-handle's hover-fade pattern but sits
   at the top-RIGHT and uses a danger-red hover state. The actual delete only fires after
   a confirmation modal so a misclick can't destroy a gallery. */
.gallery-delete-btn {
  position: absolute;
  top: 8px; right: 8px;
  z-index: 2;
  width: 32px; height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  border-radius: var(--radius-pill);
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.gallery-card:hover .gallery-delete-btn,
.gallery-card:focus-within .gallery-delete-btn {
  opacity: 1;
}
.gallery-delete-btn:hover {
  background: rgba(220, 53, 69, .85);   /* danger red */
}
@media (hover: none) {
  .gallery-delete-btn { opacity: .85; }
}
.gallery-cover {
  aspect-ratio: 4/3;
  background: var(--surface-alt) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  border-bottom: 1px solid var(--line-soft);
}
.gallery-cover-placeholder {
  font-size: 13px;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-style: italic;
}
.gallery-meta { padding: 16px 18px; }
.gallery-meta h3 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 18px;
  letter-spacing: -.01em;
  margin: 0 0 4px;
  color: var(--text);
}
.gallery-meta span {
  color: var(--text-muted);
  font-size: 13px;
}

/* ---------- EMPTY STATE ---------- */
.empty-state {
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  text-align: center;
  padding: 80px 32px;
}
.empty-state.inset { padding: 56px 32px; }
.empty-icon {
  font-size: 32px;
  color: var(--text-muted);
  margin-bottom: 12px;
}
.empty-state h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  margin: 0 0 8px;
  color: var(--text);
}
.empty-state p {
  color: var(--text-secondary);
  margin: 0 auto 20px;
  max-width: 440px;
}

/* ---------- DIALOG ---------- */
.app-dialog {
  border: none; padding: 0; background: transparent;
  max-width: 460px; width: calc(100% - 32px);
  border-radius: var(--radius-lg);
}
.app-dialog::backdrop {
  background: rgba(44, 43, 46, .35);
  backdrop-filter: blur(4px);
}
.app-dialog form {
  background: var(--surface);
  padding: 30px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--line);
}
.app-dialog h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  margin: 0 0 6px;
  color: var(--text);
}
.dialog-sub {
  color: var(--text-secondary);
  margin: 0 0 20px;
  font-size: 14px;
}
.dialog-actions {
  display: flex; justify-content: flex-end; gap: 10px;
  margin-top: 8px;
}

/* ---------- COVER EDITOR ---------- */
.cover-grid {
  display: grid; gap: 32px;
  grid-template-columns: 1.1fr 1fr;
}
.cover-drop {
  margin-top: 8px;
  border: 2px dashed var(--line-strong);
  border-radius: var(--radius);
  aspect-ratio: 3/2;
  background: var(--surface-alt);
  cursor: pointer;
  overflow: hidden;
  transition:
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease);
}
.cover-drop:hover,
.cover-drop.drag {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.cover-preview {
  width: 100%; height: 100%;
  background: center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary);
}
.cover-drop-inner { text-align: center; padding: 20px; }
.cover-drop-icon {
  font-size: 26px;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.cover-drop-inner p { margin: 4px 0; }

.template-list {
  /* Retained only for backward compat; the Cover editor now uses .template-strip/.template-row */
  display: grid; gap: 14px;
  grid-template-columns: repeat(2, 1fr);
}

/* ---------- UPLOAD PAGE ---------- */
.section-block {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 22px;
  box-shadow: var(--shadow-xs);
}
/* Reduce vertical padding when the section is collapsed so the heading row sits at a
   compact, predictable height — every collapsed section is the same height regardless
   of what's inside, which makes the page scan cleanly. */
.section-block.is-collapsed { padding: 16px 24px; }
.section-block.is-collapsed .section-head { margin-bottom: 0; }

.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-bottom: 16px;
}

/* Collapse/expand chevron button. Sits as the last item in .section-head. The chevron
   rotates 90° when the section is collapsed, so the icon itself becomes the affordance
   for "click to expand". Sized larger than typical icon buttons so it's an obvious target
   even at a glance — collapsing is a primary action on this page when users have many
   sections to scan. */
.section-collapse-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  margin-left: 10px;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.section-collapse-toggle:hover {
  background: var(--surface-alt);
  color: var(--text);
}
.section-chevron {
  width: 20px;
  height: 20px;
  transition: transform var(--dur) var(--ease);
}
.section-block.is-collapsed .section-chevron {
  /* When collapsed, the chevron points right (closed state hints "click me to open"). */
  transform: rotate(-90deg);
}

/* Body wrapper that collapses. We use max-height + opacity for the transition rather
   than grid-template-rows because the body has multiple direct children (description,
   upload-drop, thumb-grid) — each generates an implicit grid row, so 0fr on the explicit
   template doesn't shrink them. max-height with a large enough ceiling animates reliably.
   2000px covers any realistic photo grid; if a user has more than ~50 rows of thumbs the
   expand transition gets faster than expected, but that's a non-issue in practice. */
.section-body {
  overflow: hidden;
  max-height: 2000px;
  transition: max-height var(--dur) var(--ease),
              opacity   var(--dur) var(--ease),
              margin    var(--dur) var(--ease);
}
.section-block.is-collapsed .section-body {
  max-height: 0;
  opacity: 0;
  margin: 0;
  pointer-events: none;
}

.section-head h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 22px;
  letter-spacing: -.01em;
  margin: 0;
  color: var(--text);
}

/* Inline-editable section description (upload page). Click to edit, blur saves.
   We keep the height predictable across the two states so the layout doesn't
   jump as the user adds/removes a description. */
.section-desc {
  margin: -8px 0 16px;
  min-height: 22px;
}
.section-desc-text {
  font-size: 14px;
  color: var(--text-secondary);
  line-height: 1.5;
  margin: 0;
  padding: 4px 6px;
  border-radius: var(--radius-xs);
  cursor: pointer;
  display: inline-block;
  transition: background var(--dur-fast) var(--ease);
}
.section-desc-text:hover { background: var(--surface-alt); }
.section-desc-add {
  font-size: 13px;
  color: var(--text-muted);
  padding: 4px 6px;
  margin-left: -6px;     /* visually align with the section heading */
}
.section-desc-add:hover { color: var(--text-secondary); }

/* Click-to-rename section heading. Pencil icon sits inline after the name; appears
   slightly more prominent on hover so the affordance is discoverable without being
   intrusive at rest. The whole h2 is clickable, not just the pencil. */
.section-name-edit {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  padding: 2px 6px;
  margin: -2px -6px;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease);
}
.section-name-edit:hover {
  background: var(--surface-alt);
}
.section-name-pencil {
  color: var(--text-muted);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.section-name-edit:hover .section-name-pencil {
  opacity: 1;
  color: var(--text-secondary);
}
.upload-drop {
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius);
  /* Compact horizontal layout: icon + label + browse link sit on one line.
     Was a tall vertical box with 40px padding (~120px total height); now ~52px. */
  padding: 12px 18px;
  background: var(--surface-alt);
  cursor: pointer;
  transition:
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease);
  margin-bottom: 16px;
}
.upload-drop:hover,
.upload-drop.drag {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.upload-drop-inner {
  /* Single-row flex: icon, label group, hint pushed to right end. */
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--text-secondary);
  flex-wrap: wrap;       /* gracefully wrap the hint onto a second line on phones */
}
.upload-drop-icon {
  font-size: 18px;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.upload-hint {
  font-size: 11px;
  color: var(--text-muted);
  margin: 0 0 0 auto;    /* push to the right end of the row */
  letter-spacing: .02em;
}
@media (max-width: 640px) {
  /* On phones, restore vertical-ish layout for legibility but keep it tight. */
  .upload-drop { padding: 14px 16px; }
  .upload-hint { margin-left: 0; flex-basis: 100%; }
}

/* ---------- Upload progress (set inline by upload.js while a request is in flight) ---------- */
.upload-drop.uploading {
  cursor: progress;
  pointer-events: none;          /* hard lock — no re-drops while uploading */
  border-color: var(--accent);
  background: var(--accent-soft);
}
.upload-progress-inner {
  width: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.upload-progress-label {
  font-size: 13px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.upload-progress-bar {
  width: min(360px, 80%);
  height: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.upload-progress-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: var(--radius-pill);
  transition: width 120ms linear;
}
.upload-progress-pct {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .04em;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.thumb-grid {
  display: grid; gap: 8px;
  /* Min box width is the controlling parameter — 175px is half of the previous 350px,
     putting boxes at about 1.25× the original 140px size. auto-fill reflows the column
     count to match: at a typical desktop content width we get roughly 7 boxes per row.
     Aspect ratio (square) is unchanged so layout reads the same. */
  grid-template-columns: repeat(auto-fill, minmax(175px, 1fr));
}
.thumb {
  position: relative;
  aspect-ratio: 1;             /* uniform square boxes regardless of photo orientation */
  border-radius: var(--radius-sm);
  overflow: hidden;
  /* Slightly darker fill than the page background so the letterboxed area around
     non-square photos reads as "frame" rather than missing content. */
  background: var(--surface-alt);
  border: 1px solid var(--line-soft);
}
.thumb img {
  /* object-fit: contain so the FULL photo is visible inside the square box — never
     cropped, never stretched. Wide photos get top/bottom letterboxing; tall photos
     get side letterboxing. This matches the user's "show actual image" intent: a
     portrait should look like a portrait inside the thumb, not a center-cropped square. */
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
}
.thumb-del {
  position: absolute; top: 6px; left: 6px;
  background: rgba(44, 43, 46, .75);
  color: var(--text-on-accent);
  border: none; border-radius: 50%;
  width: 24px; height: 24px;
  cursor: pointer;
  font-size: 16px; line-height: 1;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
  z-index: 2;
}
.thumb:hover .thumb-del { opacity: 1; }

/* Caption affordance on each thumb. Visible always (so users discover the feature),
   shows + when no caption set, dot when there is one. Click → prompt to edit. */
.thumb-caption-btn {
  position: absolute; bottom: 6px; left: 6px;
  background: rgba(44, 43, 46, .75);
  color: var(--text-on-accent);
  border: none; border-radius: 999px;
  height: 22px;
  min-width: 22px;
  padding: 0 8px;
  cursor: pointer;
  font-size: 12px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.thumb:hover .thumb-caption-btn { opacity: 1; }
.thumb-caption-add { font-size: 16px; line-height: 1; }
.thumb-caption-marker { font-size: 8px; opacity: .9; }
.thumb-caption-overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 18px 10px 8px;
  background: linear-gradient(to top, rgba(20, 18, 15, .85), rgba(20, 18, 15, 0));
  color: #fafaf8;
  font-size: 11px;
  line-height: 1.3;
  pointer-events: none;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- SHARE PAGE ---------- */
.share-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 24px;
  box-shadow: var(--shadow-xs);
}
.share-form {
  display: flex; gap: 12px; align-items: flex-end;
}
.share-form .field { flex: 1; margin: 0; }

/* The share-form's bottom edge is the flex row of inputs/button — not an input
   baseline. The default .field-help has `margin-top: -6px` to sit tight under
   inputs, which here would visually merge with the dropdowns. Reset it. */
.share-form + .field-help {
  margin-top: 12px;
}
.share-list {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  box-shadow: var(--shadow-xs);
}
.share-items { list-style: none; padding: 0; margin: 0; }
.share-item {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; padding: 14px 0;
  border-bottom: 1px solid var(--line-soft);
}
.share-item:last-child { border-bottom: none; }
.share-scope {
  font-weight: 500; font-size: 14px;
  color: var(--text);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.share-status {
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .04em;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-alt);
  color: var(--text-secondary);
  border: 1px solid var(--line);
}
.share-status.expired {
  background: var(--danger-soft);
  color: var(--danger);
  border-color: var(--danger-soft);
}
.share-item.expired .share-url {
  text-decoration: line-through;
  color: var(--text-muted);
}
.share-url {
  color: var(--text-secondary);
  font-size: 13px;
  word-break: break-all;
  border-bottom: 1px dashed var(--line-strong);
}
.share-url:hover { color: var(--accent); }
.share-actions {
  display: flex; align-items: center; gap: 8px;
  flex-shrink: 0;
}

/* Trash button — neutral by default, red tint on hover so accidental clicks read intent.
   Same .btn-ghost base; this just overlays a hover color cue. */
.share-delete-btn {
  padding: 7px 8px;
  color: var(--text-muted);
}
.share-delete-btn:hover {
  color: #c53030;
  background: rgba(197, 48, 48, .08);
}

/* Soft fade when a row is removed via deleteShare(). */
.share-item-removing {
  opacity: 0;
  transform: translateX(8px);
  transition:
    opacity .2s var(--ease),
    transform .2s var(--ease);
}

/* Pencil icon that sits inline next to the expiry-date pill. Discoverable affordance
   for editing the date without making the user hunt for a separate action button. */
.share-expiry-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  margin-left: 4px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 50%;
  color: var(--text-muted);
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
  vertical-align: middle;
}
.share-expiry-edit:hover {
  color: var(--text);
  background: var(--surface-alt);
}
.share-expiry-edit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* Toggle */
.toggle {
  position: relative;
  display: inline-block;
  width: 40px; height: 22px;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute; cursor: pointer; inset: 0;
  background-color: var(--line-strong);
  transition: background var(--dur) var(--ease);
  border-radius: var(--radius-pill);
}
.toggle-slider::before {
  content: ""; position: absolute;
  height: 16px; width: 16px;
  left: 3px; top: 3px;
  background-color: var(--surface);
  transition: transform var(--dur) var(--ease);
  border-radius: 50%;
  box-shadow: var(--shadow-xs);
}
.toggle input:checked + .toggle-slider { background-color: var(--accent); }
.toggle input:checked + .toggle-slider::before { transform: translateX(18px); }

/* ---------- TOAST ---------- */
.toast {
  position: fixed;
  bottom: 24px; left: 50%;
  transform: translateX(-50%) translateY(100px);
  background: var(--accent);
  color: var(--text-on-accent);
  padding: 11px 20px;
  border-radius: var(--radius-pill);
  font-size: 14px; font-weight: 500;
  box-shadow: var(--shadow-lg);
  z-index: 100;
  transition: transform var(--dur-slow) var(--ease);
}
.toast.show {
  transform: translateX(-50%) translateY(0);
}
.toast.error { background: var(--danger); }

/* ============================================================
   uiConfirm() — themed replacement for window.confirm().
   Same visual language as the login modal: blurred backdrop,
   rounded card, restrained typography, danger variant for the
   confirm button on destructive actions.
   ============================================================ */
.ui-confirm {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  animation: uiConfirmFadeIn 160ms var(--ease);
}
.ui-confirm[hidden] { display: none; }

.ui-confirm-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(20, 18, 15, .58);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.ui-confirm-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: var(--surface);
  border-radius: 16px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .25);
  padding: 28px 28px 22px;
  animation: uiConfirmCardIn 220ms cubic-bezier(0.22, 1, 0.36, 1);
}

.ui-confirm-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: -.005em;
  color: var(--text);
  margin: 0 0 10px;
}
.ui-confirm-message {
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.5;
  margin: 0 0 22px;
}

.ui-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
.ui-confirm-actions .btn {
  min-width: 96px;
}

/* Date prompt — reuses the .ui-confirm shell. Adds an inline label + date input
   between the title and the actions row. */
.ui-dateprompt-label {
  display: block;
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 600;
  margin: 4px 0 8px;
}
.ui-dateprompt-input {
  width: 100%;
  font: inherit;
  font-size: 15px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text);
  margin-bottom: 22px;
  outline: none;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ui-dateprompt-input:focus {
  border-color: var(--text-secondary);
  box-shadow: 0 0 0 3px rgba(56, 55, 58, 0.08);
}

/* Text prompt — same label style as the date prompt; input or textarea per `multiline`. */
.ui-textprompt-input {
  width: 100%;
  font: inherit;
  font-size: 14px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text);
  margin-bottom: 22px;
  outline: none;
  resize: vertical;          /* only meaningful for textarea; ignored on input */
  font-family: inherit;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.ui-textprompt-input:focus {
  border-color: var(--text-secondary);
  box-shadow: 0 0 0 3px rgba(56, 55, 58, 0.08);
}
textarea.ui-textprompt-input { min-height: 90px; }

/* Danger variant — red confirm button for destructive actions like deletion. */
.btn.btn-danger {
  background: #c53030;
  color: #fff;
  border-color: #c53030;
}
.btn.btn-danger:hover {
  background: #b02828;
  border-color: #b02828;
}
.btn.btn-danger:focus-visible {
  outline: 2px solid #c53030;
  outline-offset: 2px;
}

@keyframes uiConfirmFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes uiConfirmCardIn {
  from { transform: translateY(8px) scale(.98); opacity: 0; }
  to { transform: translateY(0) scale(1); opacity: 1; }
}

@media (max-width: 540px) {
  .ui-confirm-card {
    max-width: none;
    padding: 24px 22px 20px;
  }
  .ui-confirm-actions {
    flex-direction: column-reverse;
  }
  .ui-confirm-actions .btn {
    width: 100%;
  }
}

/* ---------- HELPERS ---------- */
.narrow-page {
  max-width: 520px; margin: 80px auto;
  padding: 32px; text-align: center;
}

/* ---------- RESPONSIVE ----------
   Breakpoints used throughout the app:
     - 960px: layout shifts (sidebar → top bar, two-column → one-column)
     - 720px: search bar removed from top nav (no room)
     - 640px: deeper tightening — padding cuts, font reductions
     - 480px: phone-only adjustments (full-width CTAs, hide non-essential nav)
*/
@media (max-width: 960px) {
  .hero { grid-template-columns: 1fr; padding: 48px 24px; }
  .hero-collage { height: 360px; }
  .features-grid { grid-template-columns: repeat(2, 1fr); }

  /* Collapse the app shell into a single column; sidebar becomes a top bar */
  .app-shell { grid-template-columns: 1fr; }
  .app-side {
    position: static;
    /* Explicit resets since the desktop rule pins the sidebar to position:fixed
       with width:260px. position:static disables top/left/width but being explicit
       avoids any specificity surprises if either rule is reordered later. */
    width: auto;
    top: auto; left: auto;
    height: auto;
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 16px 20px;
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    z-index: auto;
  }
  .side-brand,
  .side-back,
  .side-gallery-title,
  .side-status { margin: 0; }
  .side-nav {
    flex-direction: row;
    overflow-x: auto;
    margin-left: auto;
    gap: 4px;
  }
  .nav-link { margin: 0; padding: 8px 12px; white-space: nowrap; }
  .nav-link.active::before { display: none; }
  .nav-separator { display: none; }
  .side-footer {
    flex-direction: row;
    flex-basis: 100%;
    margin-top: 0;
    padding-top: 10px;
    border-top: 1px solid var(--line-soft);
  }
  .side-cta { flex: 1; }
  .app-main { padding: 24px 20px 80px; }

  .cover-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  /* Top nav: tighter horizontal padding, smaller gap. */
  .nav-inner { padding: 12px 16px; gap: 12px; }
  .brand-name { font-size: 15px; }
  .nav-text-link { font-size: 13px; padding: 6px 2px; }

  /* Hero: shrink padding and image. */
  .hero { padding: 36px 18px 24px; gap: 32px; }
  .hero-collage { height: 280px; }
  .hero-sub { font-size: 15px; }
  .hero-cta { width: 100%; }
  .hero-cta .btn { flex: 1; min-width: 0; }   /* both CTAs share row, equal width */

  /* Auth shell: reduce card padding so the form doesn't push outside the viewport. */
  .auth-card { padding: 28px 20px; border-radius: var(--radius); }

  /* Login modal: full-screen on phones, not centered card. */
  .login-modal-card {
    max-width: 100%;
    width: 100%;
    margin: 0;
    border-radius: 0;
    min-height: 100vh;
    max-height: 100vh;
    overflow-y: auto;
  }

  /* Share rows stack vertically on phone. */
  .share-form { flex-direction: column; align-items: stretch; }
  .share-item { flex-direction: column; align-items: flex-start; gap: 12px; }
  .share-actions { width: 100%; justify-content: flex-start; flex-wrap: wrap; }

  /* Dashboard sidebar: smaller padding so the gallery title fits. */
  .app-main { padding: 20px 16px 80px; }

  /* Tenant header: actions cluster wraps below the photographer name on phones. */
  .tenant-header-actions { position: static; margin-top: 16px; gap: 16px; }
  .tenant-header {
    text-align: center;
    padding: 32px 16px 24px;
  }
  .tenant-search { max-width: 100%; }

  /* Section bar: tighter padding, smaller font. */
  .section-bar-inner { padding: 12px 16px; }
  .section-bar-brand .brand-name { font-size: 13px; }
  .section-bar-links .section-link { font-size: 12px; padding: 8px 10px; }

  /* Legal pages: tighter for readability. */
  .legal-page { padding: 32px 18px 48px; }
  .legal-header h1 { font-size: 24px; }

  /* Cards/tables more compact. */
  .features-grid { grid-template-columns: 1fr; gap: 16px; }
  .gallery-card-meta { padding: 14px 16px; }

  /* Lightbox controls already handled in shared.css; nothing to add here. */
}

@media (max-width: 480px) {
  /* Hide non-essential nav items (Login/Register are accessible via the auth modal trigger
     elsewhere). Keep brand + primary CTA. */
  .nav-links { gap: 8px; }
  .nav-text-link.nav-text-link-secondary { display: none; }

  /* Make every CTA in the hero full-width on tiny screens — easier tap targets. */
  .hero-cta { flex-direction: column; }
  .hero-cta .btn { width: 100%; }

  /* Auth shell: edge-to-edge cards. */
  .page-auth .page-main { padding: 24px 12px; }
  .auth-shell { max-width: 100%; }

  /* Footer: stack everything centered on tiny phones. */
  .site-footer { padding: 14px 14px; }
}

/* ============================================================
   Legal pages — Terms & Conditions, Privacy Policy.
   Centered single-column reading layout with restrained typography.
   ============================================================ */
.legal-page {
  max-width: 760px;
  margin: 0 auto;
  padding: 64px 28px 96px;
}
.legal-header {
  text-align: center;
  margin-bottom: 56px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 32px;
}
.legal-header h1 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 36px;
  letter-spacing: -.015em;
  color: var(--text);
  margin: 0 0 8px;
}
.legal-meta {
  font-size: 12px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0;
}
.legal-section { margin-bottom: 36px; }
.legal-section h2 {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 19px;
  color: var(--text);
  margin: 0 0 14px;
}
.legal-section p {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-secondary);
  margin: 0 0 12px;
}
.legal-section ul {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-secondary);
  padding-left: 24px;
  margin: 0 0 12px;
}
.legal-section li { margin-bottom: 6px; }
.legal-section a {
  color: var(--text);
  border-bottom: 1px solid var(--line);
  transition: border-bottom-color var(--dur-fast) var(--ease);
}
.legal-section a:hover { border-bottom-color: var(--text); }

.footer-link {
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease);
}
.footer-link:hover { color: var(--text); }

@media (max-width: 640px) {
  .legal-page { padding: 40px 20px 60px; }
  .legal-header h1 { font-size: 28px; }
}
