/* Design tokens for the "ВСІМ СВОЇМ" mini app.
   Dark premium surface, one neon-pink brand accent, per-category colour moods
   that never compete with it. Every value below is referenced by name; no
   component invents a colour of its own. */
:root {
  /* All but black, and neutral.  It stood on a navy once, on the argument
     that light through dark glass reads as light only if the glass has a
     colour -- but at a quarter of a tint the navy was most of what showed and
     the light never carried.  This is two per cent of grey: enough to take
     the hardness off, too little to stand in the light's way. */
  --bg: #06070a;
  --surface: #15181d;
  --card: #1c2026;
  --card-hi: #232830;
  --line: rgba(255, 255, 255, .08);
  --line-strong: rgba(255, 255, 255, .14);

  --ink: #f6f7f9;
  --text: var(--ink);
  --ink-dim: #9aa3af;
  --text-dim: var(--ink-dim);
  /* Between the two: a menu label reads as a label rather than as a headline,
     without becoming secondary the way --text-dim would make it. */
  --ink-soft: #dfe3e9;
  --text-soft: var(--ink-soft);
  /* Raised off #6b7280, which fell to 4.2:1 once the page went near black --
     under the 4.5 a body of text needs.  This is 5.4. */
  --ink-faint: #7c8492;
  /* The quiet white a card writes its place in, and now the filters too:
     legible on glass without competing with what it labels. */
  --ink-quiet: rgba(255, 255, 255, .55);
  --text-faint: var(--ink-faint);

  /* The plate a card wears where a photograph would be.  Work and Online work
     never ask for a picture, and a grey square in a row of photographs reads
     as a card that failed to load rather than one that never had a picture.
     Dark on either ground on purpose: it is ours, and ours looks like this --
     which is also why it is not redefined per theme.  The listing itself still
     opens with no picture at all; this is a plate with our name on it, not a
     photograph of anything. */
  --blank-face:
    radial-gradient(120% 96% at 50% 4%, rgba(255, 63, 146, .3), transparent 68%),
    linear-gradient(155deg, #1b1622, #0f0d14);
  --blank-ink: rgba(255, 255, 255, .88);
  --blank-name: rgba(255, 255, 255, .66);

  /* Somebody else's colours, and the only ones here that are not ours.  A
     brand mark in the wrong colour is a different brand, and these are what
     people actually recognise them by -- the shape alone was never doing all
     the work.  One value for both themes: a brand does not have a dark
     version. */
  --brand-telegram: #2aabee;
  --brand-whatsapp: #25d366;
  --brand-viber: #7360f2;
  --brand-instagram: #e1306c;
  --brand-facebook: #1877f2;
  /* Between dim and faint: a placeholder has to be readable without
     competing with what someone has typed. */
  --ink-placeholder: #7e8794;
  --placeholder: var(--ink-placeholder);

  --accent: #ff3f92;
  --accent-hi: #ff6aa8;
  --accent-deep: #d81f70;
  /* Type that sits on an accent-tinted surface: pink enough to belong to
     it, light enough to read as text rather than as another glow. */
  --accent-text: #ffd3e6;
  --accent-soft: rgba(255, 63, 146, .14);
  /* A round action: the accent filling it, a shadow under it, and a white
     line off its shoulder.  It glowed for a while, and a glowing accent on a
     black page is the lighting of a nightclub rather than a mark on a
     button -- the colour says "press this", the light said something else. */
  --neon-accent:
    0 3px 10px rgba(0, 0, 0, .45),
    inset 0 1px 0 rgba(255, 255, 255, .4);
  /* Lit from its own shoulder rather than flat across. */
  --neon-face: linear-gradient(160deg, var(--accent-hi) 0%, var(--accent) 46%, var(--accent-deep) 100%);
  /* The avatar's ring: the one place the brand's two colours meet as light
     rather than as a hairline, so they are named where every other colour is. */
  --ring-blue: #2f7bff;
  --ring-violet: #7a4dff;
  /* Ratings are gold everywhere people read them; the brand pink would
     make a rating look like an action. */
  --star: #f6c344;
  /* Open, available, confirmed. Kept apart from the brand pink, which
     means "act" rather than "good". */
  --ok: #3ddc97;

  /* Category moods, from the reference: work blue, housing warm amber,
     services crimson, market teal, info cold blue, community violet. */
  --c-work-a: #2563eb; --c-work-b: #1e3a8a;
  --c-housing-a: #f97316; --c-housing-b: #9a3412;
  --c-services-a: #ec4899; --c-services-b: #9d174d;
  --c-market-a: #14b8a6; --c-market-b: #115e59;
  --c-info-a: #3b82f6; --c-info-b: #1e40af;
  --c-community-a: #a855f7; --c-community-b: #6b21a8;

  /* Blue-lavender glass: the search field, the bottom bar and the community
     row are made of it. Kept here so a change reaches all three. */
  --glass-1: rgba(118, 136, 255, .20);
  --glass-2: rgba(150, 122, 255, .15);
  --glass-3: rgba(96, 165, 255, .11);
  --glass-line: rgba(152, 140, 255, .26);

  /* The dark plate a badge or a glyph sits on when it lies over a
     photograph. One definition, so the two can never drift apart. */
  --plate: rgba(9, 11, 14, .72);
  --plate-line: rgba(255, 255, 255, .16);
  --plate-shadow: 0 2px 10px rgba(0, 0, 0, .45);

  /* Frosted glass: a light film over a translucent dark base, with a
     highlight along the top edge. One recipe, so every glass element is
     the same material rather than three near-misses. */
  --frost-film-a: rgba(255, 255, 255, .075);
  --frost-film-b: rgba(255, 255, 255, .022);
  --frost-base: rgba(30, 34, 42, .58);
  --frost-line: rgba(255, 255, 255, .15);
  --frost-hi: rgba(255, 255, 255, .13);
  /* The whole filter, not a length: written as `blur(var(--frost-blur))` it
     expands to `blur(blur(24px) saturate(1.7))`, which is not valid CSS and is
     thrown away in silence -- the glass then has a tint and no blur at all. */
  --frost-blur: blur(24px) saturate(1.7);

  /* A card's edge is lit rather than drawn: a cool highlight on one side, a
     breath of the brand pink on the other, and a dark translucent ground
     between them.  Kept as tokens so every glass card is lit by one lamp. */
  --edge-line: rgba(255, 255, 255, .20);
  /* The edge is lit, not coloured -- white, and only white.  It was blue at
     one end and pink at the other, which is a brand painted round a card
     rather than light falling on one. */
  --edge-cool: rgba(255, 255, 255, .42);
  --edge-warm: rgba(255, 255, 255, .40);
  /* The lit top edge of a card, in daylight rather than in grey. */
  --edge-hi: rgba(255, 255, 255, .19);

  /* ---- one raised pane, wherever a surface can be pressed --------------
     The bottom bar's own glass, exactly: the same dark plate under the same
     blur, with the same sheen down its face.  It was white over a lighter
     blur for a while, which is a different material standing next to the bar
     on every screen; there is one material now.
     Its edge is the ring below rather than the bar's inset line -- the bar is
     a capsule with nothing inside it, and a card has a photograph. */
  --pane-face: linear-gradient(180deg,
    rgba(255, 255, 255, .10) 0%, rgba(255, 255, 255, .02) 42%,
    rgba(255, 255, 255, 0) 72%);
  /* One plate under all the glass -- the panes and the bar alike.  Its
     density is the day theme's business too: at .42 over a light page the
     glass composites to a mid grey, which is the one ground that carries
     neither white writing nor dark. */
  --glass-plate: rgba(9, 11, 15, .42);
  /* The lit lip along the top and the sheen down the face: what makes the
     bar read as glass rather than as a tint.  One declaration, so the bar,
     the panels that open over the page and the strong actions cannot each
     drift into a glass of their own. */
  /* The hairline that rings a piece of glass.  Named so that one surface
     can wear it in the accent -- the search bar does -- without the rest
     of the glass having to be written out a second time to say so. */
  --glass-ring: rgba(255, 255, 255, .18);
  /* The sheen down the face, on its own.  A circle takes this and not the
     lip: a lip is the top edge of something that has a top edge, and on a
     round button the 1px band across the top of its box is clipped to a
     short straight chord laid over the curve -- which is exactly what it
     looks like. */
  --glass-face:
    linear-gradient(180deg, rgba(255, 255, 255, .10) 0%, rgba(255, 255, 255, .02) 42%,
                            rgba(255, 255, 255, 0) 72%);
  --glass-sheen:
    linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .25) 18%,
                           rgba(255, 255, 255, .46) 50%, rgba(255, 255, 255, .25) 82%,
                           rgba(255, 255, 255, 0) 100%) top / 100% 1px no-repeat,
    var(--glass-face);
  /* A strong action wears the bar's own glass and says its word the way the
     "НОВЕ" mark says it: the accent as the writing, a hairline of it round
     the edge, and no fill at all.
     The writing is the one part that has to turn over with the ground.  The
     plate flips from white-on-black to black-on-white, and on the light one
     the bright pink reads 1.75:1 -- so daylight gets a deep pink instead, at
     4.8.  The solid fill this replaced gave its white 3.3 either way. */
  --action-face: var(--glass-plate-soft);
  --action-line: rgba(255, 63, 146, .38);
  --action-ink: var(--accent-hi);
  /* Nothing at night: the bar's plate over a near-black page is already the
     brightest thing in that corner, and a halo round it would be a second
     glow beside the one on the plus in the middle of the bar. */
  --action-glow: 0 0 0 0 rgba(255, 63, 146, 0);
  /* The lighter plate the profile's own buttons stand on, and now the bar at
     the foot as well.  In the dark it is lifted with white rather than
     thinned with less black: the page behind is already near black, so
     letting more of it through makes a surface no lighter at all. */
  --glass-plate-soft: rgba(255, 255, 255, .07);
  /* The same plate thinner by a fifth, for the bar on Blink alone.  The two
     engines are only now drawing the same thing, and at the far heavier
     radius Blink needs to match WebKit the bar comes out the more solid of
     the two.  WebKit is right as it is and does not read this. */
  --glass-plate-bar: rgba(255, 255, 255, .056);
  --pane-fill: var(--glass-plate);
  --pane-blur: var(--glass-frost);
  /* Light in the order it falls on a raised thing: a lit lip along the top, a
     faint catch along the bottom, the inside of its own foot in shadow, and
     the pane standing off the page.  Without the third it is a flat sheet
     with a bright line on it. */
  --pane-lift:
    inset 0 1px 0 rgba(255, 255, 255, .32),
    inset 0 -1px 0 rgba(255, 255, 255, .05),
    inset 0 -13px 17px -15px rgba(0, 0, 0, .62),
    0 1px 2px rgba(0, 0, 0, .5),
    0 10px 22px rgba(0, 0, 0, .42);
  /* What a raised round thing and a floating one cast.  Named so that the day
     can have its own: a shadow is the one part of this palette that cannot be
     the same on both grounds. */
  --lift-round: 0 4px 14px rgba(0, 0, 0, .34);
  --lift-far: 0 10px 26px rgba(0, 0, 0, .42);
  --pane-press:
    inset 0 1px 0 rgba(255, 255, 255, .2),
    inset 0 -10px 14px -13px rgba(0, 0, 0, .55),
    0 1px 2px rgba(0, 0, 0, .46),
    0 4px 10px rgba(0, 0, 0, .36);
  /* Glass, not paint: the ground is thin enough that the room's light shows
     through it.  Made opaque, the card reads as a black rectangle with a
     drawn outline, which is the one thing it must not be.  At the density the
     profile's menu was settled at: that number used to live in the profile
     alone, and every other card was a third thinner than the one card anybody
     had actually looked at. */
  /* A plain grey.  It carried a blue in it, which on a near-black page read
     as a colour rather than as a surface. */
  --edge-ground: rgba(46, 47, 50, .60);
  /* The light that pools at the foot of the screen, brought a little closer
     under a card: the same two colours as the ambient, slightly brighter, so
     a card looks lit by the room rather than outlined on top of it. */
  --edge-pool-cool: rgba(52, 98, 205, .34);
  /* Depth under a card, in shadow rather than in pink light. */
  --edge-pool-warm: rgba(6, 8, 14, .34);
  --edge-lit-cool: rgba(255, 255, 255, .58);
  --edge-lit-warm: rgba(255, 255, 255, .70);

  --r-chip: 999px;
  --r-card: 16px;
  --r-tile: 20px;
  --r-sheet: 24px;

  /* How much of the phone the page leaves at its two sides.  Its own name
     rather than a spacing step, because everything that bleeds to the edge of
     the screen has to undo exactly this and nothing else. */
  --page-side: 10px;
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px; --s8: 32px;

  /* One height for every control in the profile's settings, and the air
     between them: stated once so a third control cannot arrive at a fourth
     size. */
  --settings-row: 50px;
  --settings-gap: 8px;
  --nav-h: 64px;
  /* The glass a floating surface is made of -- the bottom bar and the
     search field both.  A whole filter, not a length -- written as
     `blur(var(--nav-glass))` it would be invalid and silently dropped.
     Telegram's own controls are the reference, and the latest iOS uses the
     same material: a dark pane at less than half opacity over a blur heavy
     enough that what is behind it survives only as moving colour.  Not
     see-through -- frosted.  The saturation is the half of it people read as
     glass rather than as smoked plastic: real glass carries the colour of
     what is behind it through, stronger than it found it. */
  --glass-frost: blur(28px) saturate(1.14) brightness(.9);
  --safe-b: env(safe-area-inset-bottom, 0px);
  /* What the bottom bar actually keeps clear.  The home indicator is a few
     pixels inside a much taller inset, and spending the whole of it left the
     bar floating well above the edge of the phone.  The bar's glass still
     reaches the edge; only its buttons stop short of the indicator. */
  --nav-floor: max(2px, calc(var(--safe-b) - 24px));
  /* The bar floats: how far its underside stands off the floor, and how far
     its ends stand off the sides of the phone. */
  --nav-lift: 8px;
  --nav-inset: var(--page-side);
  --safe-t: env(safe-area-inset-top, 0px);

  --font: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Display",
          "Segoe UI", Roboto, system-ui, sans-serif;
  /* The wordmark only: Unbounded is drawn for Ukrainian Cyrillic. Both fall
     back to faces every device has, so a font that fails to load costs
     character, never legibility. */
  --font-brand: "Unbounded", "Montserrat", "Inter", -apple-system,
                BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  --font-hand: "Caveat", "Segoe Script", "Bradley Hand", "Snell Roundhand", cursive;

  color-scheme: dark;
  /* Written on the app's own chrome -- the bar's labels -- which stands on
     glass rather than on the page and so flips with the ground under it. */
  --ink-chrome: #fff;
  /* The point of the heart that signs the wordmark: the page's own extreme,
     with a breath of the brand in it. */
  --heart-tip: #ffe9f3;
  --chrome-text: var(--ink-chrome);
}

/* ---------- daylight ---------------------------------------------------
   One switch, and only two things move under it: the ground the page stands
   on, and the colour of what is written on it.  The glass, the edges and the
   accent are the same objects in both -- glass over a light ground comes out
   light by itself, which is what glass is for and what a second set of card
   colours would have thrown away. */
:root[data-theme="day"] {
  /* Daylight thins it instead: there the page behind is bright, so less
     plate means more of it. */
  --glass-plate-soft: rgba(9, 11, 15, .11);
  --glass-plate-bar: rgba(9, 11, 15, .088);
  /* Deep enough to read on that plate: the bright pink gives 1.75 there. */
  --action-ink: #a81050;
  /* And the plate itself is pink here rather than grey.  Over a light page a
     dark wash comes out the one neutral that says nothing at all, so daylight
     washes the action in the brand instead: #edcbdc, with the deep pink on it
     at 4.97.  A halo under it as well, which the night has no need of. */
  --action-face: rgba(255, 63, 146, .18);
  --action-glow: 0 8px 22px rgba(255, 63, 146, .28);
  color-scheme: light;
  --bg: #e9eaec;
  /* The day's own writing keeps names of its own, so a rule that wants it
     back on a pane can ask for it by name rather than by literal. */
  --page-ink: #14161b;
  --page-ink-soft: #23262d;
  --page-ink-dim: #4a505b;
  --page-ink-faint: #5b626d;
  --text: var(--page-ink);
  --text-soft: var(--page-ink-soft);
  --text-dim: var(--page-ink-dim);
  --text-faint: var(--page-ink-faint);
  --placeholder: #6b7280;
  --chrome-text: var(--page-ink);
  --heart-tip: #1a1016;
  /* The density the app has always had.  It was raised to .60 to buy back
     the legibility a mid grey costs, and that darkened every card on every
     screen, which is not what anyone wanted to look at.  The glass stays what
     it was and the writing carries what it can: on a pane this thin even
     white reaches only 4:1, so the quiet greys are lifted nearly to it. */
  --ink-dim: #dbe0e7;
  --ink-faint: #ccd2da;
  /* And the shadows, which the glass above could keep and these could not.
     A shadow tuned for a dark page is invisible on it and a smear on a light
     one: at 42% black with a 22px blur, eight cards in a rail filled the gaps
     between themselves and laid a grey band under the whole row.  The same
     42% under the contact marks did the same along that strip.  Daylight
     needs a shadow that says "raised" and nothing else. */
  --pane-lift:
    inset 0 1px 0 rgba(255, 255, 255, .3),
    inset 0 -1px 0 rgba(255, 255, 255, .06),
    inset 0 -13px 17px -15px rgba(0, 0, 0, .3),
    0 1px 2px rgba(15, 20, 30, .10),
    0 6px 14px rgba(15, 20, 30, .10);
  --lift-round: 0 3px 10px rgba(15, 20, 30, .14);
  --lift-far: 0 8px 20px rgba(15, 20, 30, .16);
}

/* Glass is dark in both grounds, so the writing on it is light in both.  The
   day block above moves the page's type; this moves it back wherever it is
   standing on a pane rather than on the page.  The names are the same ones,
   so there is no second palette to keep in step -- only a second place the
   first one is asked for. */
/* `:where` rather than `:is`: this has to beat what an element inherits from
   the page and nothing else.  At `:is`'s specificity it also beat the rules
   components write for themselves -- a chip that turns white when chosen was
   given white writing to put on it. */
:root[data-theme="day"] :where(
  .gcard, .panel, .rowcard, .picker, .chatcard, .addrow,
  .bottomnav, .searchbar, .daylight, .themeswitch, .popover-panel, .sheet > section,
  .factchip, .providercard, .fchip, .chip, .cta-ghost
) {
  --text: var(--ink);
  --text-soft: var(--ink-soft);
  --text-dim: var(--ink-dim);
  --text-faint: var(--ink-faint);
  --placeholder: var(--ink-placeholder);
  --chrome-text: var(--ink-chrome);
}

/* Where the two grounds meet on one element, each is named rather than left
   to inherit -- these are the handful the switch could not settle by itself. */

/* The header's two selectors and the search field, in the plain white of the
   switch beside them rather than the softer white the night writes with. */
:root[data-theme="day"] .picker.compact .picker-text b,
:root[data-theme="day"] .picker.compact .picker-text i,
:root[data-theme="day"] .daylight,
:root[data-theme="day"] .searchbar input { color: var(--ink); }
/* The words the field suggests, not the ones a reader types: those were what
   the first pass moved, and they are the ones nobody was looking at. */
:root[data-theme="day"] .searchbar input::placeholder { color: var(--ink-soft); }

/* Who is selling: the name quiet, the line under it not.  The pink chip above
   this block is deliberately untouched. */
:root[data-theme="day"] .provider-id b { color: var(--ink-dim); }
:root[data-theme="day"] .provider-rating.quiet { color: var(--ink); }

/* Both hearts lie on a photograph, which is a ground of its own and does not
   turn over with the page. */
/* Both hearts lie on a photograph, which is a ground of its own and does not
   turn over with the page -- but only while they are empty.  A saved one is
   the accent in both grounds, and this rule used to take that away from it. */
:root[data-theme="day"] .heart:not([aria-pressed="true"]),
:root[data-theme="day"] .fav-fab:not(.on) { color: var(--ink); }

/* The profile's two selectors say what they are quietly; what they are set to
   is left as it is. */
:root[data-theme="day"] .profile-pickers .picker-text i,
:root[data-theme="day"] .themeswitch svg { color: var(--page-ink-dim); }

/* A section's chips are dark chips on a light page, so their writing is light
   -- but half a tone under the plain white, which at that size and against
   that little ground came out shouting. */
:root[data-theme="day"] .chip { color: var(--ink-soft); }

/* A form's chips are the one glass that keeps the page's own tint rather than
   a plate of its own, so on a light page they come out light -- and their
   writing goes dark with the page instead of staying white on it. */
:root[data-theme="day"] .form .chip { color: var(--page-ink); }
:root[data-theme="day"] .form .chip svg { color: var(--page-ink-dim); }


/* Chromium renders a given blur radius far lighter than WebKit does: the one
   panel that had to be settled twice in this app landed on 40 against 14,
   near three to one.  So the frost is stated again for the engine that needs
   more of it -- the bar, the panels cut from the same glass, and the strong
   actions.  A card is not among them: `--pane-blur` is pinned to the value it
   already had, which was chosen against what Android was actually showing.

   Asked of the root rather than of `@supports (-webkit-touch-callout)`, which
   Blink answers yes to as well -- so this block and the WebKit one below it
   were both applying on Android, and the later of the two won. */
:root[data-engine="blink"] {
  --glass-frost: blur(80px) saturate(1.14) brightness(.9);
  /* A card keeps the number Android has actually been rendering all this
     time -- the WebKit one, which was reaching it through the very leak this
     attribute closes.  Whatever a card should be on Blink, it is not this
     change's to decide: it is every card on every screen. */
  --pane-blur: blur(20px) saturate(1.14) brightness(.9);
}
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body, #root { height: 100%; }
body {
  margin: 0; min-width: 320px;
  background: var(--bg); color: var(--text);
  font-family: var(--font);
  font-size: 15px; line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
img { display: block; max-width: 100%; }

/* ---------- shell ---------------------------------------------------- */
.app { min-height: 100%; display: flex; flex-direction: column; position: relative; }
/* Soft light through dark glass: a few wide, low-opacity pools rather than a
   gradient with edges.  Blue only, since the pools were the brand colour and
   a page lit in the brand colour is a page where the brand marks nothing.
   The accent is spent on what a reader can press instead. */
.app::before {
  content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(46% 24% at 14% 12%, rgba(58, 104, 210, .30), transparent 68%),
    radial-gradient(40% 22% at 88% 40%, rgba(52, 96, 200, .24), transparent 70%),
    radial-gradient(66% 26% at 50% 106%, rgba(48, 92, 200, .30), transparent 74%),
    radial-gradient(52% 22% at 100% 92%, rgba(52, 96, 200, .22), transparent 72%);
}
.page {
  position: relative; z-index: 1; flex: 1;
  padding: max(var(--safe-t), var(--s4)) var(--page-side)
           calc(var(--nav-h) + var(--nav-floor) + var(--nav-lift) + var(--s4));
  max-width: 520px; width: 100%; margin: 0 auto;
}

/* ---------- one raised pane ------------------------------------------- */
/* Every surface a reader can press is cut from the same glass.  They were cut
   four different ways -- two opaque fills, a frost and a tinted ground -- and
   the profile still wore the oldest of them, which is how it came to look
   untouched while the catalogue did not.  Written as a list so that cannot
   happen again.

   The six tiles and `.infocard` are deliberately outside it: both carry a
   photograph, and glass over a photograph is a different problem. */
.gcard,
.panel,
.rowcard,
.picker,
.chatcard,
.addrow {
  background: var(--pane-face), var(--pane-fill);
  backdrop-filter: var(--pane-blur);
  -webkit-backdrop-filter: var(--pane-blur);
  box-shadow: var(--pane-lift);
  /* For the ring below, and so the ring is the only edge any of them draws. */
  position: relative; border: 1px solid transparent;
  /* Stated here rather than left to the daylight block: a pane that has to be
     told its colour from outside is a pane whose own rules get outranked, and
     that is how a chip chosen on a light page ended up white on white. */
  color: var(--text);
}

/* The lit edge every pane wears, and the only one any of them wears.  It is
   masked to the border area: without that the gradient fills the pane.  The
   profile carried a second version of this in blue and pink, which is how it
   went on looking like the old app after the glass was shared. */
.gcard::before,
.panel::before,
.rowcard::before,
.picker::before,
.chatcard::before,
.addrow::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  padding: 1px; pointer-events: none; z-index: 2;
  background:
    /* Brightest along the lower edge, because that is where the light is. */
    radial-gradient(118% 86% at 50% 116%,
      var(--edge-lit-warm) 0%, var(--edge-lit-cool) 40%, transparent 74%),
    linear-gradient(138deg,
      var(--edge-cool) 0%, rgba(255, 255, 255, .22) 26%,
      rgba(255, 255, 255, .07) 50%, rgba(255, 255, 255, .24) 74%,
      var(--edge-warm) 100%);
  -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  mask-composite: exclude;
}

/* ---------- a section's own glass -------------------------------------
   The glass is built from tokens, so a section only has to redefine them:
   every card, chip, panel and field inside it follows without knowing which
   section it is in. The tints match the tile colours on the home screen, so
   opening Work feels like opening the blue tile. */
[data-section="work"] {
  --glass-1: rgba(88, 132, 255, .22); --glass-2: rgba(70, 110, 240, .15);
  --glass-3: rgba(120, 170, 255, .11); --glass-line: rgba(120, 160, 255, .28);
}
[data-section="housing"] {
  --glass-1: rgba(255, 168, 76, .20); --glass-2: rgba(240, 130, 60, .13);
  --glass-3: rgba(255, 200, 130, .09); --glass-line: rgba(255, 176, 104, .26);
}
[data-section="services"] {
  --glass-1: rgba(255, 92, 158, .20); --glass-2: rgba(226, 62, 132, .13);
  --glass-3: rgba(255, 140, 190, .09); --glass-line: rgba(255, 116, 172, .26);
}
[data-section="market"] {
  --glass-1: rgba(52, 202, 190, .20); --glass-2: rgba(28, 168, 158, .13);
  --glass-3: rgba(120, 226, 216, .09); --glass-line: rgba(74, 208, 198, .26);
}
[data-section="info"] {
  --glass-1: rgba(96, 152, 255, .21); --glass-2: rgba(64, 116, 232, .14);
  --glass-3: rgba(140, 190, 255, .10); --glass-line: rgba(122, 168, 255, .27);
}
[data-section="community"] {
  --glass-1: rgba(176, 116, 255, .21); --glass-2: rgba(142, 78, 232, .14);
  --glass-3: rgba(200, 160, 255, .10); --glass-line: rgba(186, 138, 255, .27);
}

/* ---------- app bar ---------------------------------------------------- */
/* One row, two columns: the wordmark and the greeting on the left, the avatar
   and its chips on the right. Keeping the greeting out of this row made the
   row as tall as the avatar block and left a hole under the wordmark. */
/* The home screen starts a little below the safe area rather than against it:
   a fraction of that area rather than a fixed number, so it stays the same
   proportion on a phone whose notch is a different size.  Longhands only --
   a `margin` shorthand here would cancel this without looking like it. */
.appbar {
  display: flex; align-items: flex-start; gap: var(--s3);
  margin-top: calc(var(--safe-t) * .15); margin-bottom: var(--s4);
}
.headtext { flex: 1; min-width: 0; }
.appbar .brand { display: flex; flex-direction: column; line-height: 1.1; margin-bottom: var(--s4); }
.appbar .brand b {
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-brand); font-size: 13.5px; font-weight: 700; letter-spacing: .01em;
  /* "ВСІМ" is the half that is not the brand, so it is written in whatever
     the page writes in: white on the dark ground, black on the light one. */
  color: var(--text);
}
/* "СВОЇМ" is the half that is the brand, so it is the half that is lit: the
   neon of the bot, with just enough of its own glow to read as neon rather
   than as pink ink. */
.appbar .brand em {
  font-style: normal; color: var(--accent);
}
/* A hairline, so it signs the name rather than decorating it. */
.thinheart { flex: none; margin-bottom: 1px; }
.appbar .brand span { font-size: 10px; color: var(--text-faint); letter-spacing: .16em; text-transform: lowercase; }
/* The gap under the avatar is twice what it was: at six pixels the three
   controls read as hanging off the picture rather than standing on their own
   row, and they are not part of it -- one changes the theme and two change
   what the whole app shows. */
.identity { display: flex; flex-direction: column; align-items: center; gap: 12px; flex: none; }
/* The ring itself lives on .avatar-ring, shared with the profile; this only
   sets the size it takes here and the face's type at that size. */
/* A fifth larger, and the four pixels it used to be pushed down by are given
   back to it: the greeting column still sets the row's height. */
.avatar.avatar-ring {
  width: 62px; height: 62px; flex: none; padding: 2px; border: 0;
}
.avatar .avatar-face { font-size: 21px; }
.switch {
  display: inline-flex; align-items: center; gap: 4px;
  height: 24px; padding: 0 8px; border-radius: var(--r-chip);
  font-size: 10.5px; font-weight: 700; letter-spacing: .03em; color: var(--text-dim);
  background:
    linear-gradient(180deg, var(--frost-film-a) 0%, var(--frost-film-b) 100%),
    var(--frost-base);
  border: 1px solid var(--frost-line);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
}
.switch:active { color: var(--text); }
/* A named selector rather than two letters: the label says what changes and
   the value says what it is now, on the same glass as everything else. */
.picker {
  position: relative; display: flex; align-items: center; gap: 8px;
  padding: 7px 10px; border-radius: 14px; text-align: left; width: 100%;
  transition: border-color .14s ease;
}
.picker.open { border-color: rgba(255, 63, 146, .45); }
.picker-icon { flex: none; display: inline-flex; color: var(--text-faint); }
.picker-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.picker-text i {
  font-style: normal; font-size: 9.5px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-faint);
}
.picker-text b {
  font-size: 12.5px; font-weight: 650; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.picker-caret { flex: none; display: inline-flex; color: var(--text-faint); transform: rotate(90deg); }
.picker.open .picker-caret { transform: rotate(-90deg); }
.pickers { display: flex; flex-direction: column; gap: 6px; width: 148px; }
/* In the header the two selectors sit on one line and keep exactly the height
   the two switches had before them: the hero's height is not theirs to spend. */
.pickers.compact { flex-direction: row; width: auto; gap: 4px; }
.picker.compact {
  display: inline-flex; align-items: center; gap: 5px; width: auto;
  /* A fifth taller than the switches were: comfortable to hit, and still
     shorter than the greeting column that sets the row's height. */
  height: 30px; padding: 0 11px; border-radius: var(--r-chip);
}
.picker.compact.open { border-color: rgba(255, 63, 146, .5); }
.picker.compact .picker-text { flex-direction: row; align-items: center; gap: 4px; }
.picker.compact .picker-text i {
  font-size: 10.5px; letter-spacing: .04em; text-transform: none; color: var(--text-faint);
}
/* The value carries the same softened white it does in the profile: the two
   selectors are the same control in two places, and were reading as two. */
.picker.compact .picker-text b { font-size: 11.5px; font-weight: 700; letter-spacing: .03em; color: var(--text-soft); }
.picker.compact.open .picker-text b { color: var(--text); }
/* On the profile they are full-width rows, side by side. */
.profile-pickers { flex-direction: row; width: auto; gap: var(--settings-gap); margin: 0; }
/* Quiet next to the avatar and the menu: no leading icon, a small label, the
   value in full, and a caret that only hints. */
/* A fifth taller than the 42px they were, so the pair reads as part of the
   menu above rather than a strip beneath it -- and still short of the menu's
   own 60px row, which carries an icon and a chevron these do not. */
.profile-pickers .picker { flex: 1; min-width: 0; padding: 8px 12px; height: var(--settings-row); }
.profile-pickers .picker-icon { display: none; }
.profile-pickers .picker-text i { font-size: 9.5px; line-height: 1.1; }
/* The same softened white the three menu rows carry: a value that shone
   brighter than the labels above it read as the louder of the two, which is
   the wrong way round. */
.profile-pickers .picker-text b { font-size: 13px; line-height: 1.2; color: var(--text-soft); }
.profile-pickers .picker-caret { color: var(--text-faint); }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ---------- screen header (back · title · country) -------------------- */
.screenbar { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s4); }
/* One back button, whoever draws it.  Telegram's sits in the same corner, so
   where Telegram has one the app hides its own rather than stacking two --
   and the spacer that balanced it goes with it, or the title is centred in a
   row with a hole on one side and sits 24px off the middle of the screen. */
.app[data-native-back="true"] .backbtn,
.app[data-native-back="true"] .screenbar .barspacer { display: none; }
.screenbar .titles { flex: 1; text-align: center; }
.screenbar h1 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -.01em; }
.screenbar .titles span { font-size: 12px; color: var(--text-dim); }
.iconbtn {
  width: 36px; height: 36px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--text-dim);
  background: var(--card); border: 1px solid var(--line);
}
.iconbtn:active { background: var(--card-hi); }
/* Balances a header row against its back button, and draws nothing. */
.barspacer { width: 36px; height: 36px; flex: none; }

/* ---------- greeting + search ---------------------------------------- */
/* The one line on the screen that is addressed to a person, set in the brand's
   display face rather than the interface one. Not a decorative font — the same
   face as the wordmark, given room to read as typography. */
.greeting {
  margin: 0 0 5px; font-family: var(--font-brand);
  font-size: 25px; font-weight: 700; line-height: 1.14; letter-spacing: -.03em;
  text-wrap: balance;
}
.greeting .wave { font-family: var(--font); font-size: 21px; }
.greeting-sub { margin: 0; color: var(--text-dim); font-size: 13.5px; letter-spacing: 0; }
.name-fold {
  font: inherit; padding: 0; text-align: left;
  border-bottom: 1px dashed rgba(255, 255, 255, .28);
}
.name-fold[aria-expanded="true"] { border-bottom-color: transparent; }

/* Frosted glass: the page's own light shows through it instead of a flat fill
   sitting on top of the page. The button inside stays the accent — it is the
   one thing here that acts. */
/* The bottom bar's own glass, in a field: the same capsule, the same two
   panes, the same white edge.  Its filter cannot live on the element -- an
   element that filters its backdrop becomes a backdrop root, and the panes
   inside would filter their parent, which paints nothing -- so the element
   only holds the shape, and `isolation` keeps the panes from falling behind
   the rest of the page. */
.searchbar {
  /* Ringed in the accent rather than in white: the one piece of glass on the
     screen that is asking to be typed into.  The ring the pane already draws
     is recoloured rather than a second line laid outside it, which would have
     read as a doubled edge. */
  --glass-ring: var(--action-line);
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: var(--s2);
  border-radius: 999px; padding: 5px 5px 5px 18px; margin-bottom: var(--s4);
  min-height: 54px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .3), 0 2px 6px rgba(0, 0, 0, .22);
}
.searchbar input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: none;
  font: inherit; color: var(--text); padding: 8px 0;
  /* iOS zooms the whole page when a focused field is smaller than 16px. */
  font-size: 16px;
}
.searchbar input::placeholder { color: var(--placeholder); }
/* The round button at the end of a search bar is lit, like the plus in the
   middle of the bottom bar: both are the one thing a person came to that bar
   to press.  Written once for the pair, so the two cannot drift. */
.searchbar .go, .searchgo {
  display: grid; place-items: center; border-radius: 50%; flex: none;
  color: #fff;
  background: var(--neon-face);
  box-shadow: var(--neon-accent);
}
.searchbar .go { width: 40px; height: 40px; }

/* ---------- category grid -------------------------------------------- */
.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); margin-bottom: var(--s4); }
.tile {
  position: relative; overflow: hidden; text-align: left;
  border-radius: var(--r-tile); padding: 10px var(--s4) var(--s3);
  /* Sixteen per cent shorter than it was, to leave room for the rail beneath
     it.  Nothing is cut: this is the floor of a tile whose subtitle wraps to
     two lines, and a third line grows the tile rather than clipping it. */
  min-height: 104px;
  /* Two rows: the glyph is centred in whatever space is left above the label,
     rather than pinned to the top edge with the text crowding it. */
  display: grid; grid-template-rows: 1fr auto; row-gap: 6px;
  border: 1px solid var(--line);
  background-image: linear-gradient(150deg, var(--a) 0%, var(--b) 100%);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .38);
  transition: transform .14s ease;
}
/* The approved photographs already carry each section's colour, so they get no
   wash over them -- only enough of a scrim at the foot of the tile to keep the
   label legible. */
.tile[data-photo] {
  background-size: cover; background-position: 72% 50%; background-repeat: no-repeat;
}
/* Daylight across the tile, and the shading the label stands on underneath
   it.  The shading used to start at a tenth of black at the very top, which
   is a tile in shadow from end to end; it starts at nothing now and gathers
   towards the foot, where the words actually are. */
.tile::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(124% 88% at 14% -12%,
      rgba(255, 255, 255, .22) 0%, rgba(255, 255, 255, .06) 36%, transparent 68%),
    linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, .32) 56%, rgba(0, 0, 0, .58) 100%);
}
/* The six on the home screen all carry a photograph, so this rule is the one
   that reaches them -- it replaces both of the layers above rather than adding
   to them.  No light laid over the picture: it has its own, and a white wash
   across it only greyed it.  What is left is the shading the label stands on,
   which stays out of the way until the foot. */
.tile[data-photo]::after {
  background: linear-gradient(180deg,
    rgba(13, 15, 18, 0) 0%, rgba(13, 15, 18, .12) 50%, rgba(13, 15, 18, .56) 100%);
}
.tile > * { position: relative; z-index: 1; }
.tile:active { transform: scale(.978); }
.tile .glyph {
  align-self: center; justify-self: start;
  width: 28px; height: 28px; border-radius: 9px;
  display: grid; place-items: center; color: #fff;
  background: rgba(9, 11, 14, .42);
  border: 1px solid rgba(255, 255, 255, .13);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
/* A column, not inline: the wrapper was left inline-level, so the label and
   its subtitle ran together on one line with no space between them. Scoped to
   .tile-text as well, because .tile span also matches the glyph. */
.tile-text { display: flex; flex-direction: column; gap: 2px; min-height: 34px; justify-content: flex-end; }
.tile-text b { font-size: 16px; font-weight: 700; letter-spacing: -.01em; line-height: 1.15; color: var(--ink); }
.tile-text > span { font-size: 11.5px; line-height: 1.2; color: rgba(255, 255, 255, .76); }
/* The count sits on a photograph, so it carries its own darkened, blurred
   plate: without one it disappeared into whatever was behind it. */
.tile em {
  position: absolute; top: var(--s3); right: var(--s3); z-index: 2;
  font-style: normal; font-size: 11px; font-weight: 700; line-height: 1;
  min-width: 22px; text-align: center; font-variant-numeric: tabular-nums;
  color: rgba(255, 255, 255, .92); padding: 4px 7px; border-radius: var(--r-chip);
  background: rgba(9, 11, 14, .52);
  border: 1px solid rgba(255, 255, 255, .12);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.tile-work { --a: var(--c-work-a); --b: var(--c-work-b); }
.tile-housing { --a: var(--c-housing-a); --b: var(--c-housing-b); }
.tile-services { --a: var(--c-services-a); --b: var(--c-services-b); }
.tile-market { --a: var(--c-market-a); --b: var(--c-market-b); }
.tile-info { --a: var(--c-info-a); --b: var(--c-info-b); }
.tile-community { --a: var(--c-community-a); --b: var(--c-community-b); }

/* ---------- wide row card -------------------------------------------- */
/* ---------- what is newest here ------------------------------------- */
/* A rail between the six sections and the banner.  The card is the catalogue
   card at a third of the width -- same graphite, same glass, same blue-pink
   edge -- so the home screen introduces no second visual language. */
.rail-section { margin-bottom: var(--s4); }
.rail-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); margin-bottom: var(--s3);
}
.rail-title { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -.015em; }
.rail-all {
  display: flex; align-items: center; gap: 3px; flex: none;
  font-size: 12.5px; color: var(--text-dim); padding: 4px 0;
}
.rail-all:active { color: var(--text); }
.rail {
  display: flex; gap: 9px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* The rail runs to both edges of the screen while the page keeps its
     margins, so the card at each end is cut by the screen rather than by a
     border a few pixels inside it. */
  margin: 0 calc(var(--page-side) * -1); padding: 2px var(--page-side);
}
.rail::-webkit-scrollbar { display: none; }
.rail .gcard { flex: 0 0 38%; scroll-snap-align: start; }
.gcard.skeleton { height: 270px; }
.gcard-price { display: flex; align-items: flex-start; gap: 5px; font-style: normal; white-space: nowrap; }
.gcard-amount {
  /* Smaller than the reference's 25-27px, because the card is 112px wide and
     the widest real price -- "від 150 RON" -- has to fit beside its currency
     without either wrapping, and a fifth smaller again by eye. */
  font-size: 17.5px; font-weight: 600; line-height: 21px; letter-spacing: -.02em;
  color: #fff; font-variant-numeric: tabular-nums;
}
/* Level with the top of the digits, not their middle.  Both boxes start at
   the same line with align-items: flex-start; what remains is the half-leading
   each carries, which differs by half a pixel between 17.5px and 11px -- so
   the nudge is 1px, not the 3px that read as centred. */
.gcard-currency {
  font-size: 11px; font-weight: 500; line-height: 14px; letter-spacing: .3px;
  color: rgba(255, 255, 255, .6); margin-top: 1px; white-space: nowrap; flex: none;
}
/* "від", and its like: read at the currency's size on the same top line, so
   the figure stays the figure and the row still fits the card. */
/* Whatever the words, the currency keeps its place beside the figure rather
   than being pushed onto a line of its own. */
.gcard-price { min-width: 0; }
.gcard-amount { min-width: 0; white-space: nowrap; }

.rowcard {
  display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  border-radius: var(--r-card); padding: var(--s3) var(--s4);
}
.rowcard:active { background: var(--card-hi); }
/* The same glass as the search field, held quieter than the tiles above it:
   a call to the community, not a seventh category. The flag is its accent. */
.stronger {
  position: relative; overflow: hidden;
  padding: 11px var(--s4);
}

.stronger:active { box-shadow: var(--pane-press); transform: translateY(1px); }
.rowcard .flag { width: 30px; height: 30px; flex: none; border-radius: 9px; display: grid; place-items: center; background: rgba(9, 11, 14, .42); border: 1px solid rgba(255, 255, 255, .10); }
.rowcard .body { flex: 1; min-width: 0; }
.rowcard b { display: block; font-size: 14px; font-weight: 650; }
.rowcard span { display: block; font-size: 12px; color: var(--text-dim); }
.rowcard .chev { width: 26px; height: 26px; flex: none; border-radius: 50%; display: grid; place-items: center; background: rgba(255, 255, 255, .06); color: var(--text-faint); }

/* ---------- filter chips --------------------------------------------- */
.chips { display: flex; gap: var(--s2); overflow-x: auto; margin-bottom: var(--s4); padding-bottom: 2px; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none; border-radius: var(--r-chip); padding: 7px 14px;
  font-size: 13px; font-weight: 550; color: var(--text);
  white-space: nowrap;
  background:
    linear-gradient(115deg, var(--glass-1) 0%, var(--glass-2) 52%, var(--glass-3) 100%),
    rgba(24, 28, 40, .42);
  border: 1px solid var(--glass-line);
  backdrop-filter: blur(14px) saturate(1.3);
  -webkit-backdrop-filter: blur(14px) saturate(1.3);
}
/* Chosen: the chip turns white and its word goes dark grey.  Both are written
   here rather than read from the tokens, because this pair is an inversion of
   the surface scale and has to hold whichever ground the page is on.  It used
   to put white writing on that white -- the block that keeps a dark chip's
   writing light outranked this rule until that block gave up its weight. */
/* `:root` only to outweigh the daylight rules, which are scoped to the theme
   and would otherwise tie with this one and win on source order alone. */
:root .chip[aria-pressed="true"] {
  background: #f4f5f7; color: #14161a; border-color: transparent; font-weight: 650;
}

/* ---------- listing cards -------------------------------------------- */
.feed { display: flex; flex-direction: column; gap: var(--s3); }
.tag {
  font-size: 10.5px; color: var(--text-dim); background: var(--card-hi);
  border: 1px solid var(--line); border-radius: var(--r-chip); padding: 3px 8px;
}
.heart {
  position: absolute; top: 10px; right: 10px; width: 30px; height: 30px;
  border-radius: 50%; display: grid; place-items: center;
  color: var(--text-faint); background: rgba(0, 0, 0, .28);
}
.heart[aria-pressed="true"] { color: var(--accent); }

/* ---------- detail ---------------------------------------------------- */
.gallery { display: flex; gap: var(--s2); overflow-x: auto; margin-bottom: var(--s4); scrollbar-width: none; }
.gallery::-webkit-scrollbar { display: none; }
.gallery img, .gallery > span {
  width: 68%; max-width: 260px; flex: none; height: 190px;
  object-fit: cover; border-radius: var(--r-card); display: block;
}
.media-missing { background: var(--card); border: 1px solid var(--line); }
.headline { display: flex; flex-direction: column; gap: 5px; margin-bottom: var(--s3); }
.detail-title { margin: 0; font-size: 21px; font-weight: 800; letter-spacing: -.025em; line-height: 1.14; text-wrap: balance; }
.section { margin-bottom: var(--s4); }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.linkbtn { font-size: 12.5px; color: var(--accent); padding: 0 0 var(--s3); }
/* Between where and what.  The city and the sections share a line now, and
   without this "Усі" sat close enough to the city to read as "all cities". */
.chips-split {
  flex: none; align-self: center; width: 1px; height: 20px;
  margin: 0 2px; background: var(--edge-line);
}
.chips-wrap { flex-wrap: wrap; overflow: visible; }
/* Real headings rather than small grey labels: they were reading as system
   chrome instead of as the structure of the listing. */
.section h2 { margin: 0 0 var(--s2); font-size: 15px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
/* Graphite with a hairline. The section's colour is spent on the chip and on
   the cards in its feed; spending it on every surface here flattened the
   hierarchy instead of building one. */
.panel {
  border-radius: var(--r-card); overflow: hidden;
  border: 1px solid var(--line);
}
.panel .line { display: flex; align-items: center; gap: var(--s2); padding: 9px 12px; border-top: 1px solid var(--line); }
.panel .line:first-child { border-top: 0; }
.panel .line b { flex: 1; min-width: 0; font-weight: 600; font-size: 13.5px; }
.panel .line span { color: var(--text-dim); font-size: 12.5px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.prose { margin: 0; white-space: pre-wrap; font-size: 14px; color: var(--text-soft); }

/* ---------- buttons --------------------------------------------------- */
/* The three strong actions -- the one at the foot of a panel, the one that
   writes a listing, and the one that opens a chat -- are all said the way the
   "НОВЕ" mark is said: the accent as the writing on a plate, not a page of it
   under white.  One pair of tokens between them, so the three cannot drift. */
.cta {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  width: 100%; border-radius: var(--r-chip); padding: 15px 20px;
  font-size: 15px; font-weight: 650; color: var(--action-ink);
  background: var(--glass-sheen), var(--action-face);
  border: 1px solid var(--action-line);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow: var(--action-glow), var(--lift-far);
}
.cta:active { filter: brightness(.94); }
.cta[disabled] { opacity: .55; box-shadow: none; }
.cta-ghost { background: var(--card); border: 1px solid var(--line-strong); color: var(--text); box-shadow: none; }
/* Saving and writing sit together, but only one of them is a call to action:
   the heart is glass, the contact is the accent. */
.floating-cta {
  position: fixed; z-index: 6; flex-wrap: nowrap;
  left: 50%; transform: translateX(-50%);
  width: min(520px, 100%);
  padding: 0 var(--s4);
  bottom: calc(var(--nav-h) + var(--nav-floor) + var(--nav-lift) + var(--s3));
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  pointer-events: none;
}
.floating-cta > * { pointer-events: auto; }
/* A quiet word under a field: what is being looked up, or why nothing was
   found.  Never an error -- none of this stops anybody publishing. */
.fieldnote {
  margin: 6px 2px 0; font-size: 12px; line-height: 1.35; color: var(--text-dim);
}
/* The places an address could mean.  Shown only when there is a choice worth
   making, and the one that will be used is lit. */
.addrpicks { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.addrpick {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 9px 12px; border-radius: var(--r-chip); text-align: left;
  font-size: 12.5px; line-height: 1.3; color: var(--text);
  background: var(--glass-sheen), var(--glass-plate-soft);
  border: 1px solid var(--edge-line);
}
.addrpick > span:last-child { min-width: 0; }
.addrpick-mark { display: grid; place-items: center; flex: none; color: var(--text-dim); }
.addrpick.on { color: var(--action-ink); border-color: var(--action-line); box-shadow: var(--action-glow); }
.addrpick.on .addrpick-mark { color: var(--action-ink); }

/* Passing on and keeping, together at the near end of the bar. */
.ctaside {
  display: flex; align-items: center; gap: var(--s3); flex: none;
  transition: opacity .18s ease, transform .22s cubic-bezier(.2, .9, .3, 1),
              width .22s cubic-bezier(.2, .9, .3, 1);
}
/* Out of the way while the ways are out.  Three round buttons and a pill do
   not fit across a phone, and the pair is worth less for the second or two
   somebody is choosing how to reach an author. */
.ctaside.is-away {
  width: 0; opacity: 0; overflow: hidden;
  transform: translateX(-14px) scale(.86); pointer-events: none;
}
/* The ways an author gave, out of the button that asked for them and along
   the bar towards the thumb.  Right-aligned so the strip grows leftwards from
   the pill; it can shrink and scroll, because an author may have given more
   ways than a narrow phone has room for. */
.contactways {
  /* All the room the pair and the pill leave, so the strip ends against the
     pill it came out of rather than floating in the middle of the bar. */
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--s3); row-gap: var(--s2);
  /* Wrapping upwards rather than scrolling sideways.  A scroll container is
     its own backdrop root, and the glass on these circles then filters an
     empty one -- which painted the whole strip as a grey rectangle behind
     them.  Three ways is the most a form can write anyway; anything the bot
     wrote with more goes onto a second line above. */
  flex-wrap: wrap-reverse;
}
.contactway {
  /* How far down the queue this one is.  React sets it per way, counting from
     the pill, so the nearest circle leaves first. */
  --i: 0;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  flex: none; text-decoration: none;
  animation: way-out .24s cubic-bezier(.2, .9, .3, 1) both;
  animation-delay: calc(var(--i) * 45ms);
}
/* The mark on the app's own glass, round, so no lit lip: on a circle that 1px
   band is a straight chord laid across the curve. */
.contactway > i {
  width: 46px; height: 46px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--action-ink);
  background: var(--glass-face), var(--action-face);
  border: 1px solid var(--action-line);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow: var(--action-glow), var(--lift-round);
}
.contactway:active > i { transform: translateY(1px) scale(.97); }
/* Each messenger in its own colour, here and on the form that writes them.
   The shape alone was never doing all the work: what people recognise these
   four by is green, purple, blue and pink, and a brand mark in the wrong
   colour is a different brand.  Only the four that have one -- a phone, an
   e-mail and "something else" are nobody's. */
.contactway.is-telegram > i, .chip.is-telegram > svg, .glassfield-icon.is-telegram { color: var(--brand-telegram); }
.contactway.is-whatsapp > i, .chip.is-whatsapp > svg, .glassfield-icon.is-whatsapp { color: var(--brand-whatsapp); }
.contactway.is-viber > i, .chip.is-viber > svg, .glassfield-icon.is-viber { color: var(--brand-viber); }
.contactway.is-instagram > i, .chip.is-instagram > svg, .glassfield-icon.is-instagram { color: var(--brand-instagram); }
.contactway.is-facebook > i, .chip.is-facebook > svg, .glassfield-icon.is-facebook { color: var(--brand-facebook); }
/* Named, because this app draws one line weight and carries nobody's logo:
   two messengers would otherwise be the same circle twice. */
.contactway > span {
  font-size: 10px; font-weight: 600; line-height: 1;
  color: var(--chrome-text); text-shadow: 0 1px 2px rgba(0, 0, 0, .45);
}
/* The same glass as the pill it stands opposite, and as the bar under both:
   a plate of its own left it darker on one side of the screen than the other,
   which is the whole of what a reader notices about a pair. */
.fav-fab {
  width: 50px; height: 50px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--text);
  /* The bar's own plate rather than a strong action's: this is a toggle, not
     a call to act, and in daylight an action's plate is washed in the brand,
     which would have said "saved" before anybody had saved anything. */
  background: var(--glass-face), var(--glass-plate-soft);
  border: 1px solid var(--line-strong);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow: var(--lift-round);
}
/* Saved is the accent, and a halo to say so: the one state of this button
   that is worth seeing from across the screen. */
.fav-fab.on {
  color: var(--accent); border-color: rgba(255, 63, 146, .34);
  box-shadow: var(--action-glow), var(--lift-round);
}
.fav-fab:active { filter: brightness(1.12); }
.pill-cta {
  display: inline-flex; align-items: center; gap: 8px;
  height: 50px; padding: 0 20px; border-radius: var(--r-chip);
  font-size: 14.5px; font-weight: 700; color: var(--action-ink);
  background: var(--glass-sheen), var(--action-face);
  border: 1px solid var(--action-line);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow: var(--action-glow), 0 6px 18px rgba(0, 0, 0, .36);
}
.pill-cta:active { filter: brightness(.94); }
.pill-cta[disabled] { opacity: .6; box-shadow: none; }
/* Room for the pill, so the end of a listing can still be read. */
.page[data-cta] { padding-bottom: calc(var(--nav-h) + var(--nav-floor) + var(--nav-lift) + 84px); }

/* ---------- states ---------------------------------------------------- */
.empty { text-align: center; padding: var(--s8) var(--s4); color: var(--text-dim); }
.empty .mark { width: 56px; height: 56px; margin: 0 auto var(--s4); border-radius: 18px; display: grid; place-items: center; background: var(--card); border: 1px solid var(--line); color: var(--text-faint); }
.empty b { display: block; color: var(--text); font-size: 15px; margin-bottom: var(--s1); }
.empty p { margin: 0 0 var(--s4); font-size: 13px; }
.skeleton { border-radius: var(--r-card); background: linear-gradient(90deg, var(--card) 25%, var(--card-hi) 37%, var(--card) 63%); background-size: 400% 100%; animation: shimmer 1.3s ease infinite; }
.skeleton-listing { height: 142px; }
.skeleton-tile { height: 104px; border-radius: var(--r-tile); }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton, .shot.loading { animation: none; } }

/* ---------- bottom navigation ---------------------------------------- */
/* A sheet of glass over the feed rather than a plate on top of it: what
   scrolls under stays just visible, and softens a little before it gets
   there.  Two things follow, and neither is optional.

   The bar itself carries no backdrop-filter.  An element that filters its
   backdrop becomes a backdrop root, and the two panes below would then be
   filtering their own parent, which paints nothing -- a flat tint, and no
   error to read.  The same trap closes the day anyone gives .bottomnav, .app
   or .frame a filter, an opacity below 1, a mask, a clip-path, a blend mode,
   or a will-change naming one of those.

   The pane sits at a negative z-index inside the bar's own stacking context:
   under the five buttons, which are in flow and so paint after it, and over
   the page, which is a stacking context two levels down. */
.bottomnav {
  /* A capsule that floats clear of the phone's edges, the shape Telegram's
     own controls take at the top of this same screen.  `margin: auto` against
     two fixed sides is what centres it once max-width bites on a tablet. */
  position: fixed; z-index: 5;
  left: var(--nav-inset); right: var(--nav-inset); margin: 0 auto; max-width: 460px;
  bottom: calc(var(--nav-floor) + var(--nav-lift));
  height: var(--nav-h);
  border-radius: 999px;
  display: grid; grid-template-columns: repeat(5, 1fr);
  align-items: center;
  /* The same plate the profile's own buttons stand on.  The material was
     always one rule -- the same sheen, the same frost, the same hairline --
     and only the plate under it differed, because the bar is a sibling of
     `.page` and never saw the profile's own value. */
  --glass-plate: var(--glass-plate-soft);
  /* No shadow under it, for now: a 28px dark halo between the capsule and the
     floor of the page read as another blurred layer down there. */
}
/* The pane: one sheet, filling the capsule.  It was two for a while -- a
   sharper one at the edge and a frosted one faded in towards the middle --
   and however softly the second was faded, the eye still found its outline
   and read the bar as two frames laid on one another. */
.bottomnav::after,
.searchbar::after,
.profile-line::after,
.profile-pickers .picker::after,
.picker.compact::after,
.daylight::after,
.themeswitch::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none; border-radius: inherit;
  /* The lit edge and the sheen down the face, from the one declaration the
     panels and the strong actions read as well. */
  background: var(--glass-sheen), var(--glass-plate);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow:
    /* The outline, lit the whole way round rather than only across the top:
       a hairline at the edge and a little of its light bleeding inwards. */
    inset 0 0 0 1px var(--glass-ring),
    inset 0 0 14px rgba(255, 255, 255, .05),
    inset 0 -14px 22px -20px rgba(0, 0, 0, .7);
}
/* Chromium will not filter a backdrop from a pane sitting at a negative
   z-index, and WebKit will.  On iOS the bar is glass; on Android it was its
   plate and nothing else -- which at seven per cent white is nothing at all.
   So there, and only there, the filter goes on the bar itself, where Chromium
   does honour it, and comes off the pane.  Both at once would make the bar a
   backdrop root and leave the pane filtering its own parent, which paints
   nothing; that is the trap the note above is about, and this is the one
   arrangement that does not spring it. */
:root[data-engine="blink"] .bottomnav {
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  /* And a plate thinner again by a fifth.  The two engines are only now
     drawing the same thing at all, and at the far heavier radius Blink needs
     to match WebKit the bar comes out the more solid of the two.  iPhone is
     right as it is, so this is said where only Blink hears it. */
  --glass-plate: var(--glass-plate-bar);
}
:root[data-engine="blink"] .bottomnav::after {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* The action at the foot of a filter, half again the size it is on WebKit.
   Blink's blur at the radius that matches WebKit's leaves a livelier ground
   under the panel, and at 14px the words were being read against what is
   behind the panel rather than against the panel.  iPhone is right as it is. */
:root[data-engine="blink"] .filter-apply { font-size: 18.2px; }
/* Nothing to see through: the pane goes back to being a plate, rather than
   leaving the icons standing on whatever photograph is under them. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .bottomnav::after,
  .searchbar::after,
  .profile-line::after,
  .profile-pickers .picker::after,
  .picker.compact::after,
  .daylight::after,
  .themeswitch::after { background: rgba(16, 19, 24, .94); }
}
.bottomnav button {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* White, like the plus in the middle.  The shadow stays: white on glass
     needs something under it when a pale photograph slides past. */
  gap: 4px; color: var(--chrome-text); font-size: 10px; line-height: 1; padding: 6px 0;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .45);
}
.bottomnav button i { display: grid; place-items: center; height: 22px; }
.bottomnav button[aria-current="page"] { color: var(--accent); }
.bottomnav .add i {
  width: 48px; height: 48px; border-radius: 50%; display: grid; place-items: center;
  /* Its layout height is the 22px the other four spend, the rest given back
     as margin, so all five labels sit on one line.  Then `top` lifts what is
     painted without moving what is measured: the ball rises out of the
     capsule and takes nothing with it. */
  margin: -13px 0; position: relative; top: -12px;
  background: var(--neon-face);
  /* Lit rather than merely coloured, and tightly: a wide glow under it
     washes the glass it sits on, which is what had to be taken away before. */
  box-shadow: var(--neon-accent);
}
.bottomnav .add[aria-current="page"] span { color: var(--accent); }

/* ---------- catalogue search and filters ------------------------------ */
/* The same glass as the home screen's search, a little shorter: this one
   sits above a list rather than opening a screen. */
.catalog-search { min-height: 52px; margin-bottom: var(--s3); }
.catalog-search > svg { color: var(--text-faint); flex: none; }
.catalog-search .iconbtn { width: 30px; height: 30px; background: none; border: 0; }
.searchgo { width: 42px; height: 42px; }
/* Five filters do not fit a phone, and cutting their labels is worse than
   moving them: the row scrolls instead of wrapping into a second line. */
/* Runs to both edges of the screen while the page keeps its margins, so a
   chip arrives from the right wall and leaves under the left one.  Stopping
   short of both, it read as a box with a hidden scrollbar rather than as a
   strip that continues. */
.filterbar {
  display: flex; gap: 8px; margin-bottom: var(--s4);
  overflow-x: auto; scrollbar-width: none;
  margin-left: calc(var(--page-side) * -1); margin-right: calc(var(--page-side) * -1);
  padding: 0 var(--page-side) 2px;
}
.filterbar::-webkit-scrollbar { display: none; }
.fchip {
  position: relative;
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: 999px; white-space: nowrap;
  font-size: 13px; color: var(--ink-quiet);
  background: var(--edge-ground); border: 1px solid transparent;
  box-shadow:
    inset 0 1px 0 var(--edge-hi), 0 1px 3px rgba(3, 5, 12, .4),
    0 6px 18px -8px var(--edge-pool-cool), 0 8px 20px -10px var(--edge-pool-warm);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
}
.fchip::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  padding: 1px; pointer-events: none;
  background: linear-gradient(120deg,
    var(--edge-cool) 0%, rgba(255, 255, 255, .10) 46%, var(--edge-warm) 100%);
  -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  mask-composite: exclude;
}
/* The mark carries as much of a filter as its word does, so it is written in
   the same quiet white -- the same one a card writes its place in. */
.fchip svg { color: var(--ink-quiet); }
/* Set, a filter turns over the way a chosen section chip does: white ground,
   black word.  It said so in the accent before, which on a row where every
   chip is dark reads as one chip lit rather than as one chip chosen. */
.fchip.on {
  color: #14161a; background: #f4f5f7; border-color: transparent; font-weight: 600;
}
.fchip.on svg { color: #14161a; }
.fchip.drop { color: var(--text-faint); }
/* Only where the date says so: a badge that is always on says nothing.
   The pill "На перевірці" wears in Мої оголошення, in pink: the same shape,
   the same size of word, the same hairline and faint wash of its own colour.
   It differs in one thing, because it sits on a photograph rather than on the
   app's own ground: its plate is the heart's, the same translucent black that
   button carries a few pixels to its right, so the two overlays on one
   picture read as one pair.  The word takes the lighter pink, which is what
   holds against a photograph. */
.newbadge {
  /* A seventh shallower than the state pill it copies, because the mark sits
     on a photograph, where a shorter pill takes less of it.  Its height is
     stated and the word centred in it by the layout -- held instead by
     padding and the font's own idea of a line, the word sat off centre and
     iOS and Android disagreed about where.
     Its top edge is the heart's top edge -- the same 8 from the top of the
     picture -- so the two start on one line.  It is the shorter of the pair,
     and hangs from that line rather than being stretched to the other's
     depth. */
  position: absolute; top: 8px; left: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  height: 17.5px; padding: 0 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 650; letter-spacing: .02em; line-height: 1;
  color: var(--accent-hi);
  border: 1px solid var(--action-line);
  /* Its own plate, lighter than an action's: a mark always stands on a
     photograph, and a photograph is dark enough on its own. */
  background: rgba(0, 0, 0, .38);
}

/* A filter grows out of the chip that carries it, rather than rising from the
   foot of the screen where the reader was not looking. */
.popover { position: fixed; inset: 0; z-index: 25; }
/* Only a light shade, and no blur at all: blurring the whole page took the
   chip the panel grew out of with it, and a dropdown that comes from nowhere
   is worse than one read against a busy page.  What needs putting out of
   focus is the strip beneath the panel itself, which the panel does. */
.popover-backdrop { position: absolute; inset: 0; background: rgba(6, 8, 11, .3); }
/* The bar's own glass, on the panels that open over the page: the same lit
   lip along the top, the same sheen down the face, the same frost and the
   same plate.  Written out rather than shared with the bar's pane, because
   that pane sits at a negative z-index inside its parent and these filter
   from themselves -- the arrangement differs, the material must not.  A
   guard keeps the two in step.

   The panel keeps a drop shadow the bar has no need of: the bar stands at the
   edge of the screen and these float in the middle of one. */
.popover-panel,
.sheet > section {
  color: var(--text);
  position: absolute; overflow-y: auto; padding: 6px;
  border-radius: 16px; border: 1px solid transparent;
  --glass-plate: var(--glass-plate-soft);
  background: var(--glass-sheen), var(--glass-plate);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow:
    inset 0 0 0 1px var(--glass-ring),
    inset 0 0 14px rgba(255, 255, 255, .05),
    inset 0 -14px 22px -20px rgba(0, 0, 0, .7),
    0 1px 3px rgba(3, 5, 12, .5), 0 16px 40px rgba(3, 5, 12, .46),
    0 10px 26px -8px var(--edge-pool-cool), 0 16px 34px -12px var(--edge-pool-warm);
}
/* The way in belongs to the panel that has a class to take it off again.  It
   was written into the shared rule above once, and the report panel -- which
   has no such class -- opened at nothing but a shadow on the page. */
.popover-panel {
  color: var(--text);
  opacity: 0; transform: scale(.92) translateY(-6px);
  transition: opacity .16s ease, transform .18s cubic-bezier(.2, .9, .3, 1);
}
.popover-panel.above { transform: scale(.92) translateY(6px); }
/* The panel grows out of the button it belongs to, and says so with a notch
   cut from the same glass and lit along the same edge. */
.popover-notch {
  position: absolute; width: 12px; height: 12px; pointer-events: none;
  /* Cut from the panel's glass, or it shows as a lighter tooth. */
  --glass-plate: var(--glass-plate-soft);
  background: var(--glass-plate);
  border-top: 1px solid var(--edge-cool); border-left: 1px solid var(--edge-cool);
  transform: rotate(45deg);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  opacity: 0; transition: opacity .16s ease;
}
.popover-notch.open { opacity: 1; }
.popover-notch.above {
  border-top: 0; border-left: 0;
  border-bottom: 1px solid var(--edge-warm); border-right: 1px solid var(--edge-warm);
}
.popover-panel.open, .popover-panel.above.open { opacity: 1; transform: scale(1) translateY(0); }
/* Thrown out of the pill: from behind it, small, into place. */
@keyframes way-out {
  from { opacity: 0; transform: translateX(34px) scale(.5); }
  to { opacity: 1; transform: none; }
}
@keyframes panel-rise {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .popover-panel { transition: none; }
  .sheet > section { animation: none; }
  .contactway { animation: none; }
  .ctaside { transition: none; }
}
/* No engine split any more: the panel is cut from the bar's glass, and the
   bar is one material on both.  What used to be settled twice here -- a
   heavier blur for Chromium against a lighter plate for WebKit -- was two
   numbers chasing the same look, and the look is now one recipe. */

/* The bar's glass, settled twice for the same reason the panel above is:
   WebKit blurs harder at the same radius, and the bar is the widest piece of
   glass in the app, so the difference shows most here. */
:root[data-engine="webkit"] {
  --glass-frost: blur(20px) saturate(1.14) brightness(.9);
}
/* One line each, a hairline between them, and the current one named in the
   brand colour rather than filled with it. */
.picker-list { border: 0; border-radius: 0; }
/* Three columns, the same in every row: the flag, the name, the tick.  The
   air is inside the row rather than around it, so nothing has to grow. */
.picker-list .sheet-option {
  min-height: 28px; padding: 0 9px; gap: 9px; background: none;
  border-bottom: 1px solid rgba(255, 255, 255, .07);
}
/* A flag is wider than it looks: its own box plus a little of its own,
   so the name never sits against it. */
.picker-list .info-emoji { flex: none; width: 20px; margin-right: 3px; text-align: center; }
.picker-list .sheet-option b { flex: 1; min-width: 0; }
.picker-list .sheet-option:last-child { border-bottom: 0; }
.picker-list .sheet-option b { font-size: 12.5px; font-weight: 600; }
.picker-list .info-emoji { font-size: 13px; }
.picker-list .sheet-option[aria-selected="true"] {
  background: rgba(255, 63, 146, .06);
}
.picker-list .sheet-option[aria-selected="true"] b { color: var(--accent); font-weight: 700; }
.picker-list .sheet-tick {
  flex: none; width: 14px; text-align: center; margin-left: 10px;
  color: var(--accent); font-size: 13px; font-weight: 700;
}
/* ---------- one style for every filter -------------------------------- */
/* A filter panel has the room the specification gives it.  The country and
   language dropdowns are not filters and keep the tighter frame they were
   sized to, so this is asked for by name rather than taken from every panel. */
.popover-panel.titled { padding: 16px; }
/* The panel names itself, because it covers the chip it grew out of. */
.filterhead {
  display: flex; align-items: center; gap: 9px; margin-bottom: 12px;
}
.filterhead b { flex: 1; min-width: 0; font-size: 15px; font-weight: 650; }
.filterhead-icon {
  flex: none; width: 20px; height: 20px; display: grid; place-items: center;
  color: var(--accent);
}
.filterhead .iconbtn { flex: none; width: 24px; height: 24px; color: var(--text-faint); }

/* A list long enough to scroll is a list worth searching. */
.filter-find {
  display: flex; align-items: center; gap: 8px;
  height: 44px; padding: 0 12px; margin-bottom: 10px;
  border-radius: 12px; border: 1px solid var(--line);
  background: rgba(255, 255, 255, .04); color: var(--text-faint);
}
.filter-find input {
  flex: 1; min-width: 0; background: none; border: 0; outline: none;
  font-size: 13.5px; color: var(--text);
}
.filter-find input::placeholder { color: var(--text-faint); }

.filter-list { display: flex; flex-direction: column; }
/* Every row the same height, whatever it holds: the ring, an optional symbol,
   the words, and the tick that only the chosen row shows. */
/* The specification drew a 48px row.  Against a real list of twelve
   categories it was so open that half of them fell off the screen, so the air
   inside it came down twice: to 34, and then by a further two fifths to 28.
   The text is 20 of those pixels; what was taken is the emptiness around it,
   not the words.  Every list that drops out of a chip is set here, so they
   cannot be tightened one at a time. */
.filter-option {
  display: flex; align-items: center; gap: 10px;
  min-height: 28px; padding: 0 10px; border-radius: 10px;
  background: none; text-align: left;
}
.filter-option b { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 600; }
/* The ring is the control; filled, it is the answer. */
.filter-radio {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid var(--line-strong); box-sizing: border-box;
  transition: background .14s ease, border-color .14s ease;
}
.filter-option[aria-checked="true"] .filter-radio {
  border-color: transparent;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent) 55%, var(--accent-deep));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .4);
}
.filter-emoji { flex: none; width: 20px; text-align: center; font-size: 15px; }
.filter-glyph { flex: none; width: 20px; display: grid; place-items: center; color: var(--text-dim); }
.filter-tick { flex: none; width: 14px; text-align: center; color: var(--accent); font-size: 14px; font-weight: 700; opacity: 0; }
.filter-option[aria-checked="true"] { background: rgba(255, 63, 146, .08); }
.filter-option[aria-checked="true"] b { color: var(--accent); font-weight: 700; }
.filter-option[aria-checked="true"] .filter-glyph { color: var(--accent); }
.filter-option[aria-checked="true"] .filter-tick { opacity: 1; }

/* The action says what it will do, and how much of it. */
.filter-apply { height: 52px; padding: 0 20px; margin-top: 12px; font-size: 14px; }

/* ---------- the price range ------------------------------------------- */
.filter-tabs {
  display: flex; gap: 2px; padding: 3px; margin-bottom: 12px;
  border-radius: 12px; background: rgba(255, 255, 255, .05);
}
.filter-tabs button {
  flex: 1; height: 32px; border-radius: 9px;
  font-size: 12.5px; font-weight: 600; color: var(--text-dim);
  /* A hairline on every one of the three, drawn only on the chosen one: given
     to that one alone, picking a tab moved the words in the other two. */
  border: 1px solid transparent;
  /* "Весь діапазон" is the one label that wraps, and the browser's own
     `normal` leaves about 1.19 of the size between the lines -- two of them
     come to nearly 30px in a 32px pill, and the second sat on its edge.  Half
     that gap is 1.1, which brings the pair to 27.5px and back inside it. */
  line-height: 1.1;
}
/* Chosen, said the way every other strong action on the page says it.  No
   frost of its own: the panel it stands in already filters its backdrop, so a
   filter here would have nothing left to read but its own parent. */
.filter-tabs button.on {
  color: var(--action-ink);
  background: var(--glass-sheen), var(--action-face);
  border-color: var(--action-line);
  box-shadow: var(--action-glow), 0 4px 12px rgba(0, 0, 0, .38);
}
/* The catalogue's own spread, so the reader can see where the listings are
   before choosing where to cut. */
.filter-graph {
  display: flex; align-items: flex-end; gap: 2px; height: 60px; margin-bottom: 6px;
}
/* The bars are read the way the buttons are: what is inside the range wears
   the strong action's glass and its hairline, what is outside wears the quiet
   plate the bar at the foot of the screen wears.  A tint alone said it in the
   dark and said nothing in daylight, where the whole graph had gone pink. */
.filter-graph i {
  flex: 1; border-radius: 3px 3px 0 0;
  background: var(--glass-plate-soft);
  border: 1px solid transparent;
  transition: background .14s ease, border-color .14s ease;
}
.filter-graph i.on {
  background: var(--glass-sheen), var(--action-face);
  border-color: var(--action-line);
  box-shadow: var(--action-glow);
}

.filter-slider { position: relative; height: 18px; margin-bottom: 6px; }
.filter-track {
  position: absolute; top: 7px; left: 0; right: 0; height: 4px;
  border-radius: 2px; background: rgba(255, 255, 255, .12);
}
.filter-track.on { background: linear-gradient(90deg, var(--accent), var(--accent-deep)); }
/* Two ranges stacked: the one being dragged takes the pointer, the other
   stays out of its way. */
.filter-slider input {
  position: absolute; inset: 0; width: 100%; height: 18px;
  margin: 0; background: none; pointer-events: none;
  -webkit-appearance: none; appearance: none;
}
.filter-slider input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  width: 18px; height: 18px; border-radius: 50%; border: 2px solid #fff;
  background: linear-gradient(180deg, var(--accent-hi), var(--accent-deep));
  box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
}
.filter-slider input::-moz-range-thumb {
  pointer-events: auto;
  width: 18px; height: 18px; border-radius: 50%; border: 2px solid #fff;
  background: var(--accent); box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
}
.filter-slider input:disabled::-webkit-slider-thumb { opacity: .4; }

.filter-ends {
  display: flex; justify-content: space-between;
  font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums;
  margin-bottom: 12px;
}
/* The bands people actually think in, so the common answer is one tap. */
.filter-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.filter-chips button {
  height: 40px; padding: 0 14px; border-radius: 11px;
  font-size: 12.5px; font-weight: 600; color: var(--text-dim);
  border: 1px solid var(--line); background: rgba(255, 255, 255, .03);
}
.filter-chips button.on {
  color: var(--accent); border-color: rgba(255, 63, 146, .45);
  background: rgba(255, 63, 146, .1);
}

/* ---------- boot / auth screens --------------------------------------- */
.boot { min-height: 100vh; display: grid; place-items: center; padding: var(--s8); text-align: center; }
.boot .inner { max-width: 320px; }
.boot h1 { margin: 0 0 var(--s2); font-size: 22px; letter-spacing: .04em; }
.boot p { margin: 0; color: var(--text-dim); font-size: 14px; }

/* ---------- cover (first entry) --------------------------------------- */
/* Full-bleed, as the reference panel is: the photograph is the panel, and the
   brand sits over its lower half rather than under a cropped band of it. */
.cover {
  position: relative; overflow: hidden;
  min-height: 100vh; min-height: var(--tg-viewport-height, 100vh);
  display: flex; flex-direction: column; justify-content: flex-end;
  background: var(--bg);
}
.cover-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .85; }
.cover::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(13, 15, 18, .28) 0%, rgba(13, 15, 18, .55) 38%,
    rgba(13, 15, 18, .93) 68%, var(--bg) 100%);
}
.cover-hand {
  position: absolute; z-index: 2; left: var(--s6); right: var(--s6);
  top: calc(var(--safe-t) + var(--s6)); margin: 0;
  font-family: var(--font-hand);
  font-size: 26px; font-weight: 600; line-height: 1.2; color: #fff;
  display: flex; align-items: center; gap: var(--s2);
  text-shadow: 0 2px 14px rgba(0, 0, 0, .55);
}
.cover-body { position: relative; z-index: 2; padding: 0 var(--s6) calc(var(--safe-b) + var(--s6)); }
.cover-logo {
  font-family: var(--font-brand);
  margin: 0 0 var(--s2); font-size: 38px; line-height: 1.06; font-weight: 700;
  letter-spacing: -.02em; text-transform: uppercase;
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s2);
  text-shadow: 0 4px 30px rgba(0, 0, 0, .6);
}
/* The slogan is written in the same hand as the line at the top. */
.cover-slogan {
  margin: 0 0 var(--s4); font-family: var(--font-hand);
  font-size: 24px; font-weight: 600; line-height: 1.2; color: #fff;
  text-shadow: 0 2px 16px rgba(0, 0, 0, .5);
}
.cover-directions { margin: 0 0 var(--s6); font-size: 13px; color: var(--text-faint); }
.cover-directions i { font-style: normal; color: var(--accent); }
.cover-pickers { display: flex; gap: var(--s2); margin-bottom: var(--s3); }
.picker {
  flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--s2);
  border-radius: var(--r-chip);
  padding: 11px var(--s3); font-size: 13.5px; color: var(--text);
}
.cover-options { list-style: none; margin: 0 0 var(--s3); padding: var(--s1); background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card); }
.cover-options button {
  width: 100%; display: flex; align-items: center; gap: var(--s2);
  padding: 11px var(--s3); border-radius: 10px; font-size: 14px; color: var(--text);
}
.cover-options button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent-hi); }
.cover-footer { margin: var(--s6) 0 0; text-align: center; font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-faint); }

/* ---------- profile ---------------------------------------------------- */
/* The one strong light on the screen: the avatar's ring.  Everything else
   here is lit at about half its strength, so the face is what the eye lands
   on first. */
/* The rhythm of this screen, set once here: the handwritten line under the
   name needs room to breathe before the menu, and every block below is kept
   the same distance from the one above it. */
.profile-head {
  display: flex; flex-direction: column; align-items: center;
  gap: 14px; margin: var(--s5) 0 var(--s6);
}
/* A column, so the switch below the two selectors keeps a gap of its own
   rather than sitting against them. */
.profile-settings { margin-top: 24px; display: flex; flex-direction: column; gap: var(--settings-gap); }
.profile-settings h2 + .pickers { margin-top: -4px; }
.profile-settings h2 {
  margin: 0 0 10px 2px; font-size: 12px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint);
}
.avatar-ring {
  position: relative; width: 104px; height: 104px; border-radius: 50%;
  padding: 2px; border: 0;
  background: conic-gradient(from 210deg,
    var(--ring-blue) 0deg, var(--ring-violet) 110deg, var(--accent) 210deg,
    var(--ring-violet) 300deg, var(--ring-blue) 360deg);
}
.avatar-face {
  display: grid; place-items: center; width: 100%; height: 100%;
  border-radius: 50%; overflow: hidden; background: var(--card-hi);
  font-size: 34px; font-weight: 700; color: var(--text-dim);
}
.avatar-face img, .avatar-face .skeleton, .avatar-face .media-missing {
  width: 100%; height: 100%; object-fit: cover; border-radius: 50%;
}
.avatar-face.busy { opacity: .6; }
/* Small enough to leave the face alone, on the same dark glass as the rest. */
.avatar-camera {
  position: absolute; right: -2px; bottom: 2px;
  width: 32px; height: 32px; border-radius: 50%;
  /* On its own dark plate over a photograph, so it keeps the night's white
     in both grounds rather than turning over with the page. */
  display: grid; place-items: center; color: var(--ink);
  background: rgba(12, 15, 22, .86); border: 1px solid var(--edge-line);
  box-shadow: 0 2px 10px rgba(3, 5, 12, .55);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.avatar-menu {
  position: absolute; top: calc(100% + 10px); left: 50%; transform: translateX(-50%);
  z-index: 6; display: flex; flex-direction: column; min-width: 190px;
  border-radius: 14px; overflow: hidden; border: 1px solid var(--edge-line);
  background: rgba(12, 15, 22, .92);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
  box-shadow: 0 12px 30px rgba(3, 5, 12, .5);
}
.avatar-menu button {
  padding: 12px 14px; text-align: left; font-size: 13px; color: var(--text);
  border-bottom: 1px solid rgba(255, 255, 255, .07);
}
.avatar-menu button:last-child { border-bottom: 0; color: var(--text-dim); }
.profile-head > b { font-size: 25px; font-weight: 700; letter-spacing: -.02em; }
/* The one line on this screen written by a hand rather than set in type. */
.profile-motto {
  font-family: var(--font-hand); font-size: 22px; line-height: 1;
  color: var(--text-dim); margin-top: -4px;
}

/* One card, three rows, lit along its edge at half the avatar's strength. */
/* The menu, the two selectors under it and the pair in the header are one
   frame written once: the same glass, the same edge, the same light beneath.
   Only their shape differs, which is below. */
/* The five things a profile is for are buttons, not rows in a card, and they
   wear the bar's glass: the same pane, the same white line along the top, the
   same lit outline.  Each carries it on its own ::after, which means none of
   them may filter its own backdrop -- an element that does becomes a backdrop
   root and its pane would filter it rather than the page. */
.profile-line,
.profile-pickers .picker,
.picker.compact {
  position: relative; isolation: isolate;
  background: none; box-shadow: none; border: 1px solid transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
/* The panel round them goes: it was a card holding three rows, and they are
   five buttons. */
.profile-menu {
  display: flex; flex-direction: column; gap: 8px;
  background: none; box-shadow: none; border: 0; overflow: visible;
  /* And the filter with them.  It is a `.panel` by class, so the shared pane
     handed it a blur; with the pane's own background taken away, that blur
     had nothing to show but the page behind it, faintly displaced -- which
     read as a rectangle drawn round the three buttons. */
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.profile-menu::before { display: none; }
.profile-line::before,
.profile-pickers .picker::before,
.picker.compact::before { display: none; }


/* Each keeps its own corner: the menu's 22px on a row this short would be a
   lozenge, and the header's chips are chips. */
.profile-pickers .picker::before { border-radius: 14px; }
.picker.compact::before { border-radius: var(--r-chip); }
/* Chosen, it says so in the accent -- over the shared edge, not instead of
   the shared frame. */
.profile-pickers .picker.open::before,
.picker.compact.open::before {
  background: linear-gradient(28deg,
    rgba(255, 63, 146, .55) 0%, rgba(255, 108, 186, .3) 50%, rgba(255, 63, 146, .5) 100%);
}
.profile-line {
  width: 100%; text-align: left; min-height: 60px; gap: var(--s3);
  padding: 0 var(--s4); border-radius: 18px;
}
.profile-menu .profile-line:last-child { border-bottom: 0; }
/* Named outright, so nothing behind a row can make it read as secondary. */
/* One class deeper than ".panel .line b", so no later rule of that shape can
   reach these three rows again. */
.panel.profile-menu .profile-line b {
  flex: 1; min-width: 0;
  font-size: 15.5px; font-weight: 650; color: var(--text-soft);
  opacity: 1; text-shadow: none;
}
.profile-line:active,
.profile-pickers .picker:active { transform: translateY(1px); }
.profile-line > svg:last-child { color: var(--text-faint); }
/* Lit the way the avatar is, at about half its strength: blue at one corner,
   pink at the other, and the icon itself left plain. */
.profile-glyph {
  position: relative;
  width: 42px; height: 42px; flex: none; border-radius: 13px;
  display: grid; place-items: center; color: var(--text);
  background: rgba(255, 255, 255, .045); border: 1px solid transparent;
  box-shadow:
    0 4px 12px -6px var(--edge-pool-cool),
    0 6px 14px -8px var(--edge-pool-warm),
    inset 0 1px 0 rgba(255, 255, 255, .07);
}
.profile-glyph::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  padding: 1px; pointer-events: none;
  background: linear-gradient(140deg,
    rgba(90, 130, 255, .40) 0%, rgba(255, 255, 255, .07) 48%, rgba(255, 108, 186, .36) 100%);
  -webkit-mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
  mask-composite: exclude;
}
/* The glyph itself is the brightest thing in its plate: pure white, and a
   hair thicker than the set's default so a thin line does not read as grey at
   this size.  The lighting stays on the plate around it. */
.profile-glyph svg { position: relative; color: #fff; }
.profile-glyph svg path { stroke-width: 1.9; }
/* A lit bead rather than a flat dot: the light falls on its top edge, the
   underside is in its own shade, and it throws a little of its colour onto
   whatever it sits on. */
/* The pill a new listing is marked with, carrying a number: a filled disc in
   the accent shouted the count of something nobody asked to be alerted to. */
.counter {
  font-style: normal; font-size: 10.5px; font-weight: 650; letter-spacing: .02em;
  min-width: 24px; height: 17.5px; padding: 0 7px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--accent-hi);
  background: rgba(0, 0, 0, .38);
  border: 1px solid rgba(255, 63, 146, .38);
}

/* ---------- swipe back ------------------------------------------------- */
.frame { flex: 1; display: flex; flex-direction: column; }
/* The page springs back from a pull past its end.  While the finger is down
   there is no transition, or the page would lag behind it; the class goes on
   release and the same rule carries it home. */
.frame { transition: transform .34s cubic-bezier(.22, 1, .36, 1); }
.frame.pulling { transition: none; }
.frame .page { transition: transform .22s ease; }
.frame.swiping .page { transition: none; }

/* ---------- listing card ---------------------------------------------- */
/* Full-bleed to the very top of the page: the photograph starts where
   Telegram's own header ends, with the two controls floating over it. */
.hero {
  position: relative;
  margin: calc(max(var(--safe-t), var(--s4)) * -1) calc(var(--page-side) * -1) var(--s4);
}
.hero-empty { margin-bottom: var(--s2); }
.hero-strip {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; gap: 0;
}
.hero-strip::-webkit-scrollbar { display: none; }
/* Nearer a square than a letterbox: a photograph of a person, a room or a
   thing on a table loses less of itself here, and the whole of it is one tap
   away in any case.  Taller than four-to-three, because what a photograph
   loses to a crop it loses off the bottom. */
.hero-photo {
  width: 100%; flex: none; aspect-ratio: 4 / 3.4; height: auto; object-fit: cover;
  scroll-snap-align: center; display: block; cursor: zoom-in;
}
.hero-count {
  position: absolute; left: var(--s4); bottom: var(--s3);
  font-size: 12px; font-weight: 650; color: #fff; padding: 4px 10px;
  border-radius: var(--r-chip); background: var(--plate);
  border: 1px solid var(--plate-line);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.hero-dots { position: absolute; left: 0; right: 0; bottom: var(--s3); display: flex; justify-content: center; gap: 5px; }
.hero-dots i { width: 5px; height: 5px; border-radius: 50%; background: rgba(255, 255, 255, .38); }
.hero-dots i.on { background: var(--accent); width: 14px; border-radius: var(--r-chip); }

/* The whole photograph, nothing cropped, over everything else. */
.viewer {
  position: fixed; inset: 0; z-index: 40;
  /* Half the plate it had: the free space around a photograph is where the
     page it came from should still be, out of focus behind it. */
  background: rgba(4, 6, 10, .48);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex; flex-direction: column;
}
.viewer-strip {
  flex: 1; display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; align-items: center;
}
.viewer-strip::-webkit-scrollbar { display: none; }
.viewer-strip img, .viewer-strip .skeleton, .viewer-strip .media-missing {
  width: 100vw; flex: none; height: 78vh; object-fit: contain;
  scroll-snap-align: center;
}
/* Over the strip rather than above it.  Taking a row of its own, the bar
   pushed the photograph down by half its own height -- and an album that does
   not sit in the middle of the screen reads as one that slipped. */
.viewer-bar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between;
  padding: calc(var(--safe-t) + var(--s3)) var(--s4) var(--s3);
}
.viewer-count { font-size: 13px; font-weight: 650; color: var(--text-dim); font-variant-numeric: tabular-nums; }

.detail-top { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s2); }
.section-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600; color: var(--text);
  padding: 6px 12px; border-radius: var(--r-chip);
  background: linear-gradient(115deg, var(--glass-1), var(--glass-2));
  border: 1px solid var(--glass-line);
}
.detail-when { font-size: 12px; color: var(--text-faint); }
/* The figure and its currency, set the way a card sets them: the currency
   small and lifted to the top of the digits rather than sitting on their
   baseline at the same size, where it read as part of the number. */
.detail-price {
  margin: 0; display: flex; align-items: flex-start; gap: 6px;
  color: var(--accent); white-space: nowrap;
}
.detail-amount {
  font-size: 26px; font-weight: 800; line-height: 1;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums;
}
.detail-currency {
  font-size: 12.5px; font-weight: 700; line-height: 1;
  letter-spacing: .04em; color: var(--accent-hi);
  /* Level with the top of the digits: half the difference between the two
     cap heights, which is where the eye puts it. */
  margin-top: 3px;
}
.detail-meta { margin: 1px 0 0; display: flex; align-items: center; flex-wrap: wrap; gap: var(--s3); font-size: 12.5px; color: var(--text-dim); }
/* One rating, stated once: the figure, a single star, the count. */
.detail-meta > span { display: inline-flex; align-items: center; gap: 5px; }
/* The same: a strip of facts that continues past the screen rather than
   ending inside it. */
.factchips {
  display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  margin: 0 calc(var(--page-side) * -1) var(--s3);
  padding: 0 var(--page-side);
}
.factchips::-webkit-scrollbar { display: none; }
.factchip {
  flex: none; white-space: nowrap;
  font-size: 10.5px; font-weight: 600; color: var(--text);
  padding: 5px 10px; border-radius: var(--r-chip);
  background: var(--card); border: 1px solid var(--line);
}

/* The block that answers "who am I dealing with". Its levels are separated
   rather than run together: who they are and how they are rated, then what
   they are and how long they have been here, then what they speak. */
.providercard {
  color: var(--text);
  margin-bottom: var(--s4); border-radius: var(--r-card); overflow: hidden;
  border: 1px solid var(--line-strong);
  background:
    linear-gradient(180deg, rgba(38, 43, 51, .92) 0%, rgba(24, 28, 34, .92) 100%);
  box-shadow: 0 8px 22px rgba(0, 0, 0, .30);
}
.provider-head { display: flex; align-items: center; gap: 11px; padding: 12px 12px 11px; }
.provider-id { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.provider-id > b { font-size: 16px; font-weight: 700; letter-spacing: -.01em; }
.provider-rating { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-dim); }
.provider-rating b { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
/* A count that leads somewhere is underlined, which is the one mark a word
   can carry that says so without becoming a button. */
.tolink {
  color: inherit; font: inherit; padding: 0;
  text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: var(--ink-quiet); text-decoration-thickness: 1px;
}
.tolink:active { color: var(--accent); }
.provider-rating.quiet { color: var(--text-faint); font-size: 12px; }
.provider-facts {
  display: flex; flex-direction: column; gap: 7px;
  padding: 10px 12px 12px;
  border-top: 1px solid var(--line);
}
.provider-facts .fact {
  display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: var(--text-dim); min-width: 0;
}
.provider-facts .fact svg { flex: none; color: var(--text-faint); }
.provider { display: flex; align-items: center; gap: 11px; padding: 11px 12px; }
.provider-more {
  /* One line: stacked, the count and the word it counts stood a row apart
     and made the card taller than anything in it. */
  flex: none; display: flex; align-items: baseline; gap: 5px;
  padding: 7px 10px; border-radius: 12px; color: var(--text);
  background: rgba(9, 11, 14, .38); border: 1px solid var(--line);
}
.provider-more b { font-size: 14px; font-weight: 800; font-variant-numeric: tabular-nums; }
.provider-more > span { font-size: 11px; color: var(--text-faint); letter-spacing: .02em; }
.provider-more svg { align-self: center; color: var(--text-faint); }
.provider-more.one { opacity: .72; }
button.provider-more:active { background: rgba(255, 255, 255, .06); }
/* What else the author has: a plain list, a line each, no pictures.  Without
   rules of its own it rendered as one run of titles and prices with nothing
   between them, because the row styles it borrowed belong to `.panel`. */
.provider-others { border-top: 1px solid var(--line); }
.otherline {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 11px 12px; border-top: 1px solid var(--line);
}
.provider-others .otherline:first-child { border-top: 0; }
.otherline b {
  flex: 1; min-width: 0; font-size: 13.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.otherline > span {
  flex: none; font-size: 12.5px; color: var(--text-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.otherline svg { flex: none; color: var(--text-faint); }
.otherline:active { background: rgba(255, 255, 255, .05); }
.provider-avatar {
  width: 42px; height: 42px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-size: 18px; font-weight: 700; color: var(--text);
  background: var(--plate); border: 1px solid var(--plate-line);
}

.prose.clamped { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.section .linkbtn { padding: var(--s2) 0 0; }

.trustbar {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  padding: 9px 0; border-top: 1px solid var(--line);
  font-size: 11.5px; color: var(--text-faint);
}
.trustbar > span { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* A word in the accent reads as a word.  Where the reader is meant to press
   it -- opening a week's hours, and nothing else so far -- it is given an
   outline to press. */
.linkbtn.framed {
  padding: 5px 11px; border-radius: var(--r-chip);
  border: 1px solid rgba(255, 63, 146, .42);
  background: rgba(255, 63, 146, .10);
  font-weight: 600;
}
.linkbtn.framed:active { background: rgba(255, 63, 146, .18); }
.linkbtn.danger { color: var(--accent); flex: none; padding: 0; }

/* Reporting a listing: one form, wherever the listing is being read. */
.report-why { margin: 0 0 var(--s3); font-size: 13.5px; color: var(--text-dim); }
.report-words { margin-top: var(--s3); }
/* Quiet, and last: the reader has already decided by the time they read it. */
.report-note {
  margin: var(--s3) 0; font-size: 11.5px; line-height: 1.35; color: var(--text-faint);
}
/* Sending a report is not the app's own call to action, so it does not wear
   the accent's glow -- it is a plain, deliberate button. */
.cta-danger {
  background: var(--card); border: 1px solid var(--line-strong);
  color: var(--accent); box-shadow: none;
}
.report-done { text-align: center; padding: var(--s4) 0 var(--s2); }
.report-done .mark { margin: 0 auto var(--s3); }
.report-done b { display: block; font-size: 15px; margin-bottom: var(--s1); }
.report-done p { margin: 0 0 var(--s4); font-size: 13px; color: var(--text-dim); }
.iconbtn.on { color: var(--accent); }

/* ---------- add ------------------------------------------------------- */
/* A list of things a person can publish, not a form: creating stays in the
   bot, which already owns photo upload, moderation and the group mirror. */
.addlist { display: flex; flex-direction: column; gap: 10px; }
/* Not an outline: five layers that only together read as an edge — a
   hairline border, a tight shadow that separates the card from the ground, a
   wide soft glow, an inner highlight along the top, and a gradient ring that
   carries the cool light into pink as it crosses the card. */
.addrow {
  position: relative;
  display: flex; align-items: center; gap: 14px; text-align: left;
  padding: 14px; border-radius: 18px;
  box-shadow:
    /* the tight one: it is what stops the card floating */
    0 1px 3px rgba(3, 5, 12, .5),
    0 10px 26px rgba(3, 5, 12, .34),
    /* the room's own light, pooling under the card the way it pools at the
       foot of the screen — the same two colours, a little closer */
    0 10px 26px -8px var(--edge-pool-cool),
    0 16px 34px -12px var(--edge-pool-warm),
    inset 0 1px 0 var(--edge-hi),
    inset 0 -1px 0 rgba(255, 255, 255, .025);
  transition: transform .14s ease;
}
/* The ring: one pixel of gradient, masked so only the border area survives.
   The lower edge is the lit one, because that is where the light is. */
.addrow:active { transform: scale(.985); }
.addrow-icon {
  position: relative;
  flex: none; width: 42px; height: 42px; border-radius: 14px;
  display: grid; place-items: center; color: rgba(255, 255, 255, .94);
  border: 1px solid var(--edge-line);
  background:
    linear-gradient(150deg, rgba(255, 255, 255, .07), rgba(255, 255, 255, .02) 70%),
    rgba(255, 255, 255, .022);
  box-shadow:
    0 1px 2px rgba(3, 5, 12, .4),
    inset 0 1px 0 rgba(255, 255, 255, .10);
}
.addrow-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.addrow-text b { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.addrow-text > span { font-size: 12px; line-height: 1.3; color: var(--text-dim); }
.addrow-go {
  flex: none; width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center; color: var(--accent-hi);
  background: rgba(255, 63, 146, .07); border: 1px solid rgba(255, 63, 146, .48);
}

/* ---------- composing a listing --------------------------------------- */
/* One screen instead of one question per message: every field visible, in
   any order, on the same dark glass the rest of the product is made of. */
/* Telegram's close and menu buttons float over the top-right corner in full
   screen, and this header puts its own control there.  It starts below them
   rather than under them. */
.composehead {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s3); margin: 2px 0 var(--s4);
}
.composehead-text { min-width: 0; }
.composehead h1 {
  margin: 0 0 4px; font-size: 27px; font-weight: 800; letter-spacing: -.03em; line-height: 1.08;
}
.composehead p {
  display: flex; align-items: center; gap: 7px;
  margin: 0; font-size: 13.5px; line-height: 1.3; color: var(--text-dim);
}
.composehead-heart { flex: none; display: inline-flex; color: var(--accent); }
/* The section a listing belongs to, named once and quietly. */
.sectionpill {
  flex: none; display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 15px; border-radius: 999px; font-size: 13.5px; font-weight: 600;
  color: var(--accent-text); background: rgba(255, 63, 146, .12);
  border: 1px solid rgba(255, 63, 146, .55);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
}
/* The mark that says the pill is a way back rather than a label.  On its own
   plate, a shade darker than the pill it sits in -- given the pill's own
   ground it disappeared into it, which is the whole of what it is for.  It
   takes the pill's right padding as its own, so the pill does not grow. */
.sectionpill-edit {
  display: grid; place-items: center; flex: none;
  width: 21px; height: 21px; border-radius: 50%;
  margin: -3px -8px -3px -1px;
  background: rgba(0, 0, 0, .22);
  border: 1px solid rgba(255, 63, 146, .30);
}

.form { display: flex; flex-direction: column; gap: var(--s4); padding-bottom: 28px; }
.field { display: flex; flex-direction: column; gap: 10px; }
.field > label {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 13.5px; font-weight: 700; letter-spacing: -.01em;
}
.field > label i { font-style: normal; font-size: 11.5px; font-weight: 500; color: var(--text-faint); }
/* The count sits at the far end of the label row, the way the reference has it. */
#field-photos > label { justify-content: space-between; }

/* ---- glass fields ---- */
.glassfield {
  position: relative; display: flex; align-items: center; gap: 11px;
  padding: 0 4px 0 14px; border-radius: 16px; min-height: 54px;
  background: var(--glass-1); border: 1px solid var(--glass-line);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
}
.glassfield.tall { align-items: flex-start; padding: 14px 4px 26px 14px; min-height: 118px; }
.glassfield-icon { flex: none; display: inline-flex; color: var(--text-faint); }
.glassfield.tall .glassfield-icon { margin-top: 2px; }
.glassfield input, .glassfield textarea {
  flex: 1; min-width: 0; padding: 14px 12px 14px 0; border: 0; background: none;
  /* 16px keeps iOS from zooming the page when the caret lands here. */
  font: inherit; font-size: 16px; color: var(--text); outline: none;
}
.glassfield textarea { padding: 0 12px 0 0; resize: none; line-height: 1.45; min-height: 84px; }
.glassfield input::placeholder, .glassfield textarea::placeholder { color: var(--placeholder); }
.glassfield:focus-within { border-color: rgba(255, 63, 146, .45); }
.glassfield-suffix {
  flex: none; align-self: stretch; display: grid; place-items: center;
  padding: 0 17px; font-size: 14px; font-weight: 700; color: var(--text);
  border-left: 1px solid var(--glass-line);
}
/* The currency is a choice, not a label: the country's own is already made,
   and the two that travel are one tap away beside it. */
.glassfield-suffix.money {
  display: flex; align-items: center; gap: 2px; padding: 0 8px 0 10px;
}
.glassfield-suffix.money button {
  padding: 5px 7px; border-radius: 9px; font-size: 11.5px; font-weight: 700;
  color: var(--text-faint);
}
.glassfield-suffix.money button.on {
  color: #fff; background: rgba(255, 63, 146, .16);
  box-shadow: inset 0 0 0 1px rgba(255, 63, 146, .45);
}
.glassfield-count {
  position: absolute; right: 14px; bottom: 9px;
  font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.field[data-invalid] .glassfield, .field[data-invalid] .chips, .field[data-invalid] .shots {
  outline: 1px solid rgba(255, 63, 146, .55); outline-offset: 4px; border-radius: 18px;
}

/* ---- chips ----
   Scoped to the form.  A filter row elsewhere scrolls sideways on purpose;
   a set of choices in a form must not, because a choice you have to drag to
   find is a choice most people never see. */
.form .chips {
  display: flex; flex-wrap: wrap; gap: 9px;
  overflow: visible; margin-bottom: 0; padding-bottom: 0;
}
.form .chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 15px; border-radius: 999px; font-size: 13.5px; color: var(--text);
  background: var(--glass-1); border: 1px solid var(--glass-line);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
  transition: transform .12s ease;
}
.form .chip svg { color: var(--text-dim); }
.form .chip:active { transform: scale(.97); }
.form .chip.on {
  color: #fff; background: rgba(255, 63, 146, .16); border-color: rgba(255, 63, 146, .7);
}
.form .chip.on svg { color: var(--accent-hi); }

/* ---- photographs ---- */
/* The row scrolls sideways instead of dividing the width between five slots.
   Every size here is a fixed pixel: nothing negotiates, so nothing can be
   stretched by a long label or a photograph. */
.shots {
  display: flex; gap: 9px; flex: none;
  height: 108px; min-height: 108px; max-height: 108px;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}
.shots::-webkit-scrollbar { display: none; }
.shot {
  position: relative; flex: 0 0 92px;
  height: 108px; min-height: 108px; max-height: 108px;
  scroll-snap-align: start;
  border-radius: 16px; overflow: hidden;
  display: grid; place-items: center; color: var(--text-faint);
}
.shot-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The first slot is the invitation; the rest are only places for what follows. */
.shot.lead {
  /* Wider than the slots that follow, because it carries words. */
  flex: 0 0 132px; gap: 3px; align-content: center; padding: 8px;
  color: var(--text); background: rgba(255, 63, 146, .09);
  border: 1px solid rgba(255, 63, 146, .55);
  backdrop-filter: var(--frost-blur);
  -webkit-backdrop-filter: var(--frost-blur);
}
.shot.lead b { font-size: 12.5px; font-weight: 600; }
.shot.lead i { font-style: normal; font-size: 10.5px; color: var(--text-faint); }
.shot.lead:disabled { opacity: .45; }
.shot-plus {
  width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
  color: var(--accent-hi); border: 1px solid rgba(255, 63, 146, .6);
  background: rgba(255, 63, 146, .10);
}
/* The empty slots belong to the invitation beside them, so they are outlined
   in the same pink -- quieter, because they are where photographs will go and
   not the thing to press. */
.shot.slot {
  border: 1px dashed rgba(255, 63, 146, .30);
  background: rgba(255, 63, 146, .035);
}
/* A filled slot is the photograph and nothing else: the tint it used to carry
   sat under the picture and tinged whatever showed through its edges. */
.shot.slot.filled {
  border-style: solid; border-color: var(--glass-line); background: none;
  color: var(--text-dim);
}
.shot.slot.filled { cursor: pointer; }
/* No scrim over the photograph at all: the badge carries its own plate, so
   nothing has to be darkened for ten pixels of type to read. */
.shot-mark {
  position: absolute; left: 6px; bottom: 6px; z-index: 1;
  /* The catalogue's own "НОВЕ" pill, at the same measurements, so a badge on
     a photograph means one thing wherever it appears. */
  display: inline-flex; align-items: center; height: 17.5px;
  padding: 0 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 650; letter-spacing: .02em; line-height: 1;
  color: var(--ink); background: rgba(0, 0, 0, .38);
  border: 1px solid rgba(255, 255, 255, .22);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.shot.slot.cover { border-color: rgba(255, 63, 146, .55); }
/* The cover wears the pill the catalogue marks a new listing with: the same
   shape, the same plate, the same pink word, so one badge means one thing
   wherever it appears on a photograph. */
.shot.slot.cover .shot-mark {
  color: var(--accent-hi);
  background: rgba(0, 0, 0, .38);
  border: 1px solid rgba(255, 63, 146, .38);
  box-shadow: none;
}

.shot.loading {
  background: linear-gradient(90deg, var(--card) 25%, var(--card-hi) 37%, var(--card) 63%);
  background-size: 400% 100%; animation: shimmer 1.3s ease infinite;
}
.shot-drop {
  position: absolute; top: 5px; right: 5px; z-index: 1;
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  /* White on its own dark plate over a photograph, in both grounds: it does
     not turn over with the page, and at the page's dark it vanished. */
  color: var(--ink); background: var(--plate); border: 1px solid var(--plate-line);
}

.pricerow { display: flex; align-items: center; gap: 8px; }
.pricerow .glassfield { flex: 1; min-height: 48px; padding-left: 14px; }
.pricerow .glassfield.narrow { flex: none; width: 96px; }
.pricerow .glassfield input { padding: 12px 12px 12px 0; }
.rowdrop {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--text-dim); background: var(--glass-1); border: 1px solid var(--glass-line);
}
.form .addrowbtn { align-self: flex-start; }
/* One way of being reached, with its own kind, its own field and its own note. */
.contactrow { display: flex; flex-direction: column; gap: 10px; }
.form .chip.drop { padding: 10px 12px; color: var(--text-faint); }
.fieldnote { margin: 0; font-size: 11.5px; line-height: 1.4; color: var(--text-faint); }
.formerror { margin: 0; font-size: 12.5px; color: var(--accent-hi); }
.mark.ok { color: var(--ok); }

/* ---- the one strong action on the screen ---- */
.publish {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  margin-top: 6px; padding: 17px 20px; border-radius: 999px; width: 100%;
  color: var(--action-ink); background: var(--glass-sheen), var(--action-face);
  border: 1px solid var(--action-line);
  backdrop-filter: var(--glass-frost);
  -webkit-backdrop-filter: var(--glass-frost);
  box-shadow: var(--action-glow), var(--lift-far);
  transition: transform .12s ease, filter .12s ease;
}
.publish b { flex: 1; text-align: center; font-size: 16px; font-weight: 700; letter-spacing: -.01em; }

/* ---- how tall a form is ----
   Every capsule on a create screen stands 15% shorter than it used to.  The
   forms are long -- a flat asks fourteen questions -- and a screen that holds
   four of them instead of three is the difference between reading a form and
   scrolling one.

   Heights only: no word is made smaller, because the writing is not what was
   taking up the room.  And the two whose height is the whole point keep it --
   the photograph row, and the box somebody writes a paragraph of description
   into. */
.form .glassfield:not(.tall) { min-height: 46px; }
.form .glassfield:not(.tall) input { padding-top: 12px; padding-bottom: 12px; }
.form .chip { padding-top: 8.5px; padding-bottom: 8.5px; }
.form .chip.drop { padding: 8.5px 12px; }
.form .addrpick { padding-top: 7.5px; padding-bottom: 7.5px; }
.form .publish { padding-top: 14.5px; padding-bottom: 14.5px; }
.publish:active { transform: scale(.985); }
.publish:disabled { filter: saturate(.5) brightness(.8); }

/* ---------- ratings and reviews --------------------------------------- */
/* The stars that are not lit still have to be counted: at the faintest grey
   the palette holds they were four invisible ones beside a gold one. */
.stars { display: inline-flex; gap: 1px; color: var(--ink-quiet); }
.stars svg.on { color: var(--star); }
.rating { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-dim); }
.rating b { color: var(--text); font-weight: 700; }

.review { display: flex; gap: 10px; padding: 12px; border-top: 1px solid var(--line); }
.review:first-child { border-top: 0; }
.review-avatar {
  width: 30px; height: 30px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-size: 14px; font-weight: 700; color: var(--text-dim);
  background: var(--plate); border: 1px solid var(--plate-line);
}
.review-body { flex: 1; min-width: 0; }
.review-head { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); }
.review-head b { font-size: 13.5px; font-weight: 650; color: var(--text); }
.review-date { font-size: 11.5px; color: var(--ink-quiet); }
.review-body p { margin: 4px 0 0; font-size: 13px; line-height: 1.55; color: var(--text-dim); }

/* The owner's one answer, indented under what it answers, so it reads as a
   reply rather than as a second opinion. */
.review-reply {
  display: block; margin: 8px 0 0; padding: 8px 10px;
  border-left: 2px solid var(--line-strong); border-radius: 0 10px 10px 0;
  background: var(--edge-ground);
}
.review-reply > b { font-size: 11.5px; font-weight: 700; color: var(--ink-quiet); }
.review-reply > p { margin: 2px 0 0; }
.review-acts { display: flex; align-items: center; gap: var(--s3); margin-top: 6px; }
/* The section's link button carries bottom padding for the row it usually
   sits in alone.  Here it stands beside another one, and the two were sitting
   two pixels apart. */
.review-acts .linkbtn { padding: 0; }
.review-answer { display: block; margin-top: 8px; }
.review-answer textarea {
  width: 100%; min-height: 60px; padding: 9px 11px; border-radius: 12px;
  font: inherit; font-size: 13px; color: var(--text);
  background: var(--edge-ground); border: 1px solid var(--edge-line); resize: vertical;
}
/* What somebody who has not dealt with this provider sees instead of a form.
   No instructions: they do not need to know which button the app was watching
   for, they need the way to start. */
.reviewgate {
  padding: var(--s4); display: flex; flex-direction: column; gap: var(--s3);
  align-items: flex-start;
}
.reviewgate > p { margin: 0; font-size: 13px; line-height: 1.5; color: var(--text-dim); }
/* The line that follows this card is a different thought -- "no reviews yet"
   answers the heading, while the card answers the reader -- and run together
   they read as one paragraph that contradicts itself. */
.reviewgate + .quiet { margin-top: var(--s3); }
/* An address somebody wrote in their own description.  Underlined rather than
   only coloured: colour alone is not a link to a reader who cannot see it,
   and these are the lines people are meant to act on. */
.prose-link { color: var(--accent-hi); text-decoration: underline; text-underline-offset: 2px; word-break: break-word; }
.reviewform { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s3); }
.starpick { display: flex; gap: var(--s1); }
.starpick button { padding: 2px; }
.reviewform textarea {
  width: 100%; resize: vertical; border-radius: 12px; padding: var(--s3);
  font: inherit; font-size: 16px; color: var(--text);
  background: rgba(9, 11, 14, .45); border: 1px solid var(--line-strong); outline: 0;
}
.reviewform-actions { display: flex; gap: var(--s2); }
.notice { margin: 0 0 var(--s3); font-size: 13px; color: var(--accent-hi); }
/* Said once, to one person, and quietly: it is the instruction they asked for
   rather than news.  Three lines in the accent read as something having gone
   wrong, which is the opposite of what this says. */
.notice.quiet {
  padding: 9px 12px; border-radius: 12px; line-height: 1.45;
  color: var(--text-dim); background: var(--edge-ground);
  border: 1px solid var(--edge-line);
}
.quiet { margin: 0; font-size: 13px; color: var(--text-faint); }

/* Telegram already draws the app's name above the page, so a listing needs
   only its two controls, not a second title bar. */
.detailbar {
  position: absolute; z-index: 2; left: var(--s4); right: var(--s4);
  top: calc(var(--safe-t) + var(--s3));
  display: flex; align-items: center;
}
/* Down in the corner of the picture, where it is out of the way of anything
   Telegram floats over the top of it. */
.hero-save {
  position: absolute; z-index: 2; right: var(--s4); bottom: var(--s3);
}
.hero-empty .detailbar { position: static; padding: calc(var(--safe-t) + var(--s3)) 0 var(--s2); }
/* A hero with no picture has no corner to pin anything to: without this the
   heart floated in the empty space where the photograph would have been. */
.hero-empty .hero-save { display: none; }
.detailbar .spacer { flex: 1; }
/* Every one of these stands on a photograph: the hero's back arrow, the heart
   in its corner, the close button over a picture opened full screen.  So it
   keeps the night's white and its white hairline in both grounds, the way the
   avatar's camera badge does -- `--text` turns over with the page, and in
   daylight it left a near-black glyph on a near-black plate. */
.iconbtn.plated {
  background: var(--plate); border-color: var(--edge-line);
  backdrop-filter: blur(10px); color: var(--ink);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--plate-shadow);
}
.iconbtn.plated.on { color: var(--accent); }
/* Enough shade at the top for white controls to hold on a bright photograph. */
.hero-strip + .hero-shade { }
.hero::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 108px; z-index: 1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(9, 11, 14, .62), transparent);
}
.hero-empty::before { display: none; }


/* ---------- price rows ------------------------------------------------- */
.priceline {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: 10px 12px; border-top: 1px solid var(--line);
}
.priceline:first-child { border-top: 0; }
.priceline-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.priceline-name b { font-size: 13.5px; font-weight: 600; line-height: 1.3; }
/* How long it takes, in the quiet white a card writes its place in: the
   faintest grey the palette holds left it barely there. */
.priceline-name > span { font-size: 11.5px; color: var(--ink-quiet); }
.priceline-amount {
  flex: none; font-size: 13.5px; font-weight: 700; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; padding-top: 1px;
}

/* ---------- hours ------------------------------------------------------ */
/* Collapsed, this row is today: the hours opposite "відкрито" are as much
   today's as the word is, and were being quieted by the rule that dims the
   second half of every panel row. */
.hours-today b,
.hours-today > span:not(.dotstate) { color: var(--text); font-weight: 650; }
/* Opened, the first row is today and the six under it are the rest of the
   week: the line between them is drawn heavier than the ones that separate
   one ordinary day from the next.  On the row below, because these rows are
   parted by the top border of the one under -- setting a bottom border on
   today drew nothing at all.  Cut into the panel rather than laid on it: a
   lighter line on glass reads as one more edge, a darker one as a division. */
.hours-week .hours-today + .line { border-top: 2px solid rgba(0, 0, 0, .38); }
/* And today's row is sunk into the panel rather than merely divided from it:
   the week below reads as the list, and this reads as the answer above it. */
.hours-week .hours-today { background: rgba(0, 0, 0, .2); }
.hours-today .dotstate {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--text-faint);
}
.hours-today .dotstate.open { background: var(--ok); box-shadow: 0 0 0 3px rgba(61, 220, 151, .16); }
/* The week's opening hours: today is named, the other six are quiet.  Scoped
   to that list — written unscoped it dimmed the label of every second and
   third row in every panel in the app, including the profile's menu. */
.hours-week .line b { color: var(--text-dim); }
.hours-week .line.now b,
.hours-week .line.now span { color: var(--text); font-weight: 650; }
.panel .line:first-child b { color: var(--text); }

/* ---------- information ------------------------------------------------ */
.info-line { width: 100%; text-align: left; }
.info-line:active { background: rgba(255, 255, 255, .05); }
.info-emoji { width: 26px; flex: none; text-align: center; font-size: 16px; line-height: 1; }
.info-line > span { font-variant-numeric: tabular-nums; }
.article h2 { margin: 0 0 var(--s3); font-size: 20px; font-weight: 700; letter-spacing: -.02em; line-height: 1.2; text-wrap: balance; }
.article .prose { font-size: 14.5px; line-height: 1.6; }
.article .linkbtn { display: inline-block; margin-top: var(--s4); }

/* ---------- help -------------------------------------------------------- */
/* A page of plain answers rather than a menu of them: every row is one thing
   to know, and the number in front says how far down the list the reader is.
   The rows are taller than a menu's and hang from their top, because two of
   these run to three lines and a menu row centres one. */
.panel .helprow { align-items: flex-start; gap: 10px; padding: 11px 12px; }
.helprow > i {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  font-style: normal; font-size: 11px; font-weight: 700; line-height: 1;
  color: var(--action-ink);
  background: var(--action-face);
  border: 1px solid var(--action-line);
}
.helprow > p { margin: 0; flex: 1; min-width: 0; font-size: 13.5px; line-height: 1.45; color: var(--text-soft); }
.helpabout { margin: 0; font-size: 13px; line-height: 1.55; color: var(--text-dim); }

/* ---------- bottom sheet ----------------------------------------------- */
/* A panel that floats clear of the bottom bar, not a sheet glued to the edge
   of the screen.  It used to sit against that edge, and because the page is
   its own stacking context the bar -- five layers below it on paper -- drew
   over its foot anyway.  Standing it above the bar settles that without
   touching anything's z-index, and it takes the filters' glass while it is
   there, so a panel that opens over a listing looks like every other. */
.sheet {
  position: fixed; inset: 0; z-index: 20;
  display: flex; align-items: flex-end; justify-content: center;
  padding: var(--s4);
  padding-bottom: calc(var(--nav-h) + var(--nav-floor) + var(--nav-lift) + var(--s3));
}
.sheet-backdrop { position: absolute; inset: 0; background: rgba(6, 8, 11, .3); }
.sheet > section {
  position: relative; width: 100%; max-width: 380px;
  max-height: 100%; overflow-y: auto;
  padding: 16px; border-radius: 16px;
  /* Its own way in, run by the browser rather than waiting on a class it
     never receives. */
  animation: panel-rise .18s cubic-bezier(.2, .9, .3, 1) both;
  border: 1px solid transparent;
  box-shadow:
    inset 0 0 0 1px var(--glass-ring),
    inset 0 0 14px rgba(255, 255, 255, .05),
    inset 0 -14px 22px -20px rgba(0, 0, 0, .7),
    0 1px 3px rgba(3, 5, 12, .5), 0 18px 44px rgba(3, 5, 12, .48),
    0 10px 26px -8px var(--edge-pool-cool), 0 16px 34px -12px var(--edge-pool-warm);
}
/* The list of a dropdown, and the rows in it.  These are what the country and
   language pickers are built from as well, so they live here rather than with
   the panel that happened to be rewritten last: without them the rows fall
   back to inline-block and flow two to a line. */
.sheet-list { display: flex; flex-direction: column; border-radius: var(--r-card); overflow: hidden; border: 1px solid var(--line); }
.sheet-option {
  display: flex; align-items: center; gap: var(--s2);
  width: 100%; text-align: left; background: var(--card);
}
.sheet-option:active { background: var(--card-hi); }
.sheet-option[aria-pressed="true"] b { color: var(--accent-hi); }
.sheet-option[disabled] { opacity: .55; }
.sheet-tick { color: var(--accent); font-weight: 700; }

/* ---------- the catalogue, two to a row -------------------------------- */
/* The horizontal card showed one listing per screen-width.  This shows four,
   with a picture large enough to be the reason a reader stops at one -- and
   the columns follow the width rather than a phone model, so a tablet gets
   more of them instead of two very wide cards. */
/* Two to a row on a phone, whatever the phone.  Fitting by width let a wide
   Android viewport take three while an iPhone took two, so the same catalogue
   read as two different screens. */
/* `minmax(0, 1fr)` rather than `1fr`, which is `minmax(auto, 1fr)`: a column
   cannot then be narrower than its widest item's min-content, and one card
   holding a longer word than the rest made its whole column wider than the
   others.  A card clips its own words -- every line in it is `nowrap` with an
   ellipsis -- so nothing is lost by letting the column decide the width.
   Plain `.gcard` grids escaped it only by accident: `overflow: hidden` zeroes
   an item's automatic minimum size, and "Мої оголошення" wraps each card in a
   `.mineslot` that has no `overflow` of its own. */
.cardgrid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 13px 12px; margin-bottom: var(--s4);
}
/* A tablet has the room for a third; below that the count is not negotiable. */
@media (min-width: 600px) { .cardgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.gcard {
  position: relative; text-align: left; overflow: hidden;
  /* The picture keeps the 155px it had; the words below take what is left,
     which is a third less than they used to.  Left to its content, each card
     split wherever its own title ended. */
  height: 270px;
  display: flex; flex-direction: column;
  border-radius: 22px; padding: 0;
  border: 1px solid transparent;
}
/* Pressed: it sinks into its own shadow rather than merely brightening. */
.gcard { transition: transform .13s ease, box-shadow .13s ease; }
.gcard:active {
  transform: translateY(1px) scale(.994);
  box-shadow: var(--pane-press);
}
/* The top half of every card, so a row has one horizon and the words
   below can never outgrow the picture above. */
.gcard-media {
  position: relative; z-index: 0; flex: 0 0 155px; overflow: hidden;
  background: linear-gradient(150deg, var(--card-hi), #12151a);
  display: grid; place-items: center; color: var(--text-faint);
}
.gcard-photo, .gcard-media .skeleton { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* The plate a card wears instead of a photograph: the section's own mark and
   the name, over the brand's own ground.  Dark in both themes on purpose --
   it is our picture, and our picture looks like this. */
.gcard-blank {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; color: var(--blank-ink);
  background: var(--blank-face);
}
.gcard-blank > b {
  font-size: 9px; font-weight: 800; letter-spacing: .17em; color: var(--blank-name);
}
/* White on a photograph, where the app's faint grey disappears. */
.gcard .gcard-heart {
  top: 8px; right: 8px; width: 32px; height: 32px; z-index: 1;
  color: #fff; background: rgba(0, 0, 0, .38);
}
.gcard .gcard-heart[aria-pressed="true"] { color: var(--accent); }
/* The body fills what the picture leaves, so the foot of every card in a row
   sits on the same line however much is above it. */
.gcard-body {
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; padding: 8px 12px 9px;
  /* Where the chip's own words begin: its border, its padding, its icon and
     the gap after it.  Everything below the chip lines up with those words
     rather than with the pill's edge -- the eye follows the text, not the
     border drawn around it. */
  --chip-words: 26px;
  /* What a row with an icon of its own already spends before its words. */
  --row-icon: 17px;
}
/* One line, cut with an ellipsis.  Two reserved lines were the tallest thing
   in a half that is now a third smaller, and a name that wrapped pushed the
   price out of the card. */
.gcard-title {
  font-size: 14.5px; font-weight: 550; line-height: 17px;
  color: rgba(255, 255, 255, .92);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* A pill, not a fill: it says which kind of thing this is in a mixed
   catalogue, and nothing louder than that. */
.gcard-chip {
  display: inline-flex; align-items: center; gap: 5px; align-self: flex-start;
  margin-top: 3px; padding: 2px 8px; border-radius: 999px; max-width: 100%;
  font-size: 11px; color: var(--text-dim);
  border: 1px solid var(--line); background: rgba(255, 255, 255, .04);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gcard-chip svg { flex: none; }
/* Straight under the chip, not pinned to the foot of the card.  Pinned, a
   catalogue with no ratings at all -- market, work, housing -- left a hole
   between the category and the city on every single card. */
.gcard-foot { padding-top: 3px; display: flex; flex-direction: column; }
.gcard-where {
  display: flex; align-items: center; gap: 5px;
  padding-left: calc(var(--chip-words) - var(--row-icon));
  font-size: 12px; font-weight: 400; line-height: 15px;
  color: var(--ink-quiet);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.gcard-where svg { flex: none; }
.gcard-rating {
  display: flex; align-items: center; gap: 5px; margin-top: 2px;
  padding-left: calc(var(--chip-words) - var(--row-icon));
  font-size: 11.5px; line-height: 15px; color: var(--text-dim);
}
.gcard-rating .stars { display: inline-flex; color: var(--star); }
.gcard-rating b { color: var(--text); font-weight: 650; }
/* Indented like the rows above it, because it carries an icon like they do. */
.gcard-price { margin-top: 2px; padding-left: calc(var(--chip-words) - var(--row-icon)); }
/* A rail card is a quarter narrower than a grid one, and on a 360px Android a
   price like "від 150 RON" ran past its edge.  There the rows line up with
   each other at the card's own margin instead of with the chip's words: the
   nine pixels that costs is exactly what the price was missing.  Every phone
   from 375px up keeps the chip alignment. */
@media (max-width: 374px) { .rail .gcard-body { --chip-words: var(--row-icon); } }
/* Against the digits rather than the top of their line box, which is where
   flex-start would leave it. */
.gcard-price > svg { flex: none; margin-top: 5px; color: rgba(255, 255, 255, .55); }

/* ---------- one's own listings ---------------------------------------- */
/* The card is the catalogue's; what a person needs here and nowhere else is
   the state it is in and a way to take it down. */
/* The card, with its state and its way out laid on it rather than stacked
   under it: a column of card, badge, reason and button read as four things
   about one listing instead of as one listing. */
.mineslot { position: relative; display: flex; flex-direction: column; gap: 6px; }
/* The card keeps its own height here as it does everywhere else.  `flex: 1`
   sets a basis of zero, which overrides that height in a flex column, and the
   card was squashed to make room for what stood beneath it. */
.mineslot .gcard { flex: none; }
/* On the photograph, in the corner a listing's own badge would take. */
.minestatus {
  position: absolute; top: 8px; left: 8px; z-index: 3;
  display: inline-flex; align-items: center; height: 17.5px;
  padding: 0 9px; border-radius: 999px; max-width: calc(100% - 16px);
  font-size: 10.5px; font-weight: 650; letter-spacing: .02em; line-height: 1;
  color: var(--ink); background: rgba(0, 0, 0, .38);
  border: 1px solid rgba(255, 255, 255, .22);
}
/* With a reason under it the pill becomes a note: the same frame, the same
   plate, a second line in a lighter hand. */
.minestatus.with-reason {
  flex-direction: column; align-items: flex-start; gap: 2px;
  height: auto; padding: 5px 9px 6px; border-radius: 12px;
  background: rgba(0, 0, 0, .58);
}
.minestatus.with-reason i {
  font-style: normal; font-size: 9.5px; font-weight: 500; letter-spacing: 0;
  line-height: 1.25; color: var(--ink-quiet);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Waiting is neutral, refused is the one state that needs answering, and
   taken down is quiet: colour says which without the reader reading. */
.minestatus.is-pending { color: var(--star); border-color: rgba(246, 195, 68, .5); }
.minestatus.is-rejected { color: var(--accent-hi); border-color: rgba(255, 63, 146, .5); }
/* In the card rather than under it, in the corner the words leave empty.  Its
   own plate, because a grey mark on a grey card is not a mark. */
.mineacts {
  position: absolute; right: 8px; bottom: 8px; z-index: 3;
  display: flex; align-items: center; gap: 6px;
}
/* And the price stops short of them rather than running under: two marks and
   the air between them, plus the air between that and the figure. */
.mineslot .gcard-price { padding-right: 68px; }
/* Marks rather than words: two labelled pills are wider than the card they
   stand in on a 320px screen, and on any screen they ran into the price. */
.minedrop {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  color: var(--ink-quiet);
  background: rgba(0, 0, 0, .46);
  border: 1px solid rgba(255, 255, 255, .16);
}
.minedrop:active { color: var(--accent-hi); border-color: rgba(255, 63, 146, .5); }
.minenotice {
  margin: 0 0 var(--s3); padding: 10px 12px; border-radius: 12px;
  font-size: 12.5px; color: var(--text-dim);
  /* Outlined in the accent: it answers something the reader just did, and a
     neutral hairline made it read as one more row on the page. */
  border: 1px solid rgba(255, 63, 146, .45); background: rgba(255, 255, 255, .04);
}
/* Two actions in a confirmation, and the second is the way out. */
.sheet .cta-ghost.filter-apply { margin-top: 8px; }

/* ---------- the country's own chat ------------------------------------ */
/* One card, because there is one thing here: the group this country's people
   already talk in.  The catalogue card's glass, at a size that suits a single
   destination. */
.chatcard {
  display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  padding: var(--s4); margin-bottom: var(--s3);
  border-radius: var(--r-card); border: 1px solid var(--edge-line);
  box-shadow:
    0 1px 3px rgba(3, 5, 12, .5),
    0 10px 26px -8px var(--edge-pool-cool),
    0 16px 34px -12px var(--edge-pool-warm),
    inset 0 1px 0 var(--edge-hi);
}
.chatcard:active { filter: brightness(1.1); }
.chatcard-flag { flex: none; font-size: 30px; line-height: 1; }
.chatcard-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.chatcard-text b { font-size: 15px; font-weight: 650; color: var(--text-soft); }
/* The handle, so where the tap leads is clear before it is taken. */
.chatcard-text i { font-style: normal; font-size: 12.5px; color: var(--text-dim); }
.chatcard-go {
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(120deg, var(--accent), var(--accent-deep));
  box-shadow: 0 8px 20px rgba(0, 0, 0, .4);
}
.chatnote { margin: 0 0 var(--s4); font-size: 12.5px; line-height: 1.4; color: var(--text-faint); }

/* ---------- information hub -------------------------------------------
   Compact editorial cards, not hero tiles: one wide card opens the section,
   pairs carry the everyday topics, and the two closing cards go wide again. */
.infogrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.infocard {
  position: relative; overflow: hidden; text-align: left;
  grid-column: span 1; min-height: 134px;
  border-radius: 20px; padding: 14px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 12px;
  /* A hairline that is lit rather than drawn: the edge should be felt at the
     top and almost gone at the foot, the way glass catches a room. */
  border: 1px solid rgba(255, 255, 255, .09);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07);
  background: var(--card) center/cover no-repeat;
  transition: transform .14s ease;
}
.infocard.wide {
  grid-column: span 2; min-height: 96px;
  flex-direction: row; align-items: center; gap: 14px;
}
/* The card that opens the section: no icon, because its own eyebrow carries
   the emphasis, and a taller frame so the photograph has somewhere to be. */
.infocard.lead { min-height: 128px; align-items: stretch; }
/* Neutral glass over the photograph: it darkens where the type sits and does
   nothing else.  A coloured tint pulled seven scenes into one tonality, but it
   also took each picture's own colour away, which is the thing worth keeping. */
.infocard[data-art]::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(102deg,
      rgba(9, 10, 12, .62) 0%, rgba(9, 10, 12, .32) 48%, rgba(9, 10, 12, .06) 100%),
    linear-gradient(180deg, rgba(9, 10, 12, 0) 0%, rgba(9, 10, 12, .34) 100%),
    /* The matte film: even, colourless, barely there. */
    linear-gradient(180deg, rgba(255, 255, 255, .06) 0%, rgba(255, 255, 255, .05) 100%);
}
.infocard > * { position: relative; z-index: 1; }
.infocard[data-art] .infocard-text { text-shadow: 0 1px 10px rgba(6, 8, 12, .6); }
.infocard:active { transform: scale(.985); }
.infocard-icon {
  flex: none; width: 40px; height: 40px; border-radius: 13px;
  display: grid; place-items: center; color: rgba(255, 255, 255, .94);
  background: rgba(255, 255, 255, .07); border: 1px solid rgba(255, 255, 255, .15);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.infocard-body { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; }
.infocard.wide .infocard-body { flex: 1; }
.infocard-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; color: var(--ink); }
.infocard-eyebrow {
  font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: rgba(255, 255, 255, .58); margin-bottom: 1px;
}
.infocard-text b { font-size: 15px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; }
.infocard.wide .infocard-text b { font-size: 16px; }
.infocard.lead .infocard-text b { font-size: 19px; }
.infocard-text > span { font-size: 11.5px; line-height: 1.3; color: rgba(255, 255, 255, .66); }
.infocard.lead .infocard-text > span { font-size: 12.5px; }
/* The arrow is outlined in the brand pink, never filled with it: on a card
   this quiet a solid dot would be the loudest thing on the screen. */
.infocard-go {
  flex: none; width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center; color: var(--accent-hi);
  background: rgba(255, 63, 146, .07); border: 1px solid rgba(255, 63, 146, .48);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.infocard.wide .infocard-go { width: 36px; height: 36px; }

/* The brand block at the foot: two lines, the first in the same hand as the
   line under the name, the second plain because it is an instruction. */
.profile-brand { margin-top: 24px; justify-content: center; gap: var(--s3); padding: var(--s4); }
/* The brand's own line, wherever it closes a screen. */
.profile-brand-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.profile-brand-text b {
  font-family: var(--font-hand); font-size: 23px; font-weight: 600;
  letter-spacing: .01em; color: var(--text); line-height: 1.1;
}
.profile-brand-text i { font-style: normal; font-size: 12.5px; color: var(--text-dim); }

/* The daylight switch sits in the row of selectors and is the width of one
   glyph: it has nothing to say until it is pressed.  Not a `.picker`, though
   it stands beside two -- it opens no list, and the tests that count the
   selectors on that row would have counted it as a third. */
.daylight {
  position: relative; isolation: isolate;
  flex: none; width: 34px; height: 30px; border-radius: var(--r-chip);
  display: inline-flex; align-items: center; justify-content: center;
  /* The white the two selectors beside it are lettered in, so the three read
     as one row rather than as two things and a fainter one. */
  color: var(--text-soft); border: 1px solid transparent;
}
.daylight::before { display: none; }
.daylight:active { transform: translateY(1px); }

/* The daylight switch again, spelled out this time: in the settings it stands
   among named things and a bare glyph would be the one unlabelled control on
   the screen. */
.themeswitch {
  position: relative; isolation: isolate;
  display: flex; align-items: center; gap: var(--s3); width: 100%;
  /* The corners the two selectors above it turn, not corners of its own.
     They take theirs from the cover screen's rule, which reaches them by
     name; this reads the same token rather than the number it resolves to. */
  height: var(--settings-row); padding: 0 12px; border-radius: var(--r-chip);
  border: 1px solid transparent; text-align: left;
  /* The words the two selectors above it set their values in: same size,
     same weight, same white. */
  font-size: 13px; line-height: 1.2; font-weight: 650; color: var(--text-soft);
}
.themeswitch::before { display: none; }
/* And the mark in the grey those selectors name themselves in. */
.themeswitch svg { flex: none; color: var(--text-faint); }
.themeswitch:active { transform: translateY(1px); }

/* Daylight only: a plate under every screen.  It is the thing that was once
   under the profile's three buttons by accident -- a filter with no ground of
   its own, so what shows is the page behind it, blurred and a tenth darker --
   and it turned out to be what the light theme wanted underneath it.

   Its backdrop is the two fixed layers below it and nothing else: the page's
   own content paints above it, so scrolling does not make it redraw. */
:root[data-theme="day"] .page::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  backdrop-filter: var(--pane-blur);
  -webkit-backdrop-filter: var(--pane-blur);
}

/* The profile's own glass is thinner than the rest of the app's.  On that
   screen the panes are the screen -- five buttons, two selectors and a card --
   and at the usual density they read as a wall rather than as things standing
   on one.

   In the dark it is lifted with white rather than thinned with less black:
   the page behind them is already near black, so letting more of it through
   makes a card no lighter at all.  In daylight it is thinned, since there the
   page behind is bright and less plate means more of it. */
.page[data-screen="profile"] { --glass-plate: var(--glass-plate-soft); }
/* Daylight, in the profile: the buttons are a soft grey card rather than a
   dark pane, and what is written on them goes dark with the page.  The plate
   itself turns over with the token; only the writing is named here.

   It has to be named on the panes themselves: the block that keeps a pane's
   writing light sets it on each pane, and a value set on an element beats one
   it would otherwise inherit from this page. */
:root[data-theme="day"] .page[data-screen="profile"] :is(
  .profile-line, .picker, .themeswitch, .rowcard, .panel
) {
  --text: var(--page-ink);
  --text-soft: var(--page-ink-soft);
  --text-dim: var(--page-ink-dim);
  --text-faint: var(--page-ink-faint);
  color: var(--page-ink);
}

/* ---------- the offer to keep us on the home screen -------------------- */
/* It arrives the way a notification does: over the top of the screen, under
   whatever Telegram floats there, and it leaves pushed up with a finger or
   closed with the cross.  Fixed rather than in the page, so it belongs to the
   app and not to the screen that happens to be open. */
.toast {
  position: fixed; z-index: 30;
  left: var(--s3); right: var(--s3);
  top: calc(var(--safe-t) + var(--s2));
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: 18px;
  border: 1px solid var(--edge-line);
  background: var(--pane-face), rgba(9, 11, 15, .86);
  backdrop-filter: var(--pane-blur);
  -webkit-backdrop-filter: var(--pane-blur);
  box-shadow: var(--pane-lift);
  color: var(--ink);
  /* Off the top until it is shown, and back up when it is waved away. */
  transform: translateY(calc(-100% - var(--safe-t) - var(--s4)));
  opacity: 0;
  transition: transform .34s cubic-bezier(.22, 1, .36, 1), opacity .2s ease;
}
.toast.in { transform: translateY(0); opacity: 1; }
.toast-mark {
  width: 34px; height: 34px; flex: none; border-radius: 12px;
  display: grid; place-items: center; color: #fff;
  background: var(--neon-face);
}
.toast-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.toast-text b { font-size: 13px; font-weight: 650; }
.toast-text > span { font-size: 11.5px; color: var(--ink-quiet); }
.toast-go {
  flex: none; padding: 7px 13px; border-radius: var(--r-chip);
  font-size: 12.5px; font-weight: 650; color: #fff;
  background: var(--neon-face); box-shadow: var(--neon-accent);
}
.toast-close {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  display: grid; place-items: center; color: var(--ink-quiet);
  background: rgba(255, 255, 255, .07);
}
.toast-close:active { background: rgba(255, 255, 255, .14); }
/* What the phone said when it would not do it: there is no error to catch,
   so the note is the only report there is. */
.settingsnote {
  margin: -2px 4px 0; font-size: 11.5px; line-height: 1.35; color: var(--text-faint);
}
/* Already there: a statement rather than a button. */
.themeswitch.is-done { color: var(--ink-quiet); }
.themeswitch.is-done svg { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .toast { transition: none; } }

/* ---------- the search field that follows the reader ------------------- */
/* Past the top of a catalogue the field gives up its place in the page and
   folds to the right, into the one round mark it already carries, and rides
   along at the corner.  It folds towards its own button rather than away from
   it: the mark the reader ends up pressing is the mark that was there all
   along, so nothing appears out of nowhere.

   Width is what animates, anchored right -- the element keeps its right edge
   and loses its left, which is the fold read as a movement rather than as a
   swap.  Everything inside it fades a little ahead of the fold, or the words
   would still be there in a field too narrow to hold them. */
.searchbar.afloat {
  position: fixed; z-index: 12;
  top: calc(var(--safe-t) + var(--s2));
  right: var(--page-side); left: auto;
  width: 54px; min-height: 54px; padding: 5px; margin: 0;
  justify-content: flex-end; overflow: hidden;
  transition: width .28s cubic-bezier(.22, 1, .36, 1);
}
.searchbar.afloat > :not(.searchgo) {
  opacity: 0; transition: opacity .12s ease;
  pointer-events: none;
}
/* Opened where the reader is: the full field again, still fixed, still riding
   at the top of the screen. */
.searchbar.afloat.open {
  left: var(--page-side); right: var(--page-side); width: auto;
  padding: 5px 5px 5px 18px; justify-content: flex-start;
}
.searchbar.afloat.open > :not(.searchgo) {
  opacity: 1; transition: opacity .2s ease .12s; pointer-events: auto;
}
/* The page keeps the room the field left, so nothing jumps when it folds. */
.catalog-search-gap { height: 54px; margin-bottom: var(--s3); }
@media (prefers-reduced-motion: reduce) {
  .searchbar.afloat, .searchbar.afloat > * { transition: none; }
}


/* ---- the promotion panel ----
   An internal tool for two people, opened from the bot.  It borrows the
   app's tokens and nothing else: no section colour, no hero, no chrome --
   the whole screen is one column of figures somebody is going to be paid
   on, and every line of it has to be readable beside the same figures in a
   chat message. */
.promo { display: flex; flex-direction: column; gap: var(--s4); padding: var(--s4) 0 40px; }
.promo h1 { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -.01em; }
.promoheading { margin: var(--s4) 0 0; font-size: 15px; font-weight: 650; }
.promostate { margin: 0; font-size: 13.5px; color: var(--text-dim); }
.promoperiod { display: flex; gap: 9px; }
.promosteps { display: flex; flex-direction: column; gap: 10px; }
/* Each step is a card so that the sentence under a figure plainly belongs to
   it: run together in one column, the note of one step read as the heading
   of the next, which is how seven true numbers became unreadable. */
.promostep {
  display: flex; flex-direction: column; gap: 4px;
  padding: 13px 14px; border-radius: 14px;
  background: var(--glass-1); border: 1px solid var(--glass-line);
}
.promostep-head { margin: 0; font-size: 12.5px; color: var(--text-dim); }
.promostep-head b { color: var(--text); font-weight: 650; }
.promostep-figure { margin: 0; font-size: 16px; font-weight: 650; }
.promostep-note { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--text-faint); }
.promosections { margin: 2px 0 0; padding-left: 18px; font-size: 13.5px; color: var(--text-dim); }
.promosections li { margin: 2px 0; }
.promoaddress { display: flex; flex-direction: column; gap: 5px; }
/* The address is a button rather than a link: tapping it copies, because a
   tap that opened it would spend a click on the very counter below. */
.promocopy {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 10px 12px; border-radius: 12px; text-align: left;
  background: var(--glass-1); border: 1px solid var(--glass-line);
}
.promocopy code {
  min-width: 0; overflow-x: auto; white-space: nowrap;
  font-size: 12.5px; color: var(--text);
}
.promocopy span { flex: none; font-size: 11.5px; color: var(--accent-hi); }
.promolinks { display: flex; flex-direction: column; gap: 8px; }
.promolink {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 12px 13px; border-radius: 13px; text-align: left;
  background: var(--glass-1); border: 1px solid var(--glass-line); color: var(--text);
}
.promolink-mark { flex: none; }
.promolink-name { flex: 1; min-width: 0; font-size: 14px; font-weight: 600;
                  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.promolink-count { flex: none; font-size: 12.5px; color: var(--text-faint);
                   font-variant-numeric: tabular-nums; }


/* One fact about a link, and the way to change it: the label, the value it
   has, and a chevron only when pressing it would do something.  A watcher
   sees the same rows and none of them move. */
.promofacts { display: flex; flex-direction: column; gap: 6px; }
.promofact {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 13px; border-radius: 12px; text-align: left; color: var(--text);
  background: var(--glass-1); border: 1px solid var(--glass-line);
}
.promofact:disabled { opacity: 1; }
.promofact-label { flex: none; font-size: 12.5px; color: var(--text-faint); }
.promofact-value { flex: 1; min-width: 0; font-size: 13.5px; }
.promolink.on { border-color: var(--action-line); color: var(--action-ink); }
/* Switching a link off is not the thing anybody came here to do, so it does
   not wear the accent that the one thing they did come for wears. */
.promostop {
  width: 100%; padding: 12px; border-radius: 12px; font-size: 14px;
  color: var(--text-dim); background: var(--glass-1);
  border: 1px solid var(--glass-line);
}


/* The period chips on the panel.  A chip that is on has to say so: pressing
   one and seeing nothing change is pressing a button that does not work. */
.promo .chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 14px; border-radius: 999px; font-size: 13px; color: var(--text);
  background: var(--glass-1); border: 1px solid var(--glass-line);
  transition: transform .12s ease;
}
.promo .chip:active { transform: scale(.97); }
.promo .chip.on {
  color: var(--action-ink);
  background: var(--action-face);
  border-color: var(--action-line);
  box-shadow: var(--action-glow);
}
.promoperiod { flex-wrap: wrap; }
