/* ==========================================================================
   Khora Foods — frontend fixes (2026-08-11)

   Loaded from wp-content/mu-plugins/001-frontend-fixes.php, after Oxygen's
   universal.css. Kept out of Oxygen's own stylesheet so a CSS regeneration
   in the builder cannot wipe it. If you would rather manage it in Oxygen,
   paste it into a stylesheet there and delete this file plus its loader.
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Optical vertical centring of headings and labels
   --------------------------------------------------------------------------
   Headings site-wide use Le Murmure. Its vertical metrics are strongly
   asymmetric (measured from the font file):

       unitsPerEm 900 · ascender 750 · capHeight 730 · descender 250

   That leaves 20 units of space above the capitals but 250 below the
   baseline — 230 units, or 0.2556em, of phantom space that always sits at
   the BOTTOM of the line box. At the 36px heading size that is 9.2px, which
   is why every label looked pushed upwards inside its box even though the
   container padding was symmetric (10px / 10px).

   The fix is not padding — it is trimming the line box down to the part that
   is actually inked: cap-height at the top, baseline at the bottom. Then any
   symmetric padding produces genuinely symmetric spacing.

   Measured on /blog/ category buttons:
       before — heading box 40.5px, 11.05px above caps vs 20.25px below baseline
       after  — heading box 29.20px (= exactly capHeight), 10px vs 10px

   text-box is supported in Chrome 133+, Edge and Safari 18.2+.

   IMPORTANT — the selector list has to cover every text element that can sit
   in the same layout box as another, or the trim itself creates the
   unevenness it was meant to fix. Trimmed elements lose ~0.36em of line box;
   untrimmed neighbours keep it, so a flex column with a fixed gap ends up
   with visibly different spacing between its rows.

   That is exactly what happened in the footer: the contacts column is a flex
   column with gap:15px holding

       a.ct-link-text  "Контакти"                 -> 15.4px (trimmed)
       div.oxy-rich-text "Instagram: @khora.foods" -> 28.6px (not trimmed)
       div.oxy-rich-text "Email: info@..."         -> 28.6px (not trimmed)

   while the links column next to it is three ct-link-text in a row and
   stayed even. Hence .oxy-rich-text and .ct-text-block below. */

@supports (text-box: trim-both cap alphabetic) {
	h1, h2, h3, h4, h5, h6,
	.ct-headline,
	.ct-link-text,
	.ct-text-block,
	.oxy-rich-text,
	.ct-link-button,
	.oxy-button,
	.woocommerce a.button,
	.woocommerce button.button,
	.woocommerce .single_add_to_cart_button {
		text-box: trim-both cap alphabetic;
	}
}

/* text-box-trim only works on BLOCK containers.
   ------------------------------------------------------------------
   A flex container's children are flex items, not line boxes, so there is no
   first/last line to trim and the declaration is silently ignored — the
   computed value still reads "trim-both", which makes this very easy to miss.

   Two places on this site hit that:

   1. The "Про нас" button. Its heading carries display:flex from the builder
      (used only to centre a single text node — justify-content:center), so it
      stayed 36px while the identical "Купити" heading trimmed to 22.4px, and
      the two buttons looked different. One element out of 29 site-wide.

   2. The nav menu links (МАГАЗИН / КОНТАКТИ). These are flex too and were not
      in the trim list at all, so the label sat 5px above centre inside the
      black/yellow pill: 10.8px above the capitals against 15.8px below the
      baseline. КОШИК next to them is a trimmed .ct-headline, which is why it
      looked right and they did not.

   Restricting to :not(:has(*)) keeps this to elements that hold nothing but
   text, where flex has no layout job to do — anything with real children is
   left alone.

   The nav links need their padding rebuilt: trimming removes the line box's
   leading, which would drop the pill from 43.6px to 26.8px and visibly change
   the header. calc(0.35em + 5px) restores it exactly — 0.35em is half of
   (1.4em line-height − 0.7em cap height), plus the original 5px.

   !important throughout because Oxygen sets display and padding through id
   selectors, which outrank any class-based rule. */

@supports (text-box: trim-both cap alphabetic) {
	html body .ct-headline:not(:has(*)),
	html body .ct-link-text:not(:has(*)) {
		display: block !important;
	}

	html body .oxy-nav-menu .menu-item > a:not(:has(*)) {
		display: block !important;
		text-box: trim-both cap alphabetic !important;
		padding-top: calc(0.35em + 5px) !important;
		padding-bottom: calc(0.35em + 5px) !important;
	}
}

/* Fallback for engines without text-box (Firefox at time of writing).
   A negative bottom margin equal to the asymmetry pulls the container's
   bottom edge up by the same 0.2556em, which balances the box in exactly
   the same way whenever the container's height is driven by its content —
   which is the case for the category buttons, nav labels and card titles.
   Scoped to Le Murmure headings: the value is specific to that font's
   metrics and would be wrong for Golos Text or Bebas Neue Pro. */
@supports not (text-box: trim-both cap alphabetic) {
	h1, h2, h3, h4, h5, h6,
	.ct-headline {
		margin-bottom: -0.2556em;
	}
}


/* --------------------------------------------------------------------------
   2. Cart sidebar must sit above the sticky header
   --------------------------------------------------------------------------
   Oxygen's sticky header uses z-index 2147483640 (INT_MAX − 7) once
   .oxy-sticky-header-active is applied. The cart sidebar ships with 999999
   for the panel and 999998 for the overlay, so the header covers both.

   The obvious fix — raising the cart above the header — DOES NOT WORK, and
   this was measured rather than assumed. With the panel open and the header
   in its sticky state, hit-testing the overlap region gave:

       header z        cart z        overlay z     what paints on top
       2147483640      2147483646    2147483645    header      <- wrong
       2147483640      2147483647    2147483645    header      <- wrong
         100000          999999        999998      cart        <- correct
           9999          999999        999998      cart        <- correct

   Even z-index 2147483647 (the exact 32-bit maximum) loses to the header's
   2147483640. Up at the edge of the int range the browser stops ordering
   layers the way the numbers imply, so Oxygen's value behaves as an
   unbeatable "always on top" rather than as a comparable number.

   So the header comes DOWN instead. 100000 is still far above anything in
   the page content, but below the cart's own 999999 / 999998, which are left
   at the plugin's defaults — no override needed on the cart at all.

   Side effect, and it is the correct behaviour: Oxygen's carousel fullscreen
   controls (2147483641–2147483643) now sit above the sticky header too.

   Note on the existing override in the Oxygen stylesheet:

       .vi-wcaio-sidebar-cart-wrap, .vi-wcaio-sidebar-cart { z-index: 999999 !important }

   the -wrap half of that rule does nothing at all, because that element is
   position: static and z-index is ignored on non-positioned elements. The
   -cart half merely restates the plugin's own default. Harmless, but it is
   not what was holding the sidebar down.

   The selector is prefixed with `html body` on purpose. Oxygen prints its
   generated stylesheets after the normal enqueue queue (verified: this file
   lands at index 23 in <head>, universal.css at 28), so with equal
   specificity the older rule would win the !important tie on source order.
   Raising specificity to (0,1,2) settles it regardless of load order. */

html body .oxy-sticky-header.oxy-sticky-header-active {
	z-index: 100000 !important;
}


/* --------------------------------------------------------------------------
   3. Header cart button height
   --------------------------------------------------------------------------
   The КОШИК button did not line up with МАГАЗИН / КОНТАКТИ next to it, and it
   changed size depending on whether the cart had anything in it. Measured:

       МАГАЗИН / КОНТАКТИ   a inside li   43.6px  (33.6px line-height + 5px×2)
       КОШИК                a.ct-link     50.0px  (padding 10px×2)
        ├ h2.ct-headline    "Кошик"       16.8px
        └ div.oxy-cart-counter "1"        30.0px  <- Oxygen fixes this at 30px

   The height is set by the COUNTER, not by the label. With an item in the
   cart the badge is 30px, so 30 + 20 padding = 50px, taller than 43.6. Empty
   cart removes the badge, leaving 16.8 + 20 = 36.8px — shorter than 43.6.
   Hence "smaller when empty, bigger once a product is added".

   Trimming the label (section 1) shrank it from 27px to 16.8px, which widened
   the gap in the empty state; it did not cause the mismatch, since the badge
   was already the taller of the two.

   Fix: pin the button to the nav items' height and drop the vertical padding
   so the 30px badge cannot inflate it. align-items:center is already set on
   the link, so both the label and the badge stay centred, and the button now
   measures 43.6px in both states. min-height (not height) leaves room should
   the badge ever grow, e.g. a three-digit count.

   Targeted structurally rather than by Oxygen's generated id, so it survives
   the header being rebuilt in the builder. !important is needed because
   Oxygen sets the padding through an id selector (#div_block-79-19), which
   outranks any class-based selector however long. */

html body .oxy-header-right .ct-link:has(.oxy-cart-counter),
html body .oxy-header .ct-link:has(.oxy-cart-counter) {
	min-height: 43.6px !important; /* = nav item: 33.6px line-height + 5px top/bottom */
	padding-top: 0 !important;
	padding-bottom: 0 !important;

	/* Horizontal padding was 15px left against 5px right, which reads as
	   off-centre — most visibly on mobile, where the button is wider relative
	   to its label. Evened up.

	   The label also carried margin-right:10px to hold the counter off it.
	   That margin stays behind as dead space on the right whenever the counter
	   is hidden (empty cart), tipping the button left again. Moved to a flex
	   gap on the button instead, so the 10px only exists between two visible
	   items and disappears with the badge.

	   The right padding is 5px rather than 15px on purpose. The counter is a
	   30px round chip whose background is rgb(252,197,116) — the same colour
	   as the button, so it is invisible — holding a digit only ~11px wide.
	   That leaves ~9.5px of blank chip on each side of the number, which
	   reads as extra space rather than as part of the badge. Measured with
	   15px: 21px from the word to the digit but 26px from the digit to the
	   edge — visibly lopsided. Pulling the padding to 5px puts the digit's
	   ink 16px from the edge against the label's 15px on the left.

	   The flex gap is 5px rather than 10px for the same reason: the chip
	   contributes ~9.5px of its own blank space before the digit, so a 10px
	   gap read as 21px between the word and the number while the outer
	   padding was ~15px. Measured across values — 10px → 15/21/16,
	   6px → 15/17/16, 5px → 15/16/16, 4px → 15/15/16. 5px is the point where
	   all three spaces land within a pixel of each other, without cramping
	   the number against the word.

	   The empty-cart rule below restores 15px padding and drops the gap,
	   since with no chip there is no slack to compensate. */
	padding-left: 15px !important;
	padding-right: 5px !important;
	gap: 5px !important;
}

html body .oxy-header-right .ct-link:has(.oxy-cart-counter) > .ct-headline,
html body .oxy-header .ct-link:has(.oxy-cart-counter) > .ct-headline {
	margin-right: 0 !important;
}

/* Empty cart: the counter keeps its flex slot at zero width, so the gap above
   would still render and push the label 10px left of centre. CSS cannot test
   a rendered width or an element's text, so khora-frontend-fixes.js flags the
   state and the gap collapses here. */
html body .oxy-header .ct-link.khora-cart-nocount {
	gap: 0 !important;
	padding-right: 15px !important; /* no chip to compensate for — plain 15/15 */
}

/* The number inside the badge sat 2px high — 6px of clearance above the digit
   against 10px below, in a fixed 30×30 badge.

   The usual trim cannot help here: the badge has an explicit height (it is the
   coloured chip itself), so there is no leading for text-box to remove — the
   box is set, not derived from the line box. Verified: forcing display:block
   and trimming left the height at 30px and the digit exactly where it was.

   So the glyph is nudged instead. 0.1em = 2px at the badge's 20px font, which
   is half the 4px imbalance, and it scales if the size ever changes. Digits
   all share one ink height, so this holds for 2- and 3-digit counts too.
   Result: 8px above, 8px below. */
html body .oxy-cart-counter .oxy-cart-counter_number {
	position: relative !important;
	top: 0.1em !important;
}


/* --------------------------------------------------------------------------
   4. Cart sidebar — quantity stepper
   --------------------------------------------------------------------------
   Measured before changing anything: the input box already sits exactly
   between the two buttons (− centre 1133.5, input centre 1168.5, + centre
   1203.5 — evenly 35px apart) and already carries text-align:center, with
   1.3px of clearance above and below. So the boxes were not the problem.

   What is left that can shift the digit off-centre is the browser: a
   number input reserves room for its spin buttons on the inline-end side,
   so the visible digit can render left of the true centre even though the
   element is centred. The rules below remove that reservation and state the
   centring explicitly on all three cells, so the control is symmetric
   regardless of engine.

   The − and + glyphs were inheriting text-align:start inside their 20px
   cells, which also reads as lopsided next to a centred number.

   Specificity and !important are deliberate here. The plugin ships its own
   rules for this control in sidebar-cart-content.min.css AND generates more
   inline from its settings, printed after the normal enqueue queue — the same
   ordering trap already hit twice on this site (Oxygen's stylesheets, and the
   cart z-index). Left at plain class specificity these rules win on a cold
   load and quietly lose whenever the plugin's CSS is regenerated, which looks
   exactly like "it drifts again after a restart". Pinned so load order cannot
   decide the outcome. */

html body .vi-wcaio-sidebar-cart-pd-quantity input.vi_wcaio_qty::-webkit-outer-spin-button,
html body .vi-wcaio-sidebar-cart-pd-quantity input.vi_wcaio_qty::-webkit-inner-spin-button {
	-webkit-appearance: none !important;
	appearance: none !important;
	margin: 0 !important;
}

html body .vi-wcaio-sidebar-cart-pd-quantity input.vi_wcaio_qty {
	-moz-appearance: textfield !important;
	appearance: textfield !important;
	text-align: center !important;
	padding-left: 0 !important;
	padding-right: 0 !important;
	text-indent: 0 !important;
}

html body .vi-wcaio-sidebar-cart-pd-quantity .vi_wcaio_change_qty {
	display: flex !important;
	align-items: center !important;
	justify-content: center !important;
	text-align: center !important;
}


/* --------------------------------------------------------------------------
   5. Hero button pair ("Про нас" / "Купити")
   --------------------------------------------------------------------------
   "Про нас" looked crooked while "Купити" looked fine, and only on phones —
   which is why it did not show up in the desktop measurements. Measured in a
   real 410px viewport:

       Про нас   padding 1px top / 5px bottom   text 13 above, 17 below
                                                arrow 2 above, 6 below
       Купити    padding 5px / 5px              text 16 / 16, arrow 5 / 5

   Oxygen's @media (max-width:479px) block sets padding-top:1px on that one
   button while the bottom stays 5px. It is an authoring slip in the design,
   not a cascade problem: of the 27 .ct-link elements on the page exactly one
   has uneven vertical padding. Restoring 5px on both sides squares it up.

   The two buttons were also 2px apart in height (48 vs 46) because the
   outlined one carries a 1px border top and bottom while the filled one has
   none. Rather than subtracting the border from the padding — which would
   hard-code the border width — the row is set to align-items:stretch, so both
   buttons take the row height and each centres its own content. Generic, and
   it survives any future change to the border.

   Scoped to a .ct-div-block whose children are two adjacent .ct-link buttons;
   exactly one element matches site-wide, so nothing else is affected. */

html body .ct-div-block:has(> a.ct-link + a.ct-link) {
	align-items: stretch !important;
}

html body .ct-div-block:has(> a.ct-link + a.ct-link) > a.ct-link {
	padding-top: 5px !important;
	padding-bottom: 5px !important;
}


/* --------------------------------------------------------------------------
   6. Side-by-side photos in recipe posts
   --------------------------------------------------------------------------
   The pair of photos in the recipe body rendered far smaller than the space
   they had. The layout was never the problem: the container is 704px wide,
   display:flex with gap:20px, and each <picture> already measured 342px —
   exactly 50% minus the gap. The <img> inside was the thing that stayed
   small, at 164px.

   Reason: the image is sized from a height rather than a width, so it scales
   to fit that height and its width falls out of the aspect ratio
   (1100 × 1466 source → 218.8px tall → 164.2px wide). Giving the img
   width:100% and height:auto lets it fill the cell it already had:
   164 → 342px, height following at 456px. The originals are 1100px wide, so
   there is plenty of resolution — no upscaling.

   Targeted by shape rather than by Oxygen's generated id, so new recipe posts
   get the same behaviour without another rule: any .ct-div-block holding two
   or more adjacent <picture> children. flex:1 1 0 makes the split even
   regardless of the images' intrinsic sizes, and min-width:0 stops a wide
   image from pushing past its half. */

html body .ct-div-block:has(> picture + picture) {
	display: flex !important;
	gap: 20px !important;
	align-items: flex-start !important;
}

html body .ct-div-block:has(> picture + picture) > picture {
	flex: 1 1 0 !important;
	min-width: 0 !important;
}

html body .ct-div-block:has(> picture + picture) > picture > img {
	display: block !important;
	width: 100% !important;
	height: auto !important;
}


/* --------------------------------------------------------------------------
   7. Recipe category row
   --------------------------------------------------------------------------
   Links to empty categories are removed server-side in the loader plugin.
   If every category ends up empty the row itself would collapse to an empty
   flex box that still contributes its top margin — hide it in that case. */

#div_block-171-443:empty {
	display: none;
}


/* --------------------------------------------------------------------------
   8. Intrinsic image dimensions
   --------------------------------------------------------------------------
   The loader plugin (002-native-lazy.php) adds width and height attributes to
   Oxygen's images so the browser can reserve their space before they load.
   Those attributes also set the used width and height, which would squash any
   image that Oxygen sizes with max-width: 100% — the whole site. height: auto
   hands the height back to the aspect ratio, which is the same pairing core
   WordPress themes use. Scoped to images that actually carry the attributes,
   so nothing else changes.

   Images whose CSS pins a height are never given the attributes in the first
   place: Oxygen sets that height from an id selector, which outranks this
   rule. See kf_lazy_css_pinned_height_ids(). */

img.ct-image[width][height] {
	height: auto;
}
