/* =========================================================
   Tabu — shared design system
   Trust palette: navy/blue (structure) + teal (action/trust)
   ========================================================= */

:root{
  color-scheme:light;
  --bg:#F2F1EC;
  --surface:#FFFFFF;
  --surface-hover:#F7F6F2;
  --border:#E1DFD8;
  --border-strong:#D6D4CC;
  --text:#1B1E1C;
  --muted:#5F6461;
  --primary:#1F3D5C;
  --primary-text:#FFFFFF;
  --accent:#2E8B7D;
  --accent-hover:#25705F;
  /* --accent on white is 4.11:1 — under AA at text sizes. --accent-text is the
     same hue taken to 5.4:1 and is what every teal STRING uses; --accent stays
     for fills, borders, rings and bare icons, where the ratio does not apply. */
  --accent-text:#267463;
  /* Primary buttons put text ON the accent, which is the opposite problem to
     --accent-text (accent text on a light ground). White on #2E8B7D is 4.11:1
     and fails AA, so the FILL darkens instead. In dark the fill is already
     bright enough to carry dark text, which is cleaner than darkening it. */
  --accent-fill:#267463;
  --on-accent:#FFFFFF;
  --accent-soft:#E3EFEB;
  --danger:#B24545;
  --shadow-sm:0 1px 0 rgba(27,30,28,.07);
  --shadow-md:0 1px 2px rgba(27,30,28,.06), 0 6px 16px rgba(27,30,28,.06);
  --shadow-lg:0 2px 4px rgba(27,30,28,.06), 0 14px 32px rgba(27,30,28,.10);
  /* The rail shares the top bar's surface, so bar + rail read as one L of
     chrome around the page (in dark, --surface would sit a step lighter than
     the bar and split them). Its INTERNAL dividers carry --border directly —
     see .nav-rail-stats-section and #railYou. */
  --rail-bg:var(--topbar-bg);
  --rail-edge:var(--border);
  --topbar-bg:#FFFFFF;
  /* ONE page container. Every full-width block on every page measures itself
     from these two numbers via .page-w — see the "page container" section
     below. There used to be two widths (1280 for .shell/.trending-strip, 1180
     for .composer/.filter-row/.breadcrumb/.q-shell-wrap) centred in the same
     box, so the 100px difference showed up as a permanent 50px step per side
     between blocks stacked on the same screen. Change them here, nowhere
     else. */
  --page-max:1180px;
  --page-pad:32px;
  /* ONE type scale for the reading surfaces (question page, and the swipe
     reader next). Four sizes, no fifth: anything that needed a 5th size was
     really asking for a weight or a colour change instead. */
  --fs-caption:12px;
  --fs-control:13.5px;
  --fs-body:15px;
  --fs-title:18px;
}
:root[data-theme="dark"]{
  color-scheme:dark;
  --bg:#101113;
  --surface:#191A1D;
  --surface-hover:#202226;
  --border:rgba(255,255,255,.09);
  --border-strong:rgba(255,255,255,.09);
  --text:#ECEBE6;
  --muted:#9A9C99;
  --primary:#EDEDED;
  --primary-text:#0A0A0A;
  --accent:#3FB6A0;
  --accent-hover:#57C7B2;
  /* 7:1 on this ground already — text and fill can share one value here */
  --accent-text:#3FB6A0;
  --accent-fill:#3FB6A0;
  --on-accent:#101113;
  --accent-soft:rgba(63,182,160,.14);
  --danger:#D97A7A;
  --shadow-sm:0 1px 0 rgba(0,0,0,.5);
  --shadow-md:0 1px 2px rgba(0,0,0,.5), 0 6px 16px rgba(0,0,0,.4);
  --shadow-lg:0 2px 4px rgba(0,0,0,.5), 0 14px 32px rgba(0,0,0,.45);
  --rail-bg:var(--topbar-bg);
  --rail-edge:var(--border);
  --topbar-bg:#151619;
}

*{box-sizing:border-box;}
body{
  /* Golos Text comes from a <link> in each page's head — an @import here would
     load it a second time and hold up first paint. */
  margin:0; font-family:'Golos Text', system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
  background:var(--bg); color:var(--text);
  transition:background .2s ease, color .2s ease;
  min-height:100vh;
}
h1,h2,h3,h4{margin:0; font-weight:600; letter-spacing:0;}
button{font-family:inherit;}
a{color:inherit;}

/* ---------- header ---------- */
/* The header is now a single sticky search bar. Translucent and blurred
   rather than a solid slab: content passing underneath stays faintly visible,
   which is what keeps a persistent bar from feeling like a lid on the page.
   The bottom rule only appears once you have actually scrolled (.is-stuck),
   so a page at rest has no line drawn across it. */
header{
  /* Fixed and full-bleed: the bar spans the whole window with the logo inside
     it and the rail starting underneath, rather than beginning where the rail
     ends. Three grid columns (1fr / search / 1fr) so the field is centred on
     the window, not on the space left over beside the logo. */
  display:grid; grid-template-columns:1fr minmax(0,560px) 1fr;
  align-items:center; gap:14px; padding:0 20px; height:60px;
  position:fixed; top:0; left:0; right:0; z-index:30;
  background:var(--topbar-bg);
  border-bottom:1px solid transparent;
  transition:border-color .18s ease;
}
header.is-stuck{border-bottom-color:var(--border);}
.nav-rail-toggle{
  width:40px; height:40px; flex-shrink:0; border:none; background:none; cursor:pointer;
  border-radius:50%; color:var(--text); display:flex; align-items:center; justify-content:center;
  transition:background .12s ease;
}
.nav-rail-toggle:hover{background:var(--surface-hover);}
header .logo{flex:0 0 auto; min-width:0;}
/* the bar is out of flow, so the page has to reserve its height */
body{padding-top:60px;}
.topbar-left{display:flex; align-items:center; gap:4px; min-width:0;}
.topbar-right{display:flex; align-items:center; justify-content:flex-end; gap:6px;}
/* Borderless circles, matching the collapse toggle on the other side — a bar
   is chrome, so its controls should read as icons rather than as buttons
   sitting on top of it. */
.topbar-right .icon-btn{
  width:40px; height:40px; border-radius:50%; border:none; background:none;
}
.topbar-right .icon-btn:hover{background:var(--surface-hover);}
.topbar-right .icon-btn .dot-badge{top:8px; right:8px;}
.topbar-right .avatar{width:32px; height:32px; margin-left:4px;}
.topbar-right .auth-links{gap:10px; font-size:12.5px;}

/* the search pill itself */
.topbar-search{
  display:flex; align-items:center; gap:11px; flex:1; min-width:0;
  background:var(--bg); border:1px solid var(--border); border-radius:999px;
  padding:9px 16px; cursor:text;
  transition:border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.topbar-search:hover{border-color:var(--muted);}
.topbar-search:focus-within{
  border-color:var(--accent); box-shadow:0 0 0 4px var(--accent-soft); background:var(--surface);
}
.topbar-search svg{flex-shrink:0; opacity:.55;}
.topbar-search button[type=submit]{
  background:none; border:none; padding:0; display:flex; cursor:pointer; color:inherit;
}
/* bare field inside the pill — the pill draws the border, background and focus
   ring, so the input opts out of the shared form chrome */
.topbar-search input[type=text],
.topbar-search input[type=text]:focus{
  flex:1; min-width:0; width:100%; border:none; border-radius:0; padding:0;
  background:none; box-shadow:none; outline:none;
  font-size:14px; font-family:inherit; color:var(--text);
}
/* logo and header-right share the outer space equally (flex:1 1 0) so the nav,
   sized to its own content, lands on the true horizontal centre of the header.
   Letting the nav itself be flex:1 instead would centre it inside the leftover
   gap — which is off-centre, because the right-hand controls are much wider
   than the logo. */
.logo{font-weight:700; font-size:19px; letter-spacing:0; display:flex; align-items:center; gap:8px; text-decoration:none; color:var(--text); flex:1 1 0;}
/* MARK D, drawn from the 48×48 artwork with no extra markup: the span is the
   disc, its two counters are the bar and the stem, scaled 48→22 (×.4583). The
   counters take --topbar-bg — the surface the logo actually sits on — so they
   read as knockouts in both themes instead of as white ink on a dark bar. */
.logo .mark{
  width:22px; height:22px; border-radius:50%; background:var(--accent);
  position:relative; flex-shrink:0;
}
.logo .mark::before,
.logo .mark::after{content:""; position:absolute; background:var(--topbar-bg);}
.logo .mark::before{left:5.96px; top:5.73px; width:10.08px; height:3.21px; border-radius:1.6px;}
.logo .mark::after{left:9.4px; top:10.31px; width:3.21px; height:6.88px; border-radius:1.6px;}

nav{display:flex; gap:26px; flex:0 0 auto; justify-content:center; font-size:13.5px; color:var(--muted); font-weight:500;}
nav a{text-decoration:none; padding:6px 2px; position:relative; cursor:pointer; color:inherit; transition:color .15s ease;}
nav a::after{content:""; position:absolute; left:0; right:0; bottom:-6px; height:2px; background:var(--accent); border-radius:999px; transform:scaleX(0); transition:transform .15s ease;}
nav a:hover{color:var(--text);}
nav a.active{color:var(--text); font-weight:700;}
nav a.active::after{transform:scaleX(1);}

.header-right{display:flex; align-items:center; gap:12px; flex:1 1 0; justify-content:flex-end;}
.icon-btn{
  width:38px; height:38px; border-radius:8px; border:1px solid var(--border); background:var(--surface);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--text);
  transition:background .15s ease, transform .1s ease; position:relative;
}
.icon-btn:hover{background:var(--surface-hover);}
.icon-btn:active{transform:scale(.93);}
.icon-btn svg{opacity:.8;}

/* Shared utility for the small inline SVG icons that replaced emoji glyphs
   throughout the app (save/follow/report/edit/delete/reply/AI/attach/etc) —
   keeps them aligned with adjacent text at their natural baseline. */
.icon-inline{vertical-align:-2px; flex-shrink:0;}

/* Theme toggle: real outline icons instead of the "◐"/"☀" text characters —
   some platforms (iOS Safari included) silently render those as full-colour
   emoji instead of the plain glyph, which looked inconsistent with every
   other (SVG) icon in the app. Swapping which one is visible is done in pure
   CSS off the data-theme attribute, so there's nothing for JS to keep in sync. */
.icon-sun, .icon-moon{flex-shrink:0;}
.icon-sun{display:none;}
.icon-moon{display:block;}
:root[data-theme="dark"] .icon-sun{display:block;}
:root[data-theme="dark"] .icon-moon{display:none;}
.icon-btn .dot-badge{
  display:none; position:absolute; top:7px; right:7px; width:6px; height:6px; border-radius:50%;
  background:var(--accent); border:1.5px solid var(--topbar-bg);
}
.icon-btn .dot-badge.show{display:block;}

.btn-ask{
  background:var(--accent-fill); color:var(--on-accent); border:none; border-radius:8px; padding:10px 18px;
  font-size:13.5px; font-weight:700; cursor:pointer; white-space:nowrap; display:flex; align-items:center; gap:7px;
  transition:background .15s ease, transform .1s ease; box-shadow:var(--shadow-sm); text-decoration:none;
}
.btn-ask:hover{background:var(--accent-hover);}
.btn-ask:active{transform:scale(.96);}
.btn-ask-plus{font-size:15px; line-height:1;}

.avatar{
  width:34px;height:34px;border-radius:50%;background:var(--primary);color:var(--primary-text);
  display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12.5px;flex-shrink:0;
  cursor:pointer; border:2px solid var(--accent); transition:transform .1s ease; text-decoration:none;
}
.avatar:hover{transform:scale(1.06);}
/* An uploaded photo fills whatever circle it was dropped into and is cropped,
   never squashed — .avatar here, but also .composer-avatar and .profile-avatar,
   which own their own size and take only the contents (tabuAvatarInner). */
.avatar img, .avatar-img{width:100%; height:100%; border-radius:50%; object-fit:cover; display:block;}
/* The nav rail's «Вы → Профиль» row and the bottom bar's Профиль tab draw the
   photo at the icon's own size, so the row height and the 44px tab target are
   the ones they already were — no ring, no hover jump, nothing to re-measure. */
.nav-avatar{border:none; cursor:inherit;}
.nav-avatar:hover{transform:none;}

.auth-links{display:flex; align-items:center; gap:14px; font-size:13px; font-weight:600; white-space:nowrap;}
.auth-links a{text-decoration:none; color:var(--muted); transition:color .12s ease;}
.auth-links a:hover{color:var(--text);}
.auth-links a.primary{color:var(--accent-text);}
.logout-link{background:none; border:none; color:var(--muted); font-size:12px; cursor:pointer; text-decoration:underline; font-family:inherit; padding:0;}
.logout-link:hover{color:var(--text);}

/* ---------- header: responsive ----------
   Above 900px the header is one full row. Below it the nav links and the
   desktop-only controls step aside: navigation moves to the bottom tab bar,
   search hides behind an icon, and the theme switch moves to the Профиль
   page (see the settings card in profile.html). There is deliberately no
   overflow/hamburger menu — everything it used to hold now has a real home. */
@media(max-width:900px){
  /* no room for a centred column on a phone: logo, field, menu in one row */
  header{display:flex; height:58px; padding:0 16px; gap:10px;}
  body{padding-top:58px;}
  .topbar-left{flex:0 0 auto;}
  .topbar-right{flex:0 0 auto;}
  /* the bottom tab bar carries these on a phone */
  .topbar-right .icon-btn, .topbar-right .authArea{display:none;}
  .nav-rail-toggle{display:none;}
  nav, .header-right{display:none;}
  /* the logo kept flex:1 from the old centred top bar, where it had to balance
     the right-hand controls; here that steals a third of the row from the
     search field, so it goes back to its natural width */
  header .logo{flex:0 0 auto; font-size:17px;}
  header .topbar-search{padding:8px 14px;}
}

/* ---------- left navigation rail ----------
   Navigation moved off the top bar into a fixed left column — the shape every
   large social product uses. It gives nav room for labels and sections instead
   of a few links floating in a wide empty bar, and hands the page's full width
   back to the content. Below 900px it gives way to the header and the bottom
   tab bar: a 232px column costs too much of a phone screen.

   GEOMETRY: the rail owns one 12px gutter and every child spans it edge to
   edge, with 12px of internal padding so all text starts on the same 24px
   line. Getting this wrong is what makes a rail look amateur — a hover fill a
   few pixels wider than the button above it reads as a mistake even when
   nobody can say why.

   EDGE: a half-step of tone (--rail-bg) plus a very faint rule, rather than a
   full-contrast border. A hard line between two identical backgrounds is what
   made the boundary feel abrupt; a tonal step reads as depth instead. */
.nav-rail{display:none;}
@media(min-width:901px){
  body{padding-left:232px; transition:background .2s ease, color .2s ease, padding-left .18s ease;}
  html.rail-collapsed body{padding-left:76px;}

  .nav-rail{
    /* hangs below the bar, YouTube-style — the bar spans the full window */
    position:fixed; left:0; top:60px; bottom:0; width:232px; z-index:20;
    display:flex; flex-direction:column; padding:16px 12px;
    background:var(--rail-bg); border-right:1px solid var(--rail-edge);
    overflow-y:auto; overflow-x:hidden; scrollbar-width:thin;
    transition:width .18s ease, padding .18s ease;
  }
  .nav-rail::-webkit-scrollbar{width:6px;}
  .nav-rail::-webkit-scrollbar-thumb{background:var(--border); border-radius:999px;}

  .nav-rail-ask{width:100%; justify-content:center; margin:0 0 18px; padding:11px 12px; overflow:hidden;}

  .nav-rail-nav{display:flex; flex-direction:column; gap:4px;}
  .nav-rail-nav a{
    display:flex; align-items:center; gap:13px; padding:10px 12px; border-radius:8px;
    font-size:14px; font-weight:600; color:var(--muted); text-decoration:none;
    white-space:nowrap; overflow:hidden;
    transition:background .12s ease, color .12s ease;
  }
  .nav-rail-nav a svg{flex-shrink:0; opacity:.9;}
  /* The top bar's underline rule (nav a::after) still matches these links and
     draws a 2px bar 6px BELOW each item — an underline in a horizontal bar,
     but in a vertical column it lands on top of the next item. */
  .nav-rail-nav a::after{content:none;}
  .nav-rail-nav a:hover{background:var(--surface-hover); color:var(--text);}
  .nav-rail-nav a.active{background:var(--accent-soft); color:var(--accent-text); font-weight:700;}
  /* stated explicitly: .active and a:hover have equal specificity, so without
     this the current page is the one item that gives no feedback on hover */
  .nav-rail-nav a.active:hover{background:var(--accent-soft); color:var(--accent-text);}
  .nav-rail-nav .dot-badge{
    position:static; display:none; width:7px; height:7px; border-radius:50%;
    background:var(--accent); margin-left:auto; border:none;
  }
  .nav-rail-nav .dot-badge.show{display:block;}

  .nav-rail-section{margin-top:22px;}
  .nav-rail-section h4{
    font-size:10.5px; color:var(--muted); text-transform:uppercase;
    letter-spacing:.07em; font-weight:700; margin-bottom:6px; padding:0 12px;
    white-space:nowrap;
  }
  .nav-rail .rail-tabs{display:flex; flex-direction:column; gap:0;}
  .nav-rail .rail-tabs a{
    display:block; padding:7px 12px; border-radius:8px; background:none;
    font-size:12.5px; font-weight:600; color:var(--text); text-decoration:none;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    transition:background .12s ease, color .12s ease;
  }
  .nav-rail .rail-tabs a:hover{background:var(--surface-hover); color:var(--accent-text);}
  .nav-rail .rail-empty{font-size:11.5px; color:var(--muted); line-height:1.5; padding:0 12px;}
  .nav-rail .rail-create-tab{margin:8px 12px 0; padding:7px 10px;}
  /* the personal shortcuts reuse the nav rows, so they sit on the same gutter */
  #railYou .nav-rail-nav{gap:2px;}
  #railYou h4{margin-bottom:4px;}
  .nav-rail .rail-empty a{color:var(--accent-text);}

  /* The stats take the slack instead of the account block: the empty space now
     sits above them, so the bottom of the rail stays a single anchored cluster
     (numbers, rule, controls) rather than one lonely row at the very bottom. */
  .nav-rail-stats-section{
    margin-top:auto; padding-top:16px; margin-bottom:2px;
    border-top:1px solid var(--border);
  }
  .nav-rail-stats{display:flex; flex-direction:column; gap:1px;}
  .nav-rail-stat{
    display:flex; align-items:baseline; gap:8px; padding:5px 12px;
    font-size:11.5px; color:var(--muted); white-space:nowrap;
  }
  .nav-rail-stat b{
    font-size:13.5px; font-weight:700;
    color:var(--text); min-width:34px;
  }

  /* --- collapsed: YouTube's mini rail ---
     Icons alone left a 40px stub of content above a very tall empty column.
     YouTube's mini rail reads as full because each item is a ~68px block —
     icon stacked over a small label — and because it keeps showing the
     personal destinations, not just the top-level ones. Same here. */
  html.rail-collapsed .nav-rail{width:76px; padding-left:6px; padding-right:6px;}
  html.rail-collapsed .nav-rail .nav-rail-label,
  html.rail-collapsed .nav-rail .nav-rail-section{display:none;}
  html.rail-collapsed .nav-rail .nav-rail-ask{
    justify-content:center; padding-left:0; padding-right:0; gap:0; margin-bottom:12px;
  }
  html.rail-collapsed .nav-rail .nav-rail-nav{gap:2px;}
  html.rail-collapsed .nav-rail .nav-rail-nav a{
    flex-direction:column; justify-content:center; gap:6px;
    padding:16px 0 13px; border-radius:8px; overflow:hidden;
  }
  /* the labels come back, small and centred — that is what gives the mini rail
     its height and keeps the icons legible to someone who has not learned them */
  html.rail-collapsed .nav-rail .nav-rail-nav .nav-rail-label{
    display:block; font-size:9px; font-weight:600; line-height:1.1;
    text-align:center; letter-spacing:0; max-width:100%;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* the personal shortcuts stay reachable, under a divider like YouTube's */
  html.rail-collapsed .nav-rail #railYou{
    display:block; margin-top:8px; padding-top:8px;
    border-top:1px solid var(--border);
  }
  html.rail-collapsed .nav-rail #railYou h4{display:none;}

}

/* ---------- trust strip ---------- */
.trust-strip{
  /* not currently in any page's markup; kept on the shared container tokens
     so it cannot drift back out of alignment if it is used again */
  display:flex; gap:14px; justify-content:center; flex-wrap:wrap;
  padding:14px var(--page-pad); max-width:var(--page-max); margin:0 auto;
}
.trust-pill{
  display:flex; align-items:center; gap:7px; background:var(--surface); border:1px solid var(--border);
  border-radius:999px; padding:7px 14px; font-size:12px; color:var(--muted); font-weight:500;
}
.trust-pill .dot{width:6px;height:6px;border-radius:50%;background:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);}
.trust-pill.live .dot{animation:pulse 1.8s infinite;}
@keyframes pulse{
  0%{box-shadow:0 0 0 0 var(--accent-soft);}
  70%{box-shadow:0 0 0 6px transparent;}
  100%{box-shadow:0 0 0 0 transparent;}
}

/* ---------- ask composer ---------- */
/* Asking a question is the action the whole site depends on, and it lived only
   in the header — where it is invisible below 900px, since the header collapses
   to a title strip there. This puts it at the top of the feed, shaped like the
   thing it produces, which is where people expect to find it. (On a phone the
   composer itself is hidden too; the + in the bottom tab bar does its job.) */
.composer{margin-top:24px;}
.composer-inner{
  display:flex; align-items:center; gap:12px;
  background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:12px 14px;
  /* the only raised object on the page — everything else is flat by design */
  box-shadow:var(--shadow-md);
  transition:border-color .15s ease, box-shadow .15s ease;
}
.composer-inner:hover{border-color:var(--accent);}
.composer-avatar{
  width:34px; height:34px; border-radius:50%; flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  background:var(--accent-soft); color:var(--accent-text); font-weight:700; font-size:13px;
}
.composer-field{
  flex:1; min-width:0; text-decoration:none; color:var(--muted); font-size:14px;
  background:var(--bg); border:1px solid var(--border); border-radius:999px; padding:11px 16px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  transition:color .12s ease, border-color .12s ease;
}
.composer-inner:hover .composer-field{color:var(--text); border-color:var(--accent);}
.composer-photo{
  flex-shrink:0; display:flex; align-items:center; justify-content:center;
  width:38px; height:38px; border-radius:8px; color:var(--muted);
  border:1px solid var(--border); background:var(--bg); text-decoration:none;
  transition:color .12s ease, border-color .12s ease;
}
.composer-photo:hover{color:var(--accent); border-color:var(--accent);}

/* ---------- mobile: floating ask button (retired) ----------
   This used to appear below 720px, back when the header collapsed and took the
   ask button with it. The mobile app shell replaced it with the raised + in the
   bottom tab bar, and that shell starts at 900px — so the FAB's own 720px block
   was already being switched back off by a later rule at every width it could
   have shown at. It is gone; the rule below keeps the leftover markup in
   home.html inert at every width. */
.ask-fab{display:none;}

/* ---------- Leaflet chrome (shared: map page, post maps, home map) ----------
   OSM only ships light tiles, so dark mode is done by filtering the tile layer
   itself. Scoped to .leaflet-tile-pane, not the whole map, so pins, controls
   and panels keep their real colours. */
:root[data-theme="dark"] .leaflet-tile-pane{
  filter:invert(1) hue-rotate(180deg) brightness(.92) contrast(.85) saturate(.65);
}
/* font-family:inherit, not "nothing": leaflet.css sets its own Helvetica stack
   on .leaflet-container, so dropping our declaration hands the map chrome (and
   the pin counts inside it) back to Leaflet's font rather than to the page's. */
.leaflet-container{background:var(--surface); font-family:inherit;}
.leaflet-control-zoom a{
  background:var(--surface) !important; color:var(--text) !important;
  border-color:var(--border) !important;
}
.leaflet-control-zoom a:hover{background:var(--surface-hover) !important;}
.leaflet-control-attribution{
  background:var(--surface) !important; color:var(--muted) !important; font-size:10px !important;
}
.leaflet-control-attribution a{color:var(--accent-text) !important;}

/* the teardrop pin, carrying its discussion count */
.map-pin{
  display:flex; align-items:center; justify-content:center;
  width:30px; height:30px; border-radius:50% 50% 50% 0; transform:rotate(-45deg);
  background:var(--accent-fill); color:var(--on-accent); border:2px solid var(--surface);
  box-shadow:0 2px 8px rgba(0,0,0,.35); font-size:11px; font-weight:700;
  cursor:pointer;
}
.map-pin span{transform:rotate(45deg);}
/* the place the feed is filtered to, and the pin of the card being hovered */
.map-pin.is-active{background:var(--text); color:var(--bg);}

/* ---------- home: feed beside a live map ----------
   The map is the differentiator, so it is on screen from the moment the page
   opens rather than behind a nav link. Sticky on desktop; on narrower screens
   it moves above the feed (order:-1) instead of being dropped. */
.home-layout{display:grid; grid-template-columns:minmax(0,1fr) 380px; gap:28px; align-items:start;}
/* clears the sticky search bar, and scrolls on its own rather than letting the
   lower cards fall off the bottom where sticky can't reach them */
.home-map{
  position:sticky; top:78px; max-height:calc(100vh - 96px);
  overflow-y:auto; scrollbar-width:thin;
}
.home-map::-webkit-scrollbar{width:6px;}
.home-map::-webkit-scrollbar-thumb{background:var(--border); border-radius:999px;}
/* Not a card any more: the side column is a section of the page, separated by
   one rule, and the MAP is the object with an edge. isolation:isolate stays —
   Leaflet's panes carry z-index up to 800 and .leaflet-container is not itself
   a stacking context, so without it they paint over the fixed header. */
.home-map-card{
  background:transparent; border:none; border-top:1px solid var(--border);
  border-radius:0; overflow:hidden; isolation:isolate; margin-bottom:14px;
}
.home-map-head{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:11px 0 10px; border-bottom:none;
}
.home-map-title{font-size:13px; font-weight:600;}
.home-map-open{font-size:12px; font-weight:600; color:var(--accent-text); text-decoration:none;}
.home-map-open:hover{opacity:.8;}
#homeMap{height:360px; background:var(--surface); border-radius:8px; overflow:hidden;}
.home-map-foot{
  padding:10px 0 0; font-size:11.5px; color:var(--muted);
  border-top:none; line-height:1.5;
  display:flex; align-items:center; justify-content:space-between; gap:10px;
}
.home-map-reset{
  background:none; border:none; padding:0; cursor:pointer; font-family:inherit;
  color:var(--accent-text); font-size:11.5px; font-weight:700; white-space:nowrap;
}

/* rows inside the rail's cards */
.rail-item{
  display:block; width:100%; text-align:left; padding:9px 0; cursor:pointer;
  background:none; border:none; border-bottom:1px solid var(--border);
  color:inherit; font-family:inherit; text-decoration:none;
}
.rail-item:last-child{border-bottom:none;}
.rail-item:hover .rail-item-title,
.rail-item:hover .rail-place-name{color:var(--accent-text);}
.rail-item-title{
  font-size:12.5px; font-weight:600; line-height:1.4;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.rail-item-meta{display:block; font-size:11px; color:var(--muted); margin-top:3px;}
.rail-place{display:flex; align-items:center; gap:9px;}
.rail-place-name{
  flex:1; min-width:0; font-size:12.5px; font-weight:600;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.rail-place-count{flex-shrink:0; font-size:11px; color:var(--muted);}
.rail-note{font-size:12px; color:var(--muted); line-height:1.55;}

@media(max-width:1080px){
  .home-layout{grid-template-columns:1fr;}
  /* the map stays above the feed on narrow screens, but the discovery cards
     don't — four blocks ahead of the content people came for is too many */
  .home-map{position:static; order:-1; margin-bottom:4px; max-height:none; overflow:visible;}
  .home-map-card{margin-bottom:0;}
  .home-rail-extra{display:none;}
  #homeMap{height:230px;}
}

/* ---------- filter tabs ---------- */
.filter-row{display:flex; gap:8px; margin-top:28px; flex-wrap:wrap;}
.filter-tab{
  border:1px solid var(--border); background:var(--surface); color:var(--muted); font-size:12.5px; font-weight:600;
  padding:8px 15px; border-radius:8px; cursor:pointer; transition:all .15s ease;
}
.filter-tab.active{background:var(--primary); color:var(--primary-text); border-color:var(--primary);}
.filter-tab:not(.active):hover{background:var(--surface-hover); color:var(--text);}

/* ---------- page container ----------
   The single horizontal frame for the whole site. Anything that should line up
   with anything else on the same screen carries `page-w` in the markup and
   sets NO max-width and NO left/right padding of its own — vertical padding
   only. That includes the page-specific shells declared in each page's own
   <style> block (.q-shell-wrap, .ask-shell, .create-shell, .profile-shell,
   .dir-shell, .tab-banner): those blocks load after this file and would win a
   specificity tie, so they must not redeclare the horizontal box either.

   Width comes from --page-max/--page-pad on :root, which the mobile block at
   the bottom of this file narrows to 14px in one place rather than per
   element. Because every block now resolves to the same auto margins, the
   old three-different-left-edges / three-different-right-edges problem cannot
   come back: there is one number. */
.page-w{
  width:100%;
  max-width:var(--page-max);
  margin-left:auto; margin-right:auto;
  padding-left:var(--page-pad); padding-right:var(--page-pad);
}

/* ---------- layout ---------- */
/* horizontal box comes from .page-w on the same element */
.shell{padding-top:16px; padding-bottom:70px;}
/* NOTE: no mobile padding-bottom override lives here. There used to be a
   720px one reserving room for the floating ask button; the button is gone,
   and below 900px `.page-w{--page-pad:14px}` narrows the gutter while the
   shell keeps its own vertical padding. Clearance for the bottom tab bar comes from
   `body{padding-bottom:68px}` in that same block, which starts at the same
   900px the bar does — so there is no band where the bar can cover content. */
/* D3, second half: tab.html's feed is the same feed, so it gets the same
   treatment as .home-layout — a wider side column and gap pull the reading
   column in from 802px to 748px, close enough to the home feed's 708px that
   moving between the two pages doesn't feel like a different site. */
.layout{display:grid; grid-template-columns:minmax(0,1fr) 340px; gap:28px;}
/* 900, not 860: the sidebar has to fold away at the same width the phone shell
   turns on, or 861-900px gets the bottom tab bar and a desktop side column at
   once. Same reasoning for .profile-layout, .ask-grid and .create-grid, which
   each carry this rule in their own page. */
@media(max-width:900px){ .layout{grid-template-columns:1fr;} }

/* ---------- question feed / cards ---------- */
.feed{display:flex; flex-direction:column; gap:14px; margin-top:14px;}
.qcard{
  display:block; background:var(--surface); border:1px solid transparent; border-radius:12px;
  padding:16px 18px; cursor:pointer; transition:background .15s ease, border-color .15s ease;
  text-decoration:none; color:inherit; box-shadow:var(--shadow-sm);
}
/* no lift and no outline on hover — in a dense scrolling feed a card that jumps
   under the cursor is noise, and a teal ring around every row it passes is the
   busywork this palette exists to remove. The fill shift is enough. */
.qcard:hover{background:var(--surface-hover); border-color:transparent;}

/* one line of context above the title: tab, tag, author, when, where */
.qhead{
  display:flex; align-items:center; gap:7px; flex-wrap:wrap;
  font-size:12px; color:var(--muted); margin-bottom:8px;
}
.qhead-sep{opacity:.45;}

/* The photo, in a box whose size is known before the file is.
   TWO things are going on here, and they are the same thing.
   1. LAYOUT SHIFT. The <img> carries no width/height (the server stores none —
      see the upload handler in server.py, which saves the bytes and nothing
      else), so an unsized image is 0 tall until it decodes and then snaps to
      full height. A card measured ~120px before load and ~471px after: in a
      scrolling feed that is the row under your thumb moving as you tap it.
      aspect-ratio gives the browser the height up front, from CSS alone, so
      nothing moves — no dimensions in the markup required.
   2. PILLAR-BOXING. object-fit:contain centred the image and filled the rest
      with page background, so a portrait phone photo — which is most of them —
      sat 255px wide in a 335px card between two empty bars. cover fills the
      width instead.
   The ratio is capped rather than measured: 4:5 (Instagram's portrait cap) up
   to the point max-height takes over, which on a wide card gives a shallower
   box. A panorama therefore shows its middle rather than a 20px letterbox
   strip, and a very tall image is cropped instead of eating the screen —
   never stretched either way, since cover preserves the aspect ratio. The
   uncropped photo is one tap away on the question page, which still uses
   contain (.q-image-frame in question.html). */
.qmedia{
  margin-top:12px; border-radius:12px; overflow:hidden; background:var(--bg);
  border:1px solid var(--border); display:block; width:100%;
  aspect-ratio:4/5; max-height:440px;
}
.qimage{display:block; width:100%; height:100%; object-fit:cover; object-position:center;}
/* Set by tabuFitFeedImage() in app.js once the file's real size is known, on
   images too small to fill the box — cover would upscale and crop them for
   nothing. The box does not change, so this repaints without moving anything. */
.qimage.qimage-fit{object-fit:contain;}

.qactions{display:flex; align-items:center; gap:9px; margin-top:12px; flex-wrap:wrap;}
.qaction{
  position:relative;
  display:inline-flex; align-items:center; gap:6px; height:32px; padding:0 12px; border-radius:999px;
  font-size:var(--fs-control); font-weight:600; color:var(--muted); background:var(--bg);
  border:1px solid var(--border); text-decoration:none; cursor:pointer; white-space:nowrap;
  transition:color .12s ease, border-color .12s ease;
}
.qaction:hover{color:var(--text); border-color:var(--accent);}
/* Rendered instead of the «0 ответов» pill (tabuRenderQuestionCard in app.js):
   an empty counter is a dead end, an invitation is not. Bare, so it reads as
   the one live thing in the row rather than a second chip. */
.qaction-first{background:none; border-color:transparent; color:var(--accent-text); padding-left:4px; font-weight:600;}

.vote-col{display:flex; flex-direction:column; align-items:center; gap:4px; flex-shrink:0; padding-top:2px;}
/* horizontal variant for the feed's action bar; the question page keeps the
   vertical column beside the post */
.vote-col.vote-pill{
  flex-direction:row; align-items:center; gap:1px; height:32px; padding:0 2px;
  background:var(--bg); border:1px solid var(--border); border-radius:999px;
}
.vote-pill .vote-btn{width:30px; height:26px; border:none; background:none; border-radius:999px; font-size:var(--fs-caption);}
.vote-pill .vote-btn:hover{background:var(--surface-hover);}
.vote-pill .vote-btn.voted{background:var(--accent-fill); color:var(--on-accent);}
.vote-pill .vote-n{min-width:22px; text-align:center; font-size:var(--fs-control);}
.vote-btn{
  width:30px; height:26px; border-radius:8px; border:1px solid var(--border); background:var(--bg);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--muted);
  transition:all .12s ease; font-size:13px;
}
.vote-btn:hover{border-color:var(--accent); color:var(--accent-text);}
.vote-btn.voted{background:var(--accent); border-color:var(--accent); color:#fff;}
.vote-btn:active{transform:scale(.9);}
.vote-n{font-size:13px; font-weight:700; color:var(--text);}

.tag-row{display:flex; gap:9px; margin-bottom:9px; flex-wrap:wrap; align-items:center;}
.tab-badge{font-size:var(--fs-caption); font-weight:700; color:var(--accent-text); cursor:pointer; transition:opacity .12s ease; text-decoration:none;}
.tab-badge:hover{opacity:.75; text-decoration:underline;}
.tab-badge .slash{opacity:.55; font-weight:600;}
/* links a question back to its pin on the map */
.place-badge{
  position:relative;
  display:inline-flex; align-items:center; gap:6px; height:32px; padding:0 12px;
  font-size:var(--fs-control); font-weight:700;
  color:var(--accent-text); background:var(--accent-soft); border-radius:999px;
  text-decoration:none; white-space:nowrap; transition:opacity .12s ease;
}
.place-badge:hover{opacity:.8;}
/* …except in a card head, where it is one more piece of 12px meta text beside
   the tab and the author, not a control. The feed keeps exactly the badge it
   had; only the question page raises it to the chip above. */
.qhead .place-badge{height:auto; gap:5px; padding:3px 9px; font-size:var(--fs-caption);}
.tag{font-size:11px; font-weight:600; color:var(--muted); cursor:pointer; transition:color .12s ease;}
.tag:hover{color:var(--text);}
.tag::before{content:"·"; margin-right:9px; color:var(--border);}
.qtitle{font-size:18px; font-weight:600; margin-bottom:6px; line-height:1.34;}
/* three lines then cut — enough to judge the post, short enough that a long
   one can't push the next card off the screen */
.qsnippet{
  font-size:13.5px; color:var(--muted); line-height:1.6;
  display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
}
.verified{display:inline-flex; align-items:center; gap:3px; color:var(--accent-text); font-weight:700;}
/* the author name is a link to their profile now; it keeps the .verified look
   and only underlines on hover, like every other inline link in a card */
a.verified{text-decoration:none;}
a.verified:hover{text-decoration:underline;}
/* avatar + name as one inline unit, so dropping a 20px disc into a line of
   running meta text (question.html's .qmeta) keeps it on the same line */
.qauthor{display:inline-flex; align-items:center; gap:7px; vertical-align:middle;}
/* A 20px disc in a row whose text is 12px would make every card head 5.5px
   taller. The negative block margin takes the circle back out of the row's
   height calculation, so the head stays exactly as tall as it was (14.5px on
   desktop) and the avatar overflows into .qcard's 16px padding instead. The
   overflow is 2.75px a side — less than the row's own 7px gap, so a head that
   wraps on a phone still cannot collide with the line below it. */
.qhead-avatar{margin-top:-3px; margin-bottom:-3px;}

/* ---------- sidebar ---------- */
.sidebar-card{background:transparent; border:none; border-top:1px solid var(--border); border-radius:0; padding:18px 0; margin-bottom:16px;}
.sidebar-card h4{font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.07em; margin-bottom:14px; font-weight:700;}
.chip-row{display:flex; flex-wrap:wrap; gap:8px;}
.chip{
  background:var(--bg); border:1px solid var(--border); border-radius:999px; padding:7px 12px;
  font-size:12px; font-weight:600; color:var(--text); cursor:pointer; transition:all .12s ease; text-decoration:none;
  display:inline-flex; align-items:center;
}
.chip:hover{border-color:var(--accent); color:var(--accent-text); transform:translateY(-1px);}
.chip .slash{color:var(--accent-text); font-weight:700;}
.author-row{display:flex; align-items:center; gap:10px; padding:9px 6px; border-radius:8px; cursor:pointer; transition:background .12s ease; margin:0 -6px; text-decoration:none; color:inherit;}
.author-row:hover{background:var(--surface-hover);}
.author-name{font-size:13px; font-weight:700; display:flex; align-items:center; gap:5px;}
.author-rep{font-size:11.5px; color:var(--accent-text);}
.rank{font-size:11px; font-weight:700; color:var(--muted); width:16px;}

.stat-row{display:flex; justify-content:space-between; font-size:12px; padding:8px 0; border-bottom:1px solid var(--border);}
.stat-row:last-child{border-bottom:none;}
.stat-row b{color:var(--text); font-weight:700;}
.stat-row span:first-child{color:var(--muted);}

/* ---------- breadcrumb ---------- */
.breadcrumb{font-size:12px; color:var(--muted); margin-top:14px; margin-bottom:14px;}
.breadcrumb a{
  color:var(--accent-text); text-decoration:none;
  /* The link is 12px text, so its hit box measured ~15px tall on a phone — well
     under the 44px minimum (caught on create-tab.html at 375px). Vertical padding
     on an INLINE element grows the hit box without touching the line box, so the
     breadcrumb keeps its height; the horizontal padding is cancelled by the
     negative margin so the text does not shift sideways. */
  padding:15px 6px; margin:0 -6px;
}
.breadcrumb a:hover{text-decoration:underline;}

/* ---------- generic buttons ---------- */
.btn-primary{
  background:var(--accent-fill); color:var(--on-accent); border:none; border-radius:8px; padding:12px 20px;
  font-size:13.5px; font-weight:700; cursor:pointer; transition:background .15s ease, transform .1s ease;
}
.btn-primary:hover{background:var(--accent-hover);}
.btn-primary:active{transform:scale(.97);}
.btn-secondary{
  background:var(--surface); color:var(--text); border:1px solid var(--border); border-radius:8px; padding:12px 20px;
  font-size:13.5px; font-weight:700; cursor:pointer; transition:background .15s ease;
}
.btn-secondary:hover{background:var(--surface-hover);}

/* ---------- attach a photo ----------
   Used by the ask form and by answer/reply boxes, so it lives here rather
   than in one page's <style>. Rendered by tabuCreateImageAttacher. */
.attach-row{
  display:flex; align-items:center; gap:10px; border:1px dashed var(--border); border-radius:8px; padding:14px;
  font-size:12.5px; color:var(--muted); margin-top:8px; cursor:pointer; transition:border-color .12s ease;
}
.attach-row:hover{border-color:var(--accent); color:var(--accent-text);}
.attach-row.has-image{border-style:solid; cursor:default; padding:10px;}
.attach-row.has-image:hover{border-color:var(--border); color:var(--muted);}
.attach-preview{display:flex; align-items:center; gap:12px; width:100%;}
.attach-preview img{width:56px; height:56px; border-radius:8px; object-fit:cover; border:1px solid var(--border); flex-shrink:0;}
.attach-preview .attach-name{flex:1; min-width:0; font-size:12.5px; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.attach-remove{
  flex-shrink:0; width:26px; height:26px; border-radius:8px; border:1px solid var(--border); background:var(--bg);
  display:flex; align-items:center; justify-content:center; cursor:pointer; color:var(--muted); transition:all .12s ease;
}
.attach-remove:hover{border-color:var(--accent); color:var(--accent);}

/* the place an answer names, under its text */
.answer-place-row{margin-top:8px;}

/* a photo attached to an answer */
.answer-image{
  display:block; max-width:100%; max-height:300px; object-fit:contain; margin-top:10px;
  background:var(--bg); border:1px solid var(--border); border-radius:12px; cursor:zoom-in;
}

/* ---------- modal dialog ----------
   Shared, built from JS (tabuOpenModal in app.js) so any page can use it
   without carrying the markup — replaces the raw browser prompt()/confirm()
   boxes, which looked nothing like the rest of the app. */
.tabu-modal-backdrop{
  position:fixed; inset:0; z-index:1000; background:rgba(0,0,0,.55);
  display:flex; align-items:center; justify-content:center; padding:20px;
  opacity:0; pointer-events:none; transition:opacity .18s ease;
}
.tabu-modal-backdrop.open{opacity:1; pointer-events:auto;}
.tabu-modal{
  width:100%; max-width:380px; background:var(--surface); border:1px solid var(--border);
  border-radius:12px; padding:22px; box-shadow:0 20px 50px rgba(0,0,0,.35);
  transform:translateY(10px) scale(.985); transition:transform .18s ease;
}
.tabu-modal-backdrop.open .tabu-modal{transform:none;}
.tabu-modal h3{font-size:16.5px; font-weight:600; margin-bottom:5px;}
.tabu-modal .modal-sub{font-size:12.5px; color:var(--muted); line-height:1.55; margin-bottom:16px;}
/* only the FIRST label sits flush under the subtitle — the rest keep their
   normal spacing, or a second field's label ends up glued to the field above */
.tabu-modal label:first-of-type{margin-top:0;}
.tabu-modal-choices{display:flex; flex-wrap:wrap; gap:7px;}
.tabu-modal-choice{
  background:var(--bg); border:1px solid var(--border); border-radius:999px;
  padding:8px 14px; font-size:12px; font-weight:600; font-family:inherit;
  color:var(--muted); cursor:pointer; transition:all .12s ease;
}
.tabu-modal-choice:hover{color:var(--text); border-color:var(--accent);}
.tabu-modal-choice.selected{background:var(--accent-fill); color:var(--on-accent); border-color:transparent;}
/* the "create this type" chip reads as an action, not another option */
.tabu-modal-choice.is-new{border-style:dashed; color:var(--accent-text); border-color:var(--accent);}
.tabu-modal-choice.is-new:hover{background:var(--accent-soft);}
.tabu-modal-choice-search{margin-bottom:9px; padding:8px 12px !important; font-size:12.5px !important;}
.tabu-modal-choice-note{font-size:11.5px; line-height:1.5; margin-top:8px; color:var(--muted);}
.tabu-modal-choice-note.ok{color:var(--accent-text);}
.tabu-modal-choice-note.error{color:var(--danger);}
.tabu-modal-choice-note:empty{display:none;}

.tabu-modal-actions{display:flex; gap:9px; margin-top:22px;}
/* the confirm label is the long one, so it takes the room and "Отмена"
   sizes to its own text — otherwise equal halves wrap it on narrow phones */
.tabu-modal-actions button{padding:11px 16px;}
.tabu-modal-actions .btn-secondary{flex:0 0 auto;}
.tabu-modal-actions .btn-primary{flex:1;}
@media(max-width:520px){ .tabu-modal{max-width:none;} }

/* ---------- searchable picker ----------
   A styled replacement for <select>, whose dropdown the browser renders
   itself and refuses to theme. Built from JS (tabuCreatePicker in app.js),
   positioned fixed against its trigger button so it needs no wrapper. */
.tabu-picker-panel{
  position:fixed; z-index:1100; display:none; overflow:hidden;
  background:var(--surface); border:1px solid var(--border); border-radius:12px;
  box-shadow:0 14px 36px rgba(0,0,0,.32); width:270px;
}
.tabu-picker-panel.open{display:block;}
.tabu-picker-search{padding:9px; border-bottom:1px solid var(--border);}
.tabu-picker-search input[type=text]{
  padding:8px 11px; font-size:12.5px; border-radius:8px; background:var(--bg);
}
.tabu-picker-list{max-height:260px; overflow-y:auto; padding:6px;}
/* group heading in a picker that mixes two kinds of option */
.tabu-picker-head{
  padding:9px 10px 4px; font-size:10px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.07em;
}
.tabu-picker-head:first-child{padding-top:4px;}
.tabu-picker-item{
  display:block; width:100%; text-align:left; background:none; border:none;
  padding:9px 10px; border-radius:8px; cursor:pointer; font-family:inherit; color:var(--text);
}
.tabu-picker-item:hover{background:var(--surface-hover);}
.tabu-picker-item.selected .tabu-picker-label{color:var(--accent-text);}
.tabu-picker-label{display:block; font-size:12.5px; font-weight:600;}
.tabu-picker-sub{display:block; font-size:11px; color:var(--muted); margin-top:2px;}
.tabu-picker-empty{padding:14px 12px; font-size:12px; color:var(--muted); line-height:1.5;}

/* ---------- forms ---------- */
label{font-size:12px; font-weight:700; color:var(--text); text-transform:uppercase; letter-spacing:.03em; display:block; margin:16px 0 7px;}
label:first-child{margin-top:0;}
/* Every text-entry control shares one look. Password/email are listed
   explicitly — leaving them out is what made login fields fall back to raw
   browser styling and look unlike the rest of the form. */
textarea,
select,
input[type=text],
input[type=email],
input[type=password]{
  width:100%; border:1px solid var(--border); border-radius:8px; padding:11px 13px; font-size:13.5px;
  font-family:inherit; color:var(--text); background:var(--bg); outline:none; transition:border-color .15s ease, box-shadow .15s ease;
}
textarea:focus,
select:focus,
input[type=text]:focus,
input[type=email]:focus,
input[type=password]:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-soft);}
textarea{min-height:110px; resize:vertical;}
textarea::placeholder, input::placeholder{color:var(--muted);}

/* ---------- AI box (reused: question detail, ask form) ---------- */
.ai-box{background:var(--accent-soft); border:1px solid var(--border); border-radius:12px; padding:16px 18px;}
.ai-box .h{font-size:11.5px; font-weight:700; color:var(--accent-text); text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px; display:flex; align-items:center; gap:6px;}
.ai-box p{font-size:13.5px; color:var(--text); line-height:1.6; margin:0;}
.ai-suggest-item{font-size:12.5px; color:var(--text); padding:8px 0; border-bottom:1px solid var(--border); cursor:pointer;}
.ai-suggest-item:last-child{border-bottom:none;}
.ai-suggest-item:hover{color:var(--accent-text);}

/* ---------- badges ---------- */
.badge{font-size:10.5px; font-weight:700; padding:3px 9px; border-radius:999px;}
.badge-best{background:var(--accent-soft); color:var(--accent-text);}
.badge-official{background:var(--accent-soft); color:var(--accent-text); text-decoration:none;}
/* sponsored content is deliberately neutral, not accent-colored — it must
   read as "labeled", not as an endorsement */
.badge-sponsored{background:var(--surface-hover); color:var(--muted); border:1px solid var(--border); text-decoration:none;}
.badge-rank{background:var(--surface-hover); color:var(--muted); border:1px solid var(--border); font-size:11px; padding:4px 11px;}
.badge-rank.active{background:var(--accent-soft); color:var(--accent-text); border-color:transparent; font-weight:700;}
.rank-ladder{display:flex; gap:6px; flex-wrap:wrap; justify-content:center; margin-top:10px;}
.rank-progress{font-size:11.5px; color:var(--muted); margin-top:9px;}

/* ---------- question actions (save / follow / report / edit / delete) ----------
   They sit in the question's one action row now, beside the vote pill, so they
   carry the same pill geometry as everything else in it. */
.action-btn{
  position:relative;
  display:inline-flex; align-items:center; gap:6px; background:var(--surface); border:1px solid var(--border);
  border-radius:999px; height:32px; padding:0 13px; font-size:var(--fs-control); font-weight:600; color:var(--muted);
  cursor:pointer; transition:all .12s ease; font-family:inherit;
}
.action-btn:hover{border-color:var(--accent); color:var(--accent-text);}
.action-btn.active{background:var(--accent-soft); border-color:var(--accent); color:var(--accent-text);}
.action-btn.report-btn:hover{border-color:var(--danger); color:var(--danger);}
.action-btn.report-btn.active{background:rgba(178,69,69,.1); border-color:var(--danger); color:var(--danger); cursor:default;}

/* ---------- place sheet ----------
   The one place-picking surface (tabuPlaceSheet in app.js): a 480px dialog on
   a desktop, the whole screen on a phone, one DOM for both. It sits on the
   same z layer as .tabu-modal-backdrop and is appended before it, so the "add
   a place" dialogs the sheet itself opens land on top without needing a second
   z-index scale. */
.tabu-sheet-backdrop{
  position:fixed; inset:0; z-index:1000; display:flex; align-items:center; justify-content:center;
  background:rgba(0,0,0,.55);
}
.tabu-sheet{
  display:flex; flex-direction:column; min-height:0;
  width:480px; max-width:calc(100vw - 32px); max-height:80vh;
  background:var(--bg); border-radius:14px; overflow:hidden; box-shadow:var(--shadow-lg);
}
.tabu-sheet-head{
  position:relative; flex:0 0 56px; display:flex; align-items:center; justify-content:center;
  border-bottom:1px solid var(--border);
}
/* the title carries a place name once the sheet is inside one, and «Есентай
   Молл и ещё сколько-то слов» must not push under the back button */
.tabu-sheet-title{
  font-size:16px; font-weight:600;
  max-width:calc(100% - 112px); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.tabu-sheet-close{
  position:absolute; left:6px; top:6px; width:44px; height:44px; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; border-radius:50%; cursor:pointer; color:var(--text);
}
.tabu-sheet-close:hover{background:var(--surface-hover);}
/* a breath between the header rule and the field — with 0 it sat on the
   header's bottom border and read as jammed */
.tabu-sheet-search{flex:0 0 auto; padding:12px 16px 4px;}
.tabu-sheet-search input[type=text]{
  /* 16px is not a taste decision: below it, iOS zooms the whole page on focus
     and the sheet is suddenly wider than the screen. */
  height:44px; margin:0; padding:0 12px; font-size:16px; border-radius:10px; background:var(--surface);
}
.tabu-sheet-list{flex:1 1 auto; overflow-y:auto; -webkit-overflow-scrolling:touch; padding:6px 0 8px;}
.tabu-sheet-row{
  display:flex; flex-direction:column; justify-content:center; gap:2px;
  width:100%; height:52px; padding:0 16px; border:none; background:none; text-align:left;
  font-family:inherit; color:var(--text); cursor:pointer;
}
.tabu-sheet-row:hover{background:var(--surface-hover);}
/* a venue reads as being under its parent, 24px in from the parent's name */
.tabu-sheet-row.is-venue{padding-left:40px;}
/* A top-level place is two things at once: an answer (most people mean the
   mall) and a container. The row stays the whole row and keeps its own hover,
   the chevron takes only its 44px at the end — two targets, never one gesture
   doing both. Buttons can't nest, hence the wrapper rather than an inner one. */
.tabu-sheet-rowwrap{display:flex; align-items:center; width:100%;}
.tabu-sheet-rowwrap .tabu-sheet-row{flex:1 1 auto; width:auto; min-width:0; padding-right:2px;}
.tabu-sheet-into{
  flex:0 0 44px; width:44px; height:44px; padding:0; margin-right:4px;
  display:flex; align-items:center; justify-content:center;
  background:none; border:none; border-radius:50%; cursor:pointer; color:var(--muted);
}
.tabu-sheet-into:hover{background:var(--surface-hover); color:var(--text);}
.tabu-sheet-into:focus-visible{outline:2px solid var(--accent); outline-offset:-4px;}
.tabu-sheet-name{
  display:flex; align-items:center; gap:7px; min-width:0;
  font-size:15px; font-weight:500;
}
.tabu-sheet-note{
  flex:0 0 auto; font-size:10.5px; font-weight:700; color:var(--accent-text);
  background:var(--accent-soft); border-radius:999px; padding:2px 7px;
}
.tabu-sheet-sub{font-size:13px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
.tabu-sheet-head-row{
  padding:12px 16px 4px; font-size:10px; font-weight:700; color:var(--muted);
  text-transform:uppercase; letter-spacing:.07em;
}
.tabu-sheet-empty{padding:18px 16px; font-size:13px; color:var(--muted); line-height:1.55;}
.tabu-sheet-action{
  display:flex; align-items:center; gap:9px; width:100%; min-height:48px; padding:12px 16px;
  border:none; border-top:1px solid var(--border); background:none; cursor:pointer;
  font-family:inherit; font-size:14px; font-weight:600; color:var(--accent-text); text-align:left;
}
.tabu-sheet-action:hover{background:var(--surface-hover);}
.tabu-sheet-map{flex:1 1 auto; display:flex; flex-direction:column; min-height:0; padding:10px 16px 16px;}
/* the element declares display:flex, so the UA's [hidden] rule can't win */
.tabu-sheet-map[hidden]{display:none;}
.tabu-sheet-map-canvas{
  flex:1 1 auto; min-height:240px; border-radius:10px; border:1px solid var(--border);
  overflow:hidden; background:var(--surface); isolation:isolate;
}
:root[data-theme="dark"] .tabu-sheet-map-canvas .leaflet-tile-pane{
  filter:invert(1) hue-rotate(180deg) brightness(.92) contrast(.85) saturate(.65);
}
.tabu-sheet-hint{font-size:11.5px; color:var(--muted); line-height:1.55; margin-top:8px;}

/* ---------- answer composer: the tools row ----------
   The photo attacher and the «Место» chip sit side by side under the textarea
   (tabuAnswerComposer in app.js). The chip's 8px top margin matches the one
   .attach-row carries, so the two line up without either owning the other. */
.composer-tools{display:flex; align-items:flex-start; gap:8px; flex-wrap:wrap;}
/* 170px is the width below which «Прикрепить фото» wraps to a second line.
   Making it the flex BASIS means the chip drops to its own row first — in the
   narrow inline reply box that is the difference between two tidy rows and one
   cramped one. */
.composer-tools .composer-attach{flex:1 1 170px; min-width:0;}
.composer-place-slot{flex:0 0 auto; margin-top:8px; max-width:60%;}
.composer-place{
  position:relative;
  display:inline-flex; align-items:center; gap:6px; max-width:100%; height:32px; padding:0 14px;
  border:1px dashed var(--border); border-radius:999px; background:none; cursor:pointer;
  font-family:inherit; font-size:var(--fs-control); font-weight:600; color:var(--muted); white-space:nowrap;
}
.composer-place:hover{border-color:var(--accent); color:var(--accent-text);}
.composer-place.is-set{
  border-style:solid; border-color:transparent; background:var(--accent-soft);
  color:var(--accent-text); cursor:default; padding-right:3px;
}
.composer-place-name{min-width:0; overflow:hidden; text-overflow:ellipsis; cursor:pointer;}
.composer-place-clear{
  flex:0 0 auto; width:26px; height:26px; margin-left:2px; padding:0; border:none; border-radius:50%;
  background:none; color:inherit; cursor:pointer; display:flex; align-items:center; justify-content:center;
}
.composer-place-clear:hover{background:var(--surface-hover);}

/* ---------- toast ---------- */
.tabu-toast{
  position:fixed; bottom:24px; left:50%; transform:translateX(-50%) translateY(20px); opacity:0;
  background:var(--text); color:var(--bg); font-size:13px; font-weight:600; padding:12px 20px; border-radius:8px;
  box-shadow:var(--shadow-lg); z-index:100; pointer-events:none; transition:opacity .2s ease, transform .2s ease;
  max-width:min(90vw, 360px); text-align:center;
}
.tabu-toast.show{opacity:1; transform:translateX(-50%) translateY(0);}

/* ---------- reset demo (static build only — hidden unless tabuResetDemo exists) ---------- */
.reset-demo-btn{
  background:none; border:1px solid var(--border); color:var(--muted); font-size:11.5px; cursor:pointer;
  padding:7px 13px; border-radius:8px; font-family:inherit; margin-top:10px;
}
.reset-demo-btn:hover{border-color:var(--danger); color:var(--danger);}

/* ---------- card / surface ---------- */
.card{background:var(--surface); border:1px solid var(--border); border-radius:12px; padding:20px;}

/* ---------- stat grid (business dashboard) ---------- */
.stat-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin-bottom:16px;}
@media(max-width:860px){ .stat-grid{grid-template-columns:repeat(2,1fr);} }
.stat-card{background:var(--primary); color:var(--primary-text); border-radius:12px; padding:18px;}
.stat-card.alt{background:var(--accent-fill); color:var(--on-accent);}
.stat-card .num{font-size:26px; font-weight:700;}
.stat-card .lbl{font-size:11.5px; opacity:.75; margin-top:4px;}

.form-msg{font-size:12.5px; padding:10px 13px; border-radius:8px; margin-top:14px; display:none;}
.form-msg.error{display:block; background:rgba(178,69,69,.1); color:var(--danger); border:1px solid rgba(178,69,69,.25);}
.form-msg.success{display:block; background:var(--accent-soft); color:var(--accent-text); border:1px solid var(--border);}

/* ---------- auth pages (login / register) ----------
   A signed-out visitor has nothing to navigate yet, so these two pages drop
   the rail and the search field entirely; what replaces them is an opener —
   what Tabu is, in one line — standing beside the form. The card keeps its
   own width and the pair is centred as a unit. */
@media(min-width:901px){
  /* The rail rules near the top of this file give body a left padding for a
     rail these pages do not have. A bare `body{padding-left:0}` LOSES to
     `html.rail-collapsed body` (0,1,1 beats 0,0,1) — measured, that left the
     shell 38px off centre — so both selectors are answered by name. */
  body.auth-page,
  html.rail-collapsed body.auth-page{padding-left:0;}

  /* Vertical centring without a hard-coded footer height: body is the column,
     .auth-main takes what the footer does not. The fixed header is out of
     flow, so it is not a flex item; body's own padding-top still reserves it. */
  body.auth-page{display:flex; flex-direction:column;}
  .auth-main{flex:1 1 auto; display:flex; align-items:center;}
}
/* with the search field gone the bar's middle column has nothing in it */
header.auth-header{grid-template-columns:1fr auto;}

.auth-shell{
  width:100%; max-width:1040px; margin:0 auto; padding:0 32px;
  display:grid; grid-template-columns:1fr 400px; gap:64px; align-items:center;
}
.auth-brand{display:flex; flex-direction:column; align-items:flex-start;}
/* The header logo's disc, reused as authored: its two counters are positioned
   in pixels for a 22px mark, so it is SCALED to 40 rather than re-measured —
   the geometry stays in one place and cannot drift out of sync. */
.auth-brand-mark{flex:0 0 auto; display:block; width:40px; height:40px;}
/* .logo .mark sets a width and a height but no display: in the header it is a
   flex ITEM, which blockifies it. Outside a flex parent the same span stays
   inline and collapses to 0x0 — measured — so the box is asked for here. */
.auth-brand-mark .mark{display:block; transform:scale(1.8182); transform-origin:0 0;}
/* the counters knock out to whatever is behind them, and here that is the
   page, not the bar */
.auth-brand-mark .mark::before,
.auth-brand-mark .mark::after{background:var(--bg);}
.auth-brand h1{
  font-size:34px; font-weight:700; letter-spacing:-0.01em; line-height:1.15;
  max-width:16ch; text-wrap:balance; margin:22px 0 0;
}
.auth-brand p{font-size:15px; color:var(--muted); line-height:1.55; max-width:44ch; margin:14px 0 0;}
.auth-stats{display:flex; gap:36px; margin-top:30px; font-variant-numeric:tabular-nums;}
.auth-stat b{display:block; font-size:24px; font-weight:700; color:var(--text);}
.auth-stat span{font-size:12.5px; color:var(--muted);}

.auth-shell .card{padding:32px;}
.auth-shell h2{font-size:24px; font-weight:700; margin-bottom:6px;}
.auth-shell .sub{font-size:13.5px; color:var(--muted); margin-bottom:4px;}
.auth-shell .switch{font-size:12.5px; color:var(--muted); text-align:center; margin-top:18px;}
.auth-shell .switch a{color:var(--accent-text); text-decoration:none; font-weight:600;}
.auth-shell .switch a:hover{text-decoration:underline;}
.auth-shell .btn-primary.btn-block{min-height:44px;}
.btn-block{width:100%; margin-top:18px;}

/* ---------- password field with a show/hide toggle ----------
   A real <button type="button">, not a label or a div: it has to be reachable
   by keyboard and announced as a pressed/unpressed toggle. Both icons are in
   the markup and CSS picks one off aria-pressed, so the button's own state is
   the single source of truth and nothing rewrites innerHTML. */
.pw-field{position:relative;}
/* the field's own height is set explicitly so the 44px button matches its box
   exactly at both breakpoints (the base input lands at 40, mobile at 43) */
.pw-field input{padding-right:52px; min-height:44px;}
.pw-toggle{
  position:absolute; right:0; top:0; width:44px; height:44px; padding:0;
  border:none; background:none; color:var(--muted); border-radius:8px; cursor:pointer;
  display:flex; align-items:center; justify-content:center;
}
.pw-toggle:hover{color:var(--text);}
.pw-toggle:focus-visible{outline:2px solid var(--accent); outline-offset:-3px;}
.pw-toggle .icon-eye-off{display:none;}
.pw-toggle[aria-pressed="true"] .icon-eye{display:none;}
.pw-toggle[aria-pressed="true"] .icon-eye-off{display:block;}

/* Auth pages on a phone: one column, and the counters go — they are context
   for a wide opener, not something worth a row above the form. Placed here,
   BEFORE the mobile app shell block, which stays last in the file. */
@media(max-width:900px){
  .auth-shell{grid-template-columns:1fr; gap:24px; padding:16px 14px;}
  .auth-brand h1{font-size:24px; margin-top:16px;}
  .auth-stats{display:none;}
  .auth-shell .card{padding:22px 18px;}
}

.empty-state{text-align:center; padding:50px 20px; color:var(--muted); font-size:13.5px;}

footer{border-top:1px solid var(--border); padding:26px 32px; text-align:center; font-size:12px; color:var(--muted);}

/* The .rail-tabs / .rail-section rules below are reused by the left nav rail;
   the fixed right rail they were written for has been retired. */
.rail-content{display:flex; flex-direction:column; gap:22px;}

.rail-section h4{font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:.06em; margin-bottom:10px; font-weight:700;}
.rail-tabs{display:flex; flex-direction:column; gap:2px;}
.rail-tabs a{
  display:block; padding:7px 10px; border-radius:8px; font-size:12.5px; font-weight:600;
  color:var(--text); text-decoration:none; transition:background .12s ease;
}
.rail-tabs a:hover{background:var(--surface-hover);}
.rail-tabs .rail-empty{font-size:12px; color:var(--muted); line-height:1.5; padding:2px 2px;}
.rail-tabs .rail-empty a{display:inline; padding:0; color:var(--accent-text); font-weight:600;}
.rail-create-tab{
  display:block; margin-top:8px; padding:7px 10px; border-radius:8px; border:1px dashed var(--border);
  font-size:12px; font-weight:600; color:var(--accent-text); text-decoration:none; text-align:center;
  transition:background .12s ease, border-color .12s ease;
}
.rail-create-tab:hover{background:var(--accent-soft); border-color:var(--accent);}
.rail-stats{display:flex; flex-direction:column; gap:14px;}
.rail-stats div{font-size:12px; color:var(--muted);}
.rail-stats b{display:block; font-size:19px; color:var(--text);}

.rail-trust{display:flex; flex-direction:column; gap:8px;}
.rail-trust-pill{
  display:flex; align-items:center; gap:7px; background:var(--surface); border:1px solid var(--border);
  border-radius:999px; padding:8px 10px; font-size:11.5px; color:var(--muted); font-weight:500;
}
.rail-trust-pill .dot{width:6px; height:6px; border-radius:50%; background:var(--accent); flex-shrink:0; box-shadow:0 0 0 3px var(--accent-soft); animation:pulse 1.8s infinite;}

.rail-authors{display:flex; flex-direction:column; gap:10px;}
.rail-author{display:flex; align-items:center; gap:9px; text-decoration:none; color:inherit; padding:4px 2px; border-radius:8px; transition:background .12s ease;}
.rail-author:hover{background:var(--surface-hover);}
.rail-author-avatar{
  width:26px; height:26px; border-radius:50%; background:var(--accent-fill); color:var(--on-accent); flex-shrink:0;
  display:flex; align-items:center; justify-content:center; font-size:10.5px; font-weight:700;
}
.rail-author-avatar.alt{background:var(--primary); color:var(--primary-text);}
.rail-author-name{display:block; font-size:12.5px; font-weight:700;}
.rail-author-rep{display:block; font-size:11px; color:var(--accent-text); font-weight:500;}

.bottom-nav{display:none;}
.mobile-search-toggle{display:none;}
.mobile-search-cancel{display:none;}

/* ---------- pull to refresh ----------
   The page has its own pull gesture (tabuPullToRefresh in app.js), so Chrome's
   native one must not fire underneath it — in the installed PWA (manifest is
   display:standalone) that would reload the whole shell mid-gesture. `contain`
   only stops the scroll chaining out to the browser; nested scrollers on the
   map page keep working exactly as before. */
html, body{overscroll-behavior-y:contain;}

/* --ptr-top parks the disc directly behind the fixed header: the header is
   60px tall (56 on a phone, see the app shell block) and the disc is 34, so at
   translateY(0) its bottom edge lands exactly on the header's. Pulling
   translates it out from under the bar; z-index sits below the header's 30 so
   the bar hides it on the way in and out. */
:root{--ptr-top:26px;}
.ptr{
  position:fixed; top:var(--ptr-top); left:50%; z-index:29;
  width:34px; height:34px; border-radius:999px;
  display:flex; align-items:center; justify-content:center;
  background:var(--surface); border:1px solid var(--border); box-shadow:var(--shadow-md);
  color:var(--accent);
  pointer-events:none;
  opacity:0; transform:translate(-50%, 0);
  will-change:transform, opacity;
}
/* only set while the disc is animating on its own (snap-back, hand-off to the
   spinner, hide) — during the drag it has to track the finger with no lag */
.ptr.is-settling{transition:transform .22s ease, opacity .22s ease;}
.ptr-arrow{transition:transform .18s ease;}
/* past the threshold the arrow flips, which is the only "let go now" signal a
   disc this small has room for */
.ptr.is-armed .ptr-arrow{transform:rotate(180deg);}
.ptr-spinner{display:none;}
.ptr.is-busy .ptr-arrow{display:none;}
.ptr.is-busy .ptr-spinner{display:block; animation:ptr-spin .7s linear infinite;}
@keyframes ptr-spin{to{transform:rotate(360deg);}}
@media(prefers-reduced-motion:reduce){
  /* the disc fades in and out at a fixed spot instead of following the finger
     (app.js reads the same query and stops translating) — and nothing spins */
  .ptr.is-busy .ptr-spinner{animation:none;}
  .ptr-arrow, .ptr.is-armed .ptr-arrow{transition:none; transform:none;}
}

/* =========================================================
   MOBILE APP SHELL  (≤900px)
   A phone is not a narrow desktop. Below 900px the site becomes an app:
   navigation moves to a thumb-reachable bar at the bottom, the top bar is a
   thin title strip, and the home screen opens straight onto the feed with
   nothing in front of it. Everything here is deliberately last in the file so
   it wins over the desktop rules it overrides.
   ========================================================= */
@media(max-width:900px){

  /* ---- top app bar: a title strip, not a toolbar ---- */
  header{display:flex; height:56px; padding:0 10px 0 14px; gap:2px;}
  /* viewport-fit=cover (see every page's <head>) makes these env() values real
     on a device with a home indicator / notch and 0 everywhere else, so each
     calc() collapses to exactly the old number when there is nothing to clear. */
  body{
    padding-top:56px;
    padding-bottom:calc(68px + env(safe-area-inset-bottom));
    padding-left:env(safe-area-inset-left);
    padding-right:env(safe-area-inset-right);
  }
  /* the bar is position:fixed, so it is outside body's padding box */
  header{padding-left:calc(14px + env(safe-area-inset-left)); padding-right:calc(10px + env(safe-area-inset-right));}
  .topbar-left{flex:1 1 auto; min-width:0;}
  .topbar-right{flex:0 0 auto; gap:0;}
  /* 44px is Apple's HIG minimum for anything a thumb has to hit. The bare
     look (no border, no fill, round) comes from .topbar-right .icon-btn
     further up and applies at every width, so the two icons here stay
     identical to each other — the extra 4px is invisible, it just stops the
     bell and the search icon from being near-misses. */
  .topbar-right .icon-btn{display:flex; width:44px; height:44px;}
  /* the button grew 4px, so the unread dot moves in with it or it floats off
     the bell's corner */
  .topbar-right .icon-btn .dot-badge{top:10px; right:10px;}
  /* the account lives in the Профиль tab now, and the theme switch in the
     settings card on that page — a phone bar has room for two icons, not five */
  .topbar-right .theme-toggle, .topbar-right .authArea{display:none;}

  /* A field pinned across the top all day is desktop thinking: it eats a row
     of a small screen for something used occasionally. Search hides behind an
     icon and takes over the bar only while it is being used. */
  .topbar-search{display:none;}
  .mobile-search-toggle{display:flex;}
  .mobile-search-cancel{
    display:none; background:none; border:none; padding:0 4px 0 10px;
    font-family:inherit; font-size:14px; font-weight:600; color:var(--accent-text);
    cursor:pointer; white-space:nowrap;
  }
  header.search-open .topbar-left,
  header.search-open .mobile-search-toggle{display:none;}
  header.search-open .topbar-search{display:flex; flex:1 1 auto; padding:8px 14px;}
  header.search-open .mobile-search-cancel{display:block;}
  header.search-open .topbar-right{flex:1 1 auto;}

  /* ---- home opens on the feed, full stop ----
     The map, the counters, the trending strip and the composer each made sense
     on a wide screen; stacked on a phone they push the actual content below
     the fold. The map is one tap away in the tab bar, and the composer's job
     is done by the + button. */
  .trending-strip, .composer, .home-map, .mobile-stats, .ask-fab{display:none;}

  /* filters become one swipeable row instead of wrapping to three */
  .filter-row{
    flex-wrap:nowrap; overflow-x:auto; gap:8px; margin-top:12px; padding-bottom:2px;
    scrollbar-width:none; -ms-overflow-style:none;
  }
  .filter-row::-webkit-scrollbar{display:none;}
  .feed-filter{flex:0 0 auto; max-width:none;}
  .place-filter{margin-left:0;}

  /* one number for every .page-w block instead of a per-element override */
  .page-w{--page-pad:14px;}
  .shell{padding-top:0; padding-bottom:20px;}
  .feed{gap:10px; margin-top:12px;}
  .qcard{border-radius:12px; padding:14px 14px;}
  /* now a .page-w block like everything else — the gutter comes from --page-pad */

  /* ---- bottom tab bar ----
     Where a thumb actually rests. Five slots, with the one action we most want
     people to take raised into the middle. */
  .bottom-nav{
    display:flex; align-items:center; position:fixed; left:0; right:0; bottom:0; z-index:17;
    background:var(--topbar-bg);
    border-top:1px solid var(--border);
    padding:4px calc(4px + env(safe-area-inset-right)) calc(4px + env(safe-area-inset-bottom)) calc(4px + env(safe-area-inset-left));
  }
  .bottom-nav a{
    flex:1 1 0; min-width:0; display:flex; flex-direction:column; align-items:center; gap:3px;
    padding:7px 2px 5px; text-decoration:none; color:var(--muted);
    font-size:9.5px; font-weight:600; letter-spacing:0;
    /* containing block for .dot-badge below — without it the dot resolves
       against .bottom-nav and lands in the corner of the whole bar */
    position:relative;
  }
  .bottom-nav a svg{flex-shrink:0;}
  .bottom-nav a span{
    max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .bottom-nav a.active{color:var(--accent-text);}
  .bottom-nav a:active{opacity:.6;}
  /* The unread dot rides the Профиль tab.
     There is no notifications tab in the bar, and of the five slots Профиль is
     the only one that stands for "you": Главная/Карта/Темы are places, and the
     middle + is an action. It is also the only one that now leads anywhere
     unread-related — profile.html grew an «Уведомления» row above its settings
     card in the same change, so tapping a dotted tab actually resolves it
     instead of dead-ending. The markup is a bare <span class="dot-badge">, which
     tabuInitNotifications() in app.js already finds with its existing
     querySelectorAll('.dot-badge') off the single /api/notifications call that
     also feeds the header bell — no second fetch. */
  .bottom-nav .dot-badge{
    display:none; position:absolute; margin:-2px 0 0 16px;
    width:7px; height:7px; border-radius:50%; background:var(--accent); border:none;
  }
  .bottom-nav .dot-badge.show{display:block;}
  .bottom-nav-add{
    /* A 52px disc, per the design. It is now the tallest thing in the bar, so
       the bar's height is set by this — see body's padding-bottom below. */
    flex:0 0 auto !important; width:52px; height:52px; border-radius:50%;
    background:var(--accent-fill); color:var(--on-accent) !important; margin:0 6px;
    box-shadow:0 2px 6px rgba(0,0,0,.18);
    display:flex; align-items:center; justify-content:center; padding:0 !important;
  }
  .bottom-nav-add:active{transform:scale(.94);}

  /* ---- iOS: no field under 16px, ever ----
     Mobile Safari force-zooms the whole page the moment a focused field's
     text is smaller than 16px, and it does not zoom back out afterwards —
     the page is just left scaled and scrolled sideways. 16px is the exact
     threshold, so that is what every focusable field gets here. Desktop
     keeps its 13.5px (and 12.5px in pickers), which is the size that reads
     correctly there. Pages with their own <style> block load AFTER this file
     and win the tie, so any field styled there is fixed in the page instead
     — see map.html. */
  textarea,
  select,
  input[type=text],
  input[type=email],
  input[type=password],
  input[type=search],
  input[type=number],
  input[type=tel],
  .topbar-search input[type=text],
  .topbar-search input[type=text]:focus,
  .tabu-picker-search input[type=text]{font-size:16px;}

  /* ---- tap targets: 44px minimum ----
     Apple's HIG floor. The trick throughout is to raise the HIT area without
     inflating the drawing: the visible control grows a little, and an
     invisible ::after overlay covers the rest. Sizing the boxes themselves to
     44 would turn the vote arrows into two fat blocks and the action row into
     a wall of buttons. */

  /* Vote pill: the two arrows sit a couple of pixels apart, which is exactly
     why a thumb hits the wrong one. Each arrow is drawn at 36x32 inside a 36px
     pill and caught at 44x44 — the drawing is the control family's height, the
     overlay is the tap target. */
  .vote-col.vote-pill{height:36px; padding:0 2px; gap:2px;}
  .vote-pill .vote-btn{width:36px; height:32px;}
  /* the arrows are the one control drawn narrower than 44, so their overlay
     grows sideways too — 36 apart, 44 wide, still 16px of dead count between
     them, so neither arrow can steal the other's tap */
  .vote-pill .vote-btn::after{width:max(100%, 44px);}

  /* The vertical column is map.html's panel now — the question page moved to
     the pill above. Drawn at 36px square, caught at 40x44: the bleed goes into
     the gaps around the count, which isn't clickable, so nothing overlaps
     another target. */
  .vote-col:not(.vote-pill) .vote-btn{width:36px; height:36px; font-size:14px;}

  .vote-btn{position:relative;}
  .vote-btn::after{
    content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:max(100%, 40px); height:44px;
  }

  /* ---- one geometry for every control: drawn 36, hit 44 ----
     The comment-count chip sits in the same row as the pill, and so do the
     place chip and the composer's «Место» — matching heights are what make a
     row read as deliberate rather than padded. The overlay does the reaching
     (see .vote-btn::after above), so the row is 36px of chips and still clears
     the HIG floor. */
  .qaction, .place-badge, .composer-place{height:36px; padding:0 14px;}
  .qaction::after, .place-badge::after, .composer-place::after{
    content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:100%; height:44px;
  }
  /* the feed head's badge is meta text, not a control — it must not grow a
     tap target that steals from the card behind it */
  .qhead .place-badge{height:auto; padding:3px 9px;}
  .qhead .place-badge::after{display:none;}

  /* labelled pills on a desktop, round icons under a thumb: five labels ran to
     three cramped rows. font-size:0 rather than only hiding the <span> —
     tabuWireQuestionActions rewrites these buttons with a bare text node when
     you save or follow, and that has no span to hide. */
  .action-btn{
    width:36px; height:36px; padding:0; justify-content:center; border-radius:50%; font-size:0;
  }
  /* the label keeps its real size so nothing on the page computes to 0px;
     the button's own 0 is only there to swallow the bare text node */
  .action-btn > span{display:none; font-size:var(--fs-control);}
  .action-btn::after{
    content:""; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
    width:44px; height:44px;
  }

  /* map.html's own stylesheet (loaded after this one) declares .map-wrap's
     height again, so an override here would lose the cascade tie — see
     map.html's own mobile block instead. */

  /* the bar is 56px here, so the pull-to-refresh disc parks 4px higher */
  :root{--ptr-top:22px;}

  /* ---- place sheet: on a phone the screen IS the sheet ----
     Same DOM as the desktop dialog; the backdrop stops dimming (there is
     nothing behind it to see) and the card stretches into it. */
  .tabu-sheet-backdrop{background:none; align-items:stretch;}
  .tabu-sheet{
    flex:1 1 auto; width:100%; max-width:none; max-height:none;
    border-radius:0; box-shadow:none;
  }

  /* the chip is a thumb target here, and a long place name must not push the
     attach row to nothing */
  .composer-place-slot{max-width:50%;}
}
