/* =========================================================================
   Обекти — project pages, the availability board and the unit table.

   Loaded only on izo_project screens (see functions.php). Inherits every token
   from main.css; the only new ones are the four status colours, which are
   picked to survive next to the brand red without competing with it:
     free      the palette's existing --sale green
     reserved  the palette's existing --build amber
     sold      a desaturated grey, deliberately recessive
     inquire   ink, for things that carry no status of their own
   The brand red is never used for a status — it marks the next action, and a
   board where nine cells shout in accent has no next action left.
   ====================================================================== */

/* Status colouring is taken from the Пенемат board in ../izolacii/, verbatim.
   Two things make it work, and both were missing from the first attempt here:

   1. Fill vs outline. Свободен and резервиран are SOLID blocks with white type;
      продаден is hollow — a grey rule and muted type, so a sold flat reads as
      absent rather than as a third colour competing for attention.
   2. Muted hues on a warm ground. The first pass used the palette's --sale and
      --build darkened for contrast, which gave a saturated grass green against
      a mud brown on cool white — crude, and the two fought each other. Teal and
      wool are desaturated, sit a similar distance from the paper, and read as a
      considered pair rather than as traffic lights.

   Note: white on --resv is 3.0:1. That is Пенемат's own value, kept for the
   match; swap to #96682d if AA at cell size is required. */
.obekt,
.obekti,
.unit {
	--free: #2c6b78;
	--free-lift: #3d8b99;
	--resv: #b5813c;
	--resv-lift: #cd9a52;
	/* Darkened 30% on 2026-08-24 (was #9aa3a0) — the outline of a продаден or
	   empty square was too faint to hold the facade together at cell size.
	   2.40:1 on --warm before, 4.55:1 now.

	   Every hollow cell shares this one colour. They stay distinct by BORDER
	   STYLE — solid продаден, dashed unknown, dotted empty — never by being
	   faint, and never with an `opacity`: opacity blends the border back toward
	   the ground and silently cancels any darkening of this token. That is what
	   hid the first attempt at this change (.55 turned #6c7270 into #abadaa). */
	--sold-line: #6c7270;
	--sold-ink: #6e7876;
	--inq: #35618e;
	/* Синьото на ИЗБОРА. Не е статус — затова не участва в легендата и не
	   пълни, а огражда: изборът е нещо, което правиш ти, а запълването на
	   квадратчето принадлежи на онова, което сградата вече е решила.
	   Същият тон ползва и планът за „вие сте тук“ (--bp-current). */
	--pick: #1668dc;
	--cell: clamp(28px, 2.9vw, 38px);
}

/* -------------------------------------------------------------------------
   Buttons — two modifiers the base system did not need until now
   ---------------------------------------------------------------------- */

.btn--solid { background: var(--accent); color: #fff; }
.btn--solid:hover { background: var(--accent-dk); color: #fff; }
.btn--lg { padding: 1.25rem 2.5rem; font-size: .875rem; }

@media (max-width: 640px) {
	.btn--lg { padding: 1rem 1.5rem; font-size: .8125rem; }
}

/* -------------------------------------------------------------------------
   Breadcrumbs
   ---------------------------------------------------------------------- */

.crumbs {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .5rem;
	margin-block-end: 1.5rem;
	font-size: .75rem;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: rgb(255 255 255 / .7);
}

.crumbs a { color: inherit; text-decoration: none; border-bottom: 1px solid rgb(255 255 255 / .3); }
.crumbs a:hover { border-color: currentColor; }
.crumbs span[aria-hidden] { opacity: .45; }
.crumbs [aria-current] { color: #fff; font-weight: 700; }

.obekti .crumbs { color: var(--muted); }
.obekti .crumbs a { border-bottom-color: var(--line); }
.obekti .crumbs [aria-current] { color: var(--ink); }

/* Логото е плоча, по-висока от лентата, и умишлено се спуска под нея (виж
   .brand в main.css). Страниците с герой имат къде да я поемат; страницата на
   единицата няма — при 1440/1280/768 px плочата лягаше върху пътеката
   (най-зле -3.3 px). Пътеката се бута надолу с толкова, колкото се маха от
   разстоянието под нея, така че съдържанието отдолу не мърда. */
.unit .crumbs { margin-block-start: .75rem; margin-block-end: .75rem; }

/* -------------------------------------------------------------------------
   Project hero
   ---------------------------------------------------------------------- */

.obekt-hero {
	position: relative;
	display: flex;
	align-items: flex-end;
	min-height: clamp(420px, 56vh, 620px);
	padding-block: clamp(2.5rem, 6vw, 5rem);
	background: var(--ink) var(--hero-sm, var(--hero, none)) center / cover no-repeat;
	color: #fff;
	isolation: isolate;
}

/* Мобилно първо: най-малкият файл е стойността по подразбиране, а по-широкият
   екран го надписва. Обратното — да се сваля голямото и да се „поправя“ на
   телефон — не работи: браузърът вече е започнал да го тегли.

   Праговете са по ШИРИНАТА НА ФАЙЛА, не по точките на дизайна: 768px файл
   стига до 768px viewport при 1x и до 384px при 2x, тоест покрива всеки
   телефон. Оттам нататък 1024px, а пълният (1481px) — чак когато екранът
   наистина е широк. Файл, който го няма (кадър от темата, а не от
   библиотеката), пада на `--hero` и трите правила сочат едно и също. */
@media (min-width: 700px) {
	.obekt-hero { background-image: var(--hero-md, var(--hero, none)); }
}

@media (min-width: 1100px) {
	.obekt-hero { background-image: var(--hero, none); }
}

/* Екран с двойна плътност иска пълния файл по-рано — 768px кадър на 800px
   ретина екран е видимо мек точно там, където стои името на сградата. */
@media (min-width: 700px) and (min-resolution: 2dppx) {
	.obekt-hero { background-image: var(--hero, none); }
}

/* Two stops, not one: a flat overlay greys the render out, while a gradient
   keeps the top of the image readable and still lands enough density under the
   text for the stats to hold contrast. */
.obekt-hero__scrim {
	position: absolute;
	inset: 0;
	z-index: -1;
	background:
		linear-gradient(to top, rgb(12 13 15 / .92) 0%, rgb(12 13 15 / .72) 38%, rgb(12 13 15 / .28) 100%);
}

.obekt-hero__inner {
	width: min(var(--maxw), 100%);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.obekt-hero__phase {
	display: inline-block;
	margin: 0 0 1rem;
	padding: .4rem .8rem;
	background: var(--accent);
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
}

/* 18ch used to cap this and it broke the longest name in the set —
   „Пенемат Лейк Вю бл. 7 и 8“ — leaving the „8“ alone on a second line.
   The cap is now wide enough for that title to sit on one line at desktop, and
   `text-wrap: balance` splits the remaining cases evenly instead of shedding a
   single character, which is what a wrap on a phone should look like. */
/* Тежестта и разредката са същите като на заглавието в хероя на началната
   страница (`.hero__title strong` в main.css: 800 / -.03em). Заглавието тук
   беше 300 и на същия екран, на същия тъмен кадър, изглеждаше като друг шрифт
   — тънък до „Свободни единици“ веднага под него. Смени ли се едното, смени
   се и другото. */
.obekt-hero__title {
	margin: 0 0 .5rem;
	font-size: clamp(1.875rem, 5vw, 3.75rem);
	font-weight: 800;
	letter-spacing: -.03em;
	max-width: 27ch;
	text-wrap: balance;
}

.obekt-hero__loc {
	margin: 0 0 1rem;
	font-size: clamp(.9375rem, 1.6vw, 1.125rem);
	color: rgb(255 255 255 / .8);
}

.obekt-hero__lede {
	margin: 0 0 2rem;
	max-width: 56ch;
	font-size: clamp(.9375rem, 1.5vw, 1.0625rem);
	line-height: 1.65;
	color: rgb(255 255 255 / .78);
}

.obekt-hero__stats {
	display: flex;
	flex-wrap: wrap;
	gap: 1rem clamp(1.5rem, 4vw, 3.5rem);
	margin: 0 0 2rem;
	padding: 0;
	list-style: none;
}

.obekt-hero__stats li { display: flex; flex-direction: column; gap: .15rem; }

/* Същата тежест като заглавието над тях (800) — числата са другото, заради
   което се отваря страницата, и на 300 те висяха по-тънки от етикетите си. */
.obekt-hero__stats b {
	font-size: clamp(1.375rem, 3vw, 2rem);
	font-weight: 800;
	line-height: 1.1;
	font-variant-numeric: tabular-nums;
}

/* Lifted teal — the same hue as a free cell, raised to hold on the dark scrim.
   Тежест не се задава: откакто всички числа са 800, отделното 700 тук правеше
   първото — най-важното — по-леко от съседите му. Цветът носи акцента. */
.obekt-hero__stats li:first-child b { color: #6fb6c4; }

.obekt-hero__stats span {
	font-size: .75rem;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: rgb(255 255 255 / .62);
}

.obekt-hero__cta { display: flex; flex-wrap: wrap; gap: 1rem; margin: 0; }

/* Обявен обект — вместо трите числа стои един надпис. Той заема тяхното
   място в йерархията, затова носи техния ръст, не размера на етикет. */
.obekt-hero__soon {
	margin: 0 0 1.75rem;
	font-size: .8125rem;
	font-weight: 700;
	letter-spacing: .18em;
	text-transform: uppercase;
	color: #6fb6c4;
}

/* -------------------------------------------------------------------------
   Section furniture
   ---------------------------------------------------------------------- */

.section__lede {
	margin: 0;
	font-size: clamp(.9375rem, 1.4vw, 1.0625rem);
	line-height: 1.65;
	color: var(--muted);
}

.section__aside { margin: 1.25rem 0 0; }
.section__foot { display: flex; flex-wrap: wrap; gap: 1rem; margin: 2rem 0 0; }
.section__foot--center { justify-content: center; margin-block-start: clamp(1.75rem, 3vw, 2.5rem); }
.section--board { background: var(--warm); }

/* Заглавие вляво, покана вдясно. Бутонът се подрежда по ДОЛНАТА линия на
   заглавния блок (`align-items: end`), защото окото чете заглавието отгоре
   надолу и го намира точно там, където спира. Под 52rem колоните падат една
   под друга: бутон, свит до два реда текст в 360px, е по-труден за натискане
   от същия бутон, застанал сам на ред. */
.section__head--cta { max-width: none; }
.section__head--cta > div { max-width: 62ch; }
.section__head--cta .btn { margin-block-start: 1.25rem; }
@media (min-width: 52rem) {
	.section__head--cta {
		display: grid; grid-template-columns: minmax(0, 1fr) auto;
		gap: 2.5rem; align-items: end;
	}
	.section__head--cta .btn { margin-block-start: 0; white-space: nowrap; }
}

/* -------------------------------------------------------------------------
   Етапите на строителството
   ----------------------------------------------------------------------
   Лента, не колона. Стъпките са колони в една решетка, а линията между тях е
   ЕДНА линия през цялата лента, нарисувана като фон на пътечката — не по едно
   парче на стъпка. Парчетата се чупят на последната стъпка, на празен срок и
   на всяка промяна в броя; един фон не може.

   Точката е котвата на реда: тя стои на самата линия, а текстът виси под нея.
   Затова и линията е на височината на точката (`--dot`), а не в средата на
   клетката — центърът на клетката се мести с дължината на имената, центърът на
   точката не.

   Цветовете са същите като на таблото: тюркоаз за минало и сигурно, вълна за
   днешното, кух контур за онова, което още не е станало. Червеното (--accent)
   не влиза тук — то маркира следващото ДЕЙСТВИЕ, а етапът не е действие.
   ---------------------------------------------------------------------- */

.stages { --dot: 1.125rem; }

.stages__track {
	list-style: none; margin: 0; padding: 0;
	display: grid;
	gap: 1.5rem;
}

.stages__step { position: relative; padding-inline-start: calc(var(--dot) + .9rem); }

/* Линията между две стъпки виси на ПЪРВАТА от тях и стига до точката на
   следващата — затова последната не рисува нищо. Псевдо-елемент, а не свой
   <span>: линията не влиза в дървото за достъпност и четецът на екран не спъва
   в пет празни „графики“ между етапите.

   Изминатият участък носи цвета на завършеното: лентата показва докъде е
   стигнал строежът и когато никой не чете надписите. */
.stages__step::before {
	content: ""; position: absolute;
	inset-inline-start: calc(var(--dot) / 2 - .5px);
	inset-block: calc(.15rem + var(--dot) + .3rem) calc(-1.5rem - .15rem);
	width: 1px; background: var(--sold-line);
}
.stages__step:last-child::before { display: none; }
.stages__step--done::before { background: var(--free); }

.stages__dot {
	position: absolute; inset-inline-start: 0; inset-block-start: .15rem;
	width: var(--dot); height: var(--dot);
	border: 1px solid var(--sold-line); background: var(--paper);
	display: block;
}
.stages__step--done .stages__dot { background: var(--free); border-color: var(--free); }
.stages__step--now  .stages__dot { background: var(--resv); border-color: var(--resv); }
/* Отметката е фон, не знак в текста: знак вътре в точката влиза в четеца на
   екран като буква насред изречението. */
.stages__step--done .stages__dot {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 6.4l2.6 2.6L10 3.2' fill='none' stroke='white' stroke-width='2'/%3E%3C/svg%3E");
	background-repeat: no-repeat; background-position: center; background-size: .7rem;
}
.stages__step--now .stages__dot::after {
	content: ""; position: absolute; inset: .3rem; background: #fff;
}

.stages__state {
	display: block;
	font-size: .75rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
	color: var(--sold-ink);
}
.stages__step--done .stages__state { color: var(--free); }
.stages__step--now  .stages__state { color: var(--resv); }

.stages__name {
	display: block; margin-block-start: .2rem;
	font-size: 1.0625rem; font-weight: 700; line-height: 1.3;
}
.stages__when {
	display: block; margin-block-start: .15rem;
	font-size: .875rem; color: var(--muted);
}

/* Водоравно чак когато има място за него. Пет колони в 360px дават пет
   нечетими ивици, а водоравен скрол крие точно онова, което се търси — къде е
   сега. Затова под 48rem лентата си остава колона. */
@media (min-width: 48rem) {
	.stages__track {
		grid-template-columns: repeat(var(--stages-n, 5), minmax(0, 1fr));
		gap: 0;
	}
	.stages__step {
		padding-inline: 0 1.25rem;
		padding-block-start: calc(var(--dot) + .9rem);
	}
	/* Същата линия, легнала: от центъра на точката на този етап до точката на
	   следващия. Трите оси се пренаписват изцяло, защото `inset-block` отгоре
	   държи и горе, и долу. */
	.stages__step::before {
		inset-inline: calc(var(--dot) / 2) 0;
		inset-block: calc(var(--dot) / 2 - .5px) auto;
		width: auto; height: 1px;
	}
	.stages__dot { inset-block-start: 0; }
}

.stages__note {
	margin-block-start: clamp(1.5rem, 2.6vw, 2.25rem);
	padding-block-start: 1.25rem;
	border-block-start: 1px solid var(--line);
	max-width: 70ch;
	color: var(--muted); line-height: 1.7;
}
.stages__note p { margin: 0 0 .75rem; }
.stages__note p:last-child { margin-block-end: 0; }

/* Секцията е на хартия, не на --paper-alt: в светлата гама `--paper-alt` и
   `--warm` са ЕДИН цвят (skin-light.css), тоест лентата щеше да се слее с
   таблото над нея и двата въпроса — „кое е свободно“ и „докъде е строежът“ —
   щяха да изглеждат като един. Виж коментара при `.stages__dot`: кухата точка
   се пълни със същия фон, затова и тя е на `--paper`.
 */

/* -------------------------------------------------------------------------
   The board
   ---------------------------------------------------------------------- */

.board__bar {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1rem 2rem;
	margin-block-end: 1.5rem;
}

/* Both groups ride on one line — there is room for them — and drop to their own
   rows only when the bar actually runs out of width. */
.filters { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: .625rem 2rem; }
.filters__row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }

.filters__lbl {
	min-width: 0;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--muted);
}

.chip {
	padding: .5rem 1rem;
	border: 1px solid var(--line);
	background: var(--paper);
	color: var(--text);
	font: inherit;
	font-size: .8125rem;
	cursor: pointer;
	transition: background .12s, border-color .12s, color .12s;
}

.chip:hover { border-color: var(--ink); }
.chip.is-on { background: var(--ink); border-color: var(--ink); color: #fff; }
.chip__short { display: none; }

@media (max-width: 560px) {
	.filters { flex-direction: column; align-items: stretch; gap: .625rem; }
	.filters__lbl { min-width: 100%; }
	.chip__long { display: none; }
	.chip__short { display: inline; }
	.chip { min-width: 2.5rem; text-align: center; }
}

.board__count { margin: 0; font-size: .875rem; color: var(--muted); }
.board__count b { font-size: 1.5rem; font-weight: 700; color: var(--free); }

/* Parking and garages are listed here rather than drawn on the board — see
   the note in parts/board.php. Every extra unit, priced and status-tracked,
   the same as the price list, just without the sort/search/filter chrome
   that table needs for 200 rows and this one never will. */
.board__extras {
	margin: 1.5rem 0 0;
	padding-block-start: 1.25rem;
	border-top: 1px solid var(--line);
}

.board__extras__lede {
	margin: 0 0 1rem;
	font-size: .875rem;
	color: var(--muted);
}

.board__extras__lede b { color: var(--text); }

/* Кутията е с таван, а не по височината на списъка.

   Дотогава „Бял Шоколад“ имаше 22 паркоместа и гаража и таблицата се разгъваше
   спокойно под схемата. Ценоразписът от 20.07.2026 донесе истинските 177 на
   „Пенемат“ — 15 надземни гаража, 94 подземни и 68 външни паркоместа — и
   едностраничникът на обекта се превърна в 177 реда „Гараж“, под които схемата
   на сградата, „За проекта“ и схемите на плащане останаха на четири екрана
   разстояние. Нищо не е скрито: списъкът е цял, само стои в собствен скрол.

   `overscroll-behavior: contain` — иначе допирът, стигнал до края на кутията,
   продължава да върти страницата под нея и на телефон изглежда като че екранът
   е подскочил. */
.board__extras__scroll {
	overflow: auto;
	max-height: 26rem;
	overscroll-behavior: contain;
	border: 1px solid var(--line);
	background: var(--paper);
}

.extras-table {
	width: 100%;
	min-width: 560px;
	border-collapse: collapse;
	font-size: .8125rem;
}

/* Залепено заглавие — в кутия със скрол колоните „Цена“ и „Статус“ иначе
   излизат от екрана още на десетия ред и надолу таблицата е четири колони
   числа без имена. */
.extras-table th {
	position: sticky;
	top: 0;
	z-index: 1;
	padding: .65rem .75rem;
	background: var(--ink);
	color: #fff;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	text-align: left;
	white-space: nowrap;
}

.extras-table th[data-align="r"] { text-align: right; }

.extras-table td {
	padding: .6rem .75rem;
	border-bottom: 1px solid var(--line);
	vertical-align: middle;
}

.extras-table td[data-align="r"] { text-align: right; font-variant-numeric: tabular-nums; }

.extras-table tbody tr:last-child td { border-bottom: 0; }
.extras-table tbody tr:hover td { background: color-mix(in srgb, var(--free) 12%, var(--paper)); }

.board__layout {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: clamp(1.25rem, 2.5vw, 2rem);
	align-items: start;
}

@media (min-width: 1024px) {
	.board__layout { grid-template-columns: minmax(0, 1fr) 17rem; }
}

.board__blocks {
	display: flex;
	flex-wrap: wrap;
	/* Cards size to their widest floor rather than to an equal share: a block
	   with eight units per storey and one with four are different buildings,
	   and stretching both to one width is what made the wide one wrap its
	   floors into two rows — at which point the row no longer reads as a floor. */

	/* Top, so every heading sits directly on top of the building it names.
	   The blocks are of very different heights — блок 6 is twelve storeys, блок
	   5 is six — and two alternatives were tried for the space that leaves under
	   the short ones. Bottom-aligning the cards gave a proper shared ground line
	   and scattered the headings down the page. Stretching the cards and pushing
	   only the stacks down kept the headings in a row but left блок 5's title
	   floating six floors above its own building. Both traded a real problem for
	   a worse one: the heading is the link into that block's price list, so it
	   has to stay both scannable AND attached to its block. The empty space
	   below a short building is the cheapest of the three costs. */
	align-items: flex-start;
	gap: clamp(1.5rem, 3.5vw, 3rem);
	/* Grid and flex items default to min-width:auto, which lets a wide block
	   push the whole page sideways instead of scrolling inside its own card. */
	min-width: 0;
}

.bldg {
	flex: 0 1 auto;
	max-width: 100%;
	min-width: 0;
}



.bldg__head { margin-block-end: 1rem; }

.bldg__name {
	margin: 0 0 .15rem;
	font-size: 1rem;
	font-weight: 700;
	letter-spacing: .02em;
	text-transform: uppercase;
}

/* The heading is a link into the block's price list. Underlining it would put
   a rule through a 1rem uppercase title and read as decoration; the arrow does
   the affording instead, and only moves on hover so the resting state stays a
   heading. */
.bldg__link {
	display: inline-flex;
	align-items: baseline;
	gap: .4rem;
	color: inherit;
	text-decoration: none;
}

/* Visible at rest, not on hover. „Make БЛОК 5 clickable“ was the ask, and an
   affordance that only appears once the pointer is already on it does not tell
   anyone the title is a link — least of all on a phone. */
.bldg__go {
	display: inline-block;
	font-size: .8125rem;
	color: var(--accent);
	opacity: .55;
	transition: opacity .14s, transform .14s;
}

.bldg__link:hover { color: var(--accent); }
.bldg__link:hover .bldg__go,
.bldg__link:focus-visible .bldg__go { opacity: 1; transform: translateX(.15rem); }

.bldg__link:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
}

.bldg__meta { margin: 0; font-size: .75rem; color: var(--muted); }

.bldg__free { color: var(--free); font-weight: 700; }

.bldg__nodata { color: var(--muted); }

/* ── Вход stacks ───────────────────────────────────────────────────────────
   A block is a building, a вход is a staircase, and the flats of two
   staircases do not share a floor row. Блок 5 and блок 8 get two stacks each
   under one heading; блок 6 and блок 7 get one and no вход label, so a
   single-entrance building carries no vocabulary it does not need. */
.bldg__entrances {
	display: flex;
	align-items: flex-start;
	gap: 1rem;
	min-width: 0;
}

.entr { min-width: 0; }

.entr__name {
	margin: 0 0 .4rem 2.35rem;   /* clears the floor-number gutter */
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .06em;
	text-transform: uppercase;
	color: var(--muted);
}

.board__note {
	margin: 1.25rem 0 0;
	padding-block-start: 1rem;
	border-top: 1px solid var(--line);
	font-size: .8125rem;
	line-height: 1.6;
	color: var(--muted);
	max-width: 62ch;
}

.board__note b { color: var(--text); }

/* No scroller: the cells above shrink instead. Kept as `hidden` rather than
   removed so an unforeseen wide row clips to the card instead of dragging the
   page sideways. */
.bldg__floors {
	display: flex;
	flex-direction: column;
	gap: .3rem;
	overflow-x: hidden;
}

.floor { display: flex; align-items: center; gap: .6rem; }

.floor__no {
	flex: 0 0 1.75rem;
	font-size: .6875rem;
	font-weight: 700;
	text-align: right;
	color: var(--muted);
	font-variant-numeric: tabular-nums;
}

/* minmax(0, --cell): tracks sit at the full cell size when there is room and
   shrink to fit when there is not, so a floor can never be wider than the card
   holding it. Fixed `var(--cell)` tracks were what pushed блок 8's eight-wide
   floors past the edge and handed every block its own horizontal scrollbar on
   an iPhone. */
.floor__units {
	display: grid;
	grid-template-columns: repeat(var(--cols, 6), minmax(0, var(--cell)));
	gap: 3px;
	flex: 1 1 auto;
	min-width: 0;
}

.floor--loose .floor__no { font-size: .5625rem; letter-spacing: .04em; }

/* The cell. Colour carries the status, the glyph carries the room count — two
   channels, so the board is still readable in greyscale and to a colour-blind
   buyer, who is roughly one man in twelve. */
.cell {
	display: grid;
	place-items: center;
	width: 100%;
	max-width: var(--cell);
	aspect-ratio: 1;
	border: 1px solid transparent;
	font-size: .6875rem;
	font-weight: 700;
	text-decoration: none;
	transition: transform .14s, box-shadow .14s, opacity .14s;
}

.cell__glyph { line-height: 1; }

.cell--free     { background: var(--free); color: #fff; }
.cell--reserved { background: var(--resv); color: #fff; }
.cell--inquire  { background: var(--inq); color: #fff; }

/* Hollow on purpose — a sold flat should read as absent, not as a third colour
   competing with the two that are still for sale. */
.cell--sold {
	background: transparent;
	border-color: var(--sold-line);
	color: var(--sold-ink);
}

.cell--unknown {
	background: transparent;
	border-color: var(--sold-line);
	color: var(--muted);
	border-style: dashed;
}

/* An empty cell is a flat we know exists and know nothing else about. Drawn so
   the building reads whole, and deliberately the quietest thing on the board:
   no fill, no glyph, a dotted rule lighter than продаден's. It must not read as
   a fourth status — it is the absence of one.

   This has to sit AFTER .cell, not with the other modifiers further up: both
   selectors are 0,1,0, .cell sets `border: 1px solid transparent`, and whichever
   comes last wins. Written above, the border silently vanished and all 64 cells
   rendered as holes in the facade. Same trap as README #3, without the media
   query to blame. */
.cell--empty {
	background: transparent;
	border: 1px dotted var(--sold-line);
	cursor: help;
}

/* Hover firms the dotted rule into a solid one. It used to lift the opacity,
   which no longer exists — leaving a border-color swap here would have been a
   no-op, since the cell already carries that colour. */
.cell--empty:hover { border-style: solid; }

/* A hard offset rather than a blur: the board is a flat, drawn thing and a
   soft drop-shadow makes it look like a web widget sitting on top of one. */
.cell:hover,
.cell:focus-visible {
	transform: translateY(-2px);
	box-shadow: 0 4px 0 0 var(--ink);
	outline: none;
	z-index: 2;
}

.cell.is-dim { opacity: .16; pointer-events: none; }

/* Phone AND tablet. Above 1024 the preview sidebar appears and the blocks have
   room to flow; below it they are paired by the grid further down, because
   letting them wrap freely at these widths put three blocks on one row and
   orphaned the fourth on the next. */
@media (max-width: 1023px) {

	/* With the block on its own row there is far more width than the desktop
	   cell size spends, so the cell grows to fill it.

	   The track is sized from the row's OWN width (`100%` here resolves against
	   .floor__units) divided by --cols-max — the widest floor anywhere on the
	   board, set in parts/board.php. So every block gets an identical cell and
	   the widest one lands flush with the card edge, while narrower blocks keep
	   that cell size and simply end sooner, exactly as they do on desktop.

	   Deriving it from `100vw` instead was off by the width of the scrollbar
	   gutter, which left the blocks 1–2px apart in cell size. */
	.floor__units {
		/* flex-basis 0, not auto: with `auto` the grid's inline size stays
		   indefinite, the percentage below resolves to `auto`, and the tracks
		   collapse to min-content — which is why the cells sat at 28px in a
		   305px row. A definite basis makes the percentage resolvable. */
		flex: 1 1 0%;
		/* min(46px, …): the track is capped at the same value as the cell.
		   Without it a wide tablet column gives an 86px track holding a 46px
		   cell, and the flats drift apart inside their own floor — a row of
		   islands rather than a storey. */
		grid-template-columns:
			repeat(var(--cols, 6), minmax(0, min(46px, calc(
				(100% - (var(--cols-max, 6) - 1) * 3px) / var(--cols-max, 6)
			))));
	}

	/* The desktop cap (28px at this width) would hold the cell inside the wider
	   track the rule above creates, so it is raised rather than removed. Some
	   projects have only three units on their widest floor — Пенемат 5 и 6,
	   Бял Шоколад — and with no cap at all those fill the row as 100px squares,
	   which reads as a chart, not a building. 46px is a comfortable touch
	   target and roughly the density of the desktop board. */
	.cell { max-width: 46px; }
}


/* A unit with no published price is still real stock — it reads as available
   but is marked, so nobody quotes a number that does not exist. */
.cell { position: relative; }

.cell--nopx::after {
	content: "";
	position: absolute;
	inset-block-start: 2px;
	inset-inline-end: 2px;
	width: 4px;
	height: 4px;
	border-radius: 50%;
	background: currentColor;
	opacity: .6;
}

/* ---------- preview panel ---------- */

.preview {
	position: sticky;
	top: calc(var(--header-h) + 1rem);
	display: flex;
	flex-direction: column;
	padding: 1.25rem;
	background: var(--paper);
	border: 1px solid var(--ink);
}

@media (max-width: 1023px) {
	.preview { position: static; }
	.preview__empty .preview__hint { display: none; }
}

/* Панелът има ТРИ височини: празен (легендата), единица с цена и единица без
   цена — а цената и цената на квадрат са два реда, които просто ги няма, щом
   няма число (виж `setLine` в objects.js). Докато мишката минава по схемата,
   кутията скачаше нагоре-надолу при всяко квадратче, а с нея и копчето под
   нея (Gabe, 15.09.2026: „very annoying bug… everything jumps up and down“).
   Затова височината е закована тук, върху обвивката, а не се кърпи на реда с
   цената: празното състояние е третата височина и то също участва.
   Стойността е таванът на трите, измерен в колоната от 17rem; по-високо
   съдържание (дълго име, друг език) пак я разтяга — това е под, не таван. */
@media (min-width: 1024px) {
	.preview { min-height: 28.5rem; }

	/* Копчетата „Смени статуса“ съществуват само за редактор и се появяват
	   само при ИЗБОР — за влязъл човек панелът е с един ред по-висок и без
	   този ред щеше да подскача точно както на посетител. */
	.preview:has(.pick-admin) { min-height: 35rem; }

	/* Заковаването на кутията пази ръба ѝ, но не и копчето: без цена всичко
	   над него се вдига със 79px. Затова картата се разпъва по височината на
	   панела, а пътят към единицата стои долу — на едно и също място за всяко
	   квадратче. */
	/* `:not([hidden])` не е украса: `display: flex` тук бие `[hidden] {
	   display: none }` от main.css при равна специфичност и по-късен ред, тоест
	   скритата карта щеше да се рисува върху празното състояние. */
	.preview__card:not([hidden]),
	.preview__empty:not([hidden]) {
		flex: 1 1 auto;
		display: flex;
		flex-direction: column;
	}
	.preview__card [data-p-link] { margin-block-start: auto; }

	/* Легендата е по-къса от картата и заковаването ѝ оставяше една трета
	   бяло отдолу. Изречението под нея слиза на дъното — бележка под линия,
	   а не изтървано пространство. */
	.preview__empty .preview__note { margin-block-start: auto; }
}

.preview__hint { margin: 0 0 1.25rem; font-size: .875rem; color: var(--muted); line-height: 1.5; }

.legend { display: flex; flex-direction: column; gap: .5rem; margin: 0 0 1rem; padding: 0; list-style: none; }
.legend li { display: flex; align-items: center; gap: .6rem; font-size: .8125rem; color: var(--text); }

.swatch { width: 15px; height: 15px; border: 1px solid transparent; flex: none; }
.swatch--free { background: var(--free); }
.swatch--reserved { background: var(--resv); }
.swatch--inquire { background: var(--inq); }
.swatch--sold { background: transparent; border-color: var(--sold-line); }
.swatch--unknown { background: transparent; border-color: var(--sold-line); border-style: dashed; }
/* After .swatch for the same reason .cell--empty is after .cell. */
.swatch--empty { background: transparent; border-color: var(--sold-line); border-style: dotted; }

.preview__note { margin: 0; font-size: .75rem; color: var(--muted); line-height: 1.5; }

.preview__code { margin: 0 0 .1rem; font-size: 1.25rem; font-weight: 700; }
.preview__rooms { margin: 0 0 .6rem; font-size: .875rem; color: var(--muted); }

.preview__status,
.sheet__status {
	display: inline-block;
	margin: 0 0 1rem;
	padding: .3rem .7rem;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.preview__status.is-free, .sheet__status.is-free { background: var(--free); color: #fff; }

.preview__status.is-reserved, .sheet__status.is-reserved {
	background: color-mix(in srgb, var(--resv) 26%, #fff); color: #6b4a15;
}

.preview__status.is-inquire, .sheet__status.is-inquire {
	background: color-mix(in srgb, var(--inq) 16%, #fff); color: var(--inq);
}

.preview__status.is-sold, .sheet__status.is-sold {
	background: transparent; color: var(--sold-ink);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sold-line) 70%, transparent);
}

.preview__status.is-unknown, .sheet__status.is-unknown { background: var(--paper-alt); color: var(--muted); }

.preview__specs,
.sheet__specs { margin: 0 0 1rem; display: flex; flex-direction: column; gap: .4rem; }

.preview__specs > div,
.sheet__specs > div {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding-block-end: .4rem;
	border-bottom: 1px solid var(--line);
	font-size: .8125rem;
}

.preview__specs dt, .sheet__specs dt { color: var(--muted); }
.preview__specs dd, .sheet__specs dd { margin: 0; font-variant-numeric: tabular-nums; font-weight: 700; }

.preview__price, .sheet__price { margin: 0; font-size: 1.5rem; font-weight: 300; font-variant-numeric: tabular-nums; }
.preview__rate, .sheet__rate { margin: .1rem 0 1rem; font-size: .75rem; color: var(--muted); }

/* ---------- touch sheet ---------- */

.sheet {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: 60;
	padding: var(--gutter);
	background: var(--paper);
	border-top: 2px solid var(--ink);
	box-shadow: 0 -8px 32px rgb(0 0 0 / 18%);
	animation: sheet-up .16s ease-out;
}

@keyframes sheet-up { from { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) { .sheet { animation: none; } }
@media (min-width: 1024px) { .sheet { display: none; } }

.sheet__panel { position: relative; width: min(var(--maxw), 100%); margin-inline: auto; }

.sheet__close {
	position: absolute;
	inset-block-start: -.25rem;
	inset-inline-end: 0;
	width: 2.25rem;
	height: 2.25rem;
	border: 0;
	background: none;
	font-size: 1.75rem;
	line-height: 1;
	cursor: pointer;
	color: var(--muted);
}

.sheet__code { margin: 0 0 .1rem; font-size: 1.25rem; font-weight: 700; }
.sheet__rooms { margin: 0 0 .6rem; font-size: .875rem; color: var(--muted); }
.btn--block { display: flex; justify-content: center; width: 100%; }

/* -------------------------------------------------------------------------
   The table
   ---------------------------------------------------------------------- */

.utable__bar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .75rem 1.5rem;
	margin-block-end: 1.25rem;
}

.utable__search input {
	min-width: min(22rem, 70vw);
	padding: .7rem .9rem;
	border: 1px solid var(--line);
	background: var(--paper);
	font: inherit;
	font-size: .875rem;
}

.utable__search input:focus { outline: 2px solid var(--ink); outline-offset: -2px; }
.utable__chips { display: flex; flex-wrap: wrap; gap: .5rem; }
.utable__count { margin: 0 0 0 auto; font-size: .8125rem; color: var(--muted); }

/* Keeping the scrollbar gutter reserved stops the page shifting sideways when a
   filter shortens the list enough for the vertical scrollbar to disappear. */
html { scrollbar-gutter: stable; }

/* Блок and Тип sit on one shared line — two filter groups, not two rows.
   The line itself wraps to a second row only once combined width runs out;
   each group wraps its own chips first. */
.utable__filterline {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .625rem 2rem;
	margin-block-end: 1rem;
}

.utable__blocks {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .5rem;
}

.tablescroll {
	overflow-x: auto;
	border: 1px solid var(--line);
	background: var(--paper);
}

table.utable {
	width: 100%;
	/* Fixed, not auto — see the note in parts/unit-table.php. Column widths come
	   from the header cells and never depend on which rows are visible, so
	   filtering and searching no longer make the table jump. */
	table-layout: fixed;
	min-width: 1080px;
	border-collapse: collapse;
	font-size: .875rem;
}

.utable__th {
	position: sticky;
	top: 0;
	z-index: 3;
	padding: 0;
	background: var(--ink);
	color: #fff;
	white-space: nowrap;
}

.utable__sort {
	display: flex;
	align-items: center;
	gap: .4rem;
	width: 100%;
	padding: .85rem .75rem;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	cursor: pointer;
}

.utable__th--r .utable__sort { justify-content: flex-end; }
.utable__th--c .utable__sort { justify-content: center; }
.utable__sort:hover { background: rgb(255 255 255 / .1); }

.utable__arrow {
	width: 0;
	height: 0;
	border-inline: 4px solid transparent;
	border-block-end: 5px solid currentColor;
	opacity: 0;
}

.utable__th[aria-sort="ascending"] .utable__arrow { opacity: 1; }
.utable__th[aria-sort="descending"] .utable__arrow { opacity: 1; transform: rotate(180deg); }

.utable__td { padding: .7rem .75rem; border-bottom: 1px solid var(--line); background: var(--row-bg); }
.utable__td--r { text-align: right; font-variant-numeric: tabular-nums; }
.utable__td--c { text-align: center; font-variant-numeric: tabular-nums; }

/* The row carries the status, not a shouting chip. Free is the clean,
   full-contrast row marked only by a teal edge — „nothing in the way“.
   Reserved takes a warm wash. Sold recedes on three axes at once: cool ground,
   muted text, no edge. Straight from the Пенемат table, whose own note explains
   why: a solid orange „Резервиран“ next to a solid teal „Свободен“ made the two
   compete and pulled the eye toward the flats you cannot buy. */
.utable tbody tr {
	--row-bg: var(--paper);
	--row-edge: transparent;
	background: var(--row-bg);
}

.utable tbody tr.is-free { --row-edge: var(--free); }

.utable tbody tr.is-reserved {
	--row-bg: color-mix(in srgb, var(--resv) 13%, var(--paper));
	--row-edge: var(--resv);
}

.utable tbody tr.is-sold {
	--row-bg: color-mix(in srgb, var(--ink) 8%, var(--paper));
	color: color-mix(in srgb, var(--ink) 48%, var(--paper));
}

.utable tbody tr:hover {
	--row-bg: color-mix(in srgb, var(--free) 18%, var(--paper));
	--row-edge: var(--ink);
}

.utable tbody tr:hover .utable__td { color: var(--ink); }

/* The edge lives on the first cell rather than on the row, so it survives the
   cell backgrounds painting over it. */
.utable__row > .utable__td:first-child {
	box-shadow: inset 3px 0 0 0 var(--row-edge);
}

.utable__row.is-hidden { display: none; }

.utable__code { font-weight: 700; color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--line); }
.utable__code:hover { border-color: var(--accent); color: var(--accent); }

.utable__kind { display: block; font-size: .6875rem; color: var(--muted); }

/* Fixed columns cannot grow for a long value, so anything unexpected wraps
   inside its cell rather than spilling over the next one. */
.utable__td { overflow-wrap: anywhere; }
.utable__td--r, .utable__td--c { overflow-wrap: normal; }

.pill {
	display: inline-block;
	padding: .25rem .6rem;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .04em;
	white-space: nowrap;
}

/* Weight tracks importance: the loudest chip is always the one you can act on.
   Free is the only solid fill, reserved is a tint, sold is barely there. */
.pill--free { background: var(--free); color: #fff; }

.pill--reserved {
	background: color-mix(in srgb, var(--resv) 26%, #fff);
	color: #6b4a15;
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--resv) 55%, transparent);
}

.pill--inquire {
	background: color-mix(in srgb, var(--inq) 16%, #fff);
	color: var(--inq);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--inq) 45%, transparent);
}

.pill--sold {
	background: transparent;
	color: var(--sold-ink);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sold-line) 70%, transparent);
}

.pill--unknown { background: transparent; color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); }

.utable__go {
	font-size: .75rem;
	font-weight: 700;
	color: var(--accent);
	text-decoration: none;
	white-space: nowrap;
}

.utable__go:hover { text-decoration: underline; }
.utable__empty { margin: 1.5rem 0 0; color: var(--muted); }

/* -------------------------------------------------------------------------
   Payment schemes + facts
   ---------------------------------------------------------------------- */

.schemes {
	display: grid;
	gap: 1rem;
	margin: 1.5rem 0 0;
	padding: 0;
	list-style: none;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

.schemes__item {
	padding: 1.25rem 1.5rem;
	background: var(--paper);
	border: 1px solid var(--line);
	border-left: 3px solid var(--accent);
}

.schemes__no {
	display: block;
	margin-block-end: .35rem;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--accent);
}

.schemes__text { font-size: .9375rem; line-height: 1.6; }

.factgrid {
	display: grid;
	gap: 0 clamp(1.5rem, 4vw, 3rem);
	margin: 0;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}

.factgrid__row {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding-block: .8rem;
	border-bottom: 1px solid var(--line);
}

.factgrid dt { color: var(--muted); font-size: .875rem; }
.factgrid dd { margin: 0; font-weight: 700; font-size: .875rem; text-align: right; }

.obekt__body { margin-block-start: 2rem; max-width: 68ch; line-height: 1.7; }

.obekt__source {
	width: min(var(--maxw), 100%);
	margin: 0 auto;
	padding: 1.5rem var(--gutter) 3rem;
	font-size: .75rem;
	color: var(--muted);
	line-height: 1.6;
}

.obekt__source a { color: inherit; }

/* -------------------------------------------------------------------------
   Contact band
   ---------------------------------------------------------------------- */

.obekt-cta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 1.75rem 2rem;
}

.obekt-cta__title { margin: 0 0 .5rem; font-size: clamp(1.5rem, 3vw, 2.25rem); font-weight: 300; }
.obekt-cta__lede { margin: 0; max-width: 46ch; color: rgb(255 255 255 / .72); line-height: 1.6; }
.obekt-cta__actions { display: flex; flex-wrap: wrap; gap: .75rem; }

/* -------------------------------------------------------------------------
   Archive — Текущи обекти
   ---------------------------------------------------------------------- */

/* Главата е СВИТА (Gabe, 04.09.2026: „the white space is criminal“).
   Дотук над пътеката стояха 4rem празно, под нея табовете, а списъкът тръгваше
   след още 3.5rem — първият обект падаше под линията на сгъване на лаптоп.

   Горното поле е нула, а пътеката се мести НАДЯСНО вместо надолу: червеният
   блок с логото виси ~42px под лентата и оставя точно толкова висока ивица,
   в която дотук нямаше нищо. Пътеката е един ред от 17px и се побира в нея.
   Отместването е `--brand-w` + разстоянието на самата лента, тоест числото
   иде от логото и се движи с него — виж :root в main.css. */
.obekti-head {
	/* Нула отгоре: разчистването на надвисналото лого го носи самата пътека —
	   `.crumbs--top` в main.css. */
	padding-block: 0 clamp(1rem, 2vw, 1.5rem);
	background: var(--paper-alt);
	border-bottom: 1px solid var(--line);
}

/* ⚠️ `.crumbs` носи `margin-block-end: 1.5rem` от main.css — там пътеката стои
   в поток и си пази въздух под себе си. Тук тя е ред във flex-контейнер и
   разстоянието го дава `gap`; двете се събираха в 44px между пътеката и
   табовете. */
.obekti-head .crumbs { margin-block-end: 0; }

/* Числата са с тегло 300 и до 36px — при подразбираща се височина на реда
   всяко носи по 18px въздух над цифрата, който изглежда като поле и се
   опитваш да го махнеш от полетата, където го няма. */
.obekti-head__stats b { line-height: 1.1; }

.obekti-head__inner,
.obekti-grid {
	width: min(var(--maxw), 100%);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

/* Boxed, not underlined: these read as the page's primary switch now that the
   H1 is gone, so they need button-strength click affordance, not a quiet
   text-link treatment. Fill/border echo .pfilter--chips; ink for the active
   state, never accent — accent is reserved for the next action, not a status
   the visitor is already looking at. */
.obekti-head__tabs { display: flex; flex-wrap: wrap; gap: .75rem; margin: 0 0 1.75rem; }
/* Свити на 04.09.2026 заедно с останалата глава. Бяха `1rem 1.75rem` при 1rem
   шрифт — два бутона за 539px. Заедно с числата това е 1251px в 1200px поле,
   тоест на лаптоп числата падаха на свой ред и главата растеше със 100px без
   да е добавила нищо. */
.obekti-head__tabs a {
	display: inline-flex; align-items: center;
	padding: .8rem 1.35rem;
	font-size: .9375rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
	color: var(--text); text-decoration: none;
	background: var(--paper); border: 1px solid var(--line);
	transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.obekti-head__tabs a:hover { border-color: var(--ink); }
.obekti-head__tabs a[aria-current="page"] { background: var(--ink); color: #fff; border-color: var(--ink); }

.obekti-head__stats { display: flex; flex-wrap: wrap; gap: 1rem clamp(1.5rem, 4vw, 3rem); margin: 0; padding: 0; list-style: none; }
.obekti-head__stats li { display: flex; flex-direction: column; }
.obekti-head__stats b { font-size: clamp(1.5rem, 3vw, 2.25rem); font-weight: 300; font-variant-numeric: tabular-nums; }
.obekti-head__stats li:nth-child(2) b { color: var(--free); font-weight: 700; }
.obekti-head__stats span { font-size: .75rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }

.obekti-grid {
	display: grid;
	gap: clamp(1.25rem, 2.5vw, 2rem);
	padding-block: clamp(2rem, 4vw, 3.5rem);
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
}

.ocard {
	display: flex;
	flex-direction: column;
	background: var(--paper);
	border: 1px solid var(--line);
	overflow: hidden;
}

.ocard__media {
	position: relative;
	display: block;
	aspect-ratio: 16 / 10;
	overflow: hidden;
	background: var(--paper-alt);
}

/* Out of flow on purpose. An in-flow img with height:100% resolves against a
   parent whose height comes from aspect-ratio — circular, so the browser drops
   the ratio and the card takes the photo's own proportions instead. That is
   what left the four renders at four different heights in one row. */
.ocard__media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.ocard__phase {
	position: absolute;
	inset-block-start: 0;
	inset-inline-start: 0;
	padding: .4rem .8rem;
	background: var(--accent);
	color: #fff;
	font-size: .625rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
}

.ocard__body { display: flex; flex-direction: column; flex: 1; padding: 1.5rem; }
.ocard__title { margin: 0 0 .25rem; font-size: 1.25rem; font-weight: 700; letter-spacing: -.01em; }
.ocard__title a { color: inherit; text-decoration: none; }
.ocard__title a:hover { color: var(--accent); }
.ocard__loc { margin: 0 0 .75rem; font-size: .8125rem; color: var(--muted); }
.ocard__lede { margin: 0 0 1.25rem; font-size: .875rem; line-height: 1.6; color: var(--muted); }

.ocard__avail { margin-block-end: 1rem; }
.ocard__bar { height: 6px; background: #e9e9e7; overflow: hidden; }
.ocard__fill { display: block; height: 100%; background: var(--free); }
.ocard__free { margin: .5rem 0 0; font-size: .8125rem; color: var(--muted); }
.ocard__free b { font-size: 1.125rem; color: var(--free); }
/* Знаменателят е дроб, не изречение: „79 свободни / 177“. По-блед от думата
   пред него — той е мащабът, не новината, и окото трябва да спре на 79.
   `nowrap` държи чертата и числото заедно, ако редът се пречупи. */
.ocard__free-tot { white-space: nowrap; color: var(--line-ink, #9a9a95); }

.ocard__facts { display: flex; flex-wrap: wrap; gap: .5rem 1.5rem; margin: 0 0 1.25rem; }
.ocard__facts > div { display: flex; flex-direction: column; }
.ocard__facts dt { font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.ocard__facts dd { margin: 0; font-size: .9375rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.ocard__actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: auto 0 0; }
.ocard__actions .btn { padding: .8rem 1.25rem; font-size: .6875rem; }
.ocard__actions .btn--ghost { color: var(--ink); border-color: var(--line); }
.ocard__actions .btn--ghost:hover { background: var(--ink); color: #fff; border-color: var(--ink); }

/* Обект „очаквайте скоро“: няма лента, няма факти, няма бутон към наличности.
   Картата държи същата височина като съседките си, за да не се разкъса редът
   в мрежата — затова текстът е с `margin: auto 0` и бута действията надолу. */
/* Без изображение медията е просто сива дупка и картата изглежда счупена.
   Диагоналната щриховка казва „тук ще има снимка“, вместо да мълчи. */
.ocard--soon .ocard__media {
	min-height: 13rem;
	background:
		repeating-linear-gradient(
			135deg,
			var(--paper-alt) 0 10px,
			#ececea 10px 20px
		);
}
.ocard--soon .ocard__media img { filter: saturate(.35); }
.ocard__phase--soon { background: var(--ink); }
.ocard__soon {
	margin: 0 0 1.25rem;
	font-size: .875rem;
	line-height: 1.6;
	color: var(--muted);
}

.obekti-empty { padding: 3rem var(--gutter); color: var(--muted); }

/* -------------------------------------------------------------------------
   Print — the table is the thing people print
   ---------------------------------------------------------------------- */

@media print {
	.site-header, .site-footer, .obekt-hero__cta, .utable__bar,
	.section__aside, .section__foot, .sheet, .preview, .drawer, .lbox { display: none !important; }
	.obekt-hero { min-height: auto; color: #000; background: none; }
	.obekt-hero__scrim { display: none; }
	.crumbs { display: none; }
	.obekt-hero__title, .obekt-hero__loc, .obekt-hero__stats { color: #000; }
	.tablescroll { overflow: visible; border: 0; }
	.utable__th { position: static; background: #000 !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
	.utable { font-size: 9pt; }
	.utable__row { break-inside: avoid; }
	.utable-preview { max-height: none; }
	.utable-preview__fade { display: none; }
}

/* -------------------------------------------------------------------------
   Unit page
   ---------------------------------------------------------------------- */

/* Tokens come from the shared block at the top — .unit is listed there too. */
/* Нула отгоре по същата причина като при „Обекти“: пътеката носи собственото
   си разчистване и над 1101px стои вдясно от логото, вместо да бута заглавието
   на гаража надолу. */
.unit { padding-block: 0 clamp(3rem, 6vw, 5rem); }

.crumbs--light { color: var(--muted); }
.crumbs--light a { border-bottom-color: var(--line); }
.crumbs--light [aria-current] { color: var(--ink); }

.unit__grid { display: grid; gap: clamp(1.5rem, 3vw, 3rem); align-items: start; }

@media (min-width: 900px) {
	.unit__grid { grid-template-columns: minmax(0, 1fr) 20rem; }
}

.unit__eyebrow {
	margin: 0 0 .35rem;
	font-size: .75rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--accent);
}

.unit__title {
	margin: 0 0 1rem;
	font-size: clamp(2rem, 5vw, 3.25rem);
	font-weight: 300;
	letter-spacing: -.02em;
}

.unit__status { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; margin: 0 0 2rem; }
.unit__inferred { font-size: .75rem; color: var(--muted); max-width: 40ch; line-height: 1.5; }

.unit__specs {
	display: grid;
	gap: 0 clamp(1.5rem, 4vw, 3rem);
	margin: 0 0 2rem;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.unit__spec {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding-block: .8rem;
	border-bottom: 1px solid var(--line);
}

.unit__spec dt { color: var(--muted); font-size: .875rem; }
.unit__spec dd { margin: 0; font-size: .875rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.unit__plan { margin: 0 0 2.5rem; }
/* Планът е висок и тесен (портрет ~380x850), затова се ограничава по височина,
   не по ширина — иначе изяжда цял екран и бута цената под линията на сгъване. */
.unit__plan-img { display: block; max-width: 100%; height: auto; max-height: 70vh;
                  width: auto; margin: 0 auto; background: #fff; border-radius: 6px;
                  border: 1px solid var(--line, #e4e4e4); padding: .5rem; }
.unit__plan a { display: block; }

.unit__h2 {
	margin: 2.5rem 0 1rem;
	font-size: clamp(1.125rem, 2vw, 1.5rem);
	font-weight: 700;
	letter-spacing: -.01em;
}

.unit__h2 + .schemes { margin-top: 0; }

.unit__siblings {
	display: grid;
	gap: .5rem;
	margin: 0;
	padding: 0;
	list-style: none;
	grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
}

.sib {
	display: grid;
	gap: .15rem;
	padding: .8rem 1rem;
	border: 1px solid var(--line);
	border-left: 3px solid var(--line);
	background: var(--paper);
	text-decoration: none;
	color: var(--text);
}

.sib:hover { border-color: var(--ink); }
.sib b { font-size: 1rem; }
.sib span { font-size: .75rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.sib__status { font-weight: 700; }

.sib--free { border-left-color: var(--free); }
.sib--free .sib__status { color: var(--free); }
.sib--reserved { border-left-color: var(--resv); }
.sib--reserved .sib__status { color: var(--resv); }
.sib--sold { border-left-color: var(--sold-line); opacity: .72; }
.sib--sold .sib__status { color: var(--sold-ink); }

.unit__side { display: grid; gap: 1rem; position: sticky; top: calc(var(--header-h) + 1rem); }

@media (max-width: 899px) { .unit__side { position: static; } }

.unit__card { padding: 1.5rem; background: var(--paper-alt); border: 1px solid var(--line); }
.unit__card--quiet { background: var(--paper); }

.unit__price { margin: 0; font-size: clamp(1.75rem, 4vw, 2.5rem); font-weight: 300; font-variant-numeric: tabular-nums; }
.unit__price--none { font-size: 1.25rem; color: var(--muted); }
.unit__rate { margin: .15rem 0 0; font-size: .8125rem; color: var(--muted); }
.unit__price2 { margin: .5rem 0 0; font-size: .9375rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.unit__price2 span { font-weight: 400; color: var(--muted); font-size: .75rem; }

.unit__cta { margin: 1.5rem 0 1rem; }

.unit__contact { margin: 0; padding: 0; list-style: none; display: grid; gap: .35rem; }
.unit__contact a { font-size: .875rem; color: var(--text); text-decoration: none; border-bottom: 1px solid var(--line); }
.unit__contact a:hover { border-color: var(--accent); color: var(--accent); }

.unit__back-lbl { margin: 0 0 .25rem; font-size: .6875rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.unit__back-name { margin: 0 0 .5rem; font-size: 1.0625rem; font-weight: 700; }
.unit__back-name a { color: inherit; text-decoration: none; }
.unit__back-name a:hover { color: var(--accent); }
.unit__back-free { margin: 0 0 .75rem; font-size: .8125rem; color: var(--muted); }
.unit__back-free b { color: var(--free); font-size: 1rem; }
.unit__back-links { display: flex; gap: 1rem; margin: 0; }
.unit__back-links a { font-size: .8125rem; font-weight: 700; color: var(--accent); text-decoration: none; }
.unit__back-links a:hover { text-decoration: underline; }

/* -------------------------------------------------------------------------
   За проекта — the client's own description
   ---------------------------------------------------------------------- */

/* Секцията, която замества схемата и таблицата, докато обектът няма единици. */
.section--soon { background: var(--paper-alt); }
.soon { max-width: 46rem; text-align: center; margin-inline: auto; }
.soon__title {
	margin: 0 0 1rem;
	font-size: clamp(1.5rem, 3vw, 2rem);
	font-weight: 700;
	letter-spacing: -.01em;
}
.soon__lede { margin: 0 auto 2rem; max-width: 54ch; line-height: 1.6; color: var(--muted); }
.soon__actions { display: flex; flex-wrap: wrap; gap: .75rem; justify-content: center; margin: 0; }

.section--about { background: var(--paper); }

/* =========================================================================
   Визуализации — рендерите на обекта
   =========================================================================
   Контактен лист, не албум. Дванайсет рендера, разгърнати на половин екран
   всеки, са три екрана скрол преди схемите на плащане — купувачът или се
   отказва по средата, или така и не стига до цените. Малките еднакви плочки
   се обхождат наведнъж, а кадърът се гледа едър чак когато някой го поиска.

   `auto-fill` с `minmax`, а не фиксиран брой колони: мрежата сама решава
   колко плочки на ред според ширината — шест на настолен екран, три на
   телефон, — без нито един брейкпойнт. `min(11rem, 100%)` е предпазителят
   за много тесни екрани, където 11rem вече не се събира и голото `minmax`
   би препълнило контейнера.

   Нулев радиус и никакъв кант — рамка около рендер добавя втора сграда около
   първата. Разстоянието между кадрите е единственото, което ги разделя. */
.shots {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr));
	gap: 1px;
	background: var(--line);
	border: 1px solid var(--line);
	list-style: none;
	margin: 0;
	padding: 0;
}

.shots__item { margin: 0; background: var(--paper); }

/* Бутонът е плочката: цялата площ е мишена, а не само картинката вътре.
   `display:block` не е излишно — <img> е inline, а inline кутия оставя ред за
   долната издънка на буквите под себе си — три-четири пиксела чужд фон под
   всеки кадър, точно там, където мрежата има 1px линия. Капанът е №2 в README. */
.shots__btn {
	display: block;
	position: relative;
	width: 100%;
	padding: 0;
	border: 0;
	background: none;
	cursor: zoom-in;
	overflow: hidden;
	color: inherit;
	font: inherit;
}

/* 4:3 за всички плочки, независимо че оригиналите не са с еднакви пропорции:
   Цветея (`cveteya/02.jpg`) е 1600×1600 — КВАДРАТ. Мрежа от еднакви правоъгълници
   се чете като списък; мрежа от различни се чете като бъркотия. Рязането е
   допустимо тук именно защото плочката не е кадърът — тя е входът към него. */
.shots__btn img {
	display: block;
	width: 100%;
	height: 100%;
	aspect-ratio: 4 / 3;
	object-fit: cover;
	transition: transform .3s ease, opacity .2s ease;
}

.shots__btn:hover img,
.shots__btn:focus-visible img { transform: scale(1.06); }

/* Лупата казва, че плочката се отваря — иначе курсорът е единственият знак,
   а на телефон курсор няма. Стои постоянно, но е сдържана; на hover се
   обръща в акцента. */
.shots__zoom {
	position: absolute;
	inset-block-end: .5rem;
	inset-inline-end: .5rem;
	display: grid;
	place-items: center;
	width: 1.75rem;
	height: 1.75rem;
	background: rgb(22 24 28 / .55);
	color: #fff;
	opacity: 0;
	transform: translateY(.25rem);
	transition: opacity .2s ease, transform .2s ease, background-color .2s ease;
	pointer-events: none;
}

.shots__zoom svg { width: 1rem; height: 1rem; display: block; }

.shots__btn:hover .shots__zoom,
.shots__btn:focus-visible .shots__zoom { opacity: 1; transform: none; }
.shots__btn:hover .shots__zoom { background: var(--accent); }

/* На тъч няма hover: лупата не бива да е скрита зад състояние, което този
   екран не може да произведе. */
@media (hover: none) {
	.shots__zoom { opacity: 1; transform: none; }
	.shots__btn:hover img { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
	.shots__btn img, .shots__zoom { transition: none; }
	.shots__btn:hover img, .shots__btn:focus-visible img { transform: none; }
}

/* Без JavaScript плочките не правят нищо, затова под мрежата стоят истински
   връзки към файловете. Виждат се само в <noscript>. */
.shots__fallback {
	margin: 1rem 0 0;
	font-size: .8125rem;
	line-height: 2;
	color: var(--muted);
}
.shots__fallback a { margin-inline-end: 1rem; }

/* =========================================================================
   Лайтбокс — кадърът, отворен едър
   =========================================================================
   Нативен <dialog>: Esc, капанът за фокуса и inert-ът върху страницата отдолу
   идват от браузъра. Стилизира се само видът.

   Фонът е мастилото на сайта с прозрачност, а не черно: черното е чужд цвят
   тук и прави от панела чужд продукт, залепен върху страницата. */
.lbox {
	width: 100vw;
	max-width: 100vw;
	height: 100dvh;
	max-height: 100dvh;
	margin: 0;
	padding: 0;
	border: 0;
	background: rgb(22 24 28 / .96);
	color: #fff;
	overflow: hidden;
}

.lbox::backdrop { background: rgb(22 24 28 / .9); }

.lbox[open] {
	display: grid;
	grid-template-rows: auto minmax(0, 1fr) auto;
}

@media (prefers-reduced-motion: no-preference) {
	.lbox[open] { animation: lbox-in .18s ease-out; }
	@keyframes lbox-in { from { opacity: 0; } }
}

/* ---------- лента отгоре ---------- */

.lbox__bar {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	padding: .75rem clamp(.75rem, 2vw, 1.25rem);
}

.lbox__count {
	margin: 0;
	font-size: .8125rem;
	font-variant-numeric: tabular-nums;
	letter-spacing: .06em;
	color: rgb(255 255 255 / .55);
}
.lbox__count b { color: #fff; font-weight: 700; }

.lbox__close {
	display: grid;
	place-items: center;
	width: 2.5rem;
	height: 2.5rem;
	flex: none;
	padding: 0;
	border: 0;
	background: rgb(255 255 255 / .1);
	color: #fff;
	cursor: pointer;
	transition: background-color .2s ease;
}
.lbox__close:hover { background: var(--accent); }
.lbox__close svg { width: 1.25rem; height: 1.25rem; display: block; }

/* ---------- кадърът ---------- */

.lbox__stage {
	position: relative;
	display: grid;
	grid-template-columns: auto minmax(0, 1fr) auto;
	align-items: center;
	gap: clamp(.25rem, 1.5vw, 1rem);
	min-height: 0;
	padding-inline: clamp(.25rem, 1.5vw, 1rem);
}

.lbox__fig {
	margin: 0;
	min-width: 0;
	min-height: 0;
	height: 100%;
	display: grid;
	grid-template-rows: minmax(0, 1fr) auto;
	justify-items: center;
	gap: .75rem;
}

/* `contain`, не `cover`: тук кадърът е съдържанието, а не входът към него —
   нищо не се реже, каквито и да са пропорциите на файла. */
.lbox__img {
	max-width: 100%;
	max-height: 100%;
	width: auto;
	height: auto;
	object-fit: contain;
	margin: auto;
	transition: opacity .18s ease;
}
.lbox.is-loading .lbox__img { opacity: .35; }

.lbox__cap {
	max-width: 60ch;
	font-size: .875rem;
	line-height: 1.5;
	text-align: center;
	color: rgb(255 255 255 / .7);
}

.lbox__nav {
	display: grid;
	place-items: center;
	width: clamp(2.25rem, 5vw, 3rem);
	height: clamp(2.25rem, 5vw, 3rem);
	flex: none;
	padding: 0;
	border: 0;
	background: rgb(255 255 255 / .1);
	color: #fff;
	cursor: pointer;
	transition: background-color .2s ease;
}
.lbox__nav:hover { background: var(--accent); }
.lbox__nav svg { width: 1.5rem; height: 1.5rem; display: block; }

/* ---------- лентата с умаленията ---------- */

.lbox__strip {
	display: flex;
	gap: 2px;
	overflow-x: auto;
	overscroll-behavior-inline: contain;
	scrollbar-width: thin;
	scrollbar-color: rgb(255 255 255 / .3) transparent;
	padding: .75rem clamp(.75rem, 2vw, 1.25rem);
}

.lbox__thumb {
	flex: none;
	width: 4.5rem;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	opacity: .45;
	transition: opacity .2s ease;
}
.lbox__thumb img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.lbox__thumb:hover { opacity: .8; }

/* Текущият кадър се маркира с плътност и с чертата на акцента отдолу — само
   с непрозрачност разликата се губи на светъл кадър. */
.lbox__thumb.is-on {
	opacity: 1;
	box-shadow: inset 0 -3px 0 0 var(--accent);
}

/* Един кадър няма между какво да се обхожда. */
.lbox.is-single .lbox__nav,
.lbox.is-single .lbox__strip,
.lbox.is-single .lbox__count { display: none; }

/* Телефонът получава по-малко хром: стрелките се местят върху кадъра, за да
   не крадат от ширината му, а лентата с умаленията отпада — палецът така или
   иначе прелиства със замах. */
@media (max-width: 40rem) {
	.lbox__stage { grid-template-columns: minmax(0, 1fr); padding-inline: .5rem; }
	/* Върху кадъра, не до него: полупрозрачното бяло изчезва върху светло
	   небе, каквото са повечето рендери. Тук фонът е мастилото. */
	.lbox__nav {
		position: absolute;
		inset-block-start: 50%;
		transform: translateY(-50%);
		z-index: 1;
		background: rgb(22 24 28 / .6);
	}
	.lbox__nav--prev { inset-inline-start: .25rem; }
	.lbox__nav--next { inset-inline-end: .25rem; }
	.lbox__strip { display: none; }
}

/* =========================================================================
   Какво има наоколо — точките на интерес около обекта
   =========================================================================
   Тонът е трети, а не повторение: „схемата на сградата“ отгоре е топъл
   (--warm), „За проекта“ е бял, а тази секция е студено сиво (--paper-alt).
   Три различни фона една след друга, така че окото вижда три отделни теми,
   а „Схеми на плащане“ отдолу пак пада върху бяло. */
.section--location { background: var(--paper-alt); }

.poi {
	display: grid;
	gap: 1px;
	background: var(--line);
	grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
	border: 1px solid var(--line);
}

.poi__cat { background: var(--paper); padding: 1.5rem clamp(1.25rem, 2vw, 1.75rem); }

.poi__title {
	display: flex;
	align-items: center;
	gap: .6rem;
	margin: 0 0 1rem;
	padding-block-end: .85rem;
	border-block-end: 1px solid var(--line);
	font-size: .8125rem;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
}

.poi__icon { display: block; width: 20px; height: 20px; color: var(--accent); flex: none; }
.poi__icon svg { width: 100%; height: 100%; display: block; }

/* „от 12“ — че списъкът е рязан, а не че толкова има в квартала. */
.poi__more {
	margin-inline-start: auto;
	font-size: .6875rem;
	font-weight: 400;
	letter-spacing: .04em;
	text-transform: none;
	color: var(--muted);
}

.poi__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .7rem; }

.poi__item {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	font-size: .9375rem;
	line-height: 1.4;
}

.poi__name { min-width: 0; }
.poi__kind { display: block; font-size: .75rem; color: var(--muted); margin-block-start: .1rem; }

/* Числата се четат в колона, затова са с еднаква ширина на цифрите и се
   подравняват вдясно — окото сравнява разстояния, не имена. */
.poi__dist { flex: none; text-align: right; font-variant-numeric: tabular-nums; }
.poi__dist b { display: block; font-size: .9375rem; font-weight: 700; }
.poi__dist span { display: block; font-size: .75rem; color: var(--muted); }

.poi__source {
	margin: 1.5rem 0 0;
	max-width: 70ch;
	font-size: .8125rem;
	line-height: 1.6;
	color: var(--muted);
}



/* Named `pdesc`, not `about`: main.css already owns `.about` as a two-column
   grid for the homepage section of that name, and inheriting it silently split
   these paragraphs into ragged halves.

   An editorial grid rather than CSS `columns` — multi-column flowed six short
   paragraphs into uneven halves and would not let the opening line span. This
   puts the heading and the address in the margin and keeps the prose in one
   measured column. */
.pdesc { display: grid; gap: clamp(1.25rem, 3vw, 3.5rem); align-items: start; }

@media (min-width: 900px) {
	/* Втората колона е `1fr`, а не фиксирана мярка: текстът заема цялата
	   останала ширина по изрично искане на Gabe (2026-08-22). Дотогава тук
	   стоеше `38rem` и вдясно оставаше неизползвано поле — редът се четеше на
	   ~59 знака, но секцията изглеждаше недовършена.

	   `minmax(0, 1fr)`, не голо `1fr`: голото е `minmax(auto, 1fr)` и дълга
	   неразделима дума (URL, идентификатор на парцел) би разпънала колоната
	   отвъд контейнера. Нулевият минимум ѝ позволява да се свие. */
	.pdesc { grid-template-columns: minmax(0, 17rem) minmax(0, 1fr); }
	.pdesc__side { position: sticky; top: calc(var(--header-h) + 1.5rem); }
}

.pdesc__side { margin-block-end: 0; }

.pdesc__loc { margin: 0 0 .75rem; font-size: .9375rem; color: var(--muted); line-height: 1.5; }

.pdesc__parcel {
	margin: 0;
	padding-block-start: .85rem;
	border-top: 1px solid var(--line);
	font-size: .8125rem;
	line-height: 1.5;
}

.pdesc__parcel span { display: block; color: var(--muted); }
.pdesc__parcel b { font-variant-numeric: tabular-nums; letter-spacing: .01em; }

/* The copy is a run of one-sentence paragraphs, not flowing prose. At 1.75
   leading and 1.15em between them the gaps read wider than the lines and the
   section falls apart into seven floating captions — the client's own word for
   it was „разлято“. Tighter leading and a gap narrower than the line box hold
   them together as one block. */
.pdesc__body {
	font-size: clamp(1rem, 1.5vw, 1.0625rem);
	line-height: 1.6;
	color: var(--text);
	text-wrap: pretty;
}

.pdesc__body p { margin: 0 0 .85em; }
.pdesc__body p:last-child { margin-bottom: 0; }

/* Първият абзац беше с една стъпка по-едър шрифт — уводен ред, като в списание.
   Клиентът поиска обратното на 24.08.2026: „може ли двата абзаца да са с един и
   същ шрифт и размер на текста“. Затова остава само по-широкото поле отдолу,
   което разделя увода от останалото, без да сменя шрифта. Размерът, интерлинието
   и цветът се наследяват от .pdesc__body — не ги задавай пак тук. */
.pdesc__body > p:first-of-type {
	margin-bottom: 1em;
}


/* =========================================================================
   Flats with no published data
   ========================================================================= */

/* Kept in the table rather than dropped from it: the numbering is the client's
   own, and a list that skips 5А-1 tells the buyer nothing, while a row that
   names it and says „няма данни“ tells them exactly where to ask. Recessive,
   but not so faint it reads as a rendering fault. */
.utable tbody tr.is-nodata { --row-edge: transparent; }
.utable tbody tr.is-nodata .utable__td { color: var(--muted); }

.utable__code--plain {
	display: inline-block;
	font-weight: 700;
	color: var(--muted);
}

.utable__go--off {
	display: inline-block;
	color: var(--sold-line);
}

/* Hollow and dotted, matching the board's empty cell — the same thing said in
   the same way in both views. */
.pill--nodata {
	background: transparent;
	color: var(--muted);
	box-shadow: none;
	border: 1px dotted var(--sold-line);
}

.utable__note {
	margin: 1.25rem 0 0;
	padding-block-start: 1rem;
	border-top: 1px solid var(--line);
	font-size: .8125rem;
	line-height: 1.6;
	color: var(--muted);
	max-width: 68ch;
}

.utable__note b { color: var(--text); }

/* =========================================================================
   Payment schemes that differ per block
   ========================================================================= */

/* Пенемат's two halves settle on different Актове. One flat list would have
   offered a блок 7 buyer блок 5's schedule, so each set is captioned with the
   blocks it governs. */
.schemegroups {
	display: grid;
	gap: clamp(1.5rem, 3vw, 2.5rem);
	grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 48rem) {
	.schemegroups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.schemegroup__name {
	margin: 0 0 .75rem;
	padding-block-end: .5rem;
	border-bottom: 1px solid var(--line);
	font-size: .75rem;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--muted);
}

/* =========================================================================
   Six stacks on one board
   ========================================================================= */

/* Пенемат draws four blocks and six вход stacks side by side. Below the tablet
   break each block already takes its own row (see the .bldg rule above); its
   two stacks stay side by side inside it, which is what keeps блок 5 reading as
   one building with two staircases rather than as two buildings.

   This block sits at the END of the file on purpose: a media query adds no
   specificity, so an override written above the rule it overrides is silently
   dead. That has bitten this theme twice — see README „Five traps“, #3. */
@media (max-width: 1023px) {
	.entr__name { margin-left: 1.9rem; }

	/* Two columns, and everything on the board is half a row wide.
	   A one-вход block takes one column, so блок 6 and блок 7 sit side by side
	   instead of each taking a full row and leaving half of it empty. A two-вход
	   block spans both columns and splits them between its stacks. Either way a
	   stack is half a row, which is what makes every cell on the board the same
	   size — the thing that was wrong before.

	   The two gaps are deliberately the SAME value: the space between блок 6 and
	   блок 7 is the space between вход 8А and вход 8Б, so a stack is exactly
	   half a row in both cases. Different values and the cells drift a few
	   percent apart, which is visible when the blocks sit in one column. */
	.board__blocks {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: 1.5rem;
		row-gap: 2rem;
		align-items: start;
	}

	.bldg { min-width: 0; }

	/* Both stacks need a whole row between them. So does a board with only one
	   block on it (Жожоба) — there is nothing to pair it with, and half an empty
	   row beside it is the very thing this grid exists to remove. */
	.bldg:has(.bldg__entrances--multi),
	.bldg:only-child { grid-column: 1 / -1; }

	.bldg__entrances--multi {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 1.5rem;
	}

	/* Boards with no two-вход block stay a flex row, and there a stack sizes
	   from its content — which is .floor__units at `flex: 1 1 0%`, contributing
	   nothing to an intrinsic measurement. Without a basis they collapse to the
	   floor-number gutter and draw 13px cells. Ignored on the grid boards above,
	   where flex properties do not apply. */
	.entr { flex: 1 1 0; min-width: 0; }
}

/* „Индивидуална схема по споразумение“ — закачена към всеки обект (24.08.2026).
   Различава се от публикуваните схеми: те са факт, тя е покана за разговор.
   Затова номерът ѝ е в цвета на акцента, а текстът — леко приглушен. */
.schemes__item--individual .schemes__no { color: var(--accent); }
.schemes__item--individual .schemes__text { color: var(--muted); }

/* =========================================================================
   Обекти — списък (04.09.2026)
   =========================================================================
   „Нека обектите се показват като списък, а не на зиг заг“ — клиентът, 04.09.
   Зигзагът остава долу, защото „Реализирани обекти“ още го ползва; за него
   редуването върши работа — там редовете са снимка и име, без числа.

   Един ред, три колони: снимка · текст · релсата с наличностите и копчетата.
   Разметката пази `.ocard__*` за вътрешностите — това е същото съдържание в
   друга рамка, а не втори начин да се нарисува карта на обект.

   ⚠️ Под 48rem редът се разпада на КАРТА, а не на смачкан ред. 12rem снимка до
   текст не се побира на телефон, а свита до 90px снимката спира да е снимка и
   става иконка. Затова телефонът е стойността по подразбиране тук, а редът се
   включва в media query-то — противоположното на зигзага под него.

   Границата е 48rem, а не 60rem. На таблет (768px) картата излиза права: 16:9
   през 700px широчина прави снимката 400px висока и един обект изяжда екрана —
   точно оплакването, заради което зигзагът си отива. 720px стигат за трите
   колони (192 снимка · 352 текст · 176 релса). */
.olist {
	width: min(var(--maxw), 100%);
	margin-inline: auto;
	padding-inline: var(--gutter);
	/* Беше clamp(2rem, 4vw, 3.5rem) — половин екран между лентата с числата и
	   първия обект. Главата вече е рамка на списъка, а не отделна страница. */
	padding-block: clamp(1.25rem, 2.5vw, 2rem);
	display: grid;
	gap: clamp(.875rem, 1.8vw, 1.25rem);
}

.olist__row {
	display: grid;
	background: var(--paper);
	border: 1px solid var(--line);
	overflow: hidden;
}

.olist__media {
	position: relative;
	display: block;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	background: var(--paper-alt);
}

/* Извън потока, по същата причина като в картата: `height: 100%` върху img в
   потока се решава спрямо родител, чиято височина идва от `aspect-ratio` —
   кръгово, затова браузърът зарязва пропорцията и взима тази на файла. С
   абсолютно позициониране снимката пълни клетката, каквато я е направил редът,
   а `object-fit: cover` реже излишното. Файловете не са с еднакви пропорции
   (Цветея е квадрат, останалите ~3:2) и без това редовете излизат различни. */
.olist__media img,
.olist__media .ph {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.olist__row .ocard__phase { z-index: 1; }

.olist__main { padding: 1.15rem 1.15rem 0; }
.olist__side { padding: 1rem 1.15rem 1.15rem; }

/* На телефон резюмето е два реда. Цели три изречения избутват числото и
   копчетата под линията на сгъване и списъкът спира да е списък. */
.olist__main .ocard__lede {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
	margin-block-end: 1rem;
}

/* Фактите са последното в текстовата колона — долното поле на картата тук само
   отваря дупка над релсата. */
.olist__main .ocard__facts { margin-block-end: 0; }

/* `.ocard__actions` носи `margin: auto 0 0`: в мрежата от карти това лепи
   копчетата за долния ръб, за да са на една линия при различно дълъг текст.
   Тук релсата е центрирана като едно цяло и същото правило разтяга празнина
   между числото и копчетата. */
.olist__side .ocard__actions { margin-block-start: 0; }

/* Обектът без наличности („Очаквайте скоро“) няма лента и няма факти, тоест
   релсата му е само две копчета. Без това те увисват в горния ѝ край. */
.olist__row--soon .olist__side { display: flex; align-items: center; }

/* Без снимка сивата дупка изглежда като счупена страница. Щриховката казва
   „тук ще има снимка“, вместо да мълчи — същата, която ползва и картата. */
.olist__row--soon .olist__media {
	background:
		repeating-linear-gradient(
			135deg,
			var(--paper-alt) 0 10px,
			#ececea 10px 20px
		);
}
.olist__row--soon .olist__media img { filter: saturate(.35); }

@media (min-width: 48rem) {
	/* Трите колони. Снимката е с фиксирана ширина, за да са всички обекти на
	   една вертикала — списък с различно широки снимки се чете като разсипан.
	   Релсата също: числото „79 свободни“ и двете копчета под него искат едно
	   и също място при всеки ред, иначе окото ги търси наново. Разтяга се само
	   средната колона, тоест текстът. */
	.olist__row {
		grid-template-columns: clamp(12rem, 19vw, 16rem) minmax(0, 1fr) clamp(10.5rem, 13.5vw, 13rem);
		align-items: stretch;
	}

	/* Пропорцията отпада: клетката взима височината на РЕДА, а редът я взима от
	   текста. Така снимката се допира до горния и долния ръб на реда, вместо да
	   плува в него — и всички снимки са еднакви, защото всички редове са. */
	.olist__media { aspect-ratio: auto; }

	.olist__main,
	.olist__side {
		display: flex;
		flex-direction: column;
		justify-content: center;
		padding: clamp(1.25rem, 2vw, 1.75rem);
	}

	/* Релсата е по-тясна от текста и всеки милиметър поле се отнема от реда
	   „79 свободни / 177“. При 2vw полето беше 26px от двете страни и вътре
	   оставаха 102px — дробта падаше на втори ред. */
	.olist__side { padding-inline: clamp(1rem, 1.5vw, 1.5rem); }

	/* Тънката черта е разликата между „ред с числа накрая“ и „ред и релса“. */
	.olist__side { border-inline-start: 1px solid var(--line); }

	/* Копчето е ЕДНО и се събира по съдържание, не на пълна ширина. Разтегнато
	   през релсата, „ВИЖ ПОВЕЧЕ“ се чупеше на два реда — надпис от две думи,
	   разкъсан по средата, изглежда като две действия. `nowrap` е застраховката
	   за английския („See more“) и руския („Подробнее“), които са по-дълги. */
	.olist__side .ocard__actions .btn {
		justify-content: center;
		white-space: nowrap;
	}

	/* На широк екран резюмето се събира на ред и половина — няма какво да се
	   реже, а рязането го прекъсва по средата на изречението. */
	.olist__main .ocard__lede {
		-webkit-line-clamp: unset;
		line-clamp: unset;
		max-width: 62ch;
	}
}

/* =========================================================================
   Обекти — зигзаг (25.08.2026)
   =========================================================================
   Страницата минава от мрежа с карти на редуващи се редове, същото оформление
   като избраното за „Проекти“ (вариант 1). Структурата е `.zz*` от main.css;
   тук се оправя само това, което е различно: тялото носи и лента с наличности,
   цени и площи, а не само име и локация.

   `.obekti-zz` е обвивка, за да не се пипа зигзагът на /proekti/. */
.obekti-zz {
	width: min(var(--maxw), 100%);
	margin-inline: auto;
	padding-inline: var(--gutter);
	padding-block: clamp(2rem, 4vw, 3.5rem);
}

/* ⚠️ Тялото носи САМО .zz__body. Добавянето и на .ocard__body изглежда логично
   (той има отстоянията на картата), но двата класа се бият: .ocard__body слага
   свой падинг, а неутрализирането му с `background: none` изяжда бялото поле на
   .zz__body — текстът увисва върху снимката и цената става нечетима.
   Вътрешните класове (.ocard__title, .ocard__avail, .ocard__facts) не зависят от
   .ocard__body, затова се ползват както са. */

/* Бялото поле на зигзага е по-тясно от карта, затова лентата и фактите се
   свиват, вместо да се разливат по цялата ширина на текста. */
.obekti-zz .ocard__avail { margin-block: 1.25rem 1rem; max-width: 28rem; }
.obekti-zz .ocard__facts { max-width: 32rem; }

/* „Очаквайте скоро“ няма наличности, тоест тялото му е късо. Без това редът
   изглежда като счупен зигзаг — снимка на половин екран до три реда текст. */
@media (min-width: 64rem) {
	.obekti-zz .zz__row--soon .zz__body { justify-content: center; }
}

/* Плочката на етапа стои върху снимката, а .zz__media е позиционирана —
   наследеното от картата позициониране е спрямо друг предшественик. */
.obekti-zz .zz__media { display: block; }
.obekti-zz .ocard__phase { position: absolute; z-index: 1; inset-block-start: 1rem; inset-inline-start: 1rem; }

/* ⚠️ На обърнатите редове снимката е вдясно, а бялото поле я застъпва отляво
   (`margin-inline-end: -4rem`). Плочка в горния ляв ъгъл попада точно под това
   застъпване и се разрязва — „В ПРОДАЖБА“ се четеше като „ДАЖБА“. Затова там
   тя минава в десния ъгъл, който нищо не покрива.
   Само над 64rem: под тази ширина колоните се разгъват и застъпване няма. */
@media (min-width: 64rem) {
	.obekti-zz .zz__row--flip .ocard__phase {
		inset-inline-start: auto;
		inset-inline-end: 1rem;
	}

	/* ЕДИН РАЗМЕР ЗА ВСИЧКИ СНИМКИ (Gabe, 25.08.2026).

	   ⚠️ Не го прави с `aspect-ratio` + `align-items: center`. Пробвано е и
	   СЧУПВА оформлението: снимката спира да се допира до бялото поле, то увисва
	   върху нея и зигзагът се разпада на стълбище. Тъканта на този дизайн иска
	   снимката да се разтяга до височината на реда — това е, което прави
	   застъпването корав ръб, а не сянка.

	   Затова се изравнява РЕДЪТ, не снимката. Две неща:
	   1. Колоните стават равни. Иначе обърнатите редове хвърлят снимката в
	      по-тясната колона (`order: 2`) и тя излиза 601px срещу 678px.
	   2. Редът получава фиксирана височина. Тя е над най-високото тяло, така че
	      всички редове са еднакво високи, а щом снимката се разтяга до реда —
	      всички снимки излизат еднакви. Излишното се реже от `object-fit: cover`. */
	.obekti-zz .zz__row {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		align-items: stretch;
		min-height: 30rem;
	}
	.obekti-zz .zz__media {
		aspect-ratio: auto;
		min-height: 0;
	}
	/* ⚠️ Тук е ИСТИНСКАТА причина снимките да излизат различни.
	   Без пропорция височината на снимката се смяташе от самия файл, а файловете
	   не са с еднакви пропорции: Цветея (`cveteya/02.jpg`) е 1600×1600 — КВАДРАТ,
	   останалите са около 3:2. Затова нейният ред ставаше 640px висок, а другите
	   480px, без изобщо текстът да участва (тялото е 224px съдържание).

	   `position: absolute` изважда изображението от потока: то вече не може да
	   тегли реда нагоре, пълни снимковата клетка каквато я е направил редът, а
	   `object-fit: cover` реже излишното. Така един квадратен файл и един
	   широкоъгълен дават една и съща плочка. */
	.obekti-zz .zz__media img,
	.obekti-zz .zz__media .ph {
		position: absolute;
		inset: 0;
	}
}

/* `.ocard__actions` носи `margin: auto 0 0` — в картата това лепи бутоните за
   долния ръб, за да са на една линия при карти с различно дълъг текст. В
   зигзага редът е висок 30rem, а съдържанието е ~224px, така че същото правило
   разтяга празнина между фактите и бутоните. Тук блокът е центриран като едно
   цяло, затова автоматичното поле отпада. */
.obekti-zz .ocard__actions { margin-block-start: 0; }

/* Табовете и числата на един ред, числата вдясно (Gabe, 25.08.2026).
   Дотук лентата беше три реда един под друг — пътека, табове, статистика — и
   заемаше цял екран, преди да се стигне до самите обекти. Табовете са избор,
   числата са контекст към него: на един ред се четат като една лента.

   Без промяна в разметката: `.obekti-head__inner` става flex, пътеката отгоре
   заема цялата ширина (`flex-basis: 100%`), а `margin-inline-start: auto`
   избутва списъка с числата надясно.

   Само от 48rem нагоре — под тази ширина четирите числа и двата бутона не се
   побират на ред и подреждането в стълб е правилното. */
@media (min-width: 48rem) {
	.obekti-head__inner {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: .75rem clamp(1.5rem, 4vw, 3rem);
	}
	.obekti-head .crumbs { flex-basis: 100%; }
	/* Долното поле държеше числата под табовете — вече са един до друг. */
	.obekti-head__tabs { margin-block-end: 0; }
	.obekti-head__stats { margin-inline-start: auto; }
}

/* ⚠️ Само `margin-inline-start: auto` НЕ ги слага на един ред. Табовете са
   539px, числата 718px, а полето е 1280px — с разстоянието помежду им сборът
   надхвърля ширината и числата падат на свой ред. Отдалеч изглежда като че ли
   правилото не е сработило, защото `auto` полето ги подравнява вдясно и там.

   Затова разстоянието ВЪТРЕ в числата се стяга: четирите двойки се събират в
   ~640px и двата блока се побират. `nowrap` пази числата да не се пречупват
   помежду си, преди целият блок да слезе долу. */
@media (min-width: 64rem) {
	.obekti-head__stats {
		flex-wrap: nowrap;
		gap: clamp(1rem, 2.2vw, 2rem);
	}
	/* И надписите под числата с една стъпка надолу — четирите заедно са ~580px
	   само от букви и те са разликата между един ред и два. */
	.obekti-head__stats span { font-size: .6875rem; }
}

/* Реализиран обект — построен и предаден. Различава се от „Очаквайте скоро“
   по тон: спокоен, не подканващ, защото няма какво да се резервира. */
.ocard__phase--done{background:var(--ink);color:#fff}
.ocard__shots{
	display:flex;align-items:baseline;gap:.4rem;
	margin:.35rem 0 1rem;color:var(--muted);font-size:.9rem;
}
.ocard__shots b{font-size:1.35rem;color:var(--ink);font-weight:700;line-height:1}

/* =========================================================================
   Планът на етажа — чертежът с оцветените помещения (parts/blueprint.php)

   Един слой SVG върху една картинка. Полигоните идват от чертежа в пиксели, а
   viewBox-ът на SVG-то е точно размерът на картинката, така че двете се
   разтягат заедно и съвпадат на всяка ширина без нито едно число тук.

   Цветовете са ТЕЗИ на таблото — --free, --resv, --sold-line — и това е нарочно.
   Купувачът вече е чел легендата при схемата и в таблицата; план в друга гама
   го кара да я учи втори път. Тук те са с прозрачност, защото под тях трябва да
   се чете чертежът: плътното петно скрива номера на гаража и стрелките.
   ====================================================================== */

.bp {
	/* Опорната кутия на подсказката: тя се позиционира в пиксели спрямо ТОЗИ
	   елемент (виж place() в objects.js). Без `relative` абсолютното
	   позициониране пада върху страницата и панелът излита нагоре извън екрана. */
	position: relative;
	border: 1px solid var(--line);
	background: var(--paper);
}

.bp__bar {
	display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem;
	padding: .875rem 1rem;
	border-bottom: 1px solid var(--line);
}
/* Управлението горе, ключът към цветовете отдолу: филтър и мащаб се натискат,
   легендата се чете. На един ред петте групи се събират само на 1600px нагоре и
   се пренасят разкривено под това. */
.bp__bar--key { background: var(--paper-alt); }
/* Без скрипт чиповете не филтрират и копчетата не мащабират — целият ред си
   отива, вместо да стои и да не работи. */
.bp:not([data-ready]) .bp__bar--tools { display: none; }

/* Легендата на таблото е колона в тясна странична лента; тук стои на един ред
   под широк чертеж — и носи цветовете НА ПЛАНА, не на таблото. */
.legend--bp { flex-direction: row; flex-wrap: wrap; gap: .5rem 1.25rem; margin: 0; }
/* Бялото квадратче се нуждае от контур, за да съществува върху светъл фон. */
.legend--bp .swatch--free     { background: var(--bp-free); border-color: var(--sold-line); }
.legend--bp .swatch--reserved { background: var(--bp-resv); border-color: var(--bp-resv); }
.legend--bp .swatch--sold     { background: var(--bp-sold); border-color: var(--bp-sold); }

.bp__count { margin: 0; font-size: .875rem; color: var(--muted); }
/* Числото беше в тюркоаза на „свободен“ — цвят, който на тази страница вече не
   значи това. Бялото не става за текст, затова тук числото носи само тежест. */
.bp__count b { color: var(--ink); font-weight: 700; }

.bp__zoom { display: flex; gap: .35rem; margin-left: auto; flex: none; }
.bp__zbtn {
	font: inherit; font-size: .8125rem; line-height: 1;
	padding: .5rem .75rem; min-width: 2.25rem;
	background: var(--paper-alt); color: var(--text);
	border: 1px solid var(--line); cursor: pointer;
	transition: background .15s, border-color .15s;
}
.bp__zbtn:hover { background: var(--warm); border-color: var(--muted); }

/* Кутията пази пропорцията на листа, за да не подскочи страницата, докато
   картинката от 700 KB се зарежда. `--bp-ar` идва от самия чертеж, инлайн. */
.bp__stage {
	position: relative; overflow: hidden;
	aspect-ratio: var(--bp-ar, 4 / 3);
	background: #fff;
	touch-action: none;
}
.bp[data-ready] .bp__stage { cursor: grab; }
.bp[data-ready] .bp__stage.is-dragging { cursor: grabbing; }

.bp__canvas {
	position: absolute; inset: 0;
	transform-origin: 0 0;
	/* Копчетата местят плана с плъзгане, за да се проследи накъде отива. При
	   влачене и колелце класът пада — там движението идва от ръката и всяка
	   анимация върху него се усеща като изоставане. */
	transition: transform .18s ease-out;
}
.bp__canvas.is-live { transition: none; }
@media (prefers-reduced-motion: reduce) {
	.bp__canvas { transition: none; }
}
.bp__img { display: block; width: 100%; height: 100%; }
.bp__svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

/* Полигонът е под курсора върху цялата си площ, не само по контура. */
.bp__spot {
	fill: transparent;
	stroke: transparent; stroke-width: 2; vector-effect: non-scaling-stroke;
	transition: fill .12s ease, stroke .12s ease;
}
/* ── Цветовете на плана ────────────────────────────────────────────────
   Светофар, по искане на клиента (04.09.2026): свободно е БЯЛО, резервирано —
   оранжево, продадено — червено. Обратно на таблото и таблицата, където
   свободното е това, което носи цвят.

   Логиката тук е другата и е по-силна върху чертеж: цветът маркира заетото,
   бялото остава чисто. Купувачът гледа лист с 94 помещения и търси къде МОЖЕ,
   а не къде не може — а бялото е и единственото „запълване“, което не мъти
   номера и площта, изписани в самото помещение.

   Тези три променливи са цялата разлика. Ако някога цветовете тръгнат и по
   таблото, оттук се вдигат в --free/--resv на .obekt и този блок отпада.

   ⚠ Легендата на ТАБЛОТО е още в старата гама. Двете страници се разминават,
   докато не се вземе решение за едната или другата. */
.bp {
	--bp-free: #ffffff;
	--bp-resv: #e08a2e;
	/* По-тъмно и по-студено от оранжевото нарочно: продаден и резервиран значат
	   едно и също за купувача („не е за мен“) и разликата между тях не бива да е
	   само в топлината на тона — при 38% прозрачност двете излизаха розово и
	   праскова, което на цял лист се слива. */
	--bp-sold: #b3121b;
	--bp-inq:  #35618e;
	/* „Ти си тук“ — единицата, от която идва посетителят. По-ярко и по-чисто
	   синьо от `--bp-inq` нарочно: там синьото е ЗАПЪЛВАНЕ и значи статус („по
	   запитване“), тук е РАМКА и значи място. Двете не се срещат на един лист
	   в този обект, но ако някога се срещнат, трябва да си личи кое кое е. */
	--bp-current: var(--pick, #1668dc);
}
/* Бялото е плътно колкото да отдели свободното от хартията наоколо — листът
   не е бял, а сив от размерни вериги, оси и щриховка. Над 60% почва да яде
   надписа „Гараж 23 · A: 20,60 m²“ вътре в помещението. */
.bp__spot.is-free     { fill: color-mix(in srgb, var(--bp-free) 58%, transparent); }
.bp__spot.is-reserved { fill: color-mix(in srgb, var(--bp-resv) 42%, transparent); }
.bp__spot.is-sold     { fill: color-mix(in srgb, var(--bp-sold) 46%, transparent); }
.bp__spot.is-inquire  { fill: color-mix(in srgb, var(--bp-inq)  34%, transparent); }
/* „Без данни“ остава без цвят: празно е различно от свободно. */
.bp__spot.is-unknown  { fill: transparent; }

/* ── „Ти си тук“ ────────────────────────────────────────────────────────
   Рамка, не запълване (Gabe, 04.09.2026). Запълването носи СТАТУСА и е точно
   това, което купувачът е дошъл да провери; пребоядисаш ли единицата в синьо,
   листът ѝ казва къде е и премълчава дали е свободна. Двете са различни оси и
   искат различни средства — цвят за състоянието, контур за мястото.

   ⚠️ Дебелината минава през ДВА мащаба и двата се неутрализират тук.
   1. `vector-effect: non-scaling-stroke` маха мащаба на viewBox-а: чертежът е
      4200 единици широк, а се рисува в ~800px, тоест без него `stroke-width: 2`
      излиза 0,38px — почти нищо.
   2. Делението на `--bp-scale` маха мащаба на приближаването: `transform:
      scale()` върху платното уголемява и щрихите, така че при мащаб 5 двойката
      ставаше десетка и рамката изяждаше гаража, който огражда.
   Променливата я пише objects.js при всяко пречертаване; без скрипт остава 1 и
   рамката е точно 2px, какъвто е и планът тогава. */
.bp__spot.is-current {
	stroke: var(--bp-current);
	/* 3, не 2 (Габе, 15.09.2026): рамката е цялата причина да се отвори този
	   адрес — купувачът идва от таблицата и търси едно място сред трийсет и
	   едно. При 2 px тя се губеше между линиите на чертежа. */
	stroke-width: calc(3 / var(--bp-scale, 1));
	vector-effect: non-scaling-stroke;
	stroke-linejoin: round;
}
/* Посочването на съседен гараж слага `stroke: var(--ink)` — по-обща селекция,
   но еднаква специфичност, така че редът в файла решава. Този стои по-долу
   нарочно: докато четеш плана, синьото не бива да мига. */
.bp__link.is-current:hover .bp__spot,
.bp__link.is-current:focus-visible .bp__spot { stroke: var(--bp-current); }

.legend--bp .swatch--current {
	background: transparent;
	/* Легендата показва същата дебелина като плана — иначе обяснява друго. */
	border: 3px solid var(--bp-current);
}

.bp__link:hover .bp__spot,
.bp__link:focus-visible .bp__spot { stroke: var(--ink); }
.bp__link:hover .bp__spot.is-free     { fill: color-mix(in srgb, var(--bp-free) 85%, transparent); }
.bp__link:hover .bp__spot.is-reserved { fill: color-mix(in srgb, var(--bp-resv) 62%, transparent); }
.bp__link:hover .bp__spot.is-sold     { fill: color-mix(in srgb, var(--bp-sold) 55%, transparent); }
.bp__link:focus { outline: none; }

/* „Оцветяване: Изключено“ — чистият чертеж на архитекта.

   Пада само ЦВЕТЪТ. Помещенията остават на място и кликаеми: посочването пак ги
   оцветява (правилата за :hover по-горе са по-специфични от това) и пак вдига
   панела с площта и цената. Изгасено оцветяване не значи изгасена страница —
   планът просто спира да е табло с наличности и става това, което е. */
.bp.is-nopaint .bp__spot { fill: transparent; }

.bp__hint {
	margin: 0; padding: .75rem 1rem;
	border-top: 1px solid var(--line);
	font-size: .8125rem; color: var(--muted);
}
/* Без скрипт няма нито влачене, нито мащабиране — подсказката би лъгала. */
.bp:not([data-ready]) .bp__hint { display: none; }

/* ── Панелът с детайлите ───────────────────────────────────────────────
   На мишка следва курсора (JS слага top/left); на тъч ляга долу на екрана,
   където палецът стига до копчето. */
.bp__tip {
	position: absolute; z-index: 30; width: min(17rem, 78vw);
	padding: .875rem 1rem;
	background: var(--paper); border: 1px solid var(--line);
	box-shadow: 0 8px 30px rgb(0 0 0 / .16);
	pointer-events: none;
}
.bp__tip[hidden] { display: none; }
.bp__tip__close { display: none; }
.bp__tip__code { margin: 0 0 .4rem; font-size: 1.0625rem; font-weight: 700; }
.bp__tip__status { margin: 0 0 .6rem; }
.bp__tip__specs { display: grid; grid-template-columns: 1fr auto; gap: .15rem .75rem; margin: 0; font-size: .8125rem; }
.bp__tip__specs div { display: contents; }
.bp__tip__specs dt { color: var(--muted); }
.bp__tip__specs dd { margin: 0; font-variant-numeric: tabular-nums; text-align: right; }
.bp__tip .btn { display: none; }

/* Тъч: панелът е закачен долу, има затваряне и води до страницата на гаража —
   на телефон „кликни полигона“ и „прочети подсказката“ не могат да са едно. */
@media (hover: none) {
	.bp__tip {
		position: fixed; inset: auto 0 0 0; width: auto;
		pointer-events: auto;
		padding: 1rem 1.25rem 1.25rem;
		border: none; border-top: 1px solid var(--line);
	}
	.bp__tip__close {
		display: block; position: absolute; top: .5rem; right: .75rem;
		background: none; border: 0; font-size: 1.75rem; line-height: 1;
		color: var(--muted); cursor: pointer;
	}
	.bp__tip .btn { display: block; margin-top: .875rem; }
}

@media (max-width: 640px) {
	.bp__bar { gap: .75rem 1rem; padding: .75rem; }
	.bp__zoom { margin-left: 0; }
	.legend--bp { font-size: .75rem; }
	/* Чиповете са целта на палеца — те държат ширината и бутат мащаба на свой ред. */
	.bp__bar--tools .filters { flex: 1 1 100%; }
}

/* Копчето под таблицата с гаражите (parts/board.php). */
.board__extras__foot { margin: 1.25rem 0 0; }

/* Двете копчета под таблицата с наличностите се събират на един ред. */
.section--table .section__foot { display: flex; flex-wrap: wrap; gap: .75rem; }

@media print {
	/* На хартия планът е цял: без изрязване, без интерфейс. */
	.bp__stage { aspect-ratio: auto; overflow: visible; }
	.bp__canvas { position: static; transform: none !important; }
	.bp__zoom, .bp__hint, .bp__tip { display: none; }
}

/* =========================================================================
   Телефон — разчистване по бележките на Gabe (06.09.2026)
   =========================================================================
   Осем поправки от една обиколка по iPhone 14 в Safari. Блокът стои НАКРАЯ
   на файла нарочно: медия заявка не добавя специфичност, така че override,
   написан над правилото, което сменя, е мълчаливо мъртъв. Виж README
   „Five traps“, #3.
   ---------------------------------------------------------------------- */

/* ── По-малко въздух над „Свободни единици“ ──────────────────────────────
   Gabe, 06.09.2026, и на телефон, и на 14" лаптоп. Общото `.section` дава
   4.5rem отгоре — правилно между два текстови блока, но тук отгоре стои
   херой с плътен ръб, който вече е разделил двете неща. Въздухът след него
   само бута схемата под сгъвката.

   ⚠️ Само НАЧАЛОТО: долното поле остава от `.section`, защото под схемата
   идва следващата секция и там разделянето още трябва да се направи. */
.section--board,
.section--table,
.section--plan { padding-block-start: clamp(1.75rem, 3vw, 2.5rem); }

/* ── Хероят на обекта на голям екран ─────────────────────────────────────
   56vh е 550px на 14" — заглавието и числата заемат 260 от тях и останалото
   е празна визуализация. 44vh дава на снимката да е снимка, без да краде
   екран от съдържанието.

   ⚠️ Телефонът го нулира в блока отдолу — този ред трябва да остане НАД
   медия заявката, иначе я презаписва. */
.obekt-hero { min-height: clamp(360px, 44vh, 520px); }

@media (max-width: 640px) {

	/* ── Пътеката пада на телефон, навсякъде ─────────────────────────────
	   Един ред, който повтаря менюто и заглавието под себе си, а изяжда
	   първия екран. ⚠️ `.crumbs--top` носеше и разчистването на надвисналото
	   лого (main.css) — скрие ли се пътеката, то изчезва с нея и плочата ляга
	   върху съдържанието. Затова същото отместване минава върху страницата. */
	.crumbs { display: none; }
	.obekti-head { padding-block-start: max(.75rem, calc(var(--brand-drop) + .75rem)); }
	.unit { padding-block-start: max(1.5rem, calc(var(--brand-drop) + 1.5rem)); }

	/* ── Двата таба на един ред ──────────────────────────────────────────
	   Дотук всеки заемаше цяла ширина и изборът се четеше като две отделни
	   препратки, а не като превключвател. Шрифтът е по vw, за да събере
	   „ОБЕКТИ В ПРОДАЖБА“ на един ред и на 320px екран. */
	.obekti-head__tabs { flex-wrap: nowrap; gap: .5rem; margin-block-end: 1.25rem; }
	.obekti-head__tabs a {
		flex: 1 1 0;
		justify-content: center;
		padding: .8rem .5rem;
		font-size: clamp(.625rem, 2.9vw, .8125rem);
		letter-spacing: .03em;
		white-space: nowrap;
	}

	/* ── Числата: 2×2, центрирани ────────────────────────────────────────
	   Четири двойки в flex ред се пренасяха 3+1 и последната висеше сама
	   вляво. Мрежата ги държи в каре, а центрирането прави от тях лента,
	   а не колона от подравнени вляво откъслеци. */
	.obekti-head__stats {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: .875rem 1rem;
		text-align: center;
	}
	.obekti-head__stats li { align-items: center; }
	.obekti-head__stats b { font-size: 1.75rem; }
	.obekti-head__stats span { font-size: .6875rem; }

	/* ── Хероят на обекта: без празното поле отгоре ──────────────────────
	   `min-height: 56vh` държеше половин екран визуализация над заглавието.
	   На телефон съдържанието само определя височината; снимката остава фон,
	   а не самостоятелен кадър, който трябва да се превърти. */
	.obekt-hero {
		min-height: 0;
		padding-block: 1.25rem 1.75rem;
	}
	.obekt-hero__title { font-size: 1.75rem; margin-block-end: .35rem; }
	.obekt-hero__loc { margin-block-end: .875rem; }

	/* Четирите числа в 2×2 каре по същата причина като горе: в свободен ред
	   „НАЧАЛНА ЦЕНА“ и „ПАРКОМЕСТА И ГАРАЖИ“ са различно широки и колоните
	   не се получаваха. */
	.obekt-hero__stats {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: .875rem 1rem;
		margin-block-end: 1.25rem;
	}
	.obekt-hero__stats b { font-size: 1.5rem; }
	.obekt-hero__stats span { font-size: .6875rem; }

}

/* ── Клетките под палеца ─────────────────────────────────────────────────
   Gabe, iPhone 14: „a bit small… make them easy to press“. Причината не е в
   `--cell` — таванът вече е 46px — а в подредбата: `.board__blocks` е две
   колони под 1024px, а блок с два входа дели своята колона на още две. Така
   един вход получава четвърт екран и шестте клетки на етаж падат под 20px.

   На телефон и двете мрежи стават една колона: входът заема цялата ширина,
   таванът от 46px става достижим и клетката е истинска цел за пръст.
   Блокът с два входа престава да се чете като една сграда с две стълбища —
   това е съзнателна размяна: заглавието „БЛОК 5“ остава над двата входа и
   ги държи заедно, а на 393px четимостта на клетката тежи повече.

   ⚠️ След блока на ред 1898, който въвежда двете колони — иначе е мъртво. */
@media (max-width: 640px) {
	.board__blocks { grid-template-columns: minmax(0, 1fr); row-gap: 1.5rem; }
	.bldg__entrances--multi { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
	/* Таванът на пистата се вдига заедно с този на клетката, иначе клетката
	   стои 46px в 100px писта и етажът се разпада на острови. */
	.floor__units {
		grid-template-columns:
			repeat(var(--cols, 6), minmax(0, min(48px, calc(
				(100% - (var(--cols-max, 6) - 1) * 3px) / var(--cols-max, 6)
			))));
	}
	.cell { max-width: 48px; }
}

/* ── Панелът при натискане на клетка ─────────────────────────────────────
   Gabe: „way smaller… stats on one line like a sentence… hides the block
   minimally“. Панелът е фиксиран долу и закриваше половин екран точно от
   схемата, която току-що си натиснал — четирите реда спецификация бяха
   по-голямата част от височината. Сега те са едно изречение: етикет и
   стойност залепени, двойките разделени с въздух, без линии между тях.
   Линиите бяха за колона; в ред те само режат изречението. */
.sheet { padding: .875rem var(--gutter) 1rem; }
.sheet__close { inset-block-start: -.4rem; }

/* Номер · вид · състояние на един ред. `baseline` подравнява „7-3“ и
   „Двустаен“ по стъпалото на буквите, а не по средата на кутиите — иначе
   по-дребният вид увисва. Състоянието е чип и се държи по друг начин, затова
   излиза от подравняването с `align-self`.

   ⚠️ Дясното поле е за КОПЧЕТО ЗА ЗАТВАРЯНЕ: то е абсолютно позиционирано в
   горния десен ъгъл на панела и не заема място в реда. Без полето състоянието
   ляга под кръстчето. */
.sheet__head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: .15rem .6rem;
	margin-block-end: .6rem;
	padding-inline-end: 2.5rem;
}
.sheet__code { font-size: 1.0625rem; margin: 0; }
.sheet__rooms { margin: 0; font-size: .8125rem; }
.sheet__status {
	margin: 0 0 0 auto;
	align-self: center;
	padding: .2rem .55rem;
	font-size: .625rem;
}

/* Цената и цената на квадрат на един ред. `margin-inline-start: auto` НЕ се
   ползва: мярката принадлежи на числото пред себе си и залепена за десния ръб
   се чете като второ, отделно число. */
.sheet__money {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: .1rem .6rem;
	margin-block-end: .75rem;
}
.sheet__money .sheet__price,
.sheet__money .sheet__rate { margin: 0; }

.sheet__specs {
	flex-flow: row wrap;
	gap: .1rem 1.1rem;
	margin-block-end: .55rem;
}
.sheet__specs > div {
	display: inline-flex;
	justify-content: flex-start;
	gap: .35rem;
	padding-block-end: 0;
	border-bottom: 0;
	font-size: .75rem;
}

.sheet__price { font-size: 1.25rem; }
.sheet .btn--block { padding-block: .85rem; }

/* ── Копчето на картата вдясно ───────────────────────────────────────────
   Gabe, 06.09.2026. Под 48rem редът на списъка е КАРТА и всичко в нея е
   подравнено вляво — включително копчето, което остава да виси под лентата
   с наличностите с половин ред празно вдясно от себе си. Вдясно то затваря
   картата: окото слиза по текста и го намира там, където свършва.

   Границата е същата, на която редът става карта (`@media (min-width: 48rem)`
   по-горе), а не 640px — между 640 и 768 картата още е карта. */
@media (max-width: 47.99rem) {
	.olist__side .ocard__actions { justify-content: flex-end; }
	/* ⚠️ „Очаквайте скоро“ прави самата релса flex контейнер (`.olist__row--soon
	   .olist__side` по-горе, за да не увисне копчето в горния ѝ край). Там
	   блокът с копчето е ЕЛЕМЕНТ по съдържание — вътрешното подравняване няма
	   свободно място, което да разпредели, и копчето остава вляво, докато във
	   всички останали карти вече е вдясно. Разтягането му връща мястото. */
	.olist__row--soon .olist__side .ocard__actions { flex: 1 1 auto; }
}

/* =========================================================================
   Избор върху схемата + копчетата на администратора (06.09.2026)
   =========================================================================
   Клик върху квадратче вече ИЗБИРА единицата и заковава панела, вместо да
   отваря страницата ѝ (Gabe: „put a blue mark on the square, and in that way
   you select it“). Оттам нататък панелът принадлежи на избраното и посочването
   на съседно квадратче не го сменя — виж assets/js/objects.js.

   ⚠️ Блокът стои НАКРАЯ на файла: `.cell.is-picked` и `.cell:hover` са с
   еднаква специфичност (0,2,0), тоест побеждава по-долният. Написан по-горе,
   изборът щеше да изчезва точно докато мишката е върху него.
   ---------------------------------------------------------------------- */

/* `outline`, а не `box-shadow`: сянката е заета от посочването (`0 4px 0 0`)
   и двете щяха да се изключват взаимно. Контурът не заема място в подредбата,
   така че съседните квадратчета не мърдат, когато избереш едно. */
.cell.is-picked {
	outline: 3px solid var(--pick);
	outline-offset: -1px;
	z-index: 3;
}

/* Смениш ли статуса на „продаден“, докато е включено „Само свободни“, тъкмо
   избраното квадратче попада под филтъра и `opacity: .16` го изтрива изпод
   курсора. Избраното не се приглушава изобщо: панелът още говори за него, а и
   `pointer-events: none` от `.is-dim` би отнело единствения начин да се
   отмени изборът — второ натискане върху същото квадратче. */
.cell.is-picked.is-dim { opacity: 1; pointer-events: auto; }

.pick-admin {
	margin-block-start: .75rem;
	padding-block-start: .75rem;
	border-block-start: 1px solid var(--line);
}
.pick-admin[hidden] { display: none; }

.pick-admin__lbl {
	margin: 0 0 .45rem;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--muted);
}

.pick-admin__btns { display: flex; flex-wrap: wrap; gap: .4rem; }

/* Двете копчета делят реда поравно. Третото е скрито — то е текущият статус и
   скриването му е в JS-а, защото панелът е един, а единиците са 177. */
.pick-admin__btn {
	flex: 1 1 0;
	min-width: 0;
	padding: .6rem .5rem;
	border: 1px solid var(--line);
	background: var(--paper);
	color: var(--text);
	font: inherit;
	font-size: .75rem;
	font-weight: 700;
	cursor: pointer;
	transition: background .12s, border-color .12s, color .12s;
}
.pick-admin__btn[hidden] { display: none; }
.pick-admin__btn:disabled { opacity: .45; cursor: default; }

/* Копчето се оцветява в цвета, който ЩЕ сложи — потвърждението идва преди
   натискането, а не след него. */
.pick-admin__btn--free:hover:not(:disabled),
.pick-admin__btn--free:focus-visible:not(:disabled) {
	background: var(--free); border-color: var(--free); color: #fff;
}
.pick-admin__btn--reserved:hover:not(:disabled),
.pick-admin__btn--reserved:focus-visible:not(:disabled) {
	background: var(--resv); border-color: var(--resv); color: #fff;
}
.pick-admin__btn--sold:hover:not(:disabled),
.pick-admin__btn--sold:focus-visible:not(:disabled) {
	background: var(--sold-line); border-color: var(--sold-line); color: #fff;
}

.pick-admin__msg {
	margin: .45rem 0 0;
	min-height: 1em;
	font-size: .75rem;
	color: var(--muted);
}

/* -------------------------------------------------------------------------
   Колоната „Статус“ — етикет, който се отваря (администратор, 06.09.2026)
   -------------------------------------------------------------------------
   Gabe: „when you click it, expand the row and have the 3 buttons, once you
   apply the new status the row status change, but don't hide the field.“

   Затворена, клетката е СЪЩИЯТ етикет, който вижда и посетителят — колоната
   остава 126px и таблицата се чете отгоре надолу. Първата версия държеше трите
   копчета отворени във всеки ред: 354 реда × три копчета правят от таблицата
   формуляр и колоната трябваше да порасне на 246px.

   Отворена, трите застават ЕДНО ПОД ДРУГО в същата клетка — така ширината на
   колоната не се променя и нищо в таблицата не мърда встрани, само този ред
   става по-висок.
   ---------------------------------------------------------------------- */

/* ⚠️ `flex`, не `inline-flex`, и `width: 100%`.
   С inline-flex групата се свиваше до съдържанието си, а трите надписа са с
   различна дължина: „Свободен“ 92px, „Продаден“ 93px, „Резервиран“ 105px.
   Смениш ли статуса, етикетът порастваше или се свиваше с 13px и клетката
   подскачаше под пръста (Gabe, 06.09.2026: „it jumps when you select
   резервиран“). Разтеглена по клетката, ширината е една и съща за трите и
   няма какво да мръдне — нито при смяна, нито при отваряне на списъка. */
.spick { display: flex; width: 100%; flex-direction: column; align-items: stretch; gap: .35rem; }

/* Етикетът е и копчето. Наследява `.pill`, тоест и цвета на статуса; тук се
   маха само това, което браузърът слага на <button>. */
.spick__now {
	border: 0;
	min-width: 0;
	font: inherit;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .04em;
	text-align: center;
	cursor: pointer;
	/* Предпазна мрежа за по-дълъг надпис — руското „Забронировано“ е с четири
	   знака над „Резервиран“. Клетката е 110px и не расте: надписът се реже с
	   многоточие, вместо да избута стрелката извън копчето.
	   `min-width: 0` е задължително — без него flex-елементът отказва да се
	   свие под съдържанието си и `text-overflow` няма какво да отреже. */
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
/* Стрелка надолу (Gabe, 06.09.2026: „add an arrow pointing down, so it looks
   like the status field is clickable“). Тя е единственото, което отличава
   етикета на администратора от етикета на посетителя — иначе колоната изглежда
   еднакво в двата случая и никой не се сеща да я натисне.

   Нарисувана с рамки, а не със знака ▾: Manrope се зарежда от Google Fonts и
   няма геометрични знаци, тоест ▾ щеше да падне на резервния шрифт и да се
   различава по размер от ред до ред. `currentColor` я взима от текста на
   етикета, така че е бяла върху запълненото „Свободен“ и тъмна върху тинта на
   „Резервиран“, без да се изброяват случаите. */
.spick__now { position: relative; padding-inline-end: 1.3rem; }
.spick__now::after {
	content: "";
	position: absolute;
	inset-inline-end: .5rem;
	inset-block-start: 50%;
	width: 0;
	height: 0;
	border-inline: 4px solid transparent;
	border-block-start: 5px solid currentColor;
	transform: translateY(-50%);
	transition: transform .15s ease;
}
/* Отворено — стрелката сочи нагоре, тоест „това се прибира обратно“. */
.spick__now[aria-expanded="true"]::after { transform: translateY(-50%) rotate(180deg); }

.spick__now:hover,
.spick__now:focus-visible { box-shadow: inset 0 0 0 2px var(--ink); }

@media (prefers-reduced-motion: reduce) {
	.spick__now::after { transition: none; }
}

.spick__opts { display: flex; flex-direction: column; gap: 3px; }
.spick__opts[hidden] { display: none; }

.spick__btn {
	padding: .35rem .45rem;
	min-width: 0;
	border: 1px solid var(--line);
	background: transparent;
	color: var(--muted);
	font: inherit;
	font-size: .6875rem;
	font-weight: 700;
	line-height: 1.2;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	cursor: pointer;
	transition: background .12s, border-color .12s, color .12s;
}

/* Посочването показва цвета, който ЩЕ сложи — същият договор като при
   копчетата в панела под схемата. */
.spick__btn:hover,
.spick__btn:focus-visible { border-color: var(--ink); color: var(--ink); }

.spick__btn--free.is-on,
.spick__btn--free:hover,
.spick__btn--free:focus-visible {
	background: var(--free); border-color: var(--free); color: #fff;
}
.spick__btn--reserved.is-on,
.spick__btn--reserved:hover,
.spick__btn--reserved:focus-visible {
	background: color-mix(in srgb, var(--resv) 26%, #fff);
	border-color: color-mix(in srgb, var(--resv) 55%, transparent);
	color: #6b4a15;
}
.spick__btn--sold.is-on,
.spick__btn--sold:hover,
.spick__btn--sold:focus-visible {
	background: transparent;
	border-color: var(--sold-line);
	color: var(--sold-ink);
}

/* Текущият не е и копче — натискането му не прави нищо, а курсорът, който
   обещава действие, кара човека да натисне втори път и да се чуди. */
.spick__btn.is-on { cursor: default; }

.spick.is-busy { opacity: .5; pointer-events: none; }
.spick.is-error .spick__btn { border-color: var(--accent); color: var(--accent); }

/* Ред, който администраторът току-що е променил и който вече не отговаря на
   включения филтър. Остава на мястото си, но си личи, че е изключение —
   иначе таблицата тихо противоречи на чипа „Само свободни“ над себе си. */
.utable__row.is-kept > .utable__td:first-child { box-shadow: inset 3px 0 0 0 var(--pick); }

/* -------------------------------------------------------------------------
   Карти на единиците — наличностите на обект от къщи
   -------------------------------------------------------------------------
   Таблото подрежда клетки в сграда; четири къщи нямат какво да наредят, а
   данните им се събират в един екран. Затова обект от къщи показва единицата
   цяла тук, вместо да я праща на второ натискане (клиентът, 10.09.2026).

   Кадърът е вляво, данните вдясно (Габе, 11.09.2026): къщата се разпознава по
   вида си, не по числата. Двете колони живеят на `.ucard--media`, тоест
   единица без кадър запазва подредбата на цяла ширина — картата не е длъжна да
   има снимка, а празна кутия е по-лоша от липсваща колона.

   Разпределението е трети ред през цялата ширина: то е чертеж, който се чете,
   а не плочка до текст.
   ------------------------------------------------------------------------- */

.ucards { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); }

/* Точно две къщи — една до друга, за да се сравняват с очи, не с памет.
   Прагът е 72rem, не 64rem: мярката, която решава, не е ширината на екрана, а
   колко остава ВЪТРЕ в картата. При 1152px шелът дава 1072, тоест по ~516px на
   колона и ~452 под подложката — точно колкото искат двете колони мерки
   (`.ucard__specs`, minmax 13rem). При 1024 те се свиват в една и картата
   става двойно по-висока, тоест сравнението, заради което къщите са една до
   друга, пак иска скрол. Под прага двете се връщат една под друга, цели.
   `minmax(0, 1fr)`, не `1fr`: чертежът вътре има своя ширина и без нулевия
   минимум колоната се разтяга по него, вместо да го свие. */
@media (min-width: 72rem) {
	.ucards--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }

	/* Ситуацията е на ПАРЦЕЛА, не на къща — тя е трето дете на решетката и без
	   това щеше да падне в лявата клетка на втория ред, с празно поле до себе
	   си. Цял ред: чертежът е широк и показва и двете къщи. */
	/* `align-items: start`, не разтягане: при Акчелар Къща 3 носи кадър, описание
	   и разпределение, а Къща 4 — четири реда мерки. Опъната до същата височина
	   втората става кутия, която е три четвърти празна, и празнотата се чете
	   като „тук нещо липсва“. Всяка карта е висока колкото данните си. */

	.ucards--pair .ucard__plan--situation { grid-column: 1 / -1; }

	/* Разстоянието между колоните мерки е вързано за ШИРИНАТА НА ЕКРАНА (4vw),
	   а картата вече е половината от него: при 1152px това са 48px хаос между
	   „Обща площ“ и „Етажи“, и заради тях двете колони не се побират — мерките
	   падат една под друга и картата се удължава с четири реда. Вътре в
	   двойката разстоянието се мери по картата, не по прозореца. */
	.ucards--pair .ucard__specs { gap: 0 clamp(1.25rem, 2vw, 2rem); }

	/* В половин колона кадърът вляво е 34% от ~450px — лента от 150px, в която
	   къща не се вижда. В двойката картата пак е една колона: кадър отгоре,
	   данни отдолу, както е на телефон. */
	.ucards--pair .ucard--media { display: block; }
	.ucards--pair .ucard--media .ucard__media { aspect-ratio: 16 / 9; height: auto; }
}

.ucard { background: var(--paper-alt); border: 1px solid var(--line); }

/* Резервираната не е покана: тя стои в списъка, за да се вижда, че обектът е
   разпродаден, а не за да се натисне. Спокоен фон, без открояване. */
.ucard--reserved { background: var(--paper); }

@media (min-width: 900px) {
	/* Две колони за първия ред, една за разпределението под тях. `auto` на
	   втория ред, не `1fr`: чертежът е с естествената си височина. */
	.ucard--media {
		display: grid;
		grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
		grid-template-rows: auto auto;
	}
	.ucard--media .ucard__plan { grid-column: 1 / -1; }
	.ucard--media .ucard__media { aspect-ratio: auto; height: 100%; }
}

/* Кадърът пълни колоната си на височина — картата се мери по данните вдясно,
   които са ту три реда, ту седем. `object-fit` е и причината да няма
   съотношение тук: рендерът е 16:9, но колоната не е. */
/* Съотношението е за подредбата в една колона: там кадърът е лента отгоре и
   рендерът е 16:9. При двете колони то отпада — колоната се мери по данните
   вдясно, а `object-fit` реже излишното. Без изричното `aspect-ratio` тук
   `height:100%` върху родител с автоматична височина се изражда в лента от
   няколко десетки пиксела. */
.ucard__media { display: block; overflow: hidden; background: var(--line); aspect-ratio: 16 / 9; }
.ucard__img { display: block; width: 100%; height: 100%; object-fit: cover; }

.ucard__content {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
	padding: clamp(1.25rem, 3vw, 2rem);
}

.ucard__head {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1rem;
}

.ucard__eyebrow {
	margin: 0 0 .35rem;
	font-size: .75rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--accent);
}

.ucard__title { margin: 0; font-size: clamp(1.5rem, 3vw, 2.25rem); font-weight: 300; letter-spacing: -.02em; }
.ucard__title a { color: inherit; text-decoration: none; }
.ucard__title a:hover { color: var(--accent); }

.ucard__status { margin: 0; }

.ucard__specs {
	display: grid;
	gap: 0 clamp(1.5rem, 4vw, 3rem);
	margin: 0;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}

.ucard__spec {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	padding-block: .7rem;
	border-bottom: 1px solid var(--line);
}

/* „Още“ е изречение, не число — цял ред, и стойността се подрежда отляво
   до етикета си, вместо да бяга в десния край като мярка. */
.ucard__spec--wide { grid-column: 1 / -1; justify-content: flex-start; gap: 2rem; }
.ucard__spec--wide dd { font-weight: 400; }

.ucard__spec dt { color: var(--muted); font-size: .875rem; }
.ucard__spec dd { margin: 0; font-size: .875rem; font-weight: 700; font-variant-numeric: tabular-nums; }

.ucard__desc, .unit__desc { margin: 0; }
.ucard__desc p, .unit__desc p { margin: 0 0 1rem; }
.ucard__desc p:last-child, .unit__desc p:last-child { margin-bottom: 0; }
.ucard__desc { font-size: .9375rem; }
.unit__desc { margin-block: 0 2rem; }

/* Цената и поканата са едно и също изречение и стоят на един ред, долепени до
   долния край на картата — `margin-top:auto` в колоната с flex. */
.ucard__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	justify-content: space-between;
	gap: 1rem 2rem;
	margin-top: auto;
	padding-block-start: .25rem;
}

.ucard__money { min-width: 0; }
.ucard__price { margin: 0; font-size: clamp(1.75rem, 4vw, 2.5rem); font-weight: 300; font-variant-numeric: tabular-nums; }
.ucard__price--none { font-size: 1.25rem; color: var(--muted); }
.ucard__rate { margin: .15rem 0 0; font-size: .8125rem; color: var(--muted); }

.ucard__cta { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem; margin: 0; }
.ucard__more { font-size: .8125rem; color: var(--muted); }
.ucard__more:hover { color: var(--accent); }

.ucard__plan { margin: 0; padding: 0 clamp(1.25rem, 3vw, 2rem) clamp(1.25rem, 3vw, 2rem); }
.ucard__plan-cap {
	margin: 0 0 .75rem;
	font-size: .6875rem;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--muted);
}
.ucard__plan a { display: block; }
.ucard__plan img {
	display: block;
	width: auto;
	max-width: 100%;
	height: auto;
	max-height: 70vh;
	margin: 0 auto;
	background: #fff;
	border: 1px solid var(--line);
	border-radius: 6px;
	padding: .5rem;
}

/* Ситуацията е на парцела, не в картата на къща — тя стои сама под тях и има
   собствена рамка, за да не се чете като разпределение на последната карта. */
.ucard__plan--situation {
	padding: clamp(1.25rem, 3vw, 2rem);
	background: var(--paper-alt);
	border: 1px solid var(--line);
}

/* Чертежът е завъртян на пейзаж (11.09.2026) и иска ширината, не височината:
   `max-height` е за портретно разпределение, а тук само го смалява до нечетимо.
   Отменя се СЛЕД правилото, което го въвежда — медийна заявка не добавя
   специфичност, а редът е единственото, което решава (виж README „Five traps“). */
.ucard__plan--situation img { width: 100%; max-height: none; }

/* ---- Видео от обекта --------------------------------------------------
   Плейърът на WordPress (MediaElement) си мери кутията в ПИКСЕЛИ и ги пише
   inline при зареждане — 1280×720, взети от атрибутите на късия код. Оттам и
   двата проблема, ако не се пипне: на тесен екран кутията стърчи извън шела, а
   вътре в нея кадърът е позициониран абсолютно, тоест щом ѝ се даде
   `height: auto`, тя се свива до нула и видеото ляга ВЪРХУ визуализациите под
   себе си. Точно това се случи първия път.

   Затова височината идва от съотношение, а не от пиксели, и всички слоеве на
   плейъра се разпъват в нея. `!important` не е мързел — inline стилът на
   MediaElement се бие само така. */

.vid { max-width: 1000px; }

.vid .wp-video { width: 100% !important; max-width: 100%; }

.vid .mejs-container {
	position: relative;
	width: 100% !important;
	height: auto !important;
	aspect-ratio: 16 / 9;
	background: var(--ink);
}

.vid .mejs-inner,
.vid .mejs-mediaelement,
.vid .mejs-layers,
.vid .mejs-overlay,
.vid .mejs-poster {
	position: absolute;
	inset: 0;
	width: 100% !important;
	height: 100% !important;
}

.vid .mejs-poster { background-size: cover; background-position: center; }

/* `contain`, не `cover`: рендерът е 16:9, но заснетото с телефон видео не е, а
   изрязаното от единия край не се връща. */
.vid .mejs-mediaelement video,
.vid video.wp-video-shortcode {
	width: 100% !important;
	height: 100% !important;
	object-fit: contain;
}

.vid .mejs-controls { z-index: 2; }

/* Без JavaScript MediaElement не се вдига и остава голият <video> на ядрото —
   той трябва да изглежда като нещо, а не като лента от няколко пиксела. */
.vid > video.wp-video-shortcode {
	display: block;
	position: static;
	height: auto !important;
	aspect-ratio: 16 / 9;
	background: var(--ink);
}

/* ---- Няколко ленти на един обект --------------------------------------
   Комплекс с няколко сгради има няколко графика. Заглавието над лентата е
   това, което ги различава; разстоянието между тях трябва да е по-голямо от
   разстоянието вътре в лентата, иначе двете се четат като една дълга. */

.stages__track-wrap + .stages__track-wrap { margin-block-start: clamp(2rem, 4vw, 3.25rem); }

.stages__title {
	margin: 0 0 clamp(1rem, 2vw, 1.5rem);
	font-size: clamp(1.0625rem, 1.6vw, 1.25rem);
	font-weight: 600;
	letter-spacing: -.01em;
	color: var(--text);
}

/* „Лента 2“ е резервният надпис, когато клиентът не е дал заглавие — казва
   само, че това е друг график, и няма защо да тежи колкото истинско име. */
.stages__title--n { font-weight: 500; color: var(--muted); }
