/* ============================================================
   ISSUE #7 — Coming Soon overlay for Dream Life / Future Self
   ============================================================ */
#view-dream-life.coming-soon-active .v3-dream-cards{
  opacity:.45;pointer-events:none;filter:saturate(.7);
}
#view-dream-life.coming-soon-active .v3-dream-toggle,
#view-dream-life.coming-soon-active .v3-dream-cta{
  opacity:.55;pointer-events:none;
}
.v3-dream-coming-soon{display:none}
#view-dream-life.coming-soon-active .v3-dream-coming-soon{
  display:flex;position:absolute;inset:0;z-index:30;
  align-items:center;justify-content:center;
  padding:24px;
  /* Lighter scrim so the user can still read the Dream Life cards
     behind — they're the visual preview of what's coming. */
  background:radial-gradient(ellipse at center,rgba(26,22,18,.25) 0%,rgba(26,22,18,.42) 70%);
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
}
/* Less aggressive dim on the cards themselves so the photos read
   through the scrim instead of going muddy. */
#view-dream-life.coming-soon-active .v3-dream-cards{
  opacity:.78 !important;
  filter:saturate(.92) !important;
}
.v3-dream-cs-card{
  width:100%;max-width:380px;
  background:linear-gradient(180deg,#fdfaf2 0%,#f2e8d6 100%);
  border:1px solid rgba(168,117,85,.30);
  border-radius:22px;padding:24px 22px 20px;
  box-shadow:0 30px 60px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.6);
  text-align:center;
}
.v3-dream-cs-badge{
  display:inline-block;font-size:10px;font-weight:700;
  letter-spacing:.20em;color:#7c4a2c;
  background:rgba(168,117,85,.18);border:1px solid rgba(168,117,85,.35);
  padding:5px 12px;border-radius:999px;margin-bottom:12px;
}
.v3-dream-cs-title{
  font-family:'Cal Sans',serif;font-weight:600;
  font-size:28px;line-height:1.05;color:#1a1612;
  margin:0 0 8px;
}
.v3-dream-cs-title i{
  background:linear-gradient(180deg,#d8a78a 0%,#8b5e3c 100%);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;font-style:italic;
}
.v3-dream-cs-sub{
  font-size:13px;line-height:1.45;color:rgba(26,22,18,.72);
  margin:0 0 18px;
}
.v3-dream-cs-form{
  display:flex;flex-direction:column;gap:10px;
}
.v3-dream-cs-input{
  width:100%;box-sizing:border-box;
  padding:13px 16px;font-size:14.5px;
  background:#fff;border:1.5px solid rgba(168,117,85,.30);
  border-radius:12px;color:#1a1612;
  font-family:inherit;
}
.v3-dream-cs-input::placeholder{color:rgba(26,22,18,.40)}
.v3-dream-cs-input:focus{outline:none;border-color:#a87555}
.v3-dream-cs-btn{
  display:flex;align-items:center;justify-content:center;gap:8px;
  width:100%;padding:13px 16px;
  background:linear-gradient(180deg,#d8a78a 0%,#b8825c 100%);
  color:#fff;border:0;border-radius:999px;
  font-size:13px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;cursor:pointer;
  box-shadow:0 12px 22px rgba(168,117,85,.32),inset 0 1px 0 rgba(255,255,255,.28);
  transition:transform .15s,box-shadow .15s;
}
.v3-dream-cs-btn:hover{transform:translateY(-1px);box-shadow:0 16px 28px rgba(168,117,85,.40),inset 0 1px 0 rgba(255,255,255,.28)}
.v3-dream-cs-btn svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.6;fill:none}
@media (max-width:480px){
  .v3-dream-cs-card{padding:20px 18px 16px;border-radius:18px}
  .v3-dream-cs-title{font-size:24px}
  .v3-dream-cs-sub{font-size:12px;margin-bottom:14px}
}

/* ============================================================
   ISSUE #2 — Refine button on view-portrait + reference image
   upload widget on view-refine.
   ============================================================ */
.onb-portrait-refine{
  background:#fff;border:1.5px solid rgba(168,117,85,.45) !important;
  color:#a87555 !important;
}
.onb-portrait-refine:hover{background:#fbf6ec;border-color:#7c4a2c !important;color:#7c4a2c !important}

.refine-ref-wrap{
  position:relative;display:flex;align-items:center;gap:12px;
  margin:10px 0 4px;padding:10px 12px;
  background:#fbf5ea;border:1px dashed rgba(168,117,85,.50);
  border-radius:12px;min-height:54px;
}
.refine-ref-pick{
  display:inline-flex;align-items:center;gap:8px;flex:1;
  padding:8px 12px;background:transparent;border:0;
  color:#5b3e2a;font-size:13px;font-weight:500;
  cursor:pointer;text-align:left;font-family:inherit;
}
.refine-ref-pick svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.6;fill:none}
.refine-ref-pick .when-set{display:none}
.refine-ref-wrap.has-image .when-empty{display:none}
.refine-ref-wrap.has-image .when-set{display:inline}
.refine-ref-preview{
  display:none;width:48px;height:48px;
  border-radius:8px;object-fit:cover;
  box-shadow:0 2px 8px rgba(0,0,0,.20);flex-shrink:0;
}
.refine-ref-wrap.has-image .refine-ref-preview{display:block}
.refine-ref-clear{
  display:none;width:26px;height:26px;border-radius:50%;
  background:rgba(168,117,85,.18);border:0;color:#7c4a2c;
  cursor:pointer;font-size:18px;line-height:1;font-family:inherit;
}
.refine-ref-wrap.has-image .refine-ref-clear{display:block}
.refine-ref-clear:hover{background:rgba(168,117,85,.30);color:#1a1612}

/* ============================================================
   Tiny-viewport (320–340px) final polish — chat header was the
   only spot left where Dream Life pill could overlap the
   companion name. Truncate the name + hide pill label below 360.
   ============================================================ */
.v3-chat-head .v3-chat-name{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

@media (max-width:359px){
  /* Hide the "Dream Life" word; keep the sparkle icon as the affordance */
  .v3-chat-head [data-nav="dream-life"] span{display:none !important}
  .v3-chat-head [data-nav="dream-life"]{padding:6px 8px !important;min-width:0}
  
}

/* ============================================================
   DEEP AUDIT FIX 1 — Bottom-most content was being covered by
   the fixed bottom tabbar (z:30). Every authed APP view needs
   safe-area + tabbar-height padding at the bottom so its last
   button is always tappable. Applied at the .view scroll level
   so internal content reserves space.
   ============================================================ */
@media (max-width:899px){
  #view-home, #view-mood, #view-memory, #view-profile,
  #view-upgrade, #view-customize, #view-portrait, #view-refine{
    padding-bottom:calc(var(--tabbar-h, 76px) + env(safe-area-inset-bottom) + 16px) !important;
    box-sizing:border-box !important;
  }
}

/* DEEP AUDIT FIX 2 — Dream Life back button + Future Self card
   click-targets. The Coming Soon overlay was catching ALL clicks
   so the back button beneath it became unreachable. Make the
   backdrop visual-only (pointer-events:none) and only restore
   pointer events on the card itself, so back/tabbar/anything
   underneath stays clickable. */
.v3-dream-coming-soon{pointer-events:none !important}
.v3-dream-coming-soon .v3-dream-cs-card{pointer-events:auto !important}
/* Also lift the back button visually above the overlay so users
   immediately see they can leave the "preview" screen. */
.v3-dream-back{position:relative;z-index:35}

/* DEEP AUDIT FIX 3 — Memory cards were clipping their key:value
   text (overflow:hidden + fixed line-height). Let them grow + wrap. */
.memory-card{overflow:visible !important; height:auto !important}
.memory-card .k, .memory-card .v{
  white-space:normal !important;word-break:break-word !important;
  overflow:visible !important; text-overflow:clip !important;
}

/* DEEP AUDIT FIX 4 — Mood hero "how's your inner weather?" was
   wrapping but overflow:hidden was cutting the second line off. */
.mood-hero{overflow:visible !important; height:auto !important}
.mood-hero h2{
  white-space:normal !important; overflow:visible !important;
  text-overflow:clip !important; line-height:1.05;
}

/* DEEP AUDIT FIX 5 — view-dream-life on desktop: a 137px trapped
   scroll under .v3-dream was reported. Allow internal scroll so
   tall content (Coming Soon card on desktop) is still reachable. */
@media (min-width:900px){
  .v3-dream{overflow-y:auto; max-height:100vh}
}

/* ============================================================
   DEEP AUDIT FIX 1b — Padding-bottom inside a scroll container
   doesn't push the LAST element above a fixed bottom tabbar
   (the element scrolls to the bottom regardless of padding).
   Real fix: shrink the view's bottom inset by var(--tabbar-h)
   on mobile so the view container itself ends above the tabbar.
   Now any content (incl. customize chips, save buttons, menu
   rows) sits in an area that doesn't reach the tabbar.
   ============================================================ */
@media (max-width:899px){
  #view-home.view, #view-mood.view, #view-memory.view,
  #view-profile.view, #view-upgrade.view, #view-customize.view,
  #view-portrait.view, #view-refine.view{
    bottom:calc(var(--tabbar-h, 76px) + env(safe-area-inset-bottom)) !important;
  }
  /* No need for our earlier padding-bottom hack — strip it so the
     view doesn't double-up the offset (visible empty cream stripe). */
  #view-home, #view-mood, #view-memory, #view-profile,
  #view-upgrade, #view-customize, #view-portrait, #view-refine{
    padding-bottom:16px !important;
  }
}

/* ============================================================
   E2E FIX PASS — CSS support for the JS/HTML fixes above
   ============================================================ */

/* L1 — Onb step 1/2 NEXT shake + missing-field flash */
@keyframes v3-onb-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}
.v3-onb-shake { animation: v3-onb-shake .35s ease; }
@keyframes v3-onb-missing-flash {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220,90,70,0); }
  30%, 70% { box-shadow: 0 0 0 3px rgba(220,90,70,.30); }
}
.v3-onb-missing {
  animation: v3-onb-missing-flash 1.2s ease;
  border-radius: 14px;
}

/* M1 / M26 — inline signup error row inside step 3 card */
.v3-onb-error {
  margin-top: 12px;
  padding: 11px 14px;
  background: rgba(220, 90, 70, .12);
  border: 1px solid rgba(220, 90, 70, .35);
  border-radius: 12px;
  color: #c14a3e;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
}

/* L17 — Mood hero card retheme from legacy dark olive to V3 cream/copper */
#view-mood .mood-hero {
  background: linear-gradient(180deg, #f9efdc 0%, #f1ddc0 100%) !important;
  color: #5b3e2a !important;
  border: 1px solid rgba(168, 117, 85, .22);
  box-shadow: 0 6px 18px rgba(168, 117, 85, .12);
}
#view-mood .mood-hero .k {
  color: rgba(91, 62, 42, .72) !important;
  letter-spacing: .18em;
}
#view-mood .mood-hero h2 {
  color: #1a1612 !important;
}
#view-mood .mood-hero h2 i {
  background: linear-gradient(180deg, #d8a78a 0%, #8b5e3c 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  font-style: italic;
}

/* L12 — Login aside "welcome back" italic was illegible against the
   high-contrast couple photo. Reinforce the text-shadow scrim. */

/* L18 — SOS row label spacing (give the label and the trailing 'add' a
   real gap so "SOS emergency contactadd" no longer reads as one word) */
#sosContactRow .lbl {
  flex: 1;
  padding-right: 12px;
}

/* M14 — Splash CTA can briefly inherit pointer-events:none before the
   view picks up .active. Force-enable so the first tap always lands. */
.v3-splash-cta { pointer-events: auto !important; }

/* M13 — Make the dimmed Dream Life cards LOOK non-interactive (clear
   the hover lift + cursor:pointer when behind the Coming Soon overlay) */
#view-dream-life.coming-soon-active .v3-dream-card {
  cursor: default !important;
}
#view-dream-life.coming-soon-active .v3-dream-card:hover {
  transform: none !important;
  box-shadow: none !important;
}

/* ============================================================
   A11Y — extra-flow round
   ============================================================ */

/* L7 — Brand-consistent focus rings on V3 controls. */
*:focus-visible {
  outline: 2px solid var(--rose, #a6724b);
  outline-offset: 2px;
}
button:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
a:focus-visible {
  outline: 2px solid var(--rose, #a6724b);
  outline-offset: 2px;
  border-radius: inherit;
}
/* Skip the outline on the splash CTA which has its own ring */
.v3-splash-cta:focus-visible {
  outline-offset: 4px;
}

/* H17 — Bump helper-text colors that fell below 4.5:1 (the audit
   called out rgba(26,22,18,0.55) on cream). Keep 0.72+. */
.muted, .small.muted {
  color: rgba(26,22,18,0.78);
}
.v3-onb-label, .v3-onb-block-title, .v3-onb-block-sub {
  color: rgba(26,22,18,0.85);
}

/* C5 — Make menu-item rows + home tiles look + behave like buttons.
   role=button + tabindex=0 added via JS at boot; this just makes
   the focus ring sensible. */
.menu-item[tabindex], .tile[tabindex] {
  cursor: pointer;
}

/* ============================================================
   GLOBAL BACK BUTTON STANDARD — every view's "back" arrow uses
   the same size, position, and styling. Audit-driven (Image #68):
   previously back buttons ranged 34→40px and lived in 6 different
   positions across views.
   ============================================================ */

/* In-page topbar back (mood/memory/profile/upgrade/customize/portrait/
   refine + edit modal) — bring 40px boxes down to 36px + frosted */
.topbar .back{
  width:36px !important;height:36px !important;
  border-radius:50% !important;
  background:rgba(255,255,255,.92) !important;
  border:none !important;
  color:#1a1612 !important;
  box-shadow:0 2px 12px rgba(0,0,0,.10) !important;
  display:flex;align-items:center;justify-content:center;
}
.topbar .back .i{width:18px;height:18px}

/* Mobile: pin the in-page back arrow to the VIEWPORT's top-left edge
   (14/14) so it sits at exactly the same screen coordinates as the V3
   fullscreen back arrows (chat / dream / onb-v3 / login / portrait).
   Without this it inherited the card's offset and padding and ended
   up at top:39 left:35. */
@media (max-width:899px){
  .card .topbar{position:static;min-height:24px}
  .card .topbar .back{
    position:fixed !important;
    top:14px !important;left:14px !important;
    z-index:40;
  }
  .card .topbar .title{padding:0 50px}
}

/* Chat header back — was 34px transparent. Match the standard. */
.v3-chat-back{
  width:36px !important;height:36px !important;
  border-radius:50% !important;
  background:rgba(255,255,255,.92) !important;
  border:none !important;color:#1a1612 !important;
  box-shadow:0 2px 12px rgba(0,0,0,.10) !important;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}
.v3-chat-back svg{width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none}

/* Dream-life back — already 36px, keep size, just ensure same styling. */
.v3-dream-back{
  width:36px !important;height:36px !important;
  border-radius:50% !important;
  background:rgba(255,255,255,.92) !important;
  border:none !important;color:#1a1612 !important;
  box-shadow:0 2px 12px rgba(0,0,0,.10) !important;
  display:flex;align-items:center;justify-content:center;
}

/* Onb-v3 back — position:fixed doesn't work because the parent .view
   has `transform` (which establishes a containing block, breaking
   fixed-to-viewport). Use position:sticky so it sticks within the
   scrolling .v3-onb container. Negative margin-bottom so it doesn't
   shift the hero down. */
.v3-onb-back{
  position:sticky !important;
  top:14px !important;
  margin:14px 0 -50px 14px !important;
  align-self:flex-start;
  z-index:50 !important;
  width:36px !important;height:36px !important;
  background:rgba(255,255,255,.92) !important;
  box-shadow:0 2px 12px rgba(0,0,0,.10) !important;
}

/* Portrait + refine back — were 40px, lived outside .topbar so the
   global rule didn't catch them. Style explicitly. */
.onb-portrait-head .back, .refine-head .back, #appPortraitBack, #appRefineBack{
  width:36px !important;height:36px !important;
  border-radius:50% !important;
  background:rgba(255,255,255,.92) !important;
  border:none !important;color:#1a1612 !important;
  box-shadow:0 2px 12px rgba(0,0,0,.10) !important;
  display:flex;align-items:center;justify-content:center;
}
@media (max-width:899px){
  #appPortraitBack, #appRefineBack{
    position:absolute !important;
    top:14px !important;left:14px !important;
    z-index:5;
  }
}

/* Login back — match the standard (top-LEFT, 36px, white frosted).
   Earlier we moved it to top-RIGHT to avoid the BOB wordmark, but the
   wordmark already has a text-shadow + we add a little extra padding-left
   so the brand stays readable beside the back button. */

/* ============================================================
   UNIFY DESKTOP GUEST LAYOUT — every guest-facing V3 view (splash,
   login, onb-v3) renders as a 440px phone shell centered on a
   cream-tan desktop background. Earlier login was the only view
   still rendering the auth-split side-by-side (couple photo aside
   + form main), so the three pages looked completely different on
   desktop. Now every guest view shares the same shell.
   ============================================================ */
@media (min-width:900px){
  /* Force login to mirror the splash/onb-v3 shell shape — same
     cream-tan desktop background so all three guest views share the
     same chrome (not the dark slate from earlier). */
  #view-login.view{
    padding:0 !important;
    background:#f1ead7 !important;
    display:flex !important;
    align-items:center;justify-content:center;
  }
  /* Drop the wide aside; render as a vertical mobile-shaped shell. */
  
  /* photo banner = upper third; form = lower two thirds, sliding up
     over the photo banner so there's no white gap. */

  
  /* Promote the aside into a top photo banner like the mobile view. */

  

  

  

  

  
  /* Form card sliding up over the photo */

  /* Back button — needs to pin to the .auth-split shell (top-left of
     photo banner), so we strip position from .card and .auth-main so
     the absolute topbar walks up to .auth-split (the next relative
     ancestor). */

  

  

  

  

}

/* ============================================================
   BACK BUTTON ANCHOR — absolute-pin to the .v3-onb shell so it
   always sits at top:14 left:14 of the 900px container, regardless
   of step. (Sticky+flex was leaving step 2's back floating in the
   middle of the title because .v3-onb didn't scroll.)
   ============================================================ */
#view-onb-v3 > .v3-onb > .v3-onb-back,
#view-onb-v3 .v3-onb-back{
  position:absolute !important;
  top:14px !important;
  left:14px !important;
  margin:0 !important;
  width:36px !important;height:36px !important;
  z-index:50 !important;
}
/* Step 2 — tighten title padding so it doesn't overlap with the
   absolute-positioned back. */
body[data-v3-step="2"] .v3-onb-presence{
  padding-top:60px !important;
}

/* ============================================================
   STEP 2 PAGE 4 FIX — content was being pushed to bottom because
   .v3-onb inherits `justify-content:flex-end`. On the last page
   (only 5 portraits in a 3x3 grid) the partial grid + everything
   above it floated down. Force top-align for step 2.
   ============================================================ */
body[data-v3-step="2"] #view-onb-v3 .v3-onb{
  justify-content:flex-start !important;
}

/* ============================================================
   MOBILE STEP 1/2/3 — let the view scroll if content exceeds the
   viewport (iOS Safari URL bar shrinks usable height). Don't fight
   the existing layout pattern — just keep the .v3-onb shell at
   min-height:100dvh and allow the view to scroll naturally.
   ============================================================ */
@media (max-width:899px){
  #view-onb-v3{
    overflow-y:auto !important;
    overflow-x:hidden !important;
    -webkit-overflow-scrolling:touch !important;
  }
  #view-onb-v3 .v3-onb{
    height:auto !important;
    min-height:100dvh !important;
    max-height:none !important;
    overflow:visible !important;
  }
  /* Step 2 — top-aligned so partial last page doesn't drop content. */
  body[data-v3-step="2"] #view-onb-v3 .v3-onb{
    justify-content:flex-start !important;
  }
}

/* ============================================================
   LOGIN REDESIGN — match splash structure
   Mobile-first: full-bleed photo bg + BOB top-left + headline
   overlay + cream form section + footer privacy line.
   ============================================================ */

#view-login.view{
  padding:0 !important;
  background:#1a1612;
  overflow-y:auto;
  overflow-x:hidden;
}
/* Two-zone vertical layout: photo banner (with brand + headline
   overlay) on top + form/foot zone on the bottom. Locked to 100dvh
   so it fills exactly one screen — no empty cream stretch below. */
.v3-login{
  position:relative;
  height:100vh;height:100dvh;
  min-height:100vh;min-height:100dvh;
  background:#fdfaf2;
  display:flex;flex-direction:column;
  overflow:hidden;
}
/* Photo zone — fixed 48% of the shell, gradient blending to cream
   at its bottom edge so the form area joins seamlessly. */
.v3-login-photo{
  flex:0 0 48%;
  position:relative;
  background:
    url('/static/images/v3/Screen1.jpg?v=5') center 18% / cover no-repeat;
  overflow:hidden;
}
.v3-login-photo::after{
  /* Top scrim for brand readability + bottom blend to cream */
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,
    rgba(20,15,10,.30) 0%,
    rgba(20,15,10,0) 22%,
    rgba(20,15,10,0) 55%,
    rgba(253,250,242,.55) 88%,
    #fdfaf2 100%);
  pointer-events:none;
}
.v3-login-back{
  position:absolute;top:14px;left:14px;z-index:10;
  width:36px;height:36px;border-radius:50%;
  background:rgba(255,255,255,.92);border:none;cursor:pointer;
  color:#1a1612;display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 12px rgba(0,0,0,.18);
}
.v3-login-back svg{width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none}

/* Brand + headline overlay sit ABSOLUTE on top of the photo zone */
.v3-login-top{
  position:absolute;z-index:3;
  top:20px;left:60px;right:22px;
}
.v3-login-brand{position:relative;display:inline-block}
.v3-login-brand h1{
  font-family:'Cal Sans',serif;
  font-weight:700;font-size:24px;line-height:1;
  letter-spacing:.04em;
  background:linear-gradient(180deg,#f3c8a5 0%,#d8a78a 100%);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
  margin:0;
  text-shadow:0 2px 12px rgba(0,0,0,.45);
}
.v3-login-brand .sub{
  display:block;
  font-size:10.5px;font-weight:500;
  letter-spacing:.28em;
  color:rgba(255,255,255,.92);
  text-shadow:0 1px 8px rgba(0,0,0,.45);
  margin-top:2px;
}
.v3-login-brand .line{
  display:block;width:34px;height:1.5px;
  background:#d8a78a;margin-top:6px;border-radius:1px;
}

/* Headline overlay — anchored to the BOTTOM of the photo zone so
   it sits on the dark gradient + cream blend like splash's anchor. */
.v3-login-hero{
  position:absolute;z-index:3;
  left:22px;right:22px;bottom:18px;
}
.v3-login-headline{
  font-family:'Cal Sans',serif;
  font-weight:600;font-size:38px;line-height:1.0;
  color:#fff;
  text-shadow:0 2px 18px rgba(0,0,0,.55), 0 0 30px rgba(0,0,0,.35);
  margin:0;
}
.v3-login-headline i{
  font-style:italic;
  background:linear-gradient(180deg,#f3c8a5 0%,#d8a78a 100%);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
  text-shadow:0 2px 18px rgba(0,0,0,.55);
}
.v3-login-sub{
  margin-top:10px;
  font-size:14px;line-height:1.45;
  color:rgba(255,255,255,.92);
  text-shadow:0 1px 8px rgba(0,0,0,.5);
  max-width:340px;
}

/* Form zone — fills remaining 52% of the shell, padded + form
   stays compact + foot pinned to bottom via flex */
.v3-login-form{
  flex:1 1 auto;
  position:relative;z-index:4;
  padding:18px 22px 0;
  background:#fdfaf2;
  display:flex;flex-direction:column;
  overflow-y:auto;
  min-height:0;
}
.v3-login-field{margin-top:14px}
.v3-login-field:first-child{margin-top:0}
.v3-login-field label{
  display:block;
  text-transform:uppercase;letter-spacing:.18em;
  font-size:10.5px;font-weight:600;
  color:rgba(26,22,18,.6);
  margin-bottom:6px;
}
.v3-login-field input{
  width:100%;box-sizing:border-box;
  padding:14px 16px;font-size:14.5px;
  background:#fff;
  border:1.5px solid rgba(168,117,85,.22);
  border-radius:14px;
  color:#1a1612;font-family:inherit;
  -webkit-appearance:none;appearance:none;
}
.v3-login-field input::placeholder{color:rgba(26,22,18,.38)}
.v3-login-field input:focus{outline:none;border-color:#a87555;box-shadow:0 0 0 3px rgba(168,117,85,.15)}

.v3-login-cta{
  display:flex;align-items:center;justify-content:center;gap:10px;
  width:100%;margin-top:18px;
  padding:16px 22px;
  background:linear-gradient(180deg,#d8a78a 0%,#b8825c 100%);
  color:#fff;
  border:0;border-radius:999px;
  font-family:'Inter',sans-serif;
  font-size:14px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;
  cursor:pointer;
  box-shadow:0 14px 28px rgba(168,117,85,.35), inset 0 1px 0 rgba(255,255,255,.28);
  transition:transform .15s,box-shadow .15s;
}
.v3-login-cta:hover{transform:translateY(-1px);box-shadow:0 18px 32px rgba(168,117,85,.4), inset 0 1px 0 rgba(255,255,255,.28)}
.v3-login-cta svg{width:16px;height:16px;stroke:currentColor;stroke-width:2;fill:none}

#view-login .oauth-row.login-oauth{
  margin-top:10px;
  display:flex;flex-direction:column;gap:8px;
}
#view-login .oauth-btn{
  background:#fff !important;
  border:1.5px solid rgba(26,22,18,.10) !important;
  border-radius:999px !important;
  padding:13px !important;
  color:#1a1612 !important;
  font-size:13.5px;font-weight:500;
  display:flex;align-items:center;justify-content:center;gap:10px;
}

.v3-login-create{
  text-align:center;margin-top:14px;
  font-size:13px;color:rgba(26,22,18,.72);
}
.v3-login-create a{
  color:#a87555;font-weight:600;text-decoration:none;
  border-bottom:1px solid rgba(168,117,85,.35);padding-bottom:1px;
}
.v3-login-create a:hover{color:#7c4a2c;border-bottom-color:#7c4a2c}

.v3-login-foot{
  position:relative;z-index:3;
  margin-top:auto;  /* push to bottom of .v3-login-form flex container */
  display:flex;align-items:center;justify-content:center;gap:8px;
  padding:14px 0 max(14px, env(safe-area-inset-bottom));
  font-size:11.5px;color:rgba(26,22,18,.62);
  letter-spacing:.04em;
  background:#fdfaf2;
}
.v3-login-foot svg{width:13px;height:13px;color:#a87555;stroke:currentColor;stroke-width:1.6;fill:none;flex-shrink:0}
.v3-login-foot b{color:#a87555;font-weight:500}

/* Tiny phone — tighten headline */
@media (max-height:680px){
  .v3-login-photo{flex-basis:44%}
  .v3-login-headline{font-size:32px}
}

/* Desktop — wrap in 440px phone shell on cream-tan bg, like splash */
@media (min-width:900px){
  #view-login.view{
    background:#f1ead7;
    display:flex;align-items:center;justify-content:center;
  }
  .v3-login{
    width:440px;max-width:440px;
    height:min(900px,100vh);
    border-radius:42px;
    box-shadow:0 30px 80px rgba(0,0,0,.45);
    overflow:hidden;
  }
}
.v3-login-headline i{
  /* Solid darker copper + heavier shadow so it stays readable on the
     busy photo. */
  background:none !important;
  -webkit-background-clip:initial !important;
  -webkit-text-fill-color:initial !important;
  background-clip:initial !important;
  color:#f3c8a5 !important;
  text-shadow:0 2px 18px rgba(0,0,0,.75), 0 0 24px rgba(0,0,0,.6) !important;
}
.v3-login-headline{
  text-shadow:0 2px 22px rgba(0,0,0,.65), 0 0 30px rgba(0,0,0,.4) !important;
}
.v3-login-sub{
  text-shadow:0 2px 12px rgba(0,0,0,.7), 0 0 18px rgba(0,0,0,.45) !important;
  color:#fff !important;
}

/* ============================================================
   MOBILE STEP 1 — rewrite so the photo + headline + form flow
   top-down naturally (no calc(dvh) margins that iOS Safari
   miscalculates while the URL bar shows/hides).
   ============================================================ */
@media (max-width:899px){
  /* Base .v3-onb has justify-content:flex-end — fine on desktop where
     it pushes content to the bottom of the 900px shell — but on mobile
     where the shell is taller than content, it leaves empty space at
     the top. Force top-align for every step on mobile. */
  body[data-v3-step="1"] #view-onb-v3 .v3-onb,
  body[data-v3-step="2"] #view-onb-v3 .v3-onb,
  body[data-v3-step="3"] #view-onb-v3 .v3-onb{
    justify-content:flex-start !important;
  }
  body[data-v3-step="1"] #view-onb-v3 .v3-onb-bg.is-step1{
    /* Photo becomes a real block element in the flex column instead
       of an absolute-positioned background — its height drives layout. */
    position:relative !important;
    height:48vh !important;
    height:48dvh !important;
    min-height:280px !important;
    margin:0 !important;
    inset:auto !important;
    width:100% !important;
    z-index:1;
  }
  /* Drop the dark scrim ::after on the photo so the cream blend takes
     over below — keeps form readable + matches login's photo blend. */
  body[data-v3-step="1"] #view-onb-v3 .v3-onb-bg.is-step1::after{
    background:linear-gradient(180deg,
      rgba(20,15,10,0) 0%,
      rgba(20,15,10,.10) 45%,
      rgba(253,250,242,.45) 85%,
      #fdfaf2 100%) !important;
  }
  /* Headline overlay anchored to the bottom of the photo via absolute
     positioning INSIDE the photo bg, so no calc() viewport math. */
  /* Headline flows in normal flow below the photo. Pulled UP with
     negative margin so it visually sits on the cream-blend gradient
     at the bottom of the photo. No calc(dvh) → predictable on
     every browser (especially iOS Safari's URL-bar viewport math). */
  body[data-v3-step="1"] #view-onb-v3 .v3-onb-bg.is-step1 ~ #onbV3Stage .v3-onb-hero{
    position:relative !important;
    margin:-90px 0 0 !important;
    padding:0 22px 14px !important;
    z-index:3;
  }
  body[data-v3-step="1"] #view-onb-v3 .v3-onb-bg.is-step1 ~ #onbV3Stage .v3-onb-card{
    position:relative !important;
    margin-top:0 !important;
    padding-top:14px !important;
    background:#fdfaf2;
    border-top-left-radius:24px;
    border-top-right-radius:24px;
    box-shadow:0 -10px 30px rgba(60,40,20,.10);
    z-index:2;
  }
  body[data-v3-step="1"] #view-onb-v3 #onbV3Stage{
    position:relative !important;
    padding-top:0 !important;  /* kill the desktop 50dvh stage offset */
    z-index:2;
  }
}

/* ============================================================
   ONB STEP 2 — Refine Companion button + sub-view
   ============================================================ */

/* Two-button row at the bottom of the presence picker */
.v3-presence-actions{
  display:flex;flex-direction:column;gap:10px;
  align-items:stretch;
}

/* Refine Companion — secondary outlined pill, disabled until pick */
.v3-onb-refine{
  display:flex;align-items:center;justify-content:center;gap:8px;
  width:100%;padding:12px 18px;
  background:#fff;
  color:#a87555;
  border:1.5px solid rgba(168,117,85,.45);
  border-radius:999px;
  font-family:'Inter',sans-serif;
  font-size:13px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;
  cursor:pointer;
  transition:background .15s, border-color .15s, color .15s, opacity .15s;
}
.v3-onb-refine:hover:not(:disabled){
  background:#fbf6ec;border-color:#7c4a2c;color:#7c4a2c;
}
.v3-onb-refine:disabled{
  opacity:.45;cursor:not-allowed;
}
.v3-onb-refine svg{width:15px;height:15px;stroke:currentColor;stroke-width:1.8;fill:none}

/* --- Refine sub-stage (replaces the grid after click) --- */
.v3-onb-refine-stage{
  padding:60px 22px 22px;
  position:relative;
  display:flex;flex-direction:column;gap:12px;
  /* Let the shell parent control height (mobile = view scrolls;
     desktop = 900px shell). min-height:100dvh broke desktop by
     forcing the substep taller than the shell → buttons cropped. */
}
/* Desktop — tighter spacing + smaller canvas so every button fits
   inside the 900px phone shell without scrolling. */
@media (min-width:900px){
  .v3-onb-refine-stage{
    padding:50px 22px 14px;
    gap:8px;
  }
  .v3-onb-refine-head h1{font-size:22px !important}
  .v3-onb-refine-head p{font-size:12px !important;margin-top:3px}
  .v3-onb-refine-canvas{
    max-height:38dvh !important;
    max-width:240px !important;
    border-radius:14px !important;
  }
  .v3-onb-refine-canvas img{max-height:38dvh !important}
  .v3-onb-refine-prompt textarea{min-height:54px !important;font-size:13px !important;padding:9px 12px !important}
  .v3-onb-refine-prompt label{font-size:9.5px !important;margin-bottom:4px !important}
  .v3-onb-refine-chips{gap:5px !important;margin-top:5px !important}
  .v3-onb-refine-chips .chip{padding:5px 10px !important;font-size:10.5px !important}
  .v3-onb-refine-actions .v3-onb-refine-discard,
  .v3-onb-refine-actions .v3-onb-refine-generate{padding:10px 14px !important;font-size:12px !important}
  .v3-onb-refine-stage > .v3-onb-next{padding:10px 14px !important;font-size:12px !important;margin-top:6px}
}
.v3-onb-refine-back{
  position:absolute;top:14px;left:14px;
  width:36px;height:36px;border-radius:50%;
  background:rgba(255,255,255,.92);border:none;cursor:pointer;
  color:#1a1612;display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 12px rgba(0,0,0,.10);
  z-index:5;
}
.v3-onb-refine-back svg{width:18px;height:18px;stroke:currentColor;stroke-width:2;fill:none}

.v3-onb-refine-head{text-align:center;padding:0 14px}
.v3-onb-refine-head h1{
  font-family:'Cal Sans',serif;font-weight:600;
  font-size:24px;line-height:1.05;color:#1a1612;margin:0;
}
.v3-onb-refine-head h1 i{
  font-style:italic;
  background:linear-gradient(180deg,#d8a78a 0%,#8b5e3c 100%);
  -webkit-background-clip:text;-webkit-text-fill-color:transparent;
  background-clip:text;
}
.v3-onb-refine-head p{
  margin-top:6px;font-size:13px;line-height:1.4;
  color:rgba(26,22,18,.7);
}

.v3-onb-refine-canvas{
  position:relative;
  width:100%;
  /* Let the image set its own height (no aspect-ratio crop). Hard-cap
     so it never blows past the visible area; cream fill behind in case
     the natural aspect leaves bars on the sides. */
  max-height:60dvh;
  background:#f1ead7;
  border-radius:18px;
  overflow:hidden;
  margin:0 auto;max-width:360px;
  box-shadow:0 10px 28px rgba(60,40,20,.18);
  display:flex;align-items:center;justify-content:center;
}
.v3-onb-refine-canvas img{
  width:100%;height:auto;
  max-height:60dvh;
  object-fit:contain;
  display:block;
}
.v3-onb-refine-busy{
  position:absolute;inset:0;
  background:rgba(20,15,10,.55);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;
  color:#fff;font-size:13px;backdrop-filter:blur(6px);
}
.v3-onb-refine-busy .spinner{
  width:32px;height:32px;border-radius:50%;
  border:3px solid rgba(255,255,255,.25);
  border-top-color:#fff;
  animation:v3-spin 1s linear infinite;
}
@keyframes v3-spin{to{transform:rotate(360deg)}}

.v3-onb-refine-prompt label{
  display:block;
  text-transform:uppercase;letter-spacing:.16em;
  font-size:10.5px;font-weight:600;
  color:rgba(26,22,18,.6);
  margin-bottom:6px;
}
.v3-onb-refine-prompt textarea{
  width:100%;box-sizing:border-box;
  padding:12px 14px;font-size:14px;line-height:1.45;
  background:#fff;
  border:1.5px solid rgba(168,117,85,.22);
  border-radius:14px;
  color:#1a1612;font-family:inherit;
  resize:vertical;min-height:78px;
}
.v3-onb-refine-prompt textarea:focus{
  outline:none;border-color:#a87555;
  box-shadow:0 0 0 3px rgba(168,117,85,.15);
}
.v3-onb-refine-chips{
  display:flex;flex-wrap:wrap;gap:6px;margin-top:8px;
}
.v3-onb-refine-chips .chip{
  padding:6px 12px;font-size:11.5px;
  background:rgba(168,117,85,.10);
  border:1px solid rgba(168,117,85,.22);
  color:#7c4a2c;border-radius:999px;
  cursor:pointer;font-family:inherit;
}
.v3-onb-refine-chips .chip:hover{
  background:rgba(168,117,85,.18);
}

.v3-onb-refine-actions{
  display:flex;gap:10px;
}
.v3-onb-refine-discard{
  flex:0 0 auto;padding:12px 18px;
  background:#fff;color:rgba(26,22,18,.65);
  border:1.5px solid rgba(26,22,18,.10);
  border-radius:999px;
  font-family:inherit;font-size:13px;font-weight:500;
  cursor:pointer;
}
.v3-onb-refine-generate{
  flex:1;display:flex;align-items:center;justify-content:center;gap:8px;
  padding:12px 18px;
  background:linear-gradient(180deg,#d8a78a 0%,#b8825c 100%);
  color:#fff;border:0;border-radius:999px;
  font-family:'Inter',sans-serif;font-size:13px;font-weight:600;letter-spacing:.14em;
  text-transform:uppercase;cursor:pointer;
  box-shadow:0 10px 22px rgba(168,117,85,.30),inset 0 1px 0 rgba(255,255,255,.28);
}
.v3-onb-refine-generate:disabled{opacity:.55;cursor:not-allowed}
.v3-onb-refine-generate svg{width:14px;height:14px;stroke:currentColor;stroke-width:1.8;fill:none}

/* ============================================================
   V3 onb STEP 2 — fit-to-viewport responsive layout.
   Problem: the presence grid is the tallest step. render() locks the
   onb root to height:100% + overflow:hidden on desktop (≥900px), so the
   action menu (pager + Refine + NEXT) was clipped below the fold with no
   scroll; on mobile the whole page scrolled to reach NEXT.
   Fix: bound step 2 to the viewport. Header stays at the top, the grid
   fills the remaining height with tiles that scale to fit (no fixed 3:4
   height → 3×3 always fits), and the pager nav + page-info + Refine +
   NEXT are pinned at the bottom so they are ALWAYS visible. Last in the
   cascade + high specificity so it wins the many earlier step-2 rules.
   ============================================================ */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage{
  display:flex !important;          /* beat the global #onbV3Stage{display:block!important} */
  flex-direction:column !important;
  height:100dvh !important;
  min-height:0 !important;
  max-height:100dvh !important;
  overflow:hidden !important;
}
/* the un-classed wrap div render() appends + the presence block both
   become flex columns that fill the stage */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage > div{
  flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;
}
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-onb-presence{
  flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;
  padding-bottom:0 !important;
}
/* title + subtitle: natural height, never shrink */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-onb-presence > h1,
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-onb-presence > p{
  flex:0 0 auto;
}
/* grid area takes all remaining height (scrolls only as a last resort) */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-presence-pager{
  flex:1 1 auto;min-height:0;
  display:flex;flex-direction:column;
  overflow-y:auto;overflow-x:hidden;
  -webkit-overflow-scrolling:touch;
}
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-presence-pager .v3-onb-grid{
  flex:0 0 auto;min-height:0 !important;
  grid-auto-rows:auto;
  align-content:start;
}
/* tiles are a fixed 1:1 square so every page looks identical — incl. the last
   page with fewer than 9 portraits (rows no longer stretch to fill the height).
   The pager (overflow-y:auto) scrolls if the squares exceed the viewport. */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-onb-portrait{
  aspect-ratio:1;
  height:auto;min-height:0;
}
/* pager nav + page-info + action buttons: pinned, always visible */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-presence-nav,
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-onb-presence-foot,
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-presence-actions{
  flex:0 0 auto;
}
/* the actions row owns the bottom safe-area spacing now that presence
   has padding-bottom:0 (overrides the inline padding on the element) */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage .v3-presence-actions{
  padding:2px 22px max(18px, env(safe-area-inset-bottom,18px)) !important;
}

/* ============================================================
   V3 onb step 2 → Refine sub-step: undo the lock-to-viewport
   clamps above. The step-2 rules set #onbV3Stage to
   `height:100dvh + overflow:hidden + flex-column` so the
   grid stays one-page. But the Refine substep renders into
   the SAME #onbV3Stage (the user clicks REFINE without a fresh
   render() call, so the bg-class is still `is-step2`) and its
   content (head + portrait + textarea + chips + actions + Save)
   is taller than the viewport on mobile — buttons got clipped
   below the fold with no scroll escape (#97 report).
   Detect the substep via `:has(.v3-onb-refine-stage)` and let
   the stage grow + the view's overflow-y:auto take over.
   ============================================================ */
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage:has(.v3-onb-refine-stage){
  display:block !important;
  height:auto !important;
  min-height:0 !important;
  max-height:none !important;
  overflow:visible !important;
}
#view-onb-v3 .v3-onb-bg.is-step2 ~ #onbV3Stage:has(.v3-onb-refine-stage) > div{
  flex:0 0 auto;
  display:block;
  min-height:0;
}
/* Safety: also let the view itself scroll (inline overflow-y:auto is
   already applied by onboarding_v3.js render() — this just guards
   against any later rule re-locking it). */
body[data-v3-step="2"] #view-onb-v3:has(.v3-onb-refine-stage){
  overflow-y:auto !important;
}

/* ============================================================
   In-app "Change their portrait" — V3 presence picker reused
   post-login. View id is #view-portrait; the .v3-onb shell from
   the onb path is reused with a `.portrait-v3` modifier so the
   dark background + step-1/3 photo overlays don't bleed in. The
   .view default already has overflow-y:auto so vertical scroll
   works inside the in-app frame; we just have to make sure
   .v3-onb sizes to content (not 100vh + overflow:hidden, which
   was clipping the REFINE/NEXT actions row below the fold).
   ============================================================ */
#view-portrait .v3-onb.portrait-v3{
  /* Override the dark shell (#0f0c0a default in .v3-onb) — we want
     the same warm cream as the onboarding step-2 view. */
  background:#fdfaf2;
  /* CRITICAL: kill the inherited 100vh + overflow:hidden from the
     base .v3-onb rule — that combination clipped the actions row
     (REFINE + NEXT) below the visible fold. The in-app shell's
     own .view-host owns the height; we just want this element to
     grow with its content so all sections render and the .view
     scrolls them into view when they don't fit the viewport. */
  min-height:auto;
  height:auto;
  overflow:visible;
}
#view-portrait .v3-onb.portrait-v3 .portrait-v3-bg{
  /* Soft cream band behind the title — mirrors the 60px reserve
     used by .v3-onb-bg.is-step2 in the onb flow. No photo. */
  background:#fdfaf2;
  background-image:none;
  filter:none;
  height:60px;
  bottom:auto;
}
/* Stage just stacks its rendered sections — no flex, no overflow
   clamps, no implicit min-height. Let height come from content. */
#view-portrait .v3-onb.portrait-v3 .v3-onb-stage{
  position:relative;z-index:2;
  display:block;
}
/* In the in-app shell the V3 grid stretches naturally — let the 3×3
   page settle at its aspect-ratio default (no height clamp). */
#view-portrait .v3-onb.portrait-v3 .v3-onb-portrait{
  aspect-ratio:3/4;
  height:auto;
  min-height:0;
}
/* Actions row — Refine + NEXT stacked, full width inside their container. */
#view-portrait .v3-onb.portrait-v3 .v3-presence-actions{
  display:flex;flex-direction:column;gap:10px;
  padding:6px 22px max(20px, env(safe-area-inset-bottom,20px));
}
/* Pager doesn't need a min-height in-app — that 330–360px reservation
   was sized for the onb step's exact-viewport layout. Let it size to
   the rendered 3×3 page so dots + actions show right under it. */
#view-portrait .v3-onb.portrait-v3 .v3-presence-pager{
  overflow:visible;
}
#view-portrait .v3-onb.portrait-v3 .v3-presence-pager .v3-onb-grid{
  min-height:auto !important;
}
/* The in-app picker still ships a tabbar/sidebar (it's in APP_VIEWS),
   so leave room above the bottom dock on small screens. */
@media (max-width:899px){
  body.in-app #view-portrait .v3-onb.portrait-v3 .v3-presence-actions{
    padding-bottom:calc(82px + env(safe-area-inset-bottom,0px));
  }
}

/* Desktop / tablet (≥900px): the in-app shell is sidebar + full-width
   main, so without a clamp the V3 grid blows up to 1500px-wide tiles
   (the broken state in Image #93). Constrain the picker to a centered
   480px card so it matches profile/customize/upgrade. */
@media (min-width:900px){
  #view-portrait .v3-onb.portrait-v3{
    padding:32px 28px 40px;
    background:transparent;
  }
  /* The card surface — rounded sheet centered in the content area.
     No overflow:hidden so the contained actions row is always visible;
     no fixed height so the card grows with its sections. */
  #view-portrait .v3-onb.portrait-v3 .v3-onb-stage{
    width:100%;
    max-width:480px;
    margin:0 auto;
    background:#fdfaf2;
    border:1px solid var(--line);
    border-radius:24px;
    box-shadow:var(--shadow-sm);
    padding:20px 0 16px;
    position:relative;
  }
  /* Hide the 60px sticky cream band — the card itself is the surface. */
  #view-portrait .v3-onb.portrait-v3 .portrait-v3-bg{display:none}
  /* Back arrow pinned to the card's top-left corner (not the viewport). */
  #view-portrait .v3-onb.portrait-v3 > .v3-onb-back{
    position:absolute;
    top:46px;
    left:calc(50% - 240px + 16px);
    transform:none;
  }
  /* Trim the inner padding so the title sits closer to the card top — the
     62px from .v3-onb-presence was sized for a full-bleed onb step. */
  #view-portrait .v3-onb.portrait-v3 .v3-onb-presence{
    padding:32px 18px 12px;
    background:transparent !important;
  }
  /* Pager + grid stay inside the 480-px card → 3 cols × ~140px tiles. */
  #view-portrait .v3-onb.portrait-v3 .v3-presence-pager{
    padding:0 14px;
  }
  #view-portrait .v3-onb.portrait-v3 .v3-onb-grid{
    grid-template-columns:repeat(3, 1fr);
    gap:10px;
    padding:0 !important;
  }
  /* Action row hugs the card bottom on desktop (no tabbar). */
  #view-portrait .v3-onb.portrait-v3 .v3-presence-actions{
    padding:8px 18px 20px;
  }
  /* Page info text + nav row fit the narrower card width. */
  #view-portrait .v3-onb.portrait-v3 .v3-presence-nav{
    padding:12px 14px 2px;
  }
}


/* ============================================================
   Login + onboarding: give the desktop margin the SAME backdrop as
   the splash (หน้าแรก) — cream radial gradient + soft botanical leaf
   overlay — so the centered phone-frame no longer floats on a flat
   cream canvas. Desktop-only: on mobile the frame fills the viewport
   so there is no margin to decorate. Mirrors #view-splash.view.active
   (radial) + #view-splash::before (splash-bg.png, soft-light).
   ============================================================ */
@media (min-width:900px){
  #view-login.view,
  #view-login.view.active,
  #view-onb-v3.view,
  #view-onb-v3.view.active{
    background:
      radial-gradient(ellipse 60% 80% at 50% 0%,
        rgba(241,234,215,1) 0%,
        rgba(232,220,196,1) 45%,
        rgba(212,192,156,1) 100%) !important;
  }
  #view-login::before,
  #view-onb-v3::before{
    content:"";
    position:absolute;
    inset:0;
    background-image:url('/static/images/splash-bg.png');
    background-size:cover;
    background-position:center;
    opacity:.34;
    mix-blend-mode:soft-light;
    pointer-events:none;
  }
}


/* ============================================================
   Chat input focus ring — put it on the FIELD (.pill), not the
   inner <input>. The input sits inside the pill's asymmetric padding
   (18px left / 8px right), so its focus outline drew a rounded-rect
   floating INSIDE the white field instead of hugging the field edge
   (the "orange border ≠ field" issue). Suppress the input outline and
   ring the pill itself — box-shadow follows the pill's 999px radius
   exactly, so it hugs the field's real edge.
   ============================================================ */
.v3-chat-input input:focus,
.v3-chat-input input:focus-visible{
  outline:none !important;
}
.v3-chat-input .pill:focus-within{
  box-shadow:0 0 0 2px #b8825c, 0 4px 14px rgba(0,0,0,.08) !important;
}

/* ============================================================
   Guest views — belt & suspenders.
   applyChrome() sets `tabbar.style.display = "none"` for views
   in GUEST_VIEWS, but a stray reset elsewhere could re-show it.
   Force-hide the bottom dock + sidebar whenever the body is NOT
   marked .in-app (which only the authed in-app views set).
   ============================================================ */
body:not(.in-app) #tabbar,
body:not(.in-app) #sidebar{
  display:none !important;
}
/* Splash + login should never scroll past their hero — they're
   single-screen entry views. Clamp body on these specifically
   (NOT on onb-v3 which needs natural mobile scroll). */
@media (max-width:899px){
  body:not(.in-app):not(.v3-onb-active){
    min-height:100dvh;
    max-height:100dvh;
    overflow:hidden;
  }
}

/* ============================================================
   iOS Safari auto-zoom prevention.
   Any focused input with computed font-size < 16px triggers
   iOS Safari to zoom the viewport in on focus — and iOS often
   refuses to zoom back out when the keyboard dismisses, leaving
   the page stuck zoomed and clipped (#99/#100 report on V3 onb
   step 1 + companion-name + nickname inputs).
   Force every form control on mobile to >=16px so iOS never
   zooms in. Desktop (≥900px) keeps the tighter typography from
   the component-specific rules. Tagged !important because the
   per-component rules use the same property and need to lose at
   this breakpoint.
   ============================================================ */
@media (max-width:899px){
  input[type="text"],
  input[type="email"],
  input[type="tel"],
  input[type="number"],
  input[type="date"],
  input[type="time"],
  input[type="datetime-local"],
  input[type="search"],
  input[type="url"],
  input[type="password"],
  input:not([type]),
  textarea,
  select{
    font-size:16px !important;
  }
}

/* ============================================================
   V3 onb step 3 — placeholder overlay for empty date/time inputs.
   iOS Safari ignores the `placeholder` attribute on type=date and
   type=time, so onboarding_v3.js renders a sibling <span> with
   the placeholder text and toggles --hidden via a `change`/`input`
   listener. Position + typography must match the native
   ::placeholder on the Country of Birth field exactly (#105 user
   wants visual parity).
   ============================================================ */
.v3-onb-input-row{position:relative}
.v3-onb-date-placeholder{
  position:absolute;
  left:42px; right:14px;
  top:50%; transform:translateY(-50%);
  color:rgba(26,22,18,.45);
  font-family:inherit;font-size:14.5px;font-weight:400;
  line-height:normal;letter-spacing:normal;
  pointer-events:none;
  white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.v3-onb-date-placeholder--hidden{display:none}
/* Step 3 scopes the input to 13px + padding-left:38px (the high-
   specificity rules at #view-onb-v3 .v3-onb-bg.is-step3 …). Mirror
   the overlay so it matches the Country of Birth placeholder
   exactly — same font-size, same left edge. */
#view-onb-v3 .v3-onb-bg.is-step3 ~ #onbV3Stage .v3-onb-date-placeholder{
  font-size:13px !important;
  left:38px;
  right:14px;
}

/* ============================================================
   V3 onb mobile — let the document scroll naturally on every step.
   Years of layer-by-layer fixes left ~10 conflicting !important
   rules locking body to 100vh + overflow:hidden when v3-onb-active.
   The toughest competitor was
     body[data-v3-step="3"].v3-onb-active { height:100vh !important }
   at specificity (0,2,1). We use selector specificity (0,2,2)
   via the `html` prefix so we definitively win every step.
   #106 report: NEXT button at y=818 was clipped at the body edge
   (844) with no scroll escape because the body was hard-locked.
   ============================================================ */
@media (max-width:899px){
  html body.v3-onb-active,
  html body[data-v3-step="3"].v3-onb-active,
  html body[data-v3-step="3"],
  html body.v3-onb-active .app-shell,
  html body.v3-onb-active .content-area,
  html body.v3-onb-active .view-host,
  html body.v3-onb-active #view-onb-v3,
  html body[data-v3-step="3"] .app-shell,
  html body[data-v3-step="3"] .content-area,
  html body[data-v3-step="3"] .view-host,
  html body[data-v3-step="3"] #view-onb-v3{
    height:auto !important;
    max-height:none !important;
    min-height:100dvh !important;
    overflow:visible !important;
  }
  html:has(body.v3-onb-active){
    height:auto !important;
    max-height:none !important;
    overflow:hidden auto !important;
  }
  /* The .view default has position:absolute + inset:0. On mobile
     we need it position:relative so it flows in document, with
     min-height so it fills the viewport on initial load. */
  html body.v3-onb-active #view-onb-v3,
  html body[data-v3-step="3"] #view-onb-v3{
    position:relative !important;
    inset:auto !important;
  }
}

/* ============================================================
   Re-do introductions, step 3 — fill the empty cream gap.
   The signup step-3 reserves 30dvh for the photo + ~25dvh for
   the form (email/phone/OAuth). Redo strips the account block,
   so the form is only ~40dvh tall — leaving an awkward cream
   band between the photo and the headline.
   Push the photo to ~48dvh and let the hero text + birth block
   slide right up against it. Scoped to `body.v3-redo-active`
   so the signup layout stays untouched.
   ============================================================ */
body.v3-redo-active[data-v3-step="3"] #view-onb-v3 .v3-onb-bg.is-step3{
  height:48vh !important;
  height:48dvh !important;
  max-height:48dvh !important;
}
body.v3-redo-active[data-v3-step="3"] #view-onb-v3 .v3-onb-bg.is-step3 ~ #onbV3Stage{
  padding-top:42vh !important;
  padding-top:42dvh !important;
}
/* Trim hero top padding so the headline sits right under the photo. */
body.v3-redo-active[data-v3-step="3"] #view-onb-v3 .v3-onb-hero{
  padding-top:14px !important;
  padding-bottom:10px !important;
}
/* Position:cover the bg so it fills the larger 48dvh frame cleanly
   (default background-size:cover already does this — explicit for
   any rule that may have changed it). */
body.v3-redo-active[data-v3-step="3"] #view-onb-v3 .v3-onb-bg.is-step3{
  background-size:cover !important;
  background-position:center 30% !important;
}

