/**
 * izebuy UI components (A1).
 * Buttons · Cards · Listing card · Badges · Chips · Scroller
 * Form controls · Section header · Skeleton · Utilities · Accessibility
 * Depends on: tokens.css
 */

/* ── Breadcrumb ────────────────────────────────────────────────────────────
 * Shared trail used on category archives, the sellers directory and other
 * izebuy pages. Right-aligned on every page; colour follows --ize-breadcrumb
 * (editable in Theme Options → Appearance). */
.ize-breadcrumb {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: flex-end;
	gap: var(--ize-1) var(--ize-2);
	padding: var(--ize-2) 0;
	font-size: var(--ize-fs-breadcrumb);
	color: var(--ize-breadcrumb);
}
.ize-breadcrumb a {
	color: var(--ize-breadcrumb);
	text-decoration: none;
}
.ize-breadcrumb a:hover { opacity: 0.75; }
.ize-breadcrumb__sep { color: var(--ize-breadcrumb); opacity: 0.6; }
.ize-breadcrumb__current { color: var(--ize-breadcrumb); font-weight: 600; }

/* Legacy breadcrumb trail: classifieds_get_breadcrumbs() outputs a Bootstrap
 * <ul class="breadcrumb"> inside .page-title (single ad, category, search…).
 * Keep it right-aligned and clean (no Bootstrap grey pill), to match
 * .ize-breadcrumb above. */
.page-title .breadcrumb {
	margin: 0;
	padding: var(--ize-3) 0;
	list-style: none;
	background: none;
	text-align: right;
	font-size: var(--ize-fs-breadcrumb);
}
.page-title .breadcrumb > li { display: inline-block; }
.page-title .breadcrumb a { color: var(--ize-breadcrumb); text-decoration: none; }
.page-title .breadcrumb a:hover { opacity: 0.75; }

/* Page heading band — the title + breadcrumb row below the header (pages, blog,
 * category, search…). Give the title clear room under the nav, and sit the
 * breadcrumb opposite it, vertically centred against the title (so it reads as
 * secondary, about half the title's height) rather than floated to the top.
 * The breadcrumb-only variant (single posts/ads, author) keeps its own compact
 * spacing above and is untouched. */

/* THE shared title bar — one component for every standard page (default, full
 * width, sidebars, side menu, contact, all-categories, my-profile, blog listing).
 * It lines up with the page content below it: the cards, listings and page body
 * all sit in a --ize-container box with a --ize-4 gutter, so the title starts
 * exactly where the card starts and the breadcrumb ends exactly where the card
 * ends — clear of the screen walls. Plain, unconditional rule (no body class) so
 * it can never fall out of sync. Bootstrap's .container is a narrower FIXED width,
 * so we override it to --ize-container and keep it auto-centred. */
.page-title .container {
	width: auto;
	max-width: var(--ize-container);
	margin-left: auto;
	margin-right: auto;
	padding-left: var(--ize-4);
	padding-right: var(--ize-4);
}
.page-title .clearfix {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--ize-2) var(--ize-5);
	padding: var(--ize-6) 0 var(--ize-4);   /* mobile: 32px clear of the nav, 16px below */
}
@media (min-width: 768px) {
	/* More room under the nav on larger screens. */
	.page-title .clearfix { padding: var(--ize-7) 0 var(--ize-5); }
}
/* Let flexbox place the two sides; Bootstrap's float would break the centring. */
.page-title .clearfix .pull-left,
.page-title .clearfix .pull-right {
	float: none;
}
/* Bootstrap's .clearfix adds ::before/::after pseudo-elements (display:table) to
 * clear floats. In this flex row they become flex items and, with space-between,
 * grab the two EDGE slots — shoving the real title and breadcrumb into the middle.
 * THIS is what made the title look "centred". Drop them from the flex flow so only
 * the title and breadcrumb are spaced apart. */
.page-title .clearfix::before,
.page-title .clearfix::after {
	display: none;
}
/* Spacing comes from the band, so drop the heading's default outer margin. */
.page-title .clearfix h1 {
	margin: 0;
}
/* The flex row already centres the breadcrumb against the title. */
.page-title .clearfix .breadcrumb {
	padding: 0;
}
@media (max-width: 767px) {
	/* Stack on small screens: title on top, breadcrumb beneath it, both flush
	 * left. Side by side they're too cramped, and letting them wrap left the
	 * title drifting across the row. */
	.page-title .clearfix {
		flex-direction: column;
		align-items: flex-start;
		justify-content: flex-start;
		gap: var(--ize-2);
	}
	.page-title .clearfix .breadcrumb { text-align: left; }
}

/* ── Buttons ───────────────────────────────────────────────────────────── */
.ize-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--ize-2);
	padding: 10px 18px;
	font-size: var(--ize-fs-btn);
	font-weight: 600;
	line-height: 1;
	border: none;
	border-radius: var(--ize-radius);
	cursor: pointer;
	text-decoration: none;
	font-family: var(--ize-font-btn);
	transition: background-color 0.15s var(--ize-ease), color 0.15s var(--ize-ease);
}
/* General buttons use the brand green with dark ink (accessible); Submit stays accent. */
.ize-btn--primary { background: var(--ize-btn-bg); color: var(--ize-btn-ink); }
.ize-btn--primary:hover { background: var(--ize-btn-bg-hover); color: var(--ize-btn-ink); }
/* Dark words on the orange, as on the green: white on it is 2.7:1. */
.ize-btn--accent { background: var(--ize-accent); color: #000; }
.ize-btn--accent:hover,
.ize-btn--accent:focus,
.ize-btn--accent:active { background: var(--ize-accent-700); color: #000; }
.ize-btn--ghost { background: var(--ize-surface-2); color: var(--ize-ink); }
.ize-btn--ghost:hover { background: var(--ize-surface-3); color: var(--ize-ink); }
/* Only for what can't be undone (Delete account). White on it is 4.8:1. */
.ize-btn--danger { background: var(--ize-danger); color: #fff; }
.ize-btn--danger:hover,
.ize-btn--danger:focus { background: #b42318; color: #fff; }
.ize-btn--block { display: flex; width: 100%; }
.ize-btn--sm { padding: 7px 12px; font-size: var(--ize-fs-sm); }

/* No button — or link/input styled as a button — carries a border, anywhere.
 * Keyboard focus ring is restored below with :focus-visible. */
body.ize .btn,
body.ize .ize-btn,
body.ize button,
body.ize a.btn,
body.ize a.ize-btn,
body.ize input[type="submit"],
body.ize input[type="button"],
body.ize input[type="reset"] {
	border: none;
}

/* Legacy Bootstrap buttons (.btn on not-yet-redesigned pages: profile, blog,
   pricing) use the one brand-green button colour, same as .ize-btn.
   The Submit Ad button is .ize-btn--accent (handled above), so it's unaffected. */
body.ize .btn:not(.ize-btn) {
	background-color: var(--ize-btn-bg);
	color: var(--ize-btn-ink);
}
body.ize .btn:not(.ize-btn):hover,
body.ize .btn:not(.ize-btn):focus,
body.ize .btn:not(.ize-btn):active {
	background-color: var(--ize-btn-bg-hover);
	color: var(--ize-btn-ink);
}

/* …and the app's size and shape: Bootstrap's are 14px, square-ish (issue #58).
   Components that size their own (the sign-in box's submit) come later and win. */
body.ize .btn:not(.ize-btn) {
	padding: 10px 18px;
	font-size: var(--ize-fs-btn);
	font-weight: 600;
	line-height: 1.25;
	border-radius: var(--ize-radius);
}
body.ize .btn-sm:not(.ize-btn),
body.ize .btn-xs:not(.ize-btn) {
	padding: 7px 12px;
	font-size: var(--ize-fs-sm);
}
body.ize .btn-lg:not(.ize-btn) {
	padding: 12px 22px;
	font-size: var(--ize-fs-md);
}

/* Bootstrap's form fields on pages not yet redesigned (the blog's search and
   comment form): the app's 16px, and no fixed 34px height to clip it. The
   sign-in and question boxes style their own, further down. */
body.ize .form-control {
	height: auto;
	min-height: 44px;
	padding: 10px var(--ize-3);
	font-size: var(--ize-fs-input);
	line-height: 1.4;
	border-color: var(--ize-line);
	border-radius: var(--ize-radius-sm);
	box-shadow: none;
}
body.ize textarea.form-control { min-height: 110px; }
body.ize .form-control:focus {
	border-color: var(--ize-brand);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--ize-brand) 18%, transparent);
}

/* Accessible keyboard focus ring (EN 301 549 / WCAG 2.4.7) — keyboard only,
 * a thin neutral ring (no brand colour). */
body.ize a:focus-visible,
body.ize .btn:focus-visible,
body.ize .ize-btn:focus-visible,
body.ize button:focus-visible,
body.ize input:focus-visible,
body.ize select:focus-visible,
body.ize textarea:focus-visible {
	outline: 2px solid var(--ize-ink-3);
	outline-offset: 2px;
	border-radius: var(--ize-radius-sm);
}
/* No focus ring or stray box-shadow on mouse / touch — only the keyboard ring
 * above. Removes the "big border" seen when clicking the logo or buttons. */
body.ize a:focus:not(:focus-visible),
body.ize .btn:focus:not(:focus-visible),
body.ize .ize-btn:focus:not(:focus-visible),
body.ize button:focus:not(:focus-visible),
body.ize input:focus:not(:focus-visible),
body.ize select:focus:not(:focus-visible),
body.ize textarea:focus:not(:focus-visible) {
	outline: none;
	box-shadow: none;
}

/* ── Listing card ───────────────────────────────────────────────────────── */
.ize-listing {
	position: relative;   /* anchors the stretched link + the save-star overlay */
	display: flex;
	flex-direction: column;
	background: #fff;
	border: 1px solid transparent;   /* reserved so the hover border doesn't shift layout */
	border-radius: var(--ize-radius);
	overflow: hidden;
	box-shadow: var(--ize-shadow-sm);
	transition: box-shadow 0.15s var(--ize-ease), transform 0.15s var(--ize-ease), border-color 0.15s var(--ize-ease);
}
/* The whole card is one link, layered as a transparent overlay so a real
 * <button> (the save-star) can sit on top of it — valid markup, no nested
 * interactive elements. Card text sits below it; the star opts back above. */
.ize-listing__link {
	position: absolute;
	inset: 0;
	z-index: 1;
}
.ize-listing:hover {
	border-color: var(--ize-line);   /* a light border marks the active/hovered card */
	box-shadow: var(--ize-shadow);
	transform: translateY(-2px);
}
/* Fixed-size media box: a fixed pixel height that never changes with the image,
 * so a tall image can't stretch the card. The image is shown in full
 * (object-fit: contain) and centred — never cropped — so any text/detail at the
 * edges stays visible, letterboxed with white where the aspect ratio differs.
 * Absolute fill also makes it immune to the global `img { height: auto }` reset
 * in feed.css. */
.ize-listing__media {
	position: relative;
	width: 100%;
	height: 220px;
	background: #fff;
	overflow: hidden;
}
.ize-listing__media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
	object-position: center;
	display: block;
}
@media (max-width: 600px) {
	.ize-listing__media { height: 170px; }
}
.ize-listing__badges {
	position: absolute;
	top: var(--ize-2); left: var(--ize-2);
	display: flex;
	gap: var(--ize-1);
	flex-wrap: wrap;
}
.ize-listing__body { padding: var(--ize-3); display: flex; flex-direction: column; gap: var(--ize-1); }
.ize-listing__price { font-size: var(--ize-fs-card-price); font-weight: 700; color: var(--ize-ink); container-type: inline-size; }
/* A price from a million up is in words (classifieds_feed_card_price()), and
   on one line: where a card is too narrow for it — the home feed beside its
   sidebar, a 300px phone — it shrinks to fit rather than breaking, by no more
   than it needs. Its characters run up to 0.59em each at a small size ("MWK
   88.8 million", 16 of them, is 9.4em at 12px); 0.62em leaves a margin. */
@supports (font-size: 1cqi) {
	.ize-listing__price .ize-price--words { white-space: nowrap; font-size: min(1em, 100cqi / (var(--ize-price-chars) * 0.62)) !important; }
	.ize-listing__price .ize-price--was.ize-price--words { font-size: min(0.85em, 100cqi / (var(--ize-price-chars) * 0.62)) !important; }
}
.ize-listing__title {
	font-size: var(--ize-fs-card-title);
	font-weight: 600;
	color: var(--ize-ink-2);
	line-height: 19px;
	margin: 0;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	/* A title with no spaces in it — a part number, a pasted URL — has nowhere
	   to break, so the clamp hid the overflow without ellipsising it and the
	   text was simply cut off mid-word. Let it break, and the clamp does the
	   rest properly. */
	overflow-wrap: anywhere;
}
.ize-listing__meta { font-size: var(--ize-fs-card-meta); color: var(--ize-ink-3); display: flex; gap: var(--ize-1); align-items: center; }
/* The location can be a long freeform place string. Clamp it to a single line
 * with an ellipsis so a long value never grows the card; the date holds its width
 * so it's never the part that gets truncated. Full location stays on the ad page. */
.ize-listing__loc {
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.ize-listing__age { flex: none; white-space: nowrap; }
/* Category chip in the card footer: a quiet, self-sizing pill so it reads as a
 * label, not a button, and never competes with the price/title. */
.ize-listing__cat {
	align-self: flex-start;
	max-width: 100%;
	font-size: var(--ize-fs-card-meta);
	color: var(--ize-ink-2);
	background: var(--ize-surface-2, #f1f3f5);
	border-radius: 999px;
	padding: 2px var(--ize-2);
	display: inline-flex;
	gap: 4px;
	align-items: center;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.ize-listing__cat .fa { font-size: 0.85em; opacity: 0.7; }

/* ── Save star ("save for later") ───────────────────────────────────────────
 * A real <button> overlaid on the card (z-index above the stretched link). The
 * star is the whole control; an unobtrusive tooltip below it carries the single
 * link to the profile's Starred list — there are no other signposts to it.
 * Shared by listing cards (.ize-save-wrap--card) and the single-ad image overlay
 * (.ize-save-wrap--image, laid out in ad-single.css). */
.ize-save-wrap { position: relative; }
.ize-save-wrap--card {
	position: absolute;
	bottom: var(--ize-2);
	right: var(--ize-2);
	z-index: 3;   /* above .ize-listing__link, in the white footer */
}
.ize-save {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border: 0;
	margin: 0;
	cursor: pointer;
	font: inherit;
	background: transparent;
	color: var(--ize-ink-3);
	-webkit-appearance: none;
	appearance: none;
}
.ize-save:focus-visible { outline: 2px solid var(--ize-accent); outline-offset: 2px; border-radius: 50%; }

/* Card heart: just the icon, pinned to the card's white footer (see
 * .ize-save-wrap--card) where it stays legible — no chip or shadow needed. Once
 * saved it fills in red, as in the app (colors.sale). The 34px box stays a
 * comfortable tap target. */
.ize-save-wrap--card .ize-save {
	width: 34px;
	height: 34px;
	padding: 0;
	/* Soft white halo: the star floats over the card's bottom-right corner, so the
	 * meta line can flow underneath it. Over the white footer's whitespace the halo
	 * is invisible; where text passes beneath, it fades out cleanly instead of
	 * colliding with the icon. Lets the text use the full width, star on top. */
	background: radial-gradient(closest-side, #fff 62%, rgba(255, 255, 255, 0) 100%);
	border-radius: 50%;
	transition: transform 0.12s var(--ize-ease), color 0.12s var(--ize-ease);
}
.ize-save-wrap--card .ize-save .fa {
	font-size: 16px;
	line-height: 1;
}
.ize-save-wrap--card .ize-save:hover { transform: scale(1.08); }
.ize-save.is-starred { color: #e02424; }   /* a saved heart is red, as in the app */

/* Tooltip: a small dark box to the LEFT of the star with the profile link inside
 * and a triangle pointing at the star. Shown only when JS adds .is-tip-visible
 * (on tap/click, and only when the ad was just starred), then auto-hidden — never
 * on hover, so it can't get stuck on touch. A mouse user can hover the box itself
 * to keep it open long enough to click the link. */
.ize-save-tip {
	position: absolute;
	top: 50%;
	right: 100%;            /* sit to the LEFT of the star */
	transform: translateY(-50%);
	padding-right: 8px;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transition: opacity 0.12s var(--ize-ease);
	z-index: 4;
}
.ize-save-wrap.is-tip-visible .ize-save-tip,
.ize-save-tip:hover {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
}
.ize-save-tip__link {
	position: relative;     /* anchor for the triangle */
	display: inline-block;
	white-space: nowrap;
	background: var(--ize-ink);
	color: #fff;
	font-size: var(--ize-fs-xs);
	line-height: 1;
	padding: 6px 9px;
	border-radius: 0;       /* square box, no curves */
	text-decoration: none;
	box-shadow: var(--ize-shadow);
}
/* Triangle on the box's right edge, pointing at the star. */
.ize-save-tip__link::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 100%;
	transform: translateY(-50%);
	border: 5px solid transparent;
	border-left-color: var(--ize-ink);
}
.ize-save-tip__link:hover,
.ize-save-tip__link:focus { text-decoration: underline; color: #fff; }

@media (prefers-reduced-motion: reduce) {
	.ize-save-wrap--card .ize-save,
	.ize-save-tip { transition: none; }
}

/* ── Badges ─────────────────────────────────────────────────────────────── */
.ize-badge {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	padding: 2px 8px;
	font-size: var(--ize-fs-xs);
	font-weight: 600;
	border-radius: var(--ize-radius-pill);
	background: var(--ize-surface);
	color: var(--ize-ink-2);
	box-shadow: var(--ize-shadow-sm);
}
/* On a card, the app's badge size. */
.ize-listing__badges .ize-badge { font-size: var(--ize-fs-card-meta); }
.ize-badge--featured { background: var(--ize-accent); color: #000; }
/* Affiliate-disclosure label — a neutral dark chip (a label, not a promo). */
.ize-badge--affiliate { background: var(--ize-ink); color: #fff; }
/* "Ended" — quiet grey, flat, no shadow. A finished run is not an error, and a
   red chip would make a page that works perfectly well look broken. */
.ize-badge--ended {
	background: var(--ize-surface-3);
	color: var(--ize-ink-3);
	box-shadow: none;
	vertical-align: middle;
}

/* Horizontal store-card scroller */
.ize-scroller {
	display: flex;
	gap: var(--ize-2);
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
	padding-bottom: var(--ize-1);
}
.ize-scroller::-webkit-scrollbar { display: none; }

/* Category chip: a link-style pill with an optional count (stores directory,
 * category archives, store sections). */
.ize-cat-chip {
	display: inline-flex;
	align-items: center;
	gap: var(--ize-2);
	padding: var(--ize-2) var(--ize-3);
	background: var(--ize-surface);
	border: 1px solid var(--ize-line);
	border-radius: var(--ize-radius-pill);
	color: var(--ize-ink);
	font-size: var(--ize-fs-chip);
	white-space: nowrap;
	text-decoration: none;
	transition: border-color 0.15s var(--ize-ease), color 0.15s var(--ize-ease);
}
.ize-cat-chip__count {
	padding: 0 var(--ize-2);
	background: var(--ize-surface-2);
	border-radius: var(--ize-radius-pill);
	color: var(--ize-ink-3);
	font-size: var(--ize-fs-xs);
}

/* ── Semantic price colours (feed cards, single ad, widgets) ──────────────── */
/* Current/regular price + "Contact for details" = blue, Free = green. On a
 * discounted ad the current (sale) price stays blue and the original price is
 * struck through in red. !important so it wins over the scattered legacy
 * single-ad price rules. */
.ize-price--now,
.ize-price--sale { color: var(--ize-price-text) !important; }        /* the listing price */
.ize-price--call { color: var(--ize-price-call-text) !important; }   /* "Contact for details" */
.ize-price--free { color: var(--ize-price-free-text) !important; }   /* "Free" */
.ize-price--now,
.ize-price--call,
.ize-price--free,
.ize-price--sale {
	text-decoration: none !important;
	font-size: inherit !important;
}
.ize-price--was {
	color: #e02424 !important;                           /* red, struck-through original */
	text-decoration: line-through !important;
	font-weight: 400;
	font-size: 0.85em;
	margin-left: 4px;
	/* Whole, on the next line if there is no room: split, its currency stayed
	   beside the new price and its number went under it. */
	white-space: nowrap;
}

/* ── Discovery: "Browse by city" chip rail (home + category archives) ─────── */
.ize-discover {
	max-width: var(--ize-container);
	margin: 0 auto;
	padding: var(--ize-5) var(--ize-4) 0;
}
.ize-discover--cat { padding-left: 0; padding-right: 0; }
/* One row that scrolls sideways — "Browse by city:" then the chips, the label
 * scrolling with them — as the app's rail (izebuy-app Browse.tsx). It used to
 * stack on phones, where the chip row grew to its own width and was cut off
 * instead of scrolling. */
.ize-discover__cities {
	display: flex;
	align-items: center;
	gap: var(--ize-2);
	margin-bottom: var(--ize-5);
	overflow-x: auto;
	overflow-y: hidden;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: none;
}
.ize-discover__cities::-webkit-scrollbar { display: none; }
.ize-discover__cities > .ize-scroller { flex: 0 0 auto; overflow: visible; padding-bottom: 0; }
.ize-discover__cities-label {
	flex-shrink: 0;
	font-size: var(--ize-fs-sm);
	font-weight: 600;
	color: var(--ize-ink-2);
	white-space: nowrap;
}
/* Phones: the row runs to the screen's edges, as the app's, so a chip half in
 * view shows there is more to scroll to. */
@media (max-width: 767.98px) {
	.ize-discover__cities {
		margin-inline: calc(-1 * var(--ize-4));
		padding-inline: var(--ize-4);
	}
}

/* ── Pagination ─────────────────────────────────────────────────────────── */
.ize-pagination {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--ize-1);
	margin-top: var(--ize-6);
}
.ize-pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 40px;
	height: 40px;
	padding: 0 var(--ize-3);
	border: 1px solid var(--ize-line);
	border-radius: var(--ize-radius);
	background: var(--ize-surface);
	color: var(--ize-ink);
	font-size: var(--ize-fs-sm);
	text-decoration: none;
	transition: background-color 0.15s var(--ize-ease), border-color 0.15s var(--ize-ease), color 0.15s var(--ize-ease);
}
/* Hover + current page follow the Button colour settings (Appearance → Buttons). */
.ize-pagination a.page-numbers:hover {
	background: var(--ize-btn-bg-hover);
	border-color: var(--ize-btn-bg-hover);
	color: var(--ize-btn-ink);
}
.ize-pagination .page-numbers.current {
	background: var(--ize-btn-bg);
	border-color: var(--ize-btn-bg);
	color: var(--ize-btn-ink);
}
.ize-pagination .page-numbers.dots {
	border-color: transparent;
	background: transparent;
}

/* ── Section header ─────────────────────────────────────────────────────── */
.ize-section__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin-bottom: var(--ize-3);
}
.ize-section__title { font-size: var(--ize-fs-xl); font-weight: 700; margin: 0; color: var(--ize-ink); }

/* ── Skeleton (loading shimmer for A6 infinite scroll) ──────────────────── */
.ize-skeleton {
	background: linear-gradient(90deg, var(--ize-surface-3) 25%, var(--ize-surface-2) 37%, var(--ize-surface-3) 63%);
	background-size: 400% 100%;
	animation: ize-shimmer 1.4s ease infinite;
	border-radius: var(--ize-radius-sm);
}
@keyframes ize-shimmer {
	0%   { background-position: 100% 0; }
	100% { background-position: 0 0; }
}

/* ── Utilities ──────────────────────────────────────────────────────────── */
.ize-visually-hidden {
	position: absolute !important;
	width: 1px; height: 1px;
	padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0, 0, 0, 0);
	white-space: nowrap; border: 0;
}
.ize-muted   { color: var(--ize-ink-3); }

@media (prefers-reduced-motion: reduce) {
	.ize-listing,
	.ize-btn,
	.ize-skeleton { transition: none; animation: none; }
}

/* Footer styles (nav, widgets, copyright) live in their own component file:
 * assets/css/footer.css. */

/* ── Category grid (All Categories page): image tiles with the name overlaid ── */
/* All Categories page: full-width, centred, no sidebars — equal gutters L/R. */
.ize-cat-page {
	max-width: var(--ize-container);
	margin: 0 auto;
	padding: var(--ize-4);
}
.ize-cat-page__intro { margin-bottom: 20px; }
/* 224×224 square category tiles, centred and wrapping to fit the row. */
.ize-cat-grid {
	display: grid;
	grid-template-columns: repeat( auto-fill, 224px );
	justify-content: center;
	gap: 16px;
}
/* Each category is one clickable 224×224 box: the whole area is the image with
   the category name centred on top. Nothing else in the box. */
.ize-cat-card {
	position: relative;
	display: block;
	width: 224px;
	height: 224px;
	container-type: inline-size; /* lets the name scale to the tile (cqi units) */
	border-radius: 8px;
	overflow: hidden;
	text-decoration: none;
	background: var(--ize-topbar); /* brand fill shown if there's no image */
}
/* The image fills the entire box. The selector is scoped to .ize-cat-card so it
   out-specifies `.ize-feed-layout__main img { height:auto }` in feed.css (and the
   global `img { height:auto }` reset) — without this, the image collapses to its
   natural height and leaves the box half-empty. This was the real override. */
/* Blurred-fill: a blurred, zoomed copy fills the tile (no unsightly gaps) while the
   sharp copy on top shows the WHOLE image (never cropped). */
.ize-cat-card .ize-cat-card__bg {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	filter: blur(18px);
	transform: scale(1.2);
}
.ize-cat-card .ize-cat-card__img {
	position: absolute;
	/* `contain` shows the WHOLE image, never cropped — whatever shape it is. The
	   blurred `__bg` copy behind it fills any letterbox gaps with a soft, colour-
	   matched fade, so there are no empty bars. No scale/zoom: a fixed zoom enlarges
	   and clips square/portrait images (the "too big" problem) while only helping
	   wide ones, so we keep pure contain and let the backdrop handle the gaps. */
	inset: 6px;
	width: calc( 100% - 12px );
	height: calc( 100% - 12px );
	object-fit: contain;
	border-radius: 6px;
}
/* Name centred over the image, sitting on a translucent brand-colour panel (see
   .ize-cat-card__label) so it stays readable even over a busy image. */
.ize-cat-card__name {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0; /* no inset so the label band can span the tile edge to edge */
	text-align: center;
	color: #fff;
	font-weight: 800;
	/* Large category name that scales with the tile (~34px on a 224px tile) — big
	   without clipping longer names; wraps safely if a name is very long. */
	font-size: clamp( 1.25rem, 15cqi, 2.5rem );
	line-height: 1.05;
	overflow-wrap: break-word;
	hyphens: auto;
	text-shadow: 0 1px 3px rgba( 0, 0, 0, 0.55 );
}
/* Full-width band behind the text (edge to edge across the tile) so the category
   name stays legible over busy background images. Black @ 50% per design — adjust
   the alpha here if it needs to be darker. */
.ize-cat-card__label {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	width: 100%;
	padding: 0.45em 0.7em;
	background: rgba( 66, 191, 98, 0.8 ); /* fallback: --ize-brand @ 80% */
	background: color-mix( in srgb, var(--ize-brand) 80%, transparent );
}
.ize-cat-card__count { font-weight: 400; opacity: 0.95; }
/* On phones too narrow for fixed 224px tiles, fall back to two fluid squares. */
@media ( max-width: 520px ) {
	.ize-cat-grid { grid-template-columns: repeat( 2, 1fr ); gap: 10px; }
	.ize-cat-card { width: 100%; height: auto; aspect-ratio: 1 / 1; }
}

/* ── Social login ──────────────────────────────────────────────────────────
 * A "Social login" divider separates it from the username/password form, then a
 * full-width Google button (only one provider, so it gets its own box). The
 * button follows Google's branding: white, light border, the colour "G" mark. */
.ize-social-login__divider {
	display: flex;
	align-items: center;
	gap: 12px;
	margin: 18px 0 14px;
}
.ize-social-login__divider::before,
.ize-social-login__divider::after {
	content: "";
	flex: 1;
	height: 1px;
	background: var(--ize-line, #e6e8eb);
}
.ize-social-login__divider span {
	font-size: var(--ize-fs-xs);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--ize-ink-3, #9aa0a6);
}
.ize-gbtn {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	width: 100%;
	min-height: 44px;
	padding: 10px 16px;
	border: 1px solid #dadce0;
	border-radius: 8px;
	background: #fff;
	color: #3c4043;
	font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
	font-size: var(--ize-fs-base);
	font-weight: 500;
	line-height: 1;
	text-decoration: none;
	transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.ize-gbtn:hover { background: #f8faff; border-color: #d2e3fc; box-shadow: 0 1px 2px rgba( 60, 64, 67, 0.12 ); }
.ize-gbtn__icon { display: inline-flex; }
.ize-gbtn__icon svg { display: block; width: 18px; height: 18px; }

/* ── Dismissible notices (js/notice.js) ──────────────────────────────────────
 * A close (×) button is injected into status notices (.ize-alert / .alert) so
 * any of them — e.g. "Ad has been added to the review queue" after posting an
 * ad, or the "ad updated" notice on My Ads — can be dismissed. The button sits
 * top-right and the notice gets right padding so wrapped text never runs under
 * it. The padded selectors are specific enough to beat the account/.alert
 * overrides (.ize-account .alert) without resorting to !important. */
.ize-dismissible { position: relative; }
.ize-dismissible,
.ize-account .alert.ize-dismissible,
.ize-account .ize-alert.ize-dismissible,
.detailpage-manage .alert.ize-dismissible { padding-right: 44px; }
.ize-note-close {
	position: absolute;
	top: 8px;
	right: 8px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	padding: 0;
	border: 0;
	border-radius: var(--ize-radius);
	background: transparent;
	color: inherit;
	font-size: var(--ize-fs-lg);
	line-height: 1;
	cursor: pointer;
	opacity: 0.55;
	transition: opacity 0.15s var(--ize-ease), background-color 0.15s var(--ize-ease);
}
.ize-note-close:hover,
.ize-note-close:focus { opacity: 1; background: rgba( 0, 0, 0, 0.08 ); outline: none; }
.ize-dismissible--out { opacity: 0; transition: opacity 0.18s var(--ize-ease); }

/* ── 404 / Not Found ─────────────────────────────────────────────────────────
 * Focused, on-brand empty state — a large "4 [compass] 4" mark, a short
 * message, and clear ways back (home / browse). */
.ize-404 {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 60vh;
	padding: var(--ize-7) var(--ize-4);
	text-align: center;
}
.ize-404__inner {
	max-width: 34rem;
}
.ize-404__art {
	display: block;
	width: 100%;
	max-width: 440px;
	height: auto;
	margin: 0 auto var(--ize-5);
}
.ize-404__title {
	margin: 0 0 var(--ize-2);
	font-size: var(--ize-fs-2xl);
	color: var(--ize-ink);
}
.ize-404__sub {
	margin: 0 auto var(--ize-6);
	max-width: 30rem;
	color: var(--ize-ink-2);
	line-height: 1.6;
}
.ize-404__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ize-3);
	justify-content: center;
}
@media (max-width: 480px) {
	.ize-404__actions { flex-direction: column; }
	.ize-404__actions .ize-btn { width: 100%; }
}

/* ── Table of contents ([toc] shortcode) ──────────────────────────────────── */
.ize-toc {
	--ize-toc-accent: var(--ize-brand);
	margin: 0 0 var(--ize-5);
	padding: var(--ize-4) var(--ize-5);
	border: 1px solid var(--ize-line);
	border-left: 3px solid var(--ize-toc-accent);
	border-radius: var(--ize-radius);
	background: var(--ize-surface);
	font-size: var(--ize-fs-sm);
}
.ize-toc__title {
	margin: 0 0 var(--ize-2);
	font-size: var(--ize-fs-md);
	font-weight: 700;
	color: var(--ize-ink);
}
.ize-toc__list {
	margin: 0;
	padding-left: 1.3em;
}
.ize-toc--plain .ize-toc__list {
	list-style: none;
	padding-left: 0;
}
.ize-toc__item {
	margin: 0 0 6px;
	line-height: 1.45;
}
.ize-toc__item:last-child { margin-bottom: 0; }
/* Indent deeper heading levels (H3 under H2, etc.). */
.ize-toc__item--l1 { padding-left: 1.2em; }
.ize-toc__item--l2 { padding-left: 2.4em; }
.ize-toc__link {
	color: var(--ize-toc-accent);
	text-decoration: none;
}
.ize-toc__link:hover { text-decoration: underline; }

/* Jumped-to headings clear the sticky header instead of hiding beneath it. */
.ize-post__content :is(h2, h3, h4)[id],
.ize-page__content :is(h2, h3, h4)[id] {
	scroll-margin-top: calc(var(--ize-header-bar-h, 56px) + 16px);
}

/* ── Modals ──────────────────────────────────────────────────────────────────
 * Every front-end pop-up is legacy Bootstrap markup (.modal, in footer.php and
 * author.php): login, register, recover, ask-question, report, share, filters,
 * confirmation, ask-seller and report-seller. They all share
 * .modal-content > .modal-body > (.close + h4 + form), so these token-based
 * rules style them uniformly from one place. Scoped under body.ize for
 * specificity over the old css/bootstrap.min.css. */
body.ize .modal .modal-dialog {
	width: calc(100% - var(--ize-4));
	max-width: 440px;
	margin: var(--ize-7) auto;
}
/* The advanced-search filters need more room than a single-column form. */
body.ize #filters .modal-dialog { max-width: 720px; }
body.ize .modal .modal-content {
	border: 1px solid var(--ize-line);
	border-radius: var(--ize-radius-lg);
	box-shadow: var(--ize-shadow-lg);
	background: var(--ize-surface);
}
body.ize .modal .modal-body {
	position: relative;
	padding: var(--ize-6);
	font-family: var(--ize-font);
	color: var(--ize-ink);
}
body.ize .modal .modal-body > h4 {
	margin: 0 0 var(--ize-5);
	font-size: var(--ize-fs-xl);
	font-weight: 700;
	line-height: var(--ize-lh-heading);
	color: var(--ize-ink);
}

/* Close (×) — a small neutral chip pinned top-right. */
body.ize .modal .close {
	position: absolute;
	top: var(--ize-4);
	right: var(--ize-4);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 32px;
	height: 32px;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: var(--ize-radius-pill);
	background: var(--ize-surface-2);
	color: var(--ize-ink-3);
	font-size: var(--ize-fs-base);
	line-height: 1;
	opacity: 1;
	text-shadow: none;
	cursor: pointer;
	transition: background 0.15s var(--ize-ease), color 0.15s var(--ize-ease);
}
body.ize .modal .close:hover,
body.ize .modal .close:focus {
	background: var(--ize-surface-3);
	color: var(--ize-ink);
	opacity: 1;
}

/* Fields */
body.ize .modal label {
	display: block;
	margin: var(--ize-3) 0 var(--ize-1);
	font-size: var(--ize-fs-label);
	font-weight: 600;
	color: var(--ize-ink-2);
}
body.ize .modal .description {
	margin: 0 0 var(--ize-2);
	font-size: var(--ize-fs-sm);
	color: var(--ize-ink-3);
}
body.ize .modal .form-control {
	width: 100%;
	height: auto;
	padding: 10px var(--ize-3);
	border: 1px solid var(--ize-line);
	border-radius: var(--ize-radius-sm);
	background: var(--ize-surface);
	color: var(--ize-ink);
	font-family: var(--ize-font);
	font-size: var(--ize-fs-input);
	box-shadow: none;
	transition: border-color 0.15s var(--ize-ease), box-shadow 0.15s var(--ize-ease);
}
body.ize .modal textarea.form-control {
	min-height: 110px;
	resize: vertical;
}
body.ize .modal .form-control:focus {
	border-color: var(--ize-brand);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--ize-brand) 18%, transparent);
	outline: none;
}

/* Remember-me / forgot-password row */
body.ize .modal .row { margin-top: var(--ize-3); }
body.ize .modal .checkbox label {
	margin: 0 0 0 var(--ize-1);
	font-weight: 400;
	color: var(--ize-ink-2);
}

/* In-text links: forgot password + register/login switch */
body.ize .modal .forgot-password,
body.ize .modal .register-close-login {
	color: var(--ize-brand);
	font-weight: 600;
	font-size: var(--ize-fs-sm);
	text-decoration: none;
}
body.ize .modal .forgot-password:hover,
body.ize .modal .forgot-password:focus,
body.ize .modal .register-close-login:hover,
body.ize .modal .register-close-login:focus {
	color: var(--ize-brand-hover);
	text-decoration: underline;
}

/* Primary action — full-width brand button (its colours come from the shared
 * body.ize .btn rule; here we set size + shape only). */
body.ize .modal .submit-form-ajax {
	display: block;
	width: 100%;
	margin: var(--ize-5) 0 var(--ize-2);
	padding: 12px var(--ize-4);
	border-radius: var(--ize-radius);
	font-size: var(--ize-fs-btn);
	font-weight: 700;
	text-align: center;
}

/* Status messages */
body.ize .modal .ajax-response {
	margin-top: var(--ize-3);
	font-size: var(--ize-fs-sm);
	color: var(--ize-ink-2);
}
body.ize .modal .ajax-response:empty { margin: 0; }

/* Social sign-in sits below the form */
body.ize .modal .ize-social-login { margin-top: var(--ize-5); }

/* Share row: WhatsApp first, then the device share sheet, copy link, Facebook,
   X — on the page rather than behind a modal, because a listing travels when
   somebody forwards it. The icons are the social icons' (socials.css), under
   the word "Share": the seller's socials open their accounts, these send the
   page to somebody. */
body.ize .ize-share { margin: var(--ize-4) 0 0; }

/* The ask, right after publishing. Tinted rather than alert-coloured: nothing
   has gone wrong, and it is a suggestion, not a warning. */
body.ize .ize-share-prompt {
	margin: 0 0 var(--ize-5);
	padding: var(--ize-4);
	border: 1px solid var(--ize-line);
	border-radius: var(--ize-radius);
	background: var(--ize-surface-2);
}
body.ize .ize-share-prompt__title {
	margin: 0 0 var(--ize-1);
	font-size: var(--ize-fs-md);
}
body.ize .ize-share-prompt__note {
	margin: 0;
	color: var(--ize-ink-2);
	font-size: var(--ize-fs-sm);
	overflow-wrap: anywhere; /* it names the ad: a title with no spaces breaks */
}
body.ize .ize-share-prompt .ize-share { margin-top: var(--ize-3); }

/* "Link copied" — and, when the clipboard is blocked, why not. */
body.ize .ize-share__said {
	display: block;
	font-size: var(--ize-fs-sm);
	color: var(--ize-ok, var(--ize-ink-2));
}
body.ize .ize-share__said:empty { display: none; }

/* ── "Report a problem" link ──────────────────────────────────────────────
 * Deliberately quiet: a small, muted text link with a little flag, tucked
 * below the busy areas (submit-ad wizard, empty search results). Someone who's
 * stuck is actively hunting for a way to report it, so it doesn't need to shout
 * — it just needs to be there. It brightens to the brand colour on hover/focus. */
.ize-report-link {
	display: inline-flex;
	align-items: baseline;
	gap: 0.4em;
	color: var(--ize-ink-3);
	font-size: var(--ize-fs-xs);
	text-decoration: none;
	transition: color 0.15s var(--ize-ease, ease);
}
.ize-report-link i {
	font-size: 0.9em;
}
.ize-report-link:hover,
.ize-report-link:focus {
	color: var(--ize-brand-text);
	text-decoration: underline;
}

/* Wrappers — give the link a little breathing room without drawing the eye. */
.ize-wizard__report {
	margin: var(--ize-4) 0 0;
	padding-top: var(--ize-4);
	border-top: 1px solid var(--ize-line);
	text-align: center;
}
.ize-search-empty__report {
	margin: var(--ize-3) 0 0;
}
/* Footer safety-net link — its own slim, centred band above the copyright bar.
 * On the dark footer it borrows the footer ink (muted) and brand link colour. */
.ize-footer__report {
	padding: var(--ize-3) 0;
	text-align: center;
}
.ize-footer__report .ize-report-link {
	color: color-mix(in srgb, var(--ize-footer-ink) 70%, transparent);
}
.ize-footer__report .ize-report-link:hover,
.ize-footer__report .ize-report-link:focus {
	color: var(--ize-footer-link);
}
/* Bottom of the mobile menu drawer, sitting just under Logout. The drawer is the
 * brand colour, so the link uses the drawer's own ink and brightens on hover. */
.ize-nav-drawer__report {
	margin-top: auto;
	padding: var(--ize-4) 0 var(--ize-2);
	text-align: center;
}
.ize-nav-drawer__report .ize-report-link {
	color: color-mix(in srgb, var(--ize-topbar-ink) 75%, transparent);
}
.ize-nav-drawer__report .ize-report-link:hover,
.ize-nav-drawer__report .ize-report-link:focus {
	color: var(--ize-topbar-ink);
	text-decoration: underline;
}

/* Verified-seller badge — deliberately light blue so it never reads as the green
   used for saved/star actions. Shown next to the seller name on the single-ad
   seller card and the storefront header (classifieds_get_verified_badge()). */
.ize-verified {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	margin-left: 6px;
	padding: 1px 8px;
	font-size: var(--ize-fs-seller-badge);   /* the app's "Verified" */
	font-weight: 600;
	line-height: 1.7;
	color: #1577c2;
	background: #e9f4fe;
	border: 1px solid #bfe0fa;
	border-radius: 999px;
	vertical-align: middle;
	white-space: nowrap;
}
.ize-verified .fa {
	font-size: 0.95em;
}

/* "How It Works" guide + FAQ accordion (used by the How It Works page content). */
.ize-howto__lead { font-size: 1.05rem; color: var(--ize-ink-2, #444); }
.ize-howto__toc {
	margin: 18px 0 28px;
	padding: 14px 18px;
	background: var(--ize-surface-2, #f5f6f8);
	border: 1px solid var(--ize-surface-3, #e5e7eb);
	border-radius: 10px;
}
.ize-howto__toc ul { margin: 8px 0 0; padding-left: 18px; columns: 2; }
.ize-howto__toc li { margin-bottom: 4px; }
.ize-howto h2 { margin-top: 34px; scroll-margin-top: 90px; }
.ize-howto__note { color: var(--ize-ink-2, #666); }

.ize-faq { margin-top: 8px; }
.ize-faq__item {
	border: 1px solid var(--ize-surface-3, #e5e7eb);
	border-radius: 10px;
	margin-bottom: 10px;
	background: #fff;
}
.ize-faq__q {
	cursor: pointer;
	padding: 14px 44px 14px 16px;
	font-weight: 600;
	list-style: none;
	position: relative;
}
.ize-faq__q::-webkit-details-marker { display: none; }
.ize-faq__q::after {
	content: '+';
	position: absolute;
	right: 16px;
	top: 50%;
	transform: translateY(-50%);
	font-size: 1.3em;
	line-height: 1;
	color: #1577c2;
}
.ize-faq__item[open] .ize-faq__q::after { content: '\2212'; }
.ize-faq__a { padding: 0 16px 14px; color: var(--ize-ink-2, #444); }
@media (max-width: 600px) {
	.ize-howto__toc ul { columns: 1; }
}

/* ── Taxi and kabaza: the passenger's warning (includes/ride-warning.php) ──
 * First on a ride ad and a ride seller's page. Its first line, in English and
 * Chichewa, shows at once; tapping it opens the advice. A caution colour of
 * its own, so it is not mistaken for the ad's Safety Notice further down. */
.ize-ride-warning {
	margin: 0 0 var(--ize-4);
	background: #fff4e5;
	border: 1px solid #f2c48d;
	border-left: 4px solid #e8590c;
	border-radius: var(--ize-radius);
	color: #5b3a12;
}
.ize-ride-warning summary {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	padding: 10px 12px;
	list-style: none;
	cursor: pointer;
}
.ize-ride-warning summary::-webkit-details-marker { display: none; }
.ize-ride-warning__icon {
	flex: none;
	margin-top: 3px;
	font-size: 18px;
	color: #c2410c;
}
.ize-ride-warning__summary { min-width: 0; }
.ize-ride-warning__summary strong {
	display: block;
	font-size: 14px;
	line-height: 1.4;
}
.ize-ride-warning__summary [lang="ny"] {
	display: block;
	margin-top: 4px;
	font-size: 13px;
	line-height: 1.4;
}
.ize-ride-warning__more {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	margin-top: 6px;
	font-size: 13px;
	font-weight: 600;
	color: #9a3412;
}
.ize-ride-warning__more .fa-solid { font-size: 0.8em; transition: transform 0.15s var(--ize-ease); }
.ize-ride-warning[open] .ize-ride-warning__more .fa-solid { transform: rotate(180deg); }
.ize-ride-warning summary:focus-visible { outline: 2px solid #c2410c; outline-offset: 2px; border-radius: var(--ize-radius); }
.ize-ride-warning__body { padding: 0 12px 10px 40px; }
.ize-ride-warning__body ul {
	margin: 0 0 10px;
	padding-left: 18px;
	font-size: 14px;
	line-height: 1.5;
}
.ize-ride-warning__body ul:last-child { margin-bottom: 0; }
@media (prefers-reduced-motion: reduce) {
	.ize-ride-warning__more .fa-solid { transition: none; }
}
