/* JainBandhu - mobile-first styles */
:root {
  --primary: #b02a2a;      /* deep red/maroon */
  --primary-dark: #8b1e1e;
  /* Unread badge. Separate from --primary because dark mode lightens --primary
     to #e05a5a, where white text drops to 3.63 contrast and fails. */
  --badge-bg: #b02a2a;
  /* Primary button background. Separate from --primary so dark mode can keep
     it dark enough for white text — see the .btn rule. */
  --btn-bg: #b02a2a;
  --accent: #e8a020;       /* saffron gold */
  /* The header needs its OWN background, not --primary. In dark mode --primary
     is deliberately lightened (it is a foreground colour there, read against a
     near-black page), and the header was using it as a background — so the bar
     got lighter in dark mode and the gold "Bandhu" fell to 1.64 contrast,
     effectively invisible. A maroon header is the brand; it should not invert. */
  --header-a: #b02a2a; --header-b: #8b1e1e;
  /* Brighter than --accent: the standard saffron manages only 2.95 on the
     header maroon. This clears 4.5 on both themes' lightest header end. */
  --logo-accent: #ffd27a;
  --greet-fg: #f4d9a8;
  --bg: #fdf8f2;
  --card: #ffffff;
  --text: #33241f;
  /* Nudged darker from #8a7568: at the old value, secondary text (profile
     meta lines, hints, taglines) sat just under the readability threshold on
     white, which is hard work on a phone in daylight or for older eyes. */
  --muted: #7f6a5c;
  --border: #ead9c8;
  --radius: 12px;
  --chip-ok-bg: #e8f7ee;   --chip-ok-fg: #177245;
  /* Solid "connected" green. Dark mode flips to dark text on a light green,
     because white on a light green fails contrast badly. */
  --ok-solid-bg: #15703c;  --ok-solid-fg: #ffffff;
  --chip-warm-bg: #faf1e2; --chip-warm-fg: #8a5a00;
  --chip-info-bg: #d6eaff; --chip-info-fg: #1565c0;
  --skeleton: #efe3d5;
  --shadow: 0 2px 10px rgba(120,70,20,.07);
  /* Callout panels (kundli box, members-only notices) and status text.
     These need real dark-mode counterparts: a pale cream panel with the
     dark theme's near-white text on it is completely unreadable. */
  --note-bg: #fff7e6;   --note-border: #ffe0a3; --note-fg: #7a6a3a;
  --warn-bg: #fdebd0;   --warn-border: #f5cba7; --warn-fg: #8f5a08;
  --error-fg: #c0392b;  --success-fg: #1e8449;
  --field-bg: #ffffff;  /* inputs, dropdowns, modals, popovers */
  /* Placeholder text. Browsers default to a very light grey that washes out
     against the cream page, so "Search by name, city, work…" was barely there. */
  --placeholder: #5f4d42;   /* 7.99 on the white field */
  --divider: #f0e4d6;   /* the dashed hairlines between rows */

  /* Faint wedding-invitation motif (mango leaf, marigold dots, a small kalash)
     tiled across the page background, BEHIND the cards. The cards are opaque,
     so this never sits under text and can't affect readability. Drawn in code:
     under 1KB, sharp at any zoom, and adds no height to any page.
     TO REMOVE IT: change the value below to `none`. Nothing else to touch. */
  --page-motif: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Cpath d='M32 6 q-6 12 0 22 q6-10 0-22z' fill='%23b02a2a' fill-opacity='0.055'/%3E%3Ccircle cx='6' cy='38' r='2.6' fill='%23e8a020' fill-opacity='0.10'/%3E%3Ccircle cx='58' cy='38' r='2.6' fill='%23e8a020' fill-opacity='0.10'/%3E%3Cpath d='M26 52 q-3-9 2-13 h8 q5 4 2 13z' fill='%23b02a2a' fill-opacity='0.045'/%3E%3Ccircle cx='32' cy='36' r='2.2' fill='%23b02a2a' fill-opacity='0.05'/%3E%3C/svg%3E");
}

/* ---- Dark theme ----
   Applied by setting data-theme="dark" on <html>. Every colour the site uses
   is a variable, so only these need overriding — no component knows or cares
   which theme is active. Deliberately warm-toned rather than pure grey so it
   still feels like the same site rather than a different product. */
html[data-theme="dark"] {
  --primary: #e05a5a;
  --primary-dark: #c94545;
  --badge-bg: #c94545;     /* white text: 4.75 (dark --primary would be 3.63) */
  --accent: #e8a020;
  /* Darker maroon in dark mode, not lighter. */
  --header-a: #6d1717; --header-b: #4d0f0f;
  --logo-accent: #ffd27a;   /* 8.26 on #6d1717 */
  --greet-fg: #f4d9a8;      /* 8.59 on #6d1717 */
  --bg: #17131a;
  --card: #221c26;
  --text: #f5eee8;
  --muted: #a89aa2;
  --border: #362c3c;
  --chip-ok-bg: #1c3a2a;   --chip-ok-fg: #5fd99a;
  --ok-solid-bg: #5fd99a;  --ok-solid-fg: #17131a;
  --btn-bg: #c03c3c;
  --chip-warm-bg: #3a2f1c; --chip-warm-fg: #e8a020;
  --chip-info-bg: #16324f; --chip-info-fg: #79bbff;
  --skeleton: #2e2634;
  --shadow: 0 2px 10px rgba(0,0,0,.35);
  --note-bg: #2a2216;   --note-border: #4a3c20; --note-fg: #d9c9a3;
  --warn-bg: #3a2a14;   --warn-border: #5a4020; --warn-fg: #e8b563;
  --error-fg: #ff7b6b;  --success-fg: #5fd99a;
  --field-bg: #1c1720;
  --placeholder: #b9aca3;   /* 7.96 on the dark field */
  --divider: #362c3c;
  /* Same motif, drawn in a pale tint and dialled right down. A pattern that
     reads as "subtle texture" on cream starts to look like smudges on the
     screen against a near-black page, so dark mode needs its own strength
     rather than reusing the light-theme one. */
  --page-motif: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='64'%3E%3Cpath d='M32 6 q-6 12 0 22 q6-10 0-22z' fill='%23ffffff' fill-opacity='0.030'/%3E%3Ccircle cx='6' cy='38' r='2.6' fill='%23e8a020' fill-opacity='0.055'/%3E%3Ccircle cx='58' cy='38' r='2.6' fill='%23e8a020' fill-opacity='0.055'/%3E%3Cpath d='M26 52 q-3-9 2-13 h8 q5 4 2 13z' fill='%23ffffff' fill-opacity='0.025'/%3E%3Ccircle cx='32' cy='36' r='2.2' fill='%23ffffff' fill-opacity='0.03'/%3E%3C/svg%3E");
}
html[data-theme="dark"] .btn-secondary { background: var(--card); }
/* The jain flag's white stripe would otherwise vanish against a dark header. */
html[data-theme="dark"] .jain-flag { box-shadow: 0 0 0 1px rgba(255,255,255,.25); }
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
  font-family: 'Segoe UI', system-ui, -apple-system, 'Noto Sans', sans-serif;
  /* Motif tiles on top of the flat background colour, and the opaque cards
     then sit on top of both — so the pattern shows only in the margins and
     gaps, never behind any text.
     Deliberately NOT background-attachment:fixed: iOS Safari handles that
     badly and it causes visible scroll stutter on phones, which is a poor
     trade for a texture nobody is meant to consciously notice. */
  background-color: var(--bg);
  background-image: var(--page-motif);
  color: var(--text); line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* Nothing should ever be able to scroll the page sideways. A single
     too-wide element used to shift everything and hide controls at the
     right edge; individual scrollable strips (tab row, "new this week")
     still scroll internally, which is what we actually want.
     `clip` rather than `hidden` on purpose: `hidden` turns this into a
     scroll container, which silently breaks `position: sticky` on the
     header and the tab row below it. */
  overflow-x: clip;
}

/* ---- Header ---- */
header {
  background: linear-gradient(135deg, var(--header-a), var(--header-b));
  color: #fff; padding: 7px 16px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  position: sticky; top: 0; z-index: 50;
  /* Safety valve: with six controls (theme, A-, A+, and three languages) plus
     the logo, this row can exceed a narrow phone — especially once someone
     bumps the text size with A+. Wrapping is far better than overflowing,
     because horizontal overflow drags the WHOLE page sideways and cuts off
     the right-hand end of the tab row underneath. */
  flex-wrap: wrap;
}
/* The brand was the smallest thing in a header full of six controls. Text size
   and dark mode are set once and never touched again, so they moved behind a
   gear; language stayed on the surface because someone who reads only Kannada
   arrives at an English page and must be able to find the switch. */
.brand { display: flex; align-items: center; gap: 11px; min-width: 0; }
/* Brand logo in the header. The logo is used in its ORIGINAL colours (maroon
   "Jain" + gold "Bandhu"), so it sits on a white chip — maroon-on-maroon would
   be unreadable, and recolouring the artwork made it look washed out. */
.brand-link {
  display: flex; align-items: center; flex-shrink: 0; text-decoration: none;
  /* No shadow: the logo artwork already carries one, and doubling it up looked
     heavy against the maroon bar. */
  /* Cream, not white, and the exact cream from the logo file itself
     (#f5efe5, sampled from the master artwork). The header logo is cut from a
     JPEG master whose background cannot be keyed to transparency without
     leaving halos around its drop shadows, so the chip matches the logo's own
     background instead and the seam disappears. It also sits warmer against
     the maroon bar than stark white did. */
  background: #f5efe5;
  border-radius: clamp(9px, .6vw + 6px, 13px);
  padding: clamp(4px, .25vw + 2px, 6px) clamp(8px, .8vw + 4px, 13px);
}
/* Fluid rather than three hard steps. The logo is artwork with fine detail —
   the flag and the swastika — so at 24px on a phone it turned to mush, while
   34px left it looking undersized in a wide desktop header. clamp() grows it
   smoothly with the viewport and pins both ends, so it is never smaller than
   legible and never dominates the bar.
     375px phone -> 26px    768px tablet -> 34px    1200px+ desktop -> 44px */
.brand-logo-img {
  height: clamp(24px, 2vw + 13px, 36px);
  width: auto; display: block;
}
.brand-admin {
  color: #fff; font-size: 1.15rem; font-weight: 700; letter-spacing: .2px;
  text-shadow: 0 1px 2px rgba(0,0,0,.35); flex-shrink: 0;
}
/* No per-breakpoint logo heights any more — clamp() above covers every width
   continuously, including the sizes between these old breakpoints where the
   logo used to jump. */
/* Name and greeting sit on one line, baseline-aligned so "Jai Jinendra" rests
   on the same line as "JainBandhu" rather than floating mid-height. It wraps to
   a second line only when the header genuinely runs out of room. */
.brand-text { min-width: 0; display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; }
.logo {
  font-size: 1.6rem; font-weight: 700; letter-spacing: .2px; line-height: 1.05;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.logo span { color: var(--logo-accent); }
/* NOT .tagline: that class already exists further down for the landing page's
   own subtitle, is defined later, and would win — painting the greeting muted
   brown on the maroon header and adding a 20px margin under it. */
/* The greeting was set in the interface font at .82rem, which read as a status
   label rather than a welcome. Georgia is already the display face across the
   landing page, so using it here ties the header to the rest of the brand.
   Deliberately NOT italic: Georgia has no Kannada or Devanagari glyphs, so
   "ಜೈ ಜಿನೇಂದ್ರ" falls back to a system face and the browser would fake the
   slant, which looks broken. The Noto entries give that fallback a serif to
   land on before the generic one. */
.brand-greet {
  font-family: Georgia, 'Times New Roman', 'Noto Serif Kannada',
               'Noto Serif Devanagari', 'Noto Sans Kannada', serif;
  font-size: clamp(.82rem, .3vw + .74rem, 1.02rem);
  font-weight: 700;
  color: var(--greet-fg);
  letter-spacing: .4px;
  /* Matches the .logo text above it, so it sits on the gradient rather than
     floating flat on top of it. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  white-space: nowrap;
}
/* A small bow when the page opens, twice, then still. Deliberately NOT a
   forever loop: this sits in a sticky header on every screen, and something
   moving permanently in the corner of your eye stops reading as a greeting
   and starts reading as an advert. */
@keyframes jb-namaste {
  0%   { transform: scale(1)    rotate(0deg); }
  25%  { transform: scale(1.22) rotate(-8deg); }
  50%  { transform: scale(1.06) rotate(5deg); }
  75%  { transform: scale(1.12) rotate(-3deg); }
  100% { transform: scale(1)    rotate(0deg); }
}
.greet-hands {
  display: inline-block;      /* transforms do not apply to inline elements */
  transform-origin: 50% 80%;  /* pivots at the wrists, so it bows rather than spins */
  animation: jb-namaste 1.5s ease-in-out .5s 2 both;
}
/* Anyone who has asked their device for less motion gets none. Vestibular
   conditions make repeated movement genuinely unpleasant, not just annoying. */
@media (prefers-reduced-motion: reduce) {
  .greet-hands { animation: none; }
}

/* margin-left:auto matters more than it looks. The header is flex-wrap, so on a
   narrow phone this block drops onto a second line — and a wrapped flex line
   starts at the LEFT. The dropdowns below are anchored `right: 0` to this box,
   so once it sat on the left, a 150px-wide menu opened leftward straight off
   the screen edge (the language menu was clipped to "lish").
   An auto left margin absorbs the free space on whichever line this lands on,
   so it stays hard right whether the header wraps or not, and the menus always
   open inward. */
.hdr-actions {
  display: flex; align-items: center; gap: 6px; position: relative;
  flex-shrink: 0; margin-left: auto;
}
.hdr-btn {
  background: rgba(255,255,255,.16); border: none; color: #fff;
  padding: 7px 11px; border-radius: 8px; cursor: pointer; font-size: .85rem;
  display: inline-flex; align-items: center; gap: 4px; min-height: 38px;
}
.hdr-btn .caret { font-size: .7rem; opacity: .8; }
.hdr-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Dropdowns. position:absolute inside .hdr-actions, which is the only
   positioned ancestor, so they hang below the button that opened them. */
.hdr-menu {
  display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  background: var(--card); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 6px 20px rgba(0,0,0,.22); padding: 6px; min-width: 150px;
  /* Belt and braces for the case above: even if a future layout change puts
     this box somewhere unexpected, it can never be wider than the screen. */
  max-width: calc(100vw - 20px);
}
.hdr-menu.open { display: flex; flex-direction: column; gap: 2px; }
.hdr-menu button {
  background: none; border: none; color: var(--text); text-align: left;
  padding: 10px 12px; border-radius: 7px; cursor: pointer; font-size: .92rem;
  min-height: 42px;   /* comfortable touch target */
}
@media (hover: hover) { .hdr-menu button:hover { background: var(--chip-warm-bg); } }
.hdr-menu button.active { background: var(--chip-warm-bg); color: var(--chip-warm-fg); font-weight: 700; }

@media (max-width: 640px) {
  .logo { font-size: 1.32rem; }
  /* Narrower gap on a phone: the header has less room, and 16px there starts
     pushing the greeting onto a second line. */
  .brand-text { gap: 11px; }
  .brand { gap: 9px; }
  /* Scale the flag AND the mark on it together, or the swastika ends up
     oversized on the smaller flag. */
  .jain-flag { width: 40px; height: 31px; }
  .jf-swastika { width: 13px; height: 13px; }
}
/* Phones: the greeting is the first thing to go, never the name.
   This used to kick in only below 340px, which left every normal phone
   (360–414px) wrapping the header into two rows — logo on one line, language
   and settings on the next, with a band of empty maroon between. Without the
   greeting the logo and the two buttons fit on one row with room to spare, so
   wrapping is switched off here too: the safety valve is only needed when the
   greeting is present. */
@media (max-width: 520px) {
  .brand-greet { display: none; }
  header { flex-wrap: nowrap; }
}
.lang-btn.active { background: var(--accent); color: #4a2c00; font-weight: 600; }

/* ---- Layout ---- */
.container { max-width: 900px; margin: 0 auto; padding: 16px; }
.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 20px; margin-bottom: 16px;
  box-shadow: 0 2px 8px rgba(120,70,20,.06);
}
h1 { font-size: 1.4rem; margin-bottom: 8px; }
h2 { font-size: 1.15rem; margin-bottom: 12px; color: var(--primary); }
h3 { font-size: 1rem; margin: 16px 0 8px; color: var(--primary-dark); border-bottom: 1px solid var(--border); padding-bottom: 4px; }
.tagline { color: var(--muted); margin-bottom: 20px; }

/* ---- Landing hero (index.html) ----
   Explains the service before asking anyone to sign up. Sizes are in rem so
   they scale with the A-/A+ text-size control the rest of the site uses. */
.free-pill {
  display: inline-block; background: var(--chip-ok-bg); color: var(--chip-ok-fg);
  font-size: .72rem; font-weight: 800; letter-spacing: .04em;
  padding: 4px 11px; border-radius: 20px; margin-bottom: 10px;
}
.hero-title {
  font-size: 1.55rem; line-height: 1.22; letter-spacing: -.01em;
  color: var(--text); margin-bottom: 7px;
}
.hero-sub { color: var(--text); opacity: .8; font-size: .92rem; line-height: 1.5; margin-bottom: 15px; }
.stat-strip { display: flex; gap: 8px; margin-bottom: 15px; }
.stat-box {
  flex: 1; background: var(--bg); border-radius: 10px;
  padding: 9px 6px; text-align: center;
}
.stat-box b { display: block; color: var(--primary); font-size: 1.15rem; line-height: 1.2; }
.stat-box span { font-size: .66rem; color: var(--muted); }
.hero-alt { text-align: center; font-size: .85rem; color: var(--muted); margin: 9px 0 0; }
.hero-alt a { color: var(--primary); font-weight: 700; text-decoration: none; }
.hero-alt a:hover { text-decoration: underline; }
.feature-grid {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 8px;
  border-top: 1px solid var(--border); margin-top: 15px; padding-top: 13px;
}
.feature-grid > div {
  display: flex; align-items: center; gap: 9px;
  font-size: .82rem; font-weight: 700; color: var(--text);
}
/* At 15px these read as decoration rather than as a feature list. The icon now
   sits in a soft circular chip: the padding plus border-radius on the <svg>
   itself makes the circle, so no extra wrapper element was needed in six
   places of markup. box-sizing must be content-box here, because the global
   `* { box-sizing: border-box }` would otherwise eat the padding out of the
   19px and leave a 5px icon. */
.feature-grid svg {
  width: 19px; height: 19px; padding: 7px; box-sizing: content-box; flex-shrink: 0;
  background: var(--chip-warm-bg); border-radius: 50%;
  /* chip-warm-fg, not --primary: on the dark chip --primary only reaches 3.60,
     and this pairing is already contrast-checked by the test suite. */
  fill: none; stroke: var(--chip-warm-fg); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

/* "Forgot password", tucked under the field it belongs to rather than
   competing with the primary actions below the button. */
.field-aside { display: flex; justify-content: flex-end; margin-top: 2px; }
.quiet-link {
  background: none; border: none; cursor: pointer; padding: 2px;
  color: var(--muted); font-size: .78rem;
}
@media (hover: hover) { .quiet-link:hover { color: var(--primary); text-decoration: underline; } }

/* Separates the primary action from the genuine alternative below it. */
.or-divider { display: flex; align-items: center; gap: 10px; margin: 16px 0 12px; }
.or-divider::before, .or-divider::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.or-divider span { font-size: .72rem; color: var(--muted); }

/* Recently married, as a small testimonial rather than a bare list row. */
.story-card {
  background: var(--bg); border-radius: 12px;
  padding: 12px 13px; margin-top: 15px;
}
.story-names { font-size: .88rem; font-weight: 800; color: var(--text); }
.story-meta { font-size: .74rem; color: var(--muted); margin-top: 1px; }
.story-quote {
  margin-top: 9px; padding-left: 10px;
  border-left: 3px solid var(--accent);
  font-size: .8rem; font-style: italic; color: var(--text); opacity: .9; line-height: 1.45;
}

/* Secondary links, as one wrapping row rather than a stack of buttons. */
.mini-links {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 4px 14px; padding: 2px 8px 6px; margin-top: -6px;
}
.mini-links a {
  color: var(--muted); font-size: .78rem; text-decoration: none;
}
@media (hover: hover) { .mini-links a:hover { color: var(--primary); text-decoration: underline; } }
.story-head {
  font-size: .68rem; font-weight: 800; color: var(--muted);
  letter-spacing: .05em; text-transform: uppercase; margin-bottom: 9px;
}
.story-row { display: flex; align-items: center; gap: 10px; }
.story-avatars { display: flex; flex-shrink: 0; }
.story-avatars .av {
  width: 34px; height: 34px; border-radius: 50%; object-fit: cover;
  background: var(--border); display: flex; align-items: center; justify-content: center;
  font-size: .8rem; font-weight: 700; color: var(--primary);
}
.story-avatars .av + .av { margin-left: -10px; border: 2px solid var(--card); }
.story-text { font-size: .8rem; color: var(--text); opacity: .85; line-height: 1.4; min-width: 0; }
.story-text em { color: var(--muted); font-style: normal; }
@media (min-width: 560px) { .hero-title { font-size: 1.75rem; } }

/* ---- Forms ---- */
label { display: block; font-size: .85rem; font-weight: 600; margin: 10px 0 4px; }
input, select, textarea {
  /* min-width:0 stops a control (notably <input type="date">, which has a
     wide built-in minimum) from forcing its container wider than the screen. */
  width: 100%; min-width: 0; max-width: 100%; padding: 10px 12px; font-size: 1rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--field-bg);
  color: var(--text);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
textarea { resize: vertical; min-height: 70px; }
/* --btn-bg, not --primary.
   In dark mode --primary is deliberately LIGHTENED to #e05a5a, because there it
   is a foreground colour — headings and links read against a near-black page.
   Used as a button background under white text that gave 3.63 contrast, below
   the 4.5 needed, on every primary button on the site. --btn-bg stays a button
   background in both themes: white on it measures 6.54 light / 5.31 dark, and
   it still stands clear of the dark page at 3.45. */
.btn {
  display: inline-block; width: 100%; margin-top: 16px; padding: 12px;
  background: var(--btn-bg); color: #fff; border: none; border-radius: 8px;
  font-size: 1rem; font-weight: 600; cursor: pointer;
}
.btn-secondary { background: transparent; color: var(--primary); border: 1px solid var(--primary); }
.btn-small { width: auto; padding: 8px 16px; font-size: .9rem; margin-top: 0; }

/* Hover states live inside @media (hover: hover) so they only apply to devices
   with a real pointer. On a touchscreen the :hover state LATCHES after a tap
   and stays until you tap somewhere else, so a tapped button used to sit there
   looking permanently pressed. That is what made "Remove Connection" appear to
   turn the whole row red after you used it. */
@media (hover: hover) {
  .btn:hover { background: var(--primary-dark); }
  /* An outline button has to invert its TEXT as well as its background. The
     shared .btn:hover rule above beats .btn-secondary on specificity, so it
     used to swap in a dark maroon background while the text stayed maroon:
     1.39 contrast, i.e. a solid red block with the label invisible inside it.
     Must stay after .btn:hover, since both are (0,2,0) and order decides. */
  .btn-secondary:hover {
    background: var(--primary-dark); color: #fff; border-color: var(--primary-dark);
  }
  /* Dark mode sets .btn-secondary's background with a more specific selector
     (html[data-theme] + class), so the override needs to be specific too. */
  html[data-theme="dark"] .btn-secondary:hover {
    background: var(--primary-dark); color: #fff; border-color: var(--primary-dark);
  }
}
/* Touch devices get a press animation instead: it is tied to the finger being
   down, so it cannot latch. */
.btn:active { transform: scale(.97); }
.link-btn { background: none; border: none; color: var(--primary); cursor: pointer; margin-top: 12px; text-decoration: underline; font-size: .9rem; display: block; }
.error { color: var(--error-fg); font-size: .9rem; margin-top: 10px; min-height: 1.2em; }
.success { color: var(--success-fg); font-size: .9rem; margin-top: 10px; }
.grid2 { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 0 12px; }

/* ---- Tabs / nav ---- */
.tabs {
  display: flex; border-bottom: 2px solid var(--border); margin-bottom: 16px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: 1 0 auto; text-align: center; padding: 10px 14px; cursor: pointer;
  background: none; border: none; font-size: 1rem; color: var(--muted); font-weight: 600;
  white-space: nowrap;
}
.tab.active { color: var(--primary); border-bottom: 3px solid var(--accent); }

/* Unread badge on the Interests tab.
   Rendered as ::after from a data attribute rather than as a child element,
   because applyI18n() rewrites each tab's textContent when the language
   changes, which would silently delete a child node on every switch. */
.tab, .bottom-nav button { position: relative; }
#navInterests[data-badge]::after, #bnInterests[data-badge]::after {
  content: attr(data-badge);
  position: absolute; top: 1px; right: 1px;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  border-radius: 9px; background: var(--badge-bg); color: #fff;
  font-size: .68rem; font-weight: 700; line-height: 18px; text-align: center;
  /* Ring in the page colour so the badge reads as separate from the label
     underneath it whichever theme is active. */
  box-shadow: 0 0 0 2px var(--bg);
}

/* ---- Search results ---- */
.result-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
.profile-card {
  display: flex; gap: 14px; align-items: center;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 16px; padding: 14px; cursor: pointer;
  box-shadow: var(--shadow);
  transition: box-shadow .18s ease, transform .12s ease, border-color .18s ease;
}
@media (hover: hover) {
  .profile-card:hover { box-shadow: 0 6px 18px rgba(120,70,20,.13); border-color: var(--accent); }
}
/* Tactile press feedback — the card visibly reacts to a tap instead of
   feeling like a dead rectangle until the next screen appears. */
.profile-card:active { transform: scale(.985); }
.pc-avatar-wrap { position: relative; flex-shrink: 0; }
.avatar {
  width: 84px; height: 84px; border-radius: 22px; object-fit: cover;
  background: var(--border); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.7rem; color: var(--primary); font-weight: 800;
}
.pc-tick {
  position: absolute; bottom: -3px; right: -3px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--chip-info-fg); color: var(--card);
  border: 2.5px solid var(--card);
  display: flex; align-items: center; justify-content: center;
  font-size: .68rem; font-weight: 800;
}
.pc-info { flex: 1; min-width: 0; }
/* A name is user input, so it can be one unbroken 40-character run. Without
   this the card refuses to be narrower than that word, the text runs straight
   through the card border, and the whole PAGE picks up a horizontal scrollbar
   — the layout damage is never confined to the card. `anywhere` (not
   `break-word`) is what also lets the flex/grid column shrink around it. */
.pc-name {
  font-weight: 800; font-size: 1.12rem; letter-spacing: -.01em;
  overflow-wrap: anywhere; word-break: break-word; min-width: 0;
}
.pc-name .pc-age { font-weight: 500; color: var(--muted); font-size: .95rem; }
.pc-meta { color: var(--muted); font-size: .85rem; margin-top: 1px; }

/* Everything below renders text a MEMBER typed — names, city, occupation, sect,
   the story line. Any of them can arrive as one long unbroken run (a pasted
   value, a mistyped surname), and a single unbreakable word sets the minimum
   width of its whole column. That is what put a horizontal scrollbar on the
   page and pushed text through the card border, so this is a layout guard, not
   a cosmetic one. Grouped in one place so a new text element is easy to add. */
.pc-name, .pc-meta, .pc-sub, .story-names, .story-card, .chip, .badge,
.int-row .pc-info, .lp-title {
  overflow-wrap: anywhere;
}
.pc-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 7px; }
.badge {
  display: inline-block; background: var(--chip-warm-bg); color: var(--chip-warm-fg);
  font-size: .7rem; padding: 3px 9px; border-radius: 20px;
}

/* ---- Motion ----
   Results fade up as they arrive rather than snapping in. Kept short (~.26s)
   so it reads as responsive, not sluggish. */
@keyframes jbFadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.fade-up { animation: jbFadeUp .26s ease both; }

/* ---- Skeleton loading ----
   Grey placeholders while a search runs, instead of an empty gap. Makes the
   wait feel shorter and shows the page is working rather than broken. */
@keyframes jbShimmer { 0% { opacity: .55; } 50% { opacity: 1; } 100% { opacity: .55; } }
.skel-card {
  display: flex; gap: 14px; align-items: center;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 16px; padding: 14px;
}
.skel { background: var(--skeleton); border-radius: 8px; animation: jbShimmer 1.2s ease-in-out infinite; }
.skel-avatar { width: 84px; height: 84px; border-radius: 22px; flex-shrink: 0; }
.skel-lines { flex: 1; min-width: 0; }
.skel-line { height: 12px; margin-bottom: 8px; }

/* Anyone who has asked their device to reduce motion gets none of it. */
@media (prefers-reduced-motion: reduce) {
  .fade-up { animation: none; }
  .skel { animation: none; }
  .profile-card:active { transform: none; }
  * { scroll-behavior: auto !important; }
}

/* ---- New this week ---- */
.new-row { display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 8px; scroll-snap-type: x mandatory; }
.new-row::-webkit-scrollbar { height: 6px; }
.new-row::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.new-card {
  flex: 0 0 auto; width: 92px; text-align: center; cursor: pointer;
  scroll-snap-align: start; transition: transform .12s ease;
}
.new-card:active { transform: scale(.96); }
.new-card .avatar { width: 72px; height: 72px; border-radius: 20px; margin: 0 auto 6px; font-size: 1.4rem; }
.new-card .nm { font-size: .78rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.new-card .ct { font-size: .68rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- Long profile form: collapsible sections ----
   Same fields as before, just grouped so the page stops reading as one
   endless column and progress becomes visible. */
.fsec { border: 1px solid var(--border); border-radius: 12px; margin: 12px 0; overflow: hidden; }
.fsec-head {
  width: 100%; display: flex; align-items: center; gap: 9px;
  background: var(--bg); border: none; cursor: pointer;
  padding: 12px 13px; text-align: left; color: var(--text); font: inherit;
}
@media (hover: hover) { .fsec-head:hover { background: var(--chip-warm-bg); } }
.fsec-tick {
  width: 18px; height: 18px; flex-shrink: 0; border-radius: 50%;
  border: 2px solid var(--border);
}
.fsec.done .fsec-tick {
  background: var(--chip-ok-fg); border-color: var(--chip-ok-fg);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l6 6L20 6'/%3E%3C/svg%3E");
  background-size: 12px; background-repeat: no-repeat; background-position: center;
}
.fsec-title { flex: 1; min-width: 0; font-weight: 800; font-size: .95rem; color: var(--primary-dark); }
.fsec-count {
  flex-shrink: 0; font-size: .7rem; color: var(--muted);
  background: var(--card); padding: 3px 9px; border-radius: 20px; font-variant-numeric: tabular-nums;
}
.fsec-chev { flex-shrink: 0; color: var(--muted); transition: transform .18s; }
.fsec.open .fsec-chev { transform: rotate(180deg); }
.fsec-body { display: none; padding: 4px 13px 14px; }
.fsec.open .fsec-body { display: block; }

/* ---- Sticky save bar ----
   The form's own Save button sits ~40 fields down and scrolls out of sight,
   so people filled everything in and then navigated away. This keeps saving
   in reach and says plainly whether there's anything outstanding. */
.save-bar {
  position: sticky; bottom: 0; z-index: 46;
  display: flex; align-items: center; gap: 10px;
  background: var(--card); border-top: 1px solid var(--border);
  padding: 10px 14px; margin: 0 -20px -20px;
  box-shadow: 0 -3px 12px rgba(120,70,20,.09);
}
.save-bar .btn { margin-top: 0; width: auto; flex-shrink: 0; }
.save-state { flex: 1; min-width: 0; font-size: .8rem; color: var(--muted); }
.save-state.dirty { color: var(--warn-fg); font-weight: 700; }
.save-state.dirty::before { content: "● "; }
/* "All changes saved" used to be muted grey — quieter than the unsaved state,
   which is backwards: this is the one moment a member is looking at the bar
   for reassurance. A green pill with a tick reads as "done" at a glance and
   survives greyscale (the tick carries it, not the colour). */
.save-state.saved {
  flex: 0 1 auto; display: inline-flex; align-items: center; gap: 6px;
  color: var(--chip-ok-fg); background: var(--chip-ok-bg);
  font-weight: 700; padding: 5px 11px; border-radius: 20px;
}
.save-state.saved::before {
  content: ""; width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;
  background: var(--chip-ok-fg);
  /* the tick, drawn as a mask so it takes the same colour as the text */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='8' fill='black'/%3E%3Cpath d='M4.5 8.3l2.3 2.3 4.7-4.9' fill='none' stroke='white' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='8' fill='black'/%3E%3Cpath d='M4.5 8.3l2.3 2.3 4.7-4.9' fill='none' stroke='white' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
}
/* Brief lift the moment a save lands, so the change from amber to green is
   noticed rather than just happening somewhere in the corner of the eye. */
.save-state.just-saved { animation: savedPop .45s ease-out; }
@keyframes savedPop {
  0%   { transform: scale(.92); opacity: .4; }
  60%  { transform: scale(1.05); opacity: 1; }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) { .save-state.just-saved { animation: none; } }
/* The form's original button is redundant once the bar exists, but it stays in
   the DOM so pressing Enter in a field still submits. */
#profileSaveBtn { display: none; }
@media (max-width: 640px) { .save-bar { margin: 0 -14px -14px; } }

/* ---- Profile detail sections ----
   Each block gets an icon, a title and a filled/total count, so a long
   profile reads as a few distinct groups rather than one flat list. Every
   field is still rendered, filled or not; the count simply explains a run of
   blanks instead of leaving the reader wondering if something failed. */
/* Header: portrait, identity, and the facts people scan for first as chips. */
.p-top { display: flex; gap: 15px; align-items: center; margin-bottom: 15px; }
.p-portrait {
  width: 84px; height: 84px; border-radius: 50%; flex: none;
  object-fit: cover; font-size: 1.7rem;
}
/* The portrait is a button now — it opens the photo viewer. Strip the
   button chrome so it still reads as a plain round photo, and give it the
   affordances a photo you can open should have: a pointer, a focus ring for
   keyboard users, and a small lift on hover. */
.portrait-btn {
  padding: 0; border: none; background: none; flex: none; line-height: 0;
  border-radius: 50%; cursor: zoom-in; transition: transform .12s ease;
}
.portrait-btn:hover { transform: scale(1.04); }
.portrait-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .portrait-btn { transition: none; }
  .portrait-btn:hover { transform: none; }
}

.p-ident { min-width: 0; }
.p-name { font-size: 1.3rem; font-weight: 800; letter-spacing: -.015em; margin: 0; }
.p-name .p-age { font-weight: 500; color: var(--muted); font-size: 1.05rem; }
.p-place { font-size: .82rem; color: var(--muted); margin: 3px 0 9px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  background: var(--bg); border: 1px solid var(--border); border-radius: 999px;
  padding: 4px 11px; font-size: .74rem; font-weight: 600; color: var(--text);
}
.chip-verified { background: var(--chip-info-bg); border-color: var(--chip-info-bg); color: var(--chip-info-fg); }
.chip-match { background: var(--chip-ok-bg); border-color: var(--chip-ok-bg); color: var(--chip-ok-fg); font-weight: 700; }

/* Three columns on a wide screen, one on a phone. Hairlines between columns
   rather than boxed cards keeps it dense without feeling boxed-in. */
.dv-cols { display: grid; grid-template-columns: minmax(0,1fr); }
.dv-sec { padding: 14px 0; border-top: 1px solid var(--border); }
.dv-eyebrow {
  font-size: .66rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--accent); font-weight: 700; margin-bottom: 10px;
  display: flex; align-items: center; gap: 8px;
}
.dv-eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--divider); }
.dv-rows { display: flex; flex-direction: column; }
.dv-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
  padding: 7px 0; border-bottom: 1px solid var(--divider); font-size: .88rem;
}
.dv-row:last-child { border-bottom: none; }
.dv-k { color: var(--muted); flex-shrink: 0; }
.dv-v { font-weight: 700; text-align: right; min-width: 0; overflow-wrap: anywhere; }
/* What hasn't been filled in, named once and quietly, instead of a column of
   dashes the reader has to scroll past to discover there was nothing there. */
.dv-absent { margin-top: 10px; font-size: .76rem; color: var(--muted); opacity: .8; line-height: 1.45; }
@media (min-width: 700px) {
  .dv-cols { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .dv-cols .dv-sec { padding: 14px 16px; border-left: 1px solid var(--divider); }
  .dv-cols .dv-sec:nth-child(3n+1) { border-left: 0; padding-left: 0; }
}

/* ---- Show / hide password ---- */
.pw-wrap { position: relative; }
/* Room for the button so long passwords don't run underneath it. */
.pw-wrap input { padding-right: 46px; }
.pw-eye {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 36px; height: 36px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; cursor: pointer;
  color: var(--muted); border-radius: 8px;
}
@media (hover: hover) { .pw-eye:hover { color: var(--primary); background: var(--bg); } }
.pw-eye:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- Full-screen photo viewer ----
   Replaces target="_blank" links, which on a phone opened a new browser tab
   per photo: no close control, no way back, and tabs accumulating. */
.photo-viewer {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(12, 8, 14, .93);
  display: flex; align-items: center; justify-content: center;
  padding: 56px 12px;
}
.pv-img {
  max-width: 100%; max-height: 100%;
  object-fit: contain; border-radius: 10px;
  /* Don't let a tap on the photo itself fall through and close the viewer. */
  pointer-events: none;
}
.pv-close, .pv-nav {
  position: absolute; background: rgba(255,255,255,.14); color: #fff;
  border: none; cursor: pointer; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  line-height: 1; padding: 0;
}
@media (hover: hover) { .pv-close:hover, .pv-nav:hover { background: rgba(255,255,255,.26); } }
/* Generous hit areas: these are the controls people reach for first, and a
   small X in a corner is the usual reason a viewer feels like a trap. */
.pv-close { top: 12px; right: 12px; width: 46px; height: 46px; font-size: 1.9rem; }
.pv-nav { top: 50%; transform: translateY(-50%); width: 46px; height: 46px; font-size: 2rem; }
.pv-prev { left: 10px; }
.pv-next { right: 10px; }
.pv-count {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
  color: rgba(255,255,255,.85); font-size: .82rem;
  background: rgba(0,0,0,.4); padding: 4px 12px; border-radius: 20px;
  font-variant-numeric: tabular-nums;
}
/* Thumbnails are buttons now, not links — strip the button chrome. */
.gallery-btn { display: block; padding: 0; border: none; background: none; cursor: pointer; }
.gallery-btn img { display: block; }

/* ---- Detail view navigation ----
   Back, position, and prev/next in ONE bar. Previously Back was a button
   floating above the card and the arrows were a second row inside it, which
   read as three separate controls competing for the same job. Sticky so it
   stays reachable while scrolling a long profile. */
.detail-nav {
  position: sticky; top: 0; z-index: 44;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  background: var(--bg); padding: 8px 2px; margin-bottom: 10px;
}
.dn-back {
  display: inline-flex; align-items: center; gap: 6px;
  background: none; border: none; cursor: pointer;
  color: var(--primary); font-size: .92rem; font-weight: 700;
  padding: 6px 2px; min-height: 40px;
}
.dn-back:hover { text-decoration: underline; }
.dn-pos { font-size: .8rem; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.dn-arrows { display: flex; gap: 6px; }
.dn-arrows button {
  width: 40px; height: 40px; border-radius: 10px; cursor: pointer;
  background: var(--card); border: 1px solid var(--border);
  color: var(--primary); font-size: 1rem; line-height: 1;
  transition: border-color .15s, transform .1s;
}
@media (hover: hover) { .dn-arrows button:hover:not(:disabled) { border-color: var(--accent); } }
.dn-arrows button:active:not(:disabled) { transform: scale(.94); }
.dn-arrows button:disabled { opacity: .35; cursor: default; }
/* The tab row below sits at 50px; keep this clear of it on phones. */
@media (max-width: 640px) { .detail-nav { top: var(--header-h, 50px); } }

/* ---- Profile detail ---- */
.detail-photo { width: 140px; height: 140px; border-radius: var(--radius); object-fit: cover; }
.detail-head { display: flex; gap: 16px; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.kv { display: grid; grid-template-columns: 45% 55%; font-size: .92rem; padding: 5px 0; border-bottom: 1px dashed var(--divider); }
.kv .k { color: var(--muted); }
.kv .v { font-weight: 600; }

/* ---- Filters ---- */
.filters-panel { margin-bottom: 16px; }
.filters-panel summary { font-weight: 600; color: var(--primary); cursor: pointer; padding: 6px 0; }

/* ---- Photo upload ---- */
/* "Add your photo" prompt for members who signed up under the photo rule.
   Deliberately prominent (tinted panel + its own button) because a profile
   without a photo gets very little genuine interest. */
.photo-required {
  display: flex; align-items: flex-start; gap: 13px;
  background: var(--chip-warm-bg); border: 1px solid var(--accent);
  border-radius: 12px; padding: 14px 16px; margin-bottom: 14px;
}
.photo-required svg {
  width: 26px; height: 26px; flex-shrink: 0; margin-top: 2px;
  fill: none; stroke: var(--primary); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.photo-required b { display: block; color: var(--primary); font-size: .98rem; }
.photo-required p { font-size: .84rem; color: var(--text); opacity: .85; margin: 4px 0 10px; line-height: 1.45; }

/* Account footer: the three info links sit on one line instead of three fat
   stacked underlined links, which dominated the bottom of the profile. */
.acct-links {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 18px;
  border-top: 1px solid var(--border); margin-top: 22px; padding-top: 16px;
}
.acct-links a {
  color: var(--primary); font-size: .86rem; font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
@media (hover: hover) { .acct-links a:hover { text-decoration: underline; } }

/* Deleting an account is irreversible, so it gets its own fenced-off block
   rather than sitting directly under the ordinary links. */
.danger-zone {
  margin-top: 18px; padding: 14px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg); text-align: center;
}
.danger-zone .hint { margin: 0 0 10px; }
.danger-btn { color: #b23; border-color: #b23; margin-top: 0; }
@media (hover: hover) { .danger-btn:hover { background: #b23; border-color: #b23; color: #fff; } }

.photo-edit { display: flex; align-items: center; gap: 16px; margin-bottom: 8px; flex-wrap: wrap; }
.hint { font-size: .8rem; color: var(--muted); }

/* ---- Terms / privacy consent tick box (registration) ----
   Laid out as flex rather than an inline checkbox so the second and third lines
   of the sentence align under the text, not under the box. align-items:flex-start
   keeps the box level with the first line however far the text wraps — Kannada
   and Hindi run about 30% longer than English here. */
.consent {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 14px 0 4px; font-size: .85rem; line-height: 1.45;
  color: var(--muted); font-weight: 400; cursor: pointer;
}
.consent input[type="checkbox"] {
  width: 17px; height: 17px; margin: 2px 0 0; flex-shrink: 0;
  accent-color: var(--primary); cursor: pointer;
}
.consent a { color: var(--primary); text-decoration: underline; }
.consent a:hover { opacity: .8; }
/* The browser's own "please check this box" bubble points at the input; make it
   obvious which control it means when the form is submitted incomplete. */
.consent input:invalid:focus { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ---- Jain flag ---- */
.jain-flag {
  width: 46px; height: 36px; border-radius: 4px; overflow: hidden;
  display: inline-flex; flex-direction: column; flex-shrink: 0;
  box-shadow: 0 1px 4px rgba(0,0,0,.35); position: relative;
}
.jain-flag i { flex: 1; display: block; }
/* Stripe order, top to bottom: red, yellow, WHITE, green, black.
     red    = Siddha     (satya)
     yellow = Acharya    (achaurya)
     white  = Arihant    (ahimsa)   <- middle, carries the swastika
     green  = Upadhyaya  (brahmacharya)
     dark blue = Sadhu   (aparigraha)
   Do not reorder this to match the order the five are usually LISTED in
   (Arihant, Siddha, Acharya, Upadhyaya, Sadhu). That is the Pañca-Parameṣṭhi
   hierarchy, not the flag layout: on the flag white sits in the centre so the
   swastika can be drawn on it. Documented flag colours used verbatim. */
.jain-flag .f1 { background: #ff0000; }
.jain-flag .f2 { background: #ffce00; }
.jain-flag .f3 { background: #ffffff; }
.jain-flag .f4 { background: #009431; }
/* Dark blue, not black. Sources differ (Wikipedia even carries a note that its
   own article contradicts itself on this stripe), and cloth flags in use are
   dark blue. */
.jain-flag .f5 { background: #1a237e; }
/* Drawn as SVG paths, not a font character. A glyph is rendered by
   whatever CJK font the device happens to have, so its weight, size and even
   its position inside the line box differ between Android, iOS and desktop —
   which is why it looked wrong on a phone. Paths render identically everywhere
   and stay crisp at any size. */
.jf-swastika {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  /* Sized so the mark sits mostly WITHIN the white band, overhanging the
     yellow and green only slightly. At 24px it spanned nearly half the flag
     and, with a heavy halo, carved visible chunks out of both neighbouring
     stripes: the flag stopped reading as a flag. */
  width: 15px; height: 15px; pointer-events: none;
}
/* Built from six rectangles rather than a stroked polyline. Stroking a single
   zig-zag path produced visibly uneven arms at this size: the joins and caps
   land differently on each hook, so it read as a scribble rather than a
   swastika. Rectangles give exact, equal limbs.
   The halo is the same shapes drawn underneath with a thin outward stroke, so
   the mark stays legible where it overhangs the yellow and green stripes. */
.jf-swastika .halo { fill: #fff; stroke: #fff; stroke-width: 2.2; stroke-linejoin: round; }
.jf-swastika .mark { fill: #ff6331; }

/* ---- Running disclaimer ---- */
.disclaimer-bar {
  /* Trimmed down. This sits directly under the header on every page, so it was
     taking a visible slice of the screen before a member saw anything of the
     site. Smaller type, tighter padding and a slightly softer colour make it
     read as a standing note rather than an alert — the wording still carries
     the warning. */
  background: #40260a; color: #f0d6a4; font-size: .72rem;
  overflow: hidden; white-space: nowrap; padding: 3px 0;
  letter-spacing: .1px;
}
.disclaimer-bar span {
  display: inline-block; padding-left: 100%;
  animation: ticker 30s linear infinite;
}
@keyframes ticker {
  0% { transform: translateX(0); }
  100% { transform: translateX(-100%); }
}

/* ---- Gallery ---- */
.gallery-grid { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0; }
.gallery-item { position: relative; }
.gallery-item img {
  width: 92px; height: 92px; object-fit: cover;
  border-radius: 8px; border: 1px solid var(--border); cursor: pointer;
}
.gallery-del {
  position: absolute; top: -6px; right: -6px;
  background: var(--btn-bg); color: #fff; border: none;
  width: 22px; height: 22px; border-radius: 50%; cursor: pointer;
  font-size: 12px; line-height: 1;
}
.members-only-note {
  background: var(--chip-warm-bg); color: var(--chip-warm-fg); padding: 10px 14px;
  border-radius: 8px; margin: 12px 0; font-size: .9rem;
}

/* ---- Action buttons on profile detail ---- */
.badge.match { background: var(--chip-ok-bg); color: var(--chip-ok-fg); font-weight: 700; }
/* Interest status pills. In a long list the pill is the only thing telling one
   row from another at a glance, so connected reads green, waiting stays warm
   amber, and declined goes quiet grey rather than all three being beige. */
.badge.ok { background: var(--chip-ok-bg); color: var(--chip-ok-fg); font-weight: 700; }
.badge.off { background: var(--field-bg); color: var(--muted); }
/* The "Connected ✓" button in the profile detail. Must sit after the
   @media (hover: hover) block so it is not repainted maroon on hover: it is a
   status, not an action. Full opacity on purpose. It was previously dimmed to
   .6, which washed the label out until it was barely legible. */
.btn.btn-connected {
  background: var(--ok-solid-bg); color: var(--ok-solid-fg);
  border: 1px solid var(--ok-solid-bg); opacity: 1; cursor: default;
}

/* ---- Completeness meter ---- */
/* Profile completeness. A ring rather than a bar: the number sits inside it, so
   the percentage and the "what next" chips share one block instead of the
   label having to carry both. */
.meter-wrap { margin: 14px 0 6px; display: flex; align-items: center; gap: 16px; }
.meter-ringwrap { position: relative; width: 74px; height: 74px; flex-shrink: 0; }
/* The SVG is rotated so the arc starts at twelve o'clock; the number is a
   sibling rather than a child so the rotation does not turn it on its side. */
.meter-ring { width: 74px; height: 74px; transform: rotate(-90deg); }
.meter-ring circle { fill: none; stroke-width: 4; }
.meter-ring .mr-track { stroke: var(--divider); }
.meter-ring .mr-arc { stroke: var(--primary); stroke-linecap: round; transition: stroke-dashoffset .5s; }
.meter-pct {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem; font-weight: 700; color: var(--primary);
}
.meter-body { min-width: 0; }
.meter-label { font-size: .85rem; color: var(--muted); }
.meter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.meter-chip {
  background: var(--chip-warm-bg); color: var(--chip-warm-fg); border: none;
  font-size: .78rem; font-weight: 700; padding: 6px 11px; border-radius: 20px;
  cursor: pointer; min-height: 32px;
}
@media (hover: hover) { .meter-chip:hover { background: var(--accent); color: #4a2c00; } }
.meter-done { font-size: .85rem; color: var(--success-fg); font-weight: 700; }

/* ---- How it works / FAQ (login page) ---- */
.how-steps { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 8px; }
.how-steps > div {
  background: var(--field-bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 12px 8px; text-align: center;
}
.how-steps svg {
  width: 22px; height: 22px; fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.how-steps b { display: block; font-size: .84rem; color: var(--text); margin-top: 6px; }
.how-steps span { display: block; font-size: .74rem; color: var(--muted); margin-top: 2px; line-height: 1.35; }

.faq-card details { border-top: 1px solid var(--divider); }
.faq-card details:first-of-type { border-top: none; }
.faq-card summary {
  cursor: pointer; padding: 11px 0; font-weight: 700; color: var(--text);
  font-size: .92rem; list-style: none;
}
.faq-card summary::-webkit-details-marker { display: none; }
/* Drawn marker rather than the browser default triangle, which differs between
   engines and cannot be coloured. */
.faq-card summary::after { content: '⌄'; float: right; color: var(--muted); font-size: 1.1rem; line-height: 1; }
.faq-card details[open] summary::after { content: '⌃'; }
.faq-card p { padding: 0 0 12px; color: var(--muted); font-size: .88rem; line-height: 1.6; }

/* Filter chips. Scroll sideways on a phone rather than wrapping to three rows
   and pushing the results off screen. */
.chip-row {
  display: flex; gap: 7px; margin-bottom: 12px;
  overflow-x: auto; padding-bottom: 4px;
  scrollbar-width: none; -ms-overflow-style: none;
}
.chip-row::-webkit-scrollbar { display: none; }
.chip {
  flex-shrink: 0; white-space: nowrap; cursor: pointer;
  background: var(--card); border: 1px solid var(--border); color: var(--text);
  font-size: .84rem; padding: 8px 14px; border-radius: 20px; min-height: 38px;
}
.chip .chip-caret { font-size: .7rem; opacity: .65; }
.chip.on {
  background: var(--btn-bg); border-color: var(--btn-bg); color: #fff; font-weight: 700;
}
.chip.on .chip-caret { opacity: .9; }
.chip-clear { color: var(--muted); border-style: dashed; }
@media (hover: hover) {
  .chip:hover { border-color: var(--accent); }
  .chip.on:hover { background: var(--primary-dark); }
}

/* Live preview of your own card while editing. */
.live-preview {
  background: var(--field-bg); border: 1px solid var(--accent); border-radius: 12px;
  padding: 12px; margin: 16px 0;
}
.live-preview .lp-title {
  font-size: .72rem; letter-spacing: .5px; color: var(--chip-warm-fg);
  text-transform: uppercase; font-weight: 700; margin-bottom: 8px;
}
/* The card inside is the real search-result card, so neutralise the hover and
   pointer affordances it carries: this one is not clickable. */
.live-preview .profile-card { cursor: default; margin: 0; }
.live-preview .profile-card:active { transform: none; }

@media (min-width: 900px) {
  /* Beside the fields, following you down the page. Below 900px it stays in
     normal flow above the form: a sticky card on a phone would cover a third
     of the screen exactly while the keyboard is open. */
  .live-preview {
    position: sticky; top: calc(var(--header-h, 50px) + 12px);
    float: right; width: 300px; margin: 0 0 16px 20px;
  }
  /* A float only pushes INLINE content aside. Block boxes — every input, every
     collapsible section — still lay out at full width and render underneath it,
     so as the sticky card travelled down the page it sat on top of Native
     Place, Mother Tongue and the section headers below.
     Reserving the gutter on everything that follows the card is what actually
     stops the overlap; the sibling selector keeps it correct if sections are
     added or reordered later. 320px = the card's 300px plus its 20px margin. */
  #livePreview ~ * { margin-right: 320px; }
}

/* ---- Large page logo (help / privacy / terms) ----
   logo.png is transparent artwork whose wordmark is maroon and whose tagline is
   dark grey — both chosen to sit on the cream page. In dark mode the page goes
   near-black behind it, the maroon drops to about 2:1 contrast and the tagline
   disappears entirely, so the whole block reads as a dark smudge.
   The fix is the same one the header already uses: put it on the logo's own
   cream (#f5efe5, sampled from the master artwork), so the seam disappears
   instead of showing a white box. Light mode needs nothing — the page is
   already cream. */
.page-logo {
  display: block; width: 100%; max-width: 300px;
  margin: 6px auto 4px; text-decoration: none;
}
.page-logo img { display: block; width: 100%; height: auto; }
html[data-theme="dark"] .page-logo {
  background: #f5efe5;
  border-radius: 12px;
  padding: 10px 14px;
  max-width: 320px;   /* the padding must not shrink the artwork itself */
}

/* ---- "This has been replaced" notice (retired biodata upload) ----
   Warm, not a warning. Nothing has gone wrong and the member has done nothing
   incorrect — a red error box for a product decision reads as an accusation. */
.notice-retire {
  background: var(--field-bg); border: 1px solid var(--accent);
  border-radius: 10px; padding: 13px 15px; margin: 10px 0 12px;
  font-size: .9rem; line-height: 1.5;
}
.notice-retire b { display: block; color: var(--primary); margin-bottom: 4px; }
.notice-retire > div { color: var(--muted); }
/* The list of missing fields is the actionable part, so it is the one thing
   here in full-strength text rather than muted. */
.notice-retire .nr-missing {
  margin-top: 8px; color: var(--text); font-weight: 600; font-size: .87rem;
  overflow-wrap: anywhere;
}
.notice-retire .btn { width: auto; margin-top: 11px; }
@media (max-width: 480px) { .notice-retire .btn { width: 100%; } }

/* ---- Guest strip above search results ---- */
.guest-strip {
  display: flex; gap: 9px; align-items: flex-start;
  background: var(--chip-warm-bg); color: var(--chip-warm-fg);
  border-radius: 10px; padding: 9px 12px; margin: 0 0 12px;
  font-size: .86rem; line-height: 1.45;
}
.guest-strip a { color: var(--primary); font-weight: 700; }

/* ---- Guest lock panel (profile detail, logged out) ----
   Sits where the profile detail would be. Warm rather than alarming: this is an
   invitation to join, not an error — a red "access denied" block is the wrong
   note for someone who has just shown interest in a member. */
.guest-lock {
  text-align: center; padding: 22px 18px 20px; margin-top: 16px;
  background: var(--field-bg); border: 1px solid var(--accent); border-radius: 12px;
}
.gl-icon { font-size: 1.7rem; line-height: 1; margin-bottom: 8px; }
.gl-title { font-size: 1.08rem; color: var(--primary); margin: 0 0 5px; }
.gl-sub { color: var(--muted); font-size: .9rem; margin: 0 auto 14px; max-width: 34ch; }
.gl-list {
  list-style: none; text-align: left; display: inline-block;
  margin: 0 auto 16px; padding: 0; font-size: .89rem; color: var(--text);
}
.gl-list li { padding: 5px 0 5px 24px; position: relative; }
/* A tick, not a bullet: each line is something they GET, not a restriction. */
.gl-list li::before {
  content: "✓"; position: absolute; left: 4px; top: 5px;
  color: var(--chip-ok-fg); font-weight: 800;
}
.guest-lock .btn { display: inline-block; width: auto; min-width: 220px; text-decoration: none; }
.gl-free { color: var(--muted); font-size: .8rem; margin: 10px 0 0; }
@media (max-width: 420px) {
  .guest-lock { padding: 18px 14px; }
  .guest-lock .btn { width: 100%; min-width: 0; }
}

/* ---- Interest lists ---- */
.int-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px dashed var(--divider); }
/* min-width:0 lets the text column actually shrink and wrap normally instead
   of refusing to go below its longest word and shoving the buttons around. */
.int-row .pc-info { flex: 1 1 0; min-width: 0; }
.int-actions { display: flex; gap: 6px; flex-shrink: 0; }

/* On a phone this row has no business being one line: the status pill plus a
   button like "Remove Connection" leaves the name and age squeezed into a few
   characters, wrapping one word per line around the buttons. Let the actions
   drop to their own full-width row underneath instead. */
@media (max-width: 640px) {
  .int-row { flex-wrap: wrap; row-gap: 10px; }
  .int-actions { width: 100%; flex-wrap: wrap; }
  /* flex-grow stays 0. With 1 1 auto a row holding a single action stretched
     that one button across the whole card, so a small "Remove" read as a huge
     empty box. Buttons now size to their label and wrap when they run out of
     room; min-height keeps them a comfortable touch target. */
  .int-actions .btn { flex: 0 1 auto; min-height: 42px; margin-top: 0; }
  /* Badges (the "Connected ✓" pill) keep their natural size. */
  .int-actions .badge { align-self: center; }
}

/* ---- Banner ---- */
.banner-warn {
  background: var(--warn-bg); color: var(--warn-fg); border: 1px solid var(--warn-border);
  padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; font-size: .9rem;
}

/* ---- Print (Save as PDF biodata) ---- */
@media print {
  header, .disclaimer-bar, .tabs, .filters-panel,
  .act-primary, .act-secondary, .act-quiet, .detail-nav,
  #view-search, #view-myprofile, .btn, .members-only-note { display: none !important; }
  body { background: #fff; }
  #view-detail { display: block !important; }
  .card { border: none; box-shadow: none; }
}

/* ---- Desktop ---- */
@media (min-width: 640px) {
  .result-grid { grid-template-columns: minmax(0,1fr) minmax(0,1fr); }
  .btn { width: auto; padding: 12px 32px; }
  .grid3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 0 12px; }
}
@media (max-width: 639px) {
  .grid3 { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 0 12px; }
}

/* ---- Small phones ---- */
@media (max-width: 480px) {
  .container { padding: 10px; }
  .card { padding: 14px; }
  h1 { font-size: 1.25rem; }
  h2 { font-size: 1.05rem; }
  .logo { font-size: 1.02rem; }
  .tab { font-size: .9rem; padding: 10px 10px; }
  /* Sized so the logo plus all six controls still fit one line on a 375px
     phone (the narrowest common size) with a little room to spare. */
  header { padding: 8px 10px; gap: 6px; }
  .lang-btn { padding: 4px 6px; font-size: .72rem; }
  .jain-flag { width: 22px; height: 16px; }
  .detail-photo { width: 110px; height: 110px; }
  .kv { grid-template-columns: 42% 58%; font-size: .88rem; }
  .gallery-item img { width: 78px; height: 78px; }
  input, select, textarea { font-size: 16px; } /* prevents iOS auto-zoom on focus */
}

/* Verified badge + kundli match box */
.badge.verified { background: var(--chip-info-bg); color: var(--chip-info-fg); font-weight: 700; }
.match-box {
  margin: 12px 0; padding: 10px 14px; border-radius: 8px;
  background: var(--note-bg); border: 1px solid var(--note-border);
}
.match-box .hint { margin-top: 4px; font-size: 0.85em; color: var(--note-fg); }
/* "Indicative only — please confirm with your family astrologer." This is the
   one line in the box that manages expectations about the whole guna score,
   so it gets weight and a rule above it rather than being the faintest text
   on the card. */
.kundli-note {
  margin-top: 9px; padding-top: 9px;
  border-top: 1px solid var(--note-border);
  font-size: .84rem; font-weight: 700; line-height: 1.45;
  color: var(--note-fg);
}

/* ================= Phone bottom navigation (phones only; tablet/desktop unchanged) ================= */
.bottom-nav, .more-menu { display: none; }
@media (max-width: 640px) {
  /* Mobile navigation: keep the reliable top tab row (it scrolls sideways) and
     make it sticky under the header so it's always reachable. */
  .app-tabs { position: sticky; top: var(--header-h, 50px); background: var(--bg); z-index: 45; margin-bottom: 12px; }
  .bottom-nav { display: none !important; } /* fixed bottom bar disabled — top tabs used instead */
  .more-menu {
    display: none; position: fixed; right: 8px; bottom: calc(62px + env(safe-area-inset-bottom, 0px));
    z-index: 61; background: var(--card); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 6px 20px rgba(120,70,20,.18); overflow: hidden; min-width: 160px;
  }
  .more-menu.open { display: block; }
  .more-menu button {
    display: block; width: 100%; text-align: left; background: none; border: none;
    padding: 12px 16px; font-size: .95rem; color: var(--text); cursor: pointer;
    border-bottom: 1px solid var(--border);
  }
  .more-menu button:last-child { border-bottom: none; }
  /* tidier, thumb-friendly action buttons: primary full-width, rest in an even grid */
  .lang-btn { min-height: 34px; }
  .act-tile { min-height: 56px; }
  .act-primary { flex-direction: column; }
  .act-primary .btn { width: 100%; min-height: 46px; }
}
@media print { .bottom-nav, .more-menu { display: none !important; } }

/* Align paired form fields on phones even when labels wrap to 2 lines */
@media (max-width: 640px) {
  .grid2 > div, .grid3 > div { display: flex; flex-direction: column; }
  .grid2 > div > label, .grid3 > div > label {
    min-height: 2.7em; display: flex; align-items: flex-end; margin-bottom: 4px;
  }
}

/* ---- Guna Milan (Ashtakoota) ----
   A score ring plus one bar per koota. The bar shows at a glance how each
   one scored; the exact numbers stay on the right so nothing is lost for
   someone reading it properly or showing it to a pandit. */
.guna-head { display: flex; align-items: center; gap: 14px; margin-bottom: 12px; }
.guna-ring { width: 76px; height: 76px; flex-shrink: 0; }
.guna-sum { min-width: 0; }
.guna-sum .gl {
  font-size: .68rem; color: var(--muted); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase;
}
.guna-sum .gq { font-size: 1.05rem; font-weight: 800; margin: 1px 0 5px; }
.dosha-chip {
  display: inline-block; background: #fadbd8; color: #943126;
  font-size: .68rem; font-weight: 700; padding: 3px 9px; border-radius: 20px;
}
html[data-theme="dark"] .dosha-chip { background: #4a1f1c; color: #ff9b8f; }

.koota-list { display: flex; flex-direction: column; gap: 6px; }
.koota { display: flex; align-items: center; gap: 8px; font-size: .8rem; color: var(--note-fg); }
.koota .kn { width: 5.6em; flex-shrink: 0; }
.koota .kbar {
  flex: 1; min-width: 0; height: 7px; border-radius: 4px;
  background: var(--note-border); overflow: hidden;
}
.koota .kfill { display: block; height: 100%; border-radius: 4px; }
.koota .kfill.full { background: var(--chip-ok-fg); }
.koota .kfill.part { background: var(--accent); }
.koota .kfill.none { background: var(--error-fg); }
/* Named .kval, not .kv: `.kv` is the key/value ROW class used elsewhere, and
   reusing it here made these numbers inherit a grid layout and a dashed
   underline they were never meant to have. */
.koota .kval { width: 2.6em; text-align: right; flex-shrink: 0; color: var(--muted); font-variant-numeric: tabular-nums; }

/* ---- Profile actions, in three tiers of importance ---- */
.act-primary { display: flex; gap: 8px; margin: 14px 0 0; }
.act-primary .btn { flex: 1; margin-top: 0; }
.act-secondary { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 7px; margin-top: 8px; }
.act-tile {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  background: var(--card); border: 1px solid var(--border); border-radius: 10px;
  padding: 9px 4px; font-size: .72rem; line-height: 1.25; color: var(--text);
  cursor: pointer; text-align: center; transition: border-color .15s, transform .1s;
}
@media (hover: hover) { .act-tile:hover { border-color: var(--accent); } }
.act-tile:active { transform: scale(.97); }
.act-tile .ai { font-size: 1.05rem; line-height: 1; }
/* Shortlisting is a saved state, not a one-off action, so the tile stays
   highlighted afterwards. Warm gold rather than green: green already means
   "connected" elsewhere in the app, and a shortlist is not a connection.
   The chip-warm pair is contrast-checked in both themes by the test suite. */
.act-tile.on {
  background: var(--chip-warm-bg); border-color: var(--accent);
  color: var(--chip-warm-fg); font-weight: 700;
}
.act-tile.on .ai { color: var(--accent); }
/* Report and block: necessary, but rare and negative — they get no visual weight. */
.act-quiet { display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 11px; }
.act-quiet button {
  background: none; border: none; padding: 4px; cursor: pointer;
  color: var(--muted); font-size: .78rem; text-decoration: underline;
}
@media (hover: hover) { .act-quiet button:hover { color: var(--error-fg); } }
.act-quiet span { color: var(--muted); }

/* Invite / Share + dialog modal */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: 16px; }
.modal-box { background: var(--card); border-radius: 14px; padding: 20px; width: 100%; max-width: 340px;
  text-align: center; max-height: 92vh; overflow: auto; }
.modal-box h2 { color: var(--primary); }

/* Shown instead of the Guna Milan table when rashi/nakshatra are missing on
   either side, so the section explains itself rather than silently vanishing. */
.kundli-empty {
  background: var(--field-bg); border: 1px dashed var(--border); border-radius: 12px;
  padding: 16px; margin-top: 16px; text-align: center;
}
.kundli-empty .ke-title { font-weight: 700; color: var(--text); margin-bottom: 4px; }
.kundli-empty .ke-sub { color: var(--muted); font-size: .88rem; margin-bottom: 12px; }
.kundli-empty .btn { width: auto; margin-top: 0; }

/* Shown in place of the suggestions row when the member has filled in nothing
   we could personalise on. Asking is better than an empty row, which just
   looks like the feature is broken. */
.suggest-empty {
  background: var(--field-bg); border: 1px dashed var(--border); border-radius: 12px;
  padding: 18px 16px; text-align: center;
}
.suggest-empty .se-title { font-weight: 700; color: var(--text); margin-bottom: 4px; }
.suggest-empty .se-sub { color: var(--muted); font-size: .88rem; margin-bottom: 12px; }
.suggest-empty .btn { width: auto; margin-top: 0; }

/* ---- Biodata viewer ---- */
/* Wider than a normal modal: a biodata is a full page of text and is unreadable
   squeezed into the 340px default. */
.bio-box { max-width: 760px; text-align: left; display: flex; flex-direction: column; }
.bio-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.bio-head strong { flex: 1 1 0; min-width: 0; color: var(--primary); font-size: 1.05rem; }
.bio-close {
  flex-shrink: 0; width: 38px; height: 38px; border-radius: 8px; cursor: pointer;
  background: var(--field-bg); border: 1px solid var(--border); color: var(--text);
  font-size: 1.5rem; line-height: 1;
}
.bio-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bio-body { flex: 1 1 auto; min-height: 0; }
/* 68vh keeps the header and the download button on screen on a phone without
   the modal itself needing to scroll. */
.bio-frame { width: 100%; height: 68vh; border: 1px solid var(--border); border-radius: 8px; background: #fff; }
.bio-img { width: 100%; height: auto; border-radius: 8px; display: block; }
.bio-note { color: var(--muted); font-size: .88rem; margin-top: 10px; text-align: center; }
@media (max-width: 640px) {
  .bio-box { max-width: 100%; padding: 14px; }
  .bio-frame { height: 60vh; }
}
.modal-box .btn { margin-top: 10px; }

/* Consistent alignment for link-style buttons inside cards */
.card .link-btn { display: block; width: 100%; text-align: center; }

/* Quick "search as you type" box */
/* The magnifier sits in a wrapper rather than as a background-image, because a
   background image cannot inherit currentColor and would need a second hardcoded
   copy for dark mode. */
.qs-wrap { position: relative; margin-bottom: 12px; }
.qs-icon {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; pointer-events: none; color: var(--placeholder);
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}
.quick-search {
  width: 100%; padding: 13px 14px 13px 44px; margin-bottom: 0; font-size: 1rem;
  border: 1px solid var(--border); border-radius: 10px; background: var(--field-bg);
}
.quick-search:focus { outline: 2px solid var(--accent); border-color: var(--accent); }
/* opacity:1 is required — Firefox dims placeholders by default, which would
   undo the colour set here. */
input::placeholder, textarea::placeholder { color: var(--placeholder); opacity: 1; }
.quick-search { color: var(--text); }
.quick-search::placeholder { color: var(--placeholder); opacity: 1; }

/* Undoing a marriage: separated from "Save story" and given its own heading,
   because as a bare text link beside it the destructive action read as a
   footnote of the same weight as saving a paragraph of text. */
.undo-married {
  margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--divider);
}
.undo-married .um-title { font-weight: 700; color: var(--text); margin-bottom: 2px; }
.undo-married .um-sub { color: var(--muted); font-size: .86rem; margin-bottom: 10px; }
.undo-married .um-btn { color: var(--error-fg); border-color: var(--error-fg); }

/* Autocomplete suggestion dropdown */
.ac-wrap { position: relative; }
.ac-dd {
  position: absolute; left: 0; right: 0; top: 100%; z-index: 40; display: none;
  background: var(--card); border: 1px solid var(--border); border-top: none;
  border-radius: 0 0 8px 8px; box-shadow: 0 6px 16px rgba(120,70,20,.14);
  max-height: 220px; overflow-y: auto;
}
.ac-item { padding: 9px 12px; cursor: pointer; font-size: .92rem; }
/* .act is the keyboard-highlighted item and must always be shaded. Only the
   pointer hover half is gated, otherwise arrow-key navigation would lose its
   highlight on touch devices. */
.ac-item.act { background: var(--chip-warm-bg); }
@media (hover: hover) { .ac-item:hover { background: var(--chip-warm-bg); } }

/* Gallery re-crop (adjust) button */
.gallery-edit {
  position: absolute; bottom: -6px; left: -6px;
  background: var(--btn-bg); color: #fff; border: none;
  width: 22px; height: 22px; border-radius: 50%; cursor: pointer;
  font-size: 12px; line-height: 1;
}


/* Empty search result: an offer to be told when someone suitable joins,
   rather than a dead end. This is the most-seen screen while the community is
   still small, so it is the highest-value place to keep a visitor. */
.empty-offer {
  background: var(--chip-warm-bg, #fdf3e7);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 20px 22px;
  margin: 14px 0 8px;
  max-width: 560px;
}
.empty-offer b { display: block; font-size: 1.05rem; color: var(--primary); margin-bottom: 6px; }
.empty-offer p { color: var(--muted); font-size: .92rem; line-height: 1.55; margin: 0 0 14px; }
.empty-offer .btn { margin: 0; }
.empty-offer .empty-done {
  margin: 0; color: #1e8449; font-weight: 600;
}


/* Filter panel actions: "Apply" and the match-alert button side by side.
   They used to stack because Apply sat inside the form and the alert button
   in a separate div below it. flex-wrap means that on a very narrow phone —
   or in Kannada, where labels run about a third longer than English — they
   drop to two lines instead of squashing. */
.filter-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}
.filter-actions .btn { margin: 0; flex: 1 1 auto; white-space: nowrap; }
.filter-actions .hint { flex-basis: 100%; margin: 0; }


/* The locked avatar is an <a> for guests, so undo the link styling it would
   otherwise inherit and give it a pointer + a gentle hover lift. */
a.avatar-locked { text-decoration: none; cursor: pointer; display: flex; }
a.avatar-locked:hover { box-shadow: 0 3px 10px rgba(0,0,0,.18); }
a.avatar-locked:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ---- Locked profile photo (guests) ----
   A warm tinted square with a drawn padlock, standing in for a photo the viewer
   is not allowed to see. Nothing here is derived from the member's real picture:
   the server never sends a guest the photo filename, so there is nothing on the
   page that could be recovered. The lock is SVG so it stays sharp at every size,
   and the wording lives in the tooltip where it can be translated. */
.avatar-locked {
  position: relative; overflow: hidden;
  /* Gendered photographs used to sit here. At 84px they filled the square and
     read as a brown blob that competed with the member's name; a tint plus a
     padlock says "hidden" just as clearly, downloads nothing, and needs no
     artwork per gender. It is an <a> to the login page for guests, hence the
     link reset and the pointer. */
  text-decoration: none; cursor: pointer;
  background: linear-gradient(160deg, #f8f1e7 0%, #eee0cd 100%);
  display: flex; align-items: center; justify-content: center;
}

/* The drawn stand-in: a lotus watermark with a man's or woman's silhouette in
   front of it. Both are held well back so the lock stays the thing you notice
   and the member's name still leads the card. */
.avatar-locked .al-art { position: absolute; inset: 0; }
.avatar-locked .al-art svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.avatar-locked .al-lotus { color: #e5cdaa; opacity: .55; }
.avatar-locked .al-fig {
  color: #a98d76; opacity: .30;
  /* Sits low in the frame so the head and shoulders read, like a portrait. */
  transform: translateY(9%) scale(1.06);
}
.avatar-locked .al-lock {
  position: relative; z-index: 2;
  /* Was 40%, which at 84px made a 34px badge that dominated the card. */
  width: 30%; height: 30%; min-width: 18px; min-height: 18px;
  max-width: 28px; max-height: 28px;
  border-radius: 50%; background: #fff; border: 1px solid #e0c894;
  box-shadow: 0 1px 4px rgba(0,0,0,.14);
  display: flex; align-items: center; justify-content: center;
}
.avatar-locked .al-lock svg { width: 56%; height: 56%; fill: var(--primary); }


/* ---- Guest profile card ----
   The supplied locked artwork shown whole, with the details beneath. The image
   already carries its own rounded corner, soft border and "Login to view
   profile" caption, so nothing is drawn on top of it — it just needs to be big
   enough to read, which is why a guest gets this taller card rather than the
   84px avatar members see. */


/* The locked avatar is a link to the login page for guests, so it needs to look
   pressable rather than decorative. */
@media (hover: hover) {
  .avatar-locked:hover .al-lock { transform: scale(1.08); box-shadow: 0 3px 10px rgba(0,0,0,.24); }
}
.avatar-locked .al-lock, .avatar-locked:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }


/* Cards in a results row were wildly uneven: a fully filled profile carries an
   occupation, a city and three chips, while a half-finished one had only a
   name, so the grid looked broken rather than sparse. A floor on the height
   keeps the row tidy whatever the data, and the info block centres in it. */
.profile-card { min-height: 116px; }
.profile-card .pc-info { display: flex; flex-direction: column; justify-content: center; }
/* The "not added yet" line is deliberately quieter than a real detail, so a
   thin profile never looks like it is claiming something. */
.pc-thin { font-style: italic; opacity: .75; }
/* Collapse the chip row when there are no chips, so it stops reserving space
   under profiles that have none. */
.pc-chips:empty { display: none; }


/* Gender chip. Sits first and is tinted a little stronger than the sect and
   diet chips beside it, because on a matrimony site it is the fact people scan
   for before anything else. */
.badge.gender {
  background: var(--btn-bg); color: #fff; font-weight: 700;
  border-color: var(--btn-bg);
}


/* The four Jain matrimony topic links, sitting under the main footer row.
   Quieter than the row above so the page ends on the useful links rather than
   on a wall of keywords, while staying plainly readable and crawlable. */
.topic-links { margin-top: 2px; opacity: .82; font-size: .82rem; }


/* ---- Login / register form: reclaim the dead space above the button ---- */

/* An empty error box was still reserving min-height 1.2em plus a 10px margin —
   about 29px of nothing between "Forgot Password?" and the Login button on
   every visit where nothing had gone wrong, which is almost all of them. It
   expands normally the moment a message is put into it. */
.error:empty { min-height: 0; margin-top: 0; }

/* The primary action goes full width on the login card, even on desktop.
   A rule further up turns every .btn auto-width above 640px, which is right
   inside the member app where buttons sit in rows, but on a form it left a
   small button marooned at the left of a wide card. Full width reads as the
   obvious next step, gives a much larger tap target on a phone, and applies to
   Register and Reset Password too since they share this markup. */
#loginCard .btn,
#loginForm .btn, #registerForm .btn, #resetForm .btn, #otpForm .btn {
  width: 100%;
  margin-top: 14px;
}
