/* ===========================================================================
   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.
*/
/* Body flex layout so the footer can sit at the bottom of short pages without being
   position:fixed (which made it overlay content while scrolling — not what we want). */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
body > main,
body > .page-main {
  flex: 1 0 auto;     /* grow to fill, don't shrink — pushes footer to viewport bottom on short pages */
}
body > .site-footer {
  flex: 0 0 auto;     /* footer keeps its natural height */
}

.site-footer {
  /* Was position:fixed — switched to normal flow so the footer scrolls with the page.
     On short pages the body flex column above pushes it to the viewport bottom; on
     long pages it sits below content and the user reaches it by scrolling. */
  border-top: 1px solid var(--line);
  padding: 12px 32px;
  color: var(--text-muted);
  font-size: 13px;
  background: var(--surface-alt);
}
/* No padding-bottom reservation needed anymore — footer is in normal flow now. */

.footer-inner {
  max-width: 1680px;
  margin: 0 auto;
  /* Three equal-weight columns so the center column actually centers on the page
     regardless of how wide the left/right text is. justify-content alone would push
     the center off-center whenever the side columns differ in width. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
}
.footer-left {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-self: start;
}
.footer-center {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  justify-self: center;
  text-align: center;
}
.footer-right {
  display: flex;
  align-items: center;
  justify-self: end;
}
.footer-sep { opacity: .5; }

@media (max-width: 720px) {
  /* Stack on narrow screens — preserve order: copyright, links, version. */
  .footer-inner {
    grid-template-columns: 1fr;
    text-align: center;
  }
  .footer-left, .footer-center, .footer-right {
    justify-self: center;
  }
}

/* Build/version pill — used by the right column. Kept the same chip shape so the
   visual rhythm of the footer is unchanged from the previous layout. */
/* 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,
.footer-version {
  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; }
/* Label-text styling. Excludes the validation-message spans by class — without this
   exclusion the rule repaints "Full name is required." (rendered as a span by the
   asp-validation-for tag helper) in the muted label color, defeating the red error
   styling defined further down. */
.field > span:not(.field-validation-error):not(.field-validation-valid):not(.field-error):not(.field-hint) {
  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,
.field-validation-error,
.field > span.field-validation-error,
.field > span.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,
.field-validation-error:empty { margin-top: 0; }
/* The tag helper applies field-validation-valid on the same element when there are no
   errors — keep it invisible so the layout doesn't reserve space for an empty error.
   Includes the .field > span override for the same specificity reason as the error rule. */
.field-validation-valid,
.field > span.field-validation-valid {
  display: none;
}
.field-error.is-success,
.field > span.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,
.field > span.field-error.is-error { color: var(--danger); }

/* When a field is invalid the tag helper adds .input-validation-error to the input
   itself. Give it a red border + soft red ring so the field is clearly flagged in
   addition to the message below it. */
.field input.input-validation-error,
.field select.input-validation-error,
.field textarea.input-validation-error {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px var(--danger-soft);
}

/* 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:not(.field-validation-error):not(.field-validation-valid):not(.field-error):not(.field-hint) {
  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:not(.field-validation-error):not(.field-validation-valid):not(.field-error):not(.field-hint) {
  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 {
  /* Icon-only badge after the "Private" text was removed per spec. Sized just big
     enough to hold the 11px lock SVG with a small breathing-room border. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  background: var(--surface-alt);
  border: 1px solid var(--line);
  padding: 3px;
  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 {
  /* Three-column grid: left zone (drag + name) gets a flexible track, center zone
     (count) sizes to its content and lives exactly in the middle, right zone (delete
     + collapse) gets a flexible track justified to its end. The 1fr/auto/1fr pattern
     guarantees the center column actually centers on the row regardless of how wide
     the side zones are. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.section-head-left {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;          /* allow long section names to truncate rather than overflow */
}
.section-head-center {
  /* Auto-sized column — the count text sits naturally in the middle of the row. */
  text-align: center;
}
.section-head-right {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-self: end;
}

/* Allow the section name to truncate gracefully if it's longer than the left zone.
   Without min-width:0 on the parent the grid would refuse to shrink the column. */
.section-head-left .section-name-edit {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Collapse/expand chevron button. Sits as the last item in .section-head-right.
   The chevron rotates 90° when the section is collapsed, so the icon itself becomes
   the affordance for "click to expand". */
.section-collapse-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  /* margin-left removed — the right-zone gap handles spacing between delete + collapse. */
  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: 10px;
  /* Each thumb is LOCKED to 220px wide regardless of viewport size — what changes
     with screen width is only the COLUMN COUNT (more on wide monitors, fewer on
     narrow). Previously the second arg was `1fr`, which made each thumb stretch to
     fill the row → thumb size varied wildly between screen resolutions. Fixed-pixel
     second arg eliminates that variance. Bumped from 175px to 220px per spec
     ("little bigger"). */
  grid-template-columns: repeat(auto-fill, 260px);
  /* Center the row when there's leftover space rather than packing to the left edge,
     so partial rows look intentional rather than mis-aligned. */
  justify-content: start;
}
.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 {
  /* Per spec: expired links show in red TEXT only — no filled badge, no border.
     Reads as a red label inline with the URL rather than a chrome'd pill. */
  background: transparent;
  color: var(--danger);
  border-color: transparent;
  padding: 0;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
}
.share-item.expired {
  /* Default row background — no danger-soft tint. The red text below is enough to
     mark the row as expired without coloring the entire chrome. */
  background: var(--surface);
  border-color: var(--line);
}
.share-item.expired .share-url {
  /* URL text in red instead of struck-through-muted. Per spec: "show link text and
     expired date in red color only". */
  color: var(--danger);
  text-decoration: none;
}
.share-item.expired .share-scope {
  /* Default scope label color — only the URL + the expired badge are red. */
  color: var(--text);
}
.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; }
}

/* ---------- PASSWORD TOGGLE (show/hide eye icon) ----------
   Wraps any input[type=password] at runtime so the eye-icon button overlays on
   the input's right edge. .field already constrains the input to 100% width;
   the wrapper sits inside .field and the button is absolutely positioned. */
.password-toggle-wrap {
  position: relative;
  display: block;
}
.password-toggle-wrap input {
  /* Reserve right-edge padding so the user's typed password doesn't slide under
     the eye icon. The button is 36px wide; 36 + 8 padding gives a comfortable
     gap before the icon. */
  padding-right: 44px !important;
}
.password-toggle-btn {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  width: 36px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-xs);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.password-toggle-btn:hover {
  color: var(--text);
  background: var(--surface-alt);
}
.password-toggle-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
}

/* Skeleton loading state shown briefly while the modal fetches its form partial.
   Centered, muted — just a visual placeholder so the modal isn't completely empty
   for the few hundred ms between open and form arrival. */
.modal-loading {
  padding: 40px 20px;
  text-align: center;
  color: var(--text-muted);
  font-size: 14px;
}

/* ---------- SECTION DELETE BUTTON ----------
   Trash icon button in the section header. Visually quieter by default; warms to the
   danger color on hover. Same shape as the collapse toggle for visual consistency. */
.section-delete-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  margin-left: 4px;
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-xs);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.section-delete-btn:hover {
  background: var(--danger-soft);
  color: var(--danger);
}
.section-delete-btn:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: -1px;
}

/* ---------- SECTION DELETE CHOOSER DIALOG ----------
   Custom dialog reusing the ui-confirm CSS aesthetic. The two-option chooser uses radio
   buttons with full-card click targets — clicking anywhere on a row selects that option.
   The recommended row gets a small "Recommended" tag and slightly stronger border. */
.section-delete-card {
  /* Wider than the default ui-confirm card to accommodate the two option rows + select. */
  max-width: 460px;
}
.section-delete-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 20px 0 24px;
}
.section-delete-option {
  display: flex;
  gap: 12px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.section-delete-option:hover {
  background: var(--surface-alt);
}
.section-delete-option input[type="radio"] {
  margin-top: 3px;
  flex-shrink: 0;
  accent-color: var(--text);
  cursor: pointer;
}
/* When a radio inside is checked, give the row a stronger border + soft fill so the
   selected option reads unambiguously. :has() is supported across all evergreen browsers
   as of 2024+; if we ever need to support pre-2023 Safari, we can swap to a JS class. */
.section-delete-option:has(input:checked) {
  border-color: var(--text);
  background: var(--surface-alt);
}
.section-delete-option-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.section-delete-option-title {
  font-weight: 500;
  font-size: 14px;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.section-delete-option-desc {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.5;
}
.section-delete-recommended-tag {
  display: inline-block;
  padding: 2px 8px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .04em;
  text-transform: uppercase;
  background: #f4ecd8;
  color: #6b5a2a;
  border-radius: var(--radius-pill);
}
.section-delete-move-target {
  /* Inherit the project's standard input styling — same height/border as form fields
     so the dialog doesn't look bolted on. */
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text);
  font-size: 14px;
  font-family: inherit;
  width: 100%;
  cursor: pointer;
}
.section-delete-move-target:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* ---------- PHOTO SHARE LIST (gallery Share page) ----------
   Single-photo share rows visually distinct from gallery/section ones by having a
   thumbnail leading the row. Layout otherwise mirrors .share-item. */
.photo-share-item .photo-share-main {
  display: flex;
  align-items: center;
  gap: 12px;
}
.photo-share-thumb {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--radius-xs);
  flex-shrink: 0;
  background: var(--surface-alt);
}
.share-status.muted {
  color: var(--text-muted);
  background: transparent;
  border-color: transparent;
}
.muted-small {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Photo-share modal (the create dialog from the thumb's share icon). The card uses
   the same .ui-confirm-card shell but is sized for the form + post-create states. */
.share-photo-card {
  max-width: 480px;
}

/* ---------- COVER ACTION ROW (Cover page) ---------- */
.cover-action-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.cover-action-row .btn {
  flex: 1;
  min-width: 140px;
}

/* Outline danger button — used for cover-remove. Less alarming than solid red but still
   clearly destructive. Hovers into the soft-red fill that the existing .danger-soft
   surface paints elsewhere. */
.btn-danger-outline {
  background: transparent;
  border: 1px solid var(--danger);
  color: var(--danger);
}
.btn-danger-outline:hover {
  background: var(--danger-soft);
}

/* ---------- PROFILE TABS ----------
   Three tabs (Profile details, Login details, Reset password) atop the Profile page.
   Each tab owns its own form so the at-least-one-checked invariant for login security
   only fires when that specific form is submitted. */
.profile-tabs {
  margin-top: 8px;
}
.profile-tab-list {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 24px;
  overflow-x: auto;
  /* Hide scroll bars on overflow — narrow screens scroll horizontally if needed but
     we don't show a track. */
  scrollbar-width: none;
}
.profile-tab-list::-webkit-scrollbar { display: none; }

.profile-tab {
  background: transparent;
  border: 0;
  padding: 12px 18px;
  font: 500 14px var(--font-ui);
  color: var(--text-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;     /* overlap the list's bottom border so active underline meets it cleanly */
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
  white-space: nowrap;
}
.profile-tab:hover {
  color: var(--text);
}
.profile-tab.is-active {
  color: var(--text);
  border-bottom-color: var(--text);
}
.profile-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

.profile-tab-panel {
  display: none;
}
.profile-tab-panel.is-active {
  display: block;
}

/* ---------- TERMS CHECKBOX (registration form) ----------
   The shared .field-checkbox-label is a flex column (title + description on separate
   lines) for security-settings rows. The terms checkbox has no description — title +
   link + asterisk should sit on ONE line, with normal inline flow. Override the
   column direction back to row and let the spaces between children act as inline
   separators. */
.terms-checkbox .field-checkbox-label {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}
.terms-checkbox .field-checkbox-label a {
  text-decoration: underline;
  color: var(--text);
}
.terms-checkbox .field-checkbox-label a:hover {
  color: var(--accent);
}

/* =================================================================
   PUBLIC PAGES: Contact / Help / Pricing
   Reuses .legal-page chrome from PrivacyPolicy/TermsAndConditions as
   the outer container. Each section below adds page-specific layout.
   ================================================================= */

/* ---- Contact Us ---- */
.contact-page .contact-grid {
  /* Two columns at desktop, single column on narrow viewports. The form gets the
     flexible track since it's the primary action; info pane sits at a fixed width. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 360px);
  gap: 32px;
  margin-top: 24px;
}
@media (max-width: 720px) {
  .contact-page .contact-grid { grid-template-columns: 1fr; gap: 20px; }
}
.contact-form-card,
.contact-info-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
}
.contact-card-title {
  font: 600 18px var(--font-display, var(--font-ui));
  margin: 0 0 16px;
}
.contact-info-list {
  margin: 0;
  display: grid;
  grid-template-columns: 80px 1fr;
  gap: 8px 12px;
}
.contact-info-list dt {
  color: var(--text-muted);
  font-size: 13px;
}
.contact-info-list dd {
  margin: 0;
  font-size: 14px;
  word-break: break-word;
}
.contact-info-list a { color: var(--text); border-bottom: 1px dashed var(--line-strong); }
.contact-info-list a:hover { color: var(--accent); }
.contact-card-help {
  margin: 18px 0 0;
  font-size: 13px;
  color: var(--text-muted);
  line-height: 1.55;
}
/* Honeypot — visually hidden but technically present in the DOM so bots fill it.
   Real users never see/tab into it (tabindex=-1, off-screen positioning, aria-hidden). */
.honeypot {
  position: absolute !important;
  left: -9999px !important;
  width: 1px; height: 1px; overflow: hidden;
}

/* ---- Help ---- */
.help-page .help-section { margin-top: 32px; }
.help-page .help-section h2 {
  font: 600 22px var(--font-display, var(--font-ui));
  margin: 0 0 16px;
}
.help-faq {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  margin-bottom: 10px;
  overflow: hidden;
}
.help-faq > summary {
  /* Native disclosure widget — cursor + padding turns it into a clickable header. */
  cursor: pointer;
  padding: 14px 18px;
  font-weight: 500;
  list-style: none;             /* hide default ▶ marker */
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.help-faq > summary::-webkit-details-marker { display: none; }
.help-faq > summary::after {
  /* Custom chevron — flips when open. */
  content: '';
  width: 8px; height: 8px;
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(45deg);
  margin-left: 12px;
  transition: transform var(--dur-fast) var(--ease);
}
.help-faq[open] > summary::after { transform: rotate(-135deg); }
.help-faq > p {
  padding: 0 18px 16px;
  margin: 0;
  color: var(--text-secondary);
  line-height: 1.6;
}
.help-faq a { color: var(--text); border-bottom: 1px dashed var(--line-strong); }
.help-faq a:hover { color: var(--accent); }
.help-support-cta {
  margin-top: 40px;
  padding: 32px;
  text-align: center;
  background: var(--surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.help-support-cta h2 {
  margin: 0 0 8px;
  font: 600 22px var(--font-display, var(--font-ui));
}
.help-support-cta p { margin: 0 0 18px; color: var(--text-secondary); }

/* ---- Pricing ---- */
.pricing-header { text-align: center; }
.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  margin-top: 32px;
}
@media (max-width: 960px) {
  .pricing-grid { grid-template-columns: 1fr; }
}
.pricing-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.pricing-card.is-featured {
  /* Visually highlight the recommended plan. Subtle — border + shadow rather than a
     loud background tint, to stay consistent with the calm tone of the rest of the app. */
  border-color: var(--text);
  box-shadow: 0 12px 32px -16px rgba(0, 0, 0, .25);
}
.pricing-badge {
  position: absolute;
  top: -10px; left: 50%; transform: translateX(-50%);
  background: var(--text);
  color: var(--surface);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
}
.pricing-card-name {
  margin: 0;
  font: 600 22px var(--font-display, var(--font-ui));
}
.pricing-card-tagline {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--text-muted);
}
.pricing-card-price {
  display: baseline;
  margin: 8px 0;
}
.pricing-amount {
  font: 600 40px var(--font-display, var(--font-ui));
  line-height: 1;
}
.pricing-period {
  font-size: 14px;
  color: var(--text-muted);
  margin-left: 4px;
}
.pricing-features {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}
.pricing-features li {
  padding-left: 22px;
  position: relative;
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-secondary);
}
.pricing-features li::before {
  /* Subtle checkmark — small visual confirmation per row without dominating the layout. */
  content: '✓';
  position: absolute;
  left: 0;
  top: 0;
  color: var(--text);
  font-weight: 600;
}
.pricing-footer {
  text-align: center;
  margin-top: 40px;
  color: var(--text-muted);
  font-size: 13px;
}
.pricing-footer a { color: var(--text); border-bottom: 1px dashed var(--line-strong); }
.pricing-footer a:hover { color: var(--accent); }

/* =================================================================
   GALLERY VIEW: section-bar Play button + bottom CTA
   ================================================================= */

.section-bar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Tenant gallery: this is the 3rd grid cell child. justify-self lands it at the
     right edge of the cell. (margin-left:auto only works in flex containers.) */
  justify-self: end;
}
/* View.cshtml's tools cluster — extended to host the Play button + tool buttons.
   Already exists in shared.css with its own flex layout; we just ensure the play
   button can sit at the start of the cluster with the right gap. */
.section-bar-tools .section-bar-play {
  margin-right: 4px;
}
.section-bar-play {
  /* Pill-shaped button consistent with the lightbox controls' pill aesthetic. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  font: 600 12px var(--font-ui);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text);
  background: transparent;
  border: 1px solid var(--text);
  border-radius: var(--radius-pill);
  cursor: pointer;
  /* Defensive: section-bar-links above has white-space:nowrap on .section-link, but
     the Play button's flex layout doesn't need it. Set it explicitly so the label
     never wraps mid-button. */
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.section-bar-play:hover {
  background: var(--text);
  color: var(--surface);
}
.section-bar-play svg { flex-shrink: 0; }

/* "Create Your Own Gallery" CTA at the bottom of public galleries. Designed to
   stand out clearly — visitors should know there's an action at the end of a long
   gallery scroll. */
.gallery-cta {
  /* Defensive overrides so no parent rule can suppress this section. */
  display: block !important;
  visibility: visible !important;
  opacity: 1 !important;
  position: relative;
  z-index: 1;

  /* Compact vertical padding — content is now just one line of small text. */
  padding: 24px;
  background: var(--surface-alt);
  border-top: 1px solid var(--line);
  margin-top: 0;
  text-align: center;
}
.gallery-cta-inner {
  max-width: 1100px;
  margin: 0 auto;
}
.gallery-cta-line {
  /* Smaller than the previous headline (was 26px) — single sentence, modest weight,
     reads as a soft footer-style invitation rather than a primary CTA. */
  font-size: 14px;
  color: var(--text-secondary);
  margin: 0;
  line-height: 1.5;
}
.gallery-cta-link {
  /* "Click here" — inline link inside the sentence. Underlined + colored as the
     standard text link so it's obvious the words are clickable. */
  color: var(--text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 3px;
  margin-left: 4px;
}
.gallery-cta-link:hover {
  color: var(--accent);
}

/* Smooth-scroll for anchor links across the site. Affects the "Create Your Own Gallery"
   CTA → /#signup path most visibly: visitor arrives on the home page and glides down
   to the signup section rather than landing instantly with no context. Inherited by
   in-page anchor jumps too (e.g., footer Privacy → top of page if back-to-top is added). */
html { scroll-behavior: smooth; }

/* Respect users who prefer reduced motion — vestibular conditions etc. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* Section-bar responsive: under 960px the bar collapses to a 2-column grid where
   tools/actions sit in column 2 and links wrap to a new row. Mirror the .section-bar-tools
   placement rule for .section-bar-actions so the Play button stays visible on mobile. */
@media (max-width: 960px) {
  .section-bar-actions {
    grid-column: 2;
    justify-self: end;
  }
}
/* Hide the "Play" label text on narrow screens — the SVG icon alone is enough and
   keeps the pill from blowing out the bar's width. */
@media (max-width: 560px) {
  .section-bar-play span { display: none; }
  .section-bar-play { padding: 6px 10px; }
}

/* =================================================================
   GALLERY CARD: lock badge top-right, name truncation, photo count
   ================================================================= */

/* Lock badge — sits at the top-right of the card, immediately to the LEFT of the
   delete button. Delete is at right:8px and is 32px wide; place lock at right:48px
   (32 + 8px gap) so the two chips sit side-by-side without overlapping.

   Unlike the delete button this is ALWAYS VISIBLE (not hover-faded) — visitors
   need to know at a glance which galleries are private without having to hover. */
.gallery-lock-badge {
  position: absolute;
  top: 8px;
  right: 48px;
  z-index: 2;
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .55);
  color: rgba(255, 255, 255, .95);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  pointer-events: none;     /* purely informational — clicks pass through to the card */
}

/* Gallery name truncation. Single-line ellipsis. Combined with title attribute on
   the h3, hovering shows the browser-native tooltip with the full name. */
.gallery-name-truncate {
  /* Force single line + ellipsis. min-width:0 lets flex/grid children shrink below
     content size — without it the truncate fails inside the meta flex container. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  max-width: 100%;
  /* Keep the rest of the h3's existing visual weight; we just gate the layout. */
  margin: 0 0 4px;
}

/* Meta-line that now carries photo count + updated date. Compact single line — wraps
   naturally on very narrow cards. */
.gallery-meta-line {
  font-size: 13px;
  color: var(--text-muted);
  display: block;
}

/* Drop the old in-h3 lock badge style now that the badge lives on the card edge
   instead of next to the title text. Keep the selector defined as a no-op so any
   old cached HTML still renders without console errors / odd inline boxes. */
.gallery-private-badge {
  display: none;
}

/* =================================================================
   PLAN PAGE additions: subtitle under header, notes section below
   the cards. Existing .pricing-* rules still drive the card layout.
   ================================================================= */
.pricing-header-sub {
  max-width: 720px;
  margin: 12px auto 0;
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1.5;
  text-transform: uppercase;
  letter-spacing: .04em;
}

/* Left-aligned feature bullets (default was left already, but the pricing-features
   selector inherits center from .pricing-card > text-align rules on some browsers —
   pin explicitly for consistency). */
.pricing-features {
  text-align: left;
}
/* Left-align the whole card body per the reference image — reads better with a
   bulleted list of many items than centered content. */
.pricing-card {
  text-align: left;
}
/* Center the plan name + tagline block since the reference shows them centered
   above the feature list. */
.pricing-card-head {
  text-align: left;
}

/* Notes block below the cards — subdued, small type, no card chrome. Reads as
   fine print, which matches the "* Notes:" label in the reference image. */
.pricing-notes {
  margin-top: 40px;
  padding: 0 4px;
  color: var(--text-secondary);
  font-size: 13px;
  line-height: 1.55;
}
.pricing-notes-label {
  margin: 0 0 8px;
  color: var(--text);
}
.pricing-notes ul {
  margin: 0;
  padding-left: 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.pricing-notes li {
  padding-left: 4px;
}
.pricing-notes a { color: var(--text); border-bottom: 1px dashed var(--line-strong); }
.pricing-notes a:hover { color: var(--accent); }

/* =================================================================
   REGISTER FORM: plan-picker block (radio row at top of the form)
   ================================================================= */
.field-plan-picker {
  /* Style like a regular .field so it aligns with the rest of the register form.
     Fieldset defaults strip out here — no border, no default padding. */
  border: none;
  padding: 0;
  margin: 0 0 14px;
}
.field-plan-picker legend {
  /* Legend acts as the field label. Same font weight + size as the other .field
     spans above the inputs. */
  padding: 0;
  margin-bottom: 6px;
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
}
.plan-details-link {
  /* Blue text link inside the label per the reference image. */
  color: var(--accent, #2563eb);
  text-decoration: underline;
  text-underline-offset: 3px;
  margin-left: 2px;
  font-weight: 400;
}
.plan-details-link:hover { opacity: .8; }

.plan-radio-row {
  /* Three equal cells that never wrap. Was flex-wrap: wrap with variable gap →
     "Starter (Free)" often overflowed to two lines while the other two stayed one
     line, producing the uneven look reported. Fixed layout keeps them symmetric. */
  display: flex;
  flex-wrap: nowrap;
  gap: 6px;
  padding: 10px 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--surface);
}
.plan-radio {
  /* Each option takes an equal share of the row so the three cells are the same
     width regardless of label length. justify-content:center puts the radio + label
     as a centered pair inside its cell — visually symmetric across all three. */
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
  user-select: none;
  min-width: 0;   /* let flex actually shrink evenly */
}
.plan-radio span {
  /* Never wrap the label — the layout depended on all three being single-line. */
  white-space: nowrap;
}
.plan-radio input[type="radio"] {
  /* Reset all inherited styles from .field input which is used for text inputs
     elsewhere in the register form. Without this override the radio inherits
     width:100%, padding:11px 14px, and the border-radius — inflating it into a
     huge filled circle that overlaps its label (see screenshot of the bug).
     Explicit fixed pixel dimensions ensure the radio looks the same no matter
     what container it sits in. */
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  border: none;
  background: none;
  box-shadow: none;
  cursor: pointer;
  accent-color: var(--text);
  flex-shrink: 0;
  /* appearance: auto restores the browser's native radio rendering in case any
     other rule set appearance:none — belt-and-suspenders defensive. */
  -webkit-appearance: radio;
  appearance: radio;
}
.plan-radio input[type="radio"]:hover,
.plan-radio input[type="radio"]:focus {
  /* Don't apply the text-input focus ring / hover border — those are wrong for
     a native radio and would draw a large rectangle around the circle. */
  border: none;
  box-shadow: none;
  outline: none;
}
