/*
 * Public try-on look.
 *
 * Everything is scoped under .ktr-*. The active theme is Neve, but the site
 * also loads Elementor Pro, MegaMenu and ~55 other plugins' stylesheets, so
 * bare element selectors would be a coin flip.
 *
 * Colours are deliberately neutral rather than Mazoo-branded: this block sits
 * inside her own post, in her own theme, and should read as part of her site.
 * The pink accent matches her storefront theme token (#ef62c5) so the two
 * surfaces feel related without this one looking like a foreign embed.
 */

.ktr-look {
	--ktr-accent: #ef62c5;
	--ktr-accent-hover: #d957b1;
	--ktr-ink: #272626;
	--ktr-muted: #7a7a7a;
	--ktr-hairline: #e8e8e8;

	/*
	 * Her theme's corner. Neve rounds its buttons — the comment form's "Post
	 * Comment" is the one everybody sees — at 3px, so every image and CTA this
	 * plugin draws uses the same, and the block reads as part of her site
	 * rather than a rounder embed dropped into it. Overlay chrome (the round
	 * download icon, the small pills sitting ON a result) keeps its own shape:
	 * those are badges, not her buttons.
	 */
	--ktr-radius: 3px;

	/*
	 * Titles and controls wear her site's sans; body-ish text keeps the
	 * inherited Garamond.
	 *
	 * "Open Sans" is not a guess — it is already loaded here (normal/500/bold)
	 * and is what her theme gives links and the product titles on this very
	 * card, so this matches rather than introduces a face. Arial/Helvetica
	 * behind it is her theme's own heading stack, which is where a site
	 * without Open Sans would land anyway.
	 */
	--ktr-sans: "Open Sans", Arial, Helvetica, sans-serif;

	/*
	 * A width of its own, centred — NOT however wide the post column happens
	 * to be.
	 *
	 * Neve drops the sidebar at ~950px, and the content column it hands us
	 * jumps from 634px to 899px as the window gets NARROWER. Riding that, the
	 * look block redrew itself completely across two adjacent window sizes:
	 * 189px product cells one side of the line, 322px the other, on a layout
	 * that is otherwise identical. Nothing the block can do about the theme's
	 * jump except stop tracking it.
	 *
	 * 640 = the 224px look column + 16px gap + two 192px product cells. It is
	 * set by the NARROWEST column the theme ever hands us with the sidebar up
	 * — 634px, right at the breakpoint — so that the block clears its own cap
	 * on both sides of the jump and stops moving. Measured content widths on
	 * her theme: 789 at 1600px, 732 at 1100, 634 at 960, 899 at 940. A wider
	 * cap would leave the wobble it was added to remove.
	 */
	max-width: 640px;

	margin: 2em auto;
	color: var(--ktr-ink);
}

/* ------------------------------------------------------------- jump menu -- */

/*
 * The thumbnails above the outfits, when the post asks for one.
 *
 * It WRAPS and grows; it does not scroll sideways. A scroller shows five
 * outfits and hides the rest behind a gesture nobody is told about — and the
 * whole point of this row is that every outfit on the post is reachable in one
 * press. A tall block is the honest cost of a post with thirty outfits on it.
 *
 * No rule under it. The buttons already end in a hard edge, and a line beneath
 * them read as a second divider stacked on the one that the first outfit's own
 * heading provides.
 */
.ktr-menu {
	margin: 0 0 24px;
}

/*
 * Says what the row is. The nav's `aria-label` says the longer version of it
 * ("Jump to an outfit") for a screen reader; this is the sighted half.
 *
 * Everything about how it LOOKS comes from `.ktr-look-label`, which it also
 * carries — this only sets the space under it, which is smaller than an
 * outfit heading's because what follows is a row of buttons rather than a
 * picture.
 */
.ktr-menu-title {
	margin: 0 0 4px;
}

/*
 * The line under the heading, saying what a press does.
 *
 * Body face rather than the sans the heading wears: it is a sentence to read,
 * not a label. Small, light and centred under the title it belongs to, with
 * `line-height` named for the same reason the title needs one — the theme's
 * body leading is 2.4, which would put this in a 29px line box.
 */
.ktr-menu-note {
	margin: 0 0 10px;
	font-size: 12px;
	line-height: 1.4;
	text-align: center;
	color: #9a9a9a;
}

/* On the text's own baseline, not the line box's bottom — an arrow hanging
   below the words reads as a separate mark rather than part of the sentence. */
.ktr-menu-arrow {
	vertical-align: -2px;
}

/*
 * Qualified by `.ktr-look`, unlike almost everything else in this file.
 *
 * This is the plugin's only <ul> on the front end, and it lands inside
 * `.entry-content`, where the theme styles lists by element: `.entry-content
 * ul` is (0,1,1) and beats a lone `.ktr-menu-list` at (0,1,0). The margin
 * reset here was simply losing — measured 30px top and bottom of Neve's,
 * against the 0 this asks for, which is most of the gap between the heading
 * and the buttons. The `li` rule below loses the same way.
 *
 * The file's opening note says bare element selectors would be a coin flip in
 * this theme; this is the same coin from the other side.
 */
.ktr-look .ktr-menu-list {
	list-style: none;
	margin: 0;
	padding: 0 0 8px;
	display: grid;
	/*
	 * Fixed tracks, filled and then wrapped — never `1fr`, which would stretch
	 * three outfits into three enormous buttons and size the shortcuts
	 * differently on every post. A track that depends only on the viewport
	 * means an outfit button is the same object on every post, and a row holds
	 * as many as fit.
	 *
	 * `auto-fit` rather than `auto-fill`, and that is what centring hangs on:
	 * auto-fill keeps the empty tracks, so the grid is always the full width
	 * of the block and `justify-content` has nothing to move. auto-fit
	 * collapses them, so three outfits make a three-track grid that then
	 * centres. Once they wrap, every track is occupied and the grid fills the
	 * width again — centred and left-aligned become the same thing, which is
	 * why this needs no breakpoint.
	 *
	 * The `min()` is the narrow-phone half. At a flat 142px a 360px screen
	 * fits TWO buttons — a post with nine outfits becomes five rows of a very
	 * tall block, and the shortcut costs more scrolling than it saves. The
	 * second term is "a third of the row, gaps taken out", so three always fit
	 * however narrow the screen gets; the buttons shrink instead of the row
	 * dropping to two. On anything wider the 142px wins and nothing changes,
	 * so this replaces a breakpoint rather than adding one.
	 *
	 * `100%` inside the track resolves against the grid's own content box, and
	 * a `<length-percentage>` is a fixed breadth — which is what `auto-fit`
	 * requires. The gap is a custom property so the subtraction cannot drift
	 * away from the actual gap the next time one of them is touched.
	 */
	--ktr-menu-gap: 10px;
	grid-template-columns: repeat(auto-fit, min(142px, (100% - var(--ktr-menu-gap) * 2) / 3));
	gap: var(--ktr-menu-gap);
	justify-content: center;
}

.ktr-look .ktr-menu-list li {
	margin: 0;
	padding: 0;
}

/*
 * Each item is a button with a picture on it, not a picture with a caption.
 *
 * That distinction is the whole design of this row. A thumbnail and a line of
 * grey text is a gallery — it sits in the middle of an article, nothing else
 * on her posts behaves like it, and there is no reason for a reader to try
 * pressing it. So it borrows the language of the one button she already has on
 * these posts (`.ktr-try-btn`): a raised surface, and a filled label in the
 * accent underneath.
 */
.ktr-menu-item {
	display: block;
	padding: 4px 4px 5px;
	border: 1px solid #e2e2e4;
	border-radius: var(--ktr-radius, 3px);
	background: #fff;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.07);
	text-decoration: none;
	color: inherit;
}

.ktr-menu-item:hover,
.ktr-menu-item:focus {
	border-color: var(--ktr-accent, #ef62c5);
	background: #fff;
	color: inherit;
	box-shadow: 0 2px 6px rgba(239, 98, 197, 0.28);
}

/*
 * Pressed: it goes down and loses its lift.
 *
 * A phone has no hover, so this is the ONLY feedback a tap gets before the
 * page moves — and the page moving is easy to read as an accident if nothing
 * acknowledged the press.
 */
.ktr-menu-item:active {
	transform: translateY(1px);
	box-shadow: none;
}

.ktr-menu-thumb {
	display: block;
	width: 100%;
	aspect-ratio: 9 / 16;
	object-fit: cover;
	border-radius: var(--ktr-radius, 3px);
	background: #f2f2f2;
}



/*
 * The label IS the button face: filled, in the accent, the way `.ktr-try-btn`
 * below it is.
 *
 * Tinted rather than solid at rest, and solid on hover and focus. A hundred
 * saturated pink chips across a row would shout over the article they sit in;
 * a tint reads as pressable without competing, and still has somewhere to go
 * when the pointer arrives.
 */
.ktr-menu-label {
	font-family: var(--ktr-sans, sans-serif);
	display: block;
	margin-top: 5px;
	padding: 3px 2px;
	border-radius: var(--ktr-radius, 3px);
	background: #fbeaf6;
	font-size: 11px;
	font-weight: 700;
	line-height: 1.2;
	text-align: center;
	color: #b8378f;
}

.ktr-menu-item:hover .ktr-menu-label,
.ktr-menu-item:focus .ktr-menu-label {
	background: var(--ktr-accent, #ef62c5);
	color: #fff;
}



/*
 * One look per row: the still on the left, its shoppable items on the right —
 * the same shape as the storefront's PostPage. The earlier auto-fill grid put
 * three looks side by side, which left nowhere for the items to go.
 */
.ktr-look-frames {
	display: flex;
	flex-direction: column;
	gap: 32px;
}

.ktr-frame-card {
	margin: 0;
	padding: 0;

	/*
	 * Where a jump from the menu lands.
	 *
	 * Her theme's header is sticky (MegaMenu), so an anchor scrolled flush to
	 * the top of the viewport puts the outfit's own label underneath it — the
	 * reader arrives at a picture with no idea which one they asked for.
	 * Generous rather than exact, because the header's height changes with the
	 * viewport and landing slightly early is invisible where landing late is
	 * the bug.
	 */
	scroll-margin-top: 120px;
}

/*
 * "Outfit 1" / "Outfit 2" — the storefront's treatment (centred, italic +
 * underlined + bold), so the same thing is PRESENTED the same way on
 * kerinawang.com and shop.trymazoo.com even though it is no longer NAMED the
 * same way there ("Look 1"). See Renderer::render_frame for that split.
 */
.ktr-look-label {
	font-family: var(--ktr-sans, sans-serif);
	margin: 0 0 10px;
	text-align: center;
	font-size: 16px;
	line-height: 1.3;
}

.ktr-look-label strong {
	font-weight: 700;
}

.ktr-look-row {
	display: flex;
	align-items: flex-start;
	gap: 16px;
}

/*
 * Fixed-width column. 224px — the storefront's 320px less 30%.
 *
 * The still is a 9:16 portrait, so its width sets the whole row's height: at
 * 320 it ran to 569px tall and the two product cards beside it floated in the
 * top third of a very long column. The button below it is width: 100%, so it
 * narrows with the picture and the pair still read as one control.
 */
.ktr-look-media {
	flex: 0 0 224px;
	width: 224px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.ktr-frame-img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 9 / 16;
	object-fit: cover;
	border-radius: var(--ktr-radius, 3px);
	background: #f2f2f2;
}

/*
 * Below the image, full width — not overlaid on it as before. An overlay sat
 * on top of whatever the still showed at that spot, and on a 9:16 portrait
 * that is usually the garment's hem.
 */
.ktr-try-btn {
	font-family: var(--ktr-sans, sans-serif);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 1px;
	width: 100%;
	padding: 8px 14px;
	border: 0;
	border-radius: var(--ktr-radius, 3px);
	background: var(--ktr-accent);
	color: #fff;
	line-height: 1.2;
	cursor: pointer;
}

/*
 * Two lines: the invitation, then the disclosure.
 *
 * "Try it on" alone was ambiguous on a fashion blog — it reads as a shopping
 * link. The second line says what actually happens, in a size that stays out of
 * the way of the first.
 */
.ktr-try-lead {
	font-size: 15px;
	font-weight: 600;
}

.ktr-try-sub {
	font-size: 11px;
	font-weight: 400;
	letter-spacing: 0.02em;
	opacity: 0.85;
}

.ktr-try-btn:hover,
.ktr-try-btn:focus {
	background: var(--ktr-accent-hover);
	color: #fff;
}

/*
 * Two columns, as the storefront does.
 *
 * The row gap is much larger than the column gap on purpose: every card ends in
 * a line of small grey text and begins with a picture, so at an even 12px the
 * rows ran together and the brand/price of one row read as a caption for the
 * one below it. The columns don't need the same room — the picture edges
 * already separate them.
 */
.ktr-look-items {
	flex: 1 1 auto;
	min-width: 0; /* lets long titles wrap instead of forcing the row wider */
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 24px 16px;
	align-content: start;
}

/*
 * A plain container, NOT a link — see the note in Renderer::render_product
 * about make_clickable nesting anchors. The image + title are one link and the
 * URL below is another, side by side.
 */
.ktr-item {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.ktr-item-main {
	display: flex;
	flex-direction: column;
	min-width: 0;
	text-decoration: none;
	color: inherit;
}

.ktr-item-main:hover,
.ktr-item-main:focus {
	color: inherit;
}

.ktr-item-img {
	display: block;
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	border-radius: var(--ktr-radius, 3px);
	background: #f3f3f3;
}

.ktr-item-img-empty {
	display: block;
}

/*
 * One line-height for the whole card.
 *
 * The four lines of text under a thumbnail used to be set at 1.3, 1.35 and
 * whatever the theme gave the brand and price (Neve's body leading, ~1.6), so
 * the block loosened as it went down and no two cards agreed. 1.25 everywhere,
 * declared once here and inherited, keeps them tight and identical.
 */
.ktr-item {
	line-height: 1.25;
}

.ktr-item-head {
	margin-top: 6px;
	display: flex;
	align-items: flex-start;
	min-width: 0;
}

.ktr-item-title {
	font-family: var(--ktr-sans, sans-serif);
	flex: 1;
	min-width: 0;
	font-size: 13px;
	font-weight: 600;
	/* Two lines then ellipsis — her titles run long, and an unbounded one would
	   drive the two grid cells to different heights. It was three, which on a
	   long title left the picture stranded above a paragraph. */
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/*
 * The affiliate URL, in the brand pink and underlined — the storefront styles
 * it with its `text-blue` token, which for Kerina resolves to this same pink.
 *
 * One line, ellipsised. It used to `word-break: break-all` so the whole
 * shortlink was readable, but the links are not all short — a few are full
 * product URLs — and one of those wrapped to four lines and pushed the card's
 * brand and price out of line with its neighbour's. Nobody retypes these; the
 * card is the click target.
 */
.ktr-item-link {
	display: block;
	margin-top: 3px;
	font-size: 11px;
	color: var(--ktr-accent);
	text-decoration: underline;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/*
 * Sits straight under the link, not pushed to the bottom of the cell.
 *
 * It used to be `margin-top: auto`, which bottom-aligned the grey lines across
 * a row. That only looks deliberate when both cards have the same number of
 * lines above: a one-line title beside a two-line one, or a grey line that
 * wraps in one cell and not the other, opened a gap under the shorter card's
 * link with its grey line stranded at the bottom of it. Following the link
 * keeps the whole text block together and the two cards' grey lines start from
 * the same place whenever their titles do.
 */
.ktr-item-foot {
	padding-top: 4px;
	min-width: 0;
}

/*
 * "Brand (size, price)" — one grey line, left-aligned, nothing opposite it.
 *
 * It wraps rather than ellipsising, and is capped at two lines. `size` is free
 * text and sometimes a whole discount code, which on one line would push the
 * price past the ellipsis and lose it — the price being the part nobody would
 * want cut. Two lines instead of one costs nothing in alignment: the foot is
 * bottom-anchored by `margin-top: auto` and the grid row stretches to its
 * tallest card either way.
 */
.ktr-item-brand {
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	font-size: 11px;
	color: var(--ktr-muted);
}

/*
 * Mobile: stack, and centre the look. 782px is WordPress's own admin
 * breakpoint and close enough to the storefront's md.
 */
@media screen and (max-width: 782px) {
	.ktr-look-row {
		flex-direction: column;
		align-items: stretch;
		gap: 12px;
	}

	.ktr-look-media {
		flex: none;
		width: 100%;
		max-width: 224px;
		align-self: center;
	}

	/*
	 * The cells keep the desktop shape — two columns, picture filling its
	 * cell — but stop growing, and the pair centres in what is left.
	 *
	 * This is the ONE place the cap belongs, and the reason is the stack above
	 * it. While the look sits beside the products, the products get the row
	 * minus a 224px column, so the cells are around 240px on their own and
	 * shrink smoothly with the window. The moment the look moves on top, that
	 * whole width falls to the grid and each cell would jump to ~360px — two
	 * 1:1 photos filling a screen.
	 *
	 * Capping at 240 makes the crossing invisible: 241px cells at 783px wide,
	 * 240px cells at 782px. An earlier cut applied this from 1024px down and
	 * that was the visible jump — it fired in the middle of the side-by-side
	 * layout, where nothing about the layout had changed.
	 *
	 * Centring rather than left-aligning puts the slack outside the pair, so
	 * the products share a centre line with the look image now above them.
	 */
	.ktr-look-items {
		grid-template-columns: repeat(2, minmax(0, 240px));
		justify-content: center;
	}
}

/* ---------------------------------------------------------------- modal -- */

/*
 * One screen at a time: a centred card on desktop, full screen on a phone.
 *
 * It used to be webapp/shop's TryOnDrawer — a bottom sheet whose four steps
 * went four-across on a wide window and became four mandatory scroll-snap
 * sections on a phone. See the header of tryon.js for why that shape didn't
 * survive the move onto her own post. The `ktr-d-` prefix is unchanged: it is
 * in every rule and every class name in tryon.js, and renaming it would be a
 * large diff that changes nothing.
 *
 * Appended to <body>, never inside .ktr-look — the look renders inside post
 * content, and any ancestor with a transform, filter or `contain` (Elementor
 * and MegaMenu both apply them) makes a position:fixed child position against
 * THAT element instead of the viewport.
 */

.ktr-d-backdrop {
	position: fixed;
	inset: 0;
	z-index: 999998;
	background: rgba(0, 0, 0, 0.5);
}

body.ktr-d-open {
	overflow: hidden; /* stops the page scrolling behind the sheet on iOS */
}

/*
 * A centred card, sized by `inset: 0` + `margin: auto` rather than by a
 * transform. The panel is a positioning context for the off-screen Turnstile
 * mount, and `translate(-50%, -50%)` on it would make every fixed descendant
 * position against the panel instead of the viewport — including the crop
 * overlay, which is a full-viewport sheet.
 *
 * The height is DEFINITE and stays that way. `.ktr-d-frame` is a
 * `container-type: size` container and the card inside it is sized off
 * `100cqh`; a size container only reports a height the engine considers
 * definite, and every box between here and it is a flex child taking its
 * height from its parent. Let this be `fit-content` or a bare `max-height`
 * and the chain breaks at the top: 100cqh resolves to 0, and every card in
 * the modal collapses to a zero-by-zero box.
 */
.ktr-d-panel {
	position: fixed;
	inset: 0;
	/*
	 * 620x760 is set by the CONFIRM screen, the one with two 9:16 cards side
	 * by side. At 520 wide those cards were width-limited — 218x388 in a
	 * 610px-tall slot — and the screen was a third empty. 620 is the width at
	 * which they become height-limited instead, so the pair fills the box and
	 * the single-card screens still have a card as tall as the modal allows.
	 */
	width: min(620px, calc(100vw - 32px));
	height: min(760px, 92vh);
	margin: auto;
	z-index: 999999; /* MegaMenu's sticky header sits high */
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border-radius: 18px;
	background: #fff;
	color: #272626;
	box-shadow: 0 12px 40px rgba(0, 0, 0, 0.28);

	/* The look's tokens are scoped to .ktr-look, which is not an ancestor. */
	--ktr-accent: #ef62c5;
	--ktr-accent-hover: #d957b1;
	--ktr-ink: #272626;
	--ktr-muted: #7a7a7a;
	--ktr-radius: 3px;
	--ktr-sans: "Open Sans", Arial, Helvetica, sans-serif;

	/*
	 * The drawer's typeface.
	 *
	 * This used to be set at runtime from the title's computed font family, on
	 * the theory that the title was an <h2> and would resolve to her theme's
	 * heading face. It never did: the drawer is appended to <body>, outside the
	 * .entry-content scope her heading rules are written against, so the title
	 * inherited plain body Garamond and handed the whole drawer a serif. Naming
	 * the face is both what was wanted and what actually happens.
	 */
	--ktr-d-font: var(--ktr-sans, sans-serif);
}

/*
 * Every control in the drawer wears it.
 *
 * The panel sets it for everything that inherits — hints, empty states, the
 * scroll nudge — so no stray line of her body serif is left sitting between
 * two sans ones. Buttons and inputs do NOT inherit font-family by default (the
 * UA stylesheet gives them a system font), so those still opt in by name.
 */
.ktr-d-panel {
	font-family: var(--ktr-d-font);
}

.ktr-d-title,
.ktr-d-btn,
.ktr-d-go,
.ktr-d-pill,
.ktr-d-replace,
.ktr-d-example,
.ktr-d-headline,
.ktr-d-celllabel,
.ktr-d-checks li,
.ktr-d-loadtitle,
.ktr-d-error {
	font-family: var(--ktr-d-font, inherit);
}

.ktr-d-head {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 18px 24px;
	border-bottom: 1px solid #eee;
}

.ktr-d-title {
	margin: 0;
	font-size: 24px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-align: center;
	color: var(--ktr-accent);
}

/*
 * The two ways out, one in each corner, and the title centred between them.
 *
 * Absolutely positioned rather than laid out as flex siblings: the head
 * centres the title with `justify-content: center`, and in flow these two
 * would push it off-centre by however much wider one is than the other.
 */
.ktr-d-close,
.ktr-d-back {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	border: 0;
	background: none;
	line-height: 1;
	color: var(--ktr-muted);
	cursor: pointer;
}

.ktr-d-close {
	right: 14px;
	font-size: 28px;
	padding: 4px 10px;
}

/*
 * A 44px square, which is the minimum comfortable touch target and the reason
 * this is not simply the × mirrored: the chevron inside it is 22px, and on a
 * phone the top-left corner is where the thumb goes for "out of here".
 *
 * Neve paints bare buttons — `button:hover` sets `var(--primarybtnhoverbg)`,
 * her accent pink — so the hover state is neutralised the way the sample link
 * and the result tabs are, with specificity rather than !important.
 */
.ktr-d-back {
	left: 8px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border-radius: 50%;
}

.ktr-d-head .ktr-d-back:hover,
.ktr-d-head .ktr-d-back:focus {
	background: none;
	box-shadow: none;
	color: var(--ktr-ink, #272626);
}

.ktr-d-back svg {
	display: block;
}

/*
 * The body holds exactly ONE screen, and the screen fills it. Nothing here
 * scrolls — the product shelf on the result is the only exception and it
 * scrolls sideways, inside itself.
 */
.ktr-d-body {
	flex: 1;
	min-height: 0;
	display: flex;
	overflow: hidden;
}

.ktr-d-screen {
	flex: 1;
	min-width: 0;
	min-height: 0;
	display: flex;
	flex-direction: column;
	gap: 12px;
	padding: 16px 20px 20px;
	box-sizing: border-box;
}

/* The wait has nothing to fill the space with, so it sits in the middle. */
.ktr-d-screen-center {
	align-items: center;
	justify-content: center;
	gap: 16px;
}

.ktr-d-heading {
	flex: none;
	text-align: center;
}

.ktr-d-headline {
	margin: 0;
	font-size: 18px;
	font-weight: 700;
	line-height: 1.25;
	color: var(--ktr-ink, #272626);
}

/*
 * The bottom of every screen that has an action: the primary button, its note,
 * and (on the confirm screen) the Turnstile mount.
 *
 * `margin-top: auto` rather than `justify-content: space-between` on the
 * screen — the picture above it is the flexible box, and pushing the footer
 * down from here leaves that free to take everything else.
 */
.ktr-d-footer {
	flex: none;
	margin-top: auto;
	padding-top: 4px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 8px;
}

/* Sizes the 9:16 card to whichever of height/width runs out first. */
.ktr-d-frame {
	flex: 1;
	min-height: 0;
	width: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	container-type: size;
}

.ktr-d-card,
.ktr-d-empty,
.ktr-d-cardwrap {
	aspect-ratio: 9 / 16;
	width: min(100cqw, calc(100cqh * 9 / 16));
	border-radius: var(--ktr-radius, 3px);
}

.ktr-d-card {
	display: block;
	height: auto;
	object-fit: cover;
	background: #f2f2f2;
}

.ktr-d-cardwrap {
	position: relative;
	display: block;
}

/* ------------------------------------------------- confirm: the look + you */

/*
 * The two inputs, side by side, with a `+` between them.
 *
 * The two cells share the row equally (`flex: 1 1 0`) so a portrait look and a
 * portrait photo come out the same size — comparing them is the point of the
 * screen. `min-width: 0` matters: without it a flex item refuses to shrink
 * below its content's intrinsic width, and the pair overflows a narrow phone.
 */
.ktr-d-pair {
	flex: 1;
	min-height: 0;
	display: flex;
	align-items: stretch;
	justify-content: center;
	gap: 10px;
}

/* Also a .ktr-d-frame — the size container the card measures itself against. */
.ktr-d-cell {
	flex: 1 1 0;
	min-width: 0;
	min-height: 0;
}

/*
 * Picture and caption as one block, centred INSIDE the slot.
 *
 * The caption used to be a sibling of the slot, and the slot is taller than
 * the card whenever width is the binding constraint — which on this screen,
 * with two 9:16 cards side by side, is always. The label then sat at the
 * bottom of all that empty space, a hundred pixels below the thing it labels.
 *
 * The width is the card's own formula with the caption's line subtracted from
 * the height budget, so the pair still fits when height is what runs out.
 */
.ktr-d-cellinner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	width: min(100cqw, calc((100cqh - 22px) * 9 / 16));
}

/* Sized by the wrapper above, not by their own container-unit formula. */
.ktr-d-cellinner > .ktr-d-card,
.ktr-d-cellinner > .ktr-d-cardwrap {
	width: 100%;
}

.ktr-d-celllabel {
	flex: none;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	text-align: center;
	color: var(--ktr-muted);
}

/*
 * Centred against the PICTURES, not the cells. The captions hang under both,
 * so the block's centre is half a caption line below the pictures' centre —
 * hence the nudge, which is that half line back up.
 */
.ktr-d-plus {
	flex: none;
	align-self: center;
	margin-top: -14px;
	font-size: 22px;
	font-weight: 700;
	line-height: 1;
	color: var(--ktr-muted);
}

/* ---------------------------------------------------- working: over the body */

/*
 * The scrim under the ring.
 *
 * Same device as the example shot's: dim the whole picture rather than paint a
 * panel on part of it, so the ring and its caption have a floor wherever they
 * fall. Below them in paint order — they carry z-index, this does not.
 */
.ktr-d-workingwrap::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: rgba(0, 0, 0, 0.5);
	pointer-events: none;
}

.ktr-d-working {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 14px;
}

/* On the photo the ring and its number invert — see the base rules, which are
   written for a light box and are still what the no-photo fallback uses. */
.ktr-d-workingwrap .ktr-d-progresstrack {
	stroke: #fff;
	opacity: 0.35;
}

.ktr-d-workingwrap .ktr-d-progressarc {
	stroke: #fff;
}

.ktr-d-workingwrap .ktr-d-progresspct,
.ktr-d-workingwrap .ktr-d-loadtitle {
	color: #fff;
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* -------------------------------------------------- result: picture + button */

/*
 * The picture and "Shop this outfit" as one column, sized by the SAME formula
 * the card would use on its own — which is what makes the button exactly as
 * wide as the picture.
 *
 * A button outside the slot cannot match it. The card's width is
 * `min(100cqw, 100cqh * 9/16)` against the slot, and which half of that `min`
 * wins depends on the slot's height — whatever is left after the heading and
 * the tabs, at this viewport, in this theme. Nothing outside the container can
 * know that number, so the only way to agree with it is to be measured by it.
 *
 * --ktr-d-cta is the button's own block (its height plus the gap above it),
 * subtracted from the height budget so the column still fits when height is
 * what runs out. Zero without a button: an outfit with no products has none,
 * and reserving space for it would shrink that picture for nothing.
 */
.ktr-d-resultinner {
	--ktr-d-cta: 0px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	width: min(100cqw, calc((100cqh - var(--ktr-d-cta)) * 9 / 16));
}

.ktr-d-resultinner.has-cta {
	/* 50px button + the 12px gap above it. */
	--ktr-d-cta: 62px;
}

/* Both children are sized by the wrapper, not by their own rules: the card
   would otherwise re-derive the container formula and the button would keep
   the 340px cap it wears in a footer. */
.ktr-d-resultinner > .ktr-d-cardwrap {
	width: 100%;
}

.ktr-d-resultinner > .ktr-d-go {
	width: 100%;
	max-width: none;
	flex: none;
}

/* ------------------------------------------------------ result: You/Original */

/*
 * The same two labels as `TryOnComparisonImage` in the iOS app. A segmented
 * control rather than two buttons: they are two views of one picture, and
 * spacing them apart would read as two separate actions.
 */
.ktr-d-tabs {
	flex: none;
	align-self: center;
	display: inline-flex;
	padding: 3px;
	border-radius: 999px;
	background: #f1f1f1;
}

.ktr-d-tab {
	appearance: none;
	border: 0;
	border-radius: 999px;
	padding: 6px 18px;
	background: none;
	color: var(--ktr-muted);
	font-family: var(--ktr-d-font, inherit);
	font-size: 13px;
	font-weight: 600;
	line-height: 1.2;
	cursor: pointer;
}

/*
 * Neve paints bare buttons — `button:hover` sets `var(--primarybtnhoverbg)`,
 * which on her site is this stylesheet's own accent pink — so an unqualified
 * `.ktr-d-tab` filled with pink on hover and the off tab looked selected.
 * Held off with specificity rather than !important, the same way the sample
 * link is.
 */
.ktr-d-tabs .ktr-d-tab:hover,
.ktr-d-tabs .ktr-d-tab:focus {
	background: none;
	color: var(--ktr-ink, #272626);
	box-shadow: none;
}

.ktr-d-tabs .ktr-d-tab.is-on,
.ktr-d-tabs .ktr-d-tab.is-on:hover,
.ktr-d-tabs .ktr-d-tab.is-on:focus {
	background: #fff;
	color: var(--ktr-ink, #272626);
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.16);
}

/*
 * The stand-in is a full-size card — it fills the step's 9:16 slot exactly as
 * the look does in step 1 — and is marked as a stand-in by being dimmed all
 * over instead.
 *
 * The whole picture sits back a step, so the eye reads it as reference rather
 * than as the visitor's own photo, and the "Example" label and the checklist
 * both get a floor to sit on wherever they fall on it.
 *
 * Below the label and the checklist in paint order — those carry z-index: 1,
 * this does not — so it dims the photograph and nothing written on it.
 */
.ktr-d-examplewrap::after {
	content: "";
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background: rgba(0, 0, 0, 0.4);
	pointer-events: none;
}

.ktr-d-cardwrap .ktr-d-card {
	width: 100%;
}

.ktr-d-empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
	padding: 16px;
	box-sizing: border-box;
	border: 1px dashed #d0d0d0;
	color: var(--ktr-muted);
	font-size: 14px;
	text-align: center;
}

.ktr-d-emptytext {
	margin: 0;
	font-size: 13px;
}


/*
 * The shot list rides ON the example photo.
 *
 * It describes that picture, and as its own block of text it was pushing the
 * picture it describes down the step — above or below, either way it competed
 * with the one thing the step exists to show. Over the top of it, in the
 * picture's own dead space, it costs the step nothing.
 *
 * The list itself: `width: max-content` so it is exactly as wide as its
 * longest line, `margin: 0 auto` so that block sits in the middle. The lines
 * inside it all start from one left edge.
 *
 * Centring the list ITEMS instead would give each row its own left edge — the
 * ticks would zig-zag down and there would be nothing to read along.
 */
.ktr-d-checks {
	list-style: none;
	margin: 0 auto;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 7px;
	width: max-content;
	max-width: 100%;
}

.ktr-d-checks li {
	display: flex;
	align-items: center;
	gap: 7px;
	font-size: 12px;
	line-height: 1.35;
	color: var(--ktr-ink, #272626);
}

/*
 * The box the list and the Upload button share, and what the scrim is painted
 * on: full width of the picture, with the list inside keeping its own width
 * and centring.
 *
 * The button is in here rather than under the card so that nothing sits below
 * the picture — the picture then gets the step's whole 9:16 slot. It lands
 * directly beneath the text that tells you what to photograph, which is the
 * order you read them in.
 */
.ktr-d-checkswrap {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
}

/*
 * The scrim fades out rather than ending on a hard edge — her example is a
 * bright, pale room, and a flat band across it read as a second element pasted
 * on. It only needs to be dark enough behind the text; the far end of the
 * gradient is doing nothing but hiding the seam.
 */
.ktr-d-cardwrap .ktr-d-checkswrap {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 1;
	box-sizing: border-box;
	padding: 34px 12px 14px;
	border-radius: 0 0 var(--ktr-radius, 3px) var(--ktr-radius, 3px);
	background: linear-gradient(
		to top,
		rgba(0, 0, 0, 0.68),
		rgba(0, 0, 0, 0.55) 60%,
		rgba(0, 0, 0, 0)
	);
	text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.ktr-d-cardwrap .ktr-d-checks li {
	color: #fff;
}

/* A button, not a caption — it keeps its own edges off the shadow. */
.ktr-d-cardwrap .ktr-d-upload {
	text-shadow: none;
}

/* The placeholder box has no picture to sit on, so the list stays in flow. */
.ktr-d-empty .ktr-d-checkswrap {
	margin-top: 10px;
	width: 100%;
}

.ktr-d-check {
	color: var(--ktr-accent);
	font-weight: 700;
}

.ktr-d-btn {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 10px 24px;
	border: 0;
	border-radius: var(--ktr-radius, 3px);
	background: var(--ktr-accent);
	color: #fff;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
}

.ktr-d-btn:hover:not(:disabled),
.ktr-d-btn:focus:not(:disabled) {
	background: var(--ktr-accent-hover);
	color: #fff;
}

/* Spent allowance — present but inert, matching .ktr-d-go:disabled. */
.ktr-d-btn:disabled {
	background: #cfcfcf;
	color: #fff;
	cursor: not-allowed;
}

.ktr-d-btn-ghost {
	background: none;
	border: 1px solid #dcdcde;
	color: #272626;
}

.ktr-d-btn-ghost:hover,
.ktr-d-btn-ghost:focus {
	background: #f6f7f7;
	color: #272626;
}

/* The file input covers its label so the whole pill is the hit target. */
.ktr-d-file {
	position: absolute;
	inset: 0;
	opacity: 0;
	cursor: pointer;
}

/*
 * The way out of step 2 for someone with no photo of their own.
 *
 * A text link, not a button: it sits directly under the upload button and a
 * second filled button beside it would read as an equal choice. Underlined
 * because on the scrim there is no other cue that it is pressable.
 *
 * Pulled up out of the wrap's 12px gap — that gap spaces the checklist from
 * the button, and the same distance again below reads as a separate element
 * rather than a footnote to the button it belongs to.
 */
.ktr-d-samplelink {
	margin-top: -5px;
	padding: 4px 2px;
	border: 0;
	background: none;
	color: inherit;
	font-family: inherit;
	font-size: 13px;
	line-height: 1.3;
	text-decoration: underline;
	text-underline-offset: 2px;
	cursor: pointer;
}

/*
 * It is a bare <button>, and Neve paints bare buttons: `button:hover` sets
 * `var(--primarybtnhoverbg)`, which on her site is the same pink this
 * stylesheet uses as its accent — so hovering the text link filled it with a
 * pink slab. Neutralised across every interactive state, not :hover alone;
 * :focus and :active paint it too, and :focus persists after the click that
 * chose the sample.
 *
 * Held off with SPECIFICITY rather than !important (this stylesheet has none
 * and is better for it): the wrapper class puts these above a theme's plain
 * `button:hover`. The only feedback left is a slight fade, which is what a
 * text link should do.
 */
.ktr-d-checkswrap .ktr-d-samplelink,
.ktr-d-checkswrap .ktr-d-samplelink:hover,
.ktr-d-checkswrap .ktr-d-samplelink:focus,
.ktr-d-checkswrap .ktr-d-samplelink:active {
	border: 0;
	background: none;
	box-shadow: none;
	text-decoration: underline;
}

.ktr-d-checkswrap .ktr-d-samplelink:hover {
	opacity: 0.75;
}

.ktr-d-samplelink:disabled {
	cursor: default;
	opacity: 0.6;
}

/* On the example shot it rides the scrim with the checklist. */
.ktr-d-cardwrap .ktr-d-samplelink {
	color: #fff;
}

/* In the placeholder box there is no scrim, so it takes the muted body tone. */
.ktr-d-empty .ktr-d-samplelink {
	color: var(--ktr-muted);
}

/*
 * Marks the stand-in shot so it isn't mistaken for an uploaded photo.
 *
 * Top centre, out of the way of the shot list along the bottom of the same
 * picture.
 */
.ktr-d-example {
	position: absolute;
	top: 10px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 1;
	/*
	 * Bare text, no chip. A pill made it look like a control on the picture —
	 * something to press — when it is only a caption. The dim over the whole
	 * example gives it enough to sit on; the shadow covers the pale top corner
	 * of her example shot.
	 */
	color: #fff;
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
	white-space: nowrap;
}

/*
 * A small chip in the picture's top-left corner.
 *
 * Deliberately understated: it is a correction, not one of the screen's two
 * actions, and on the confirm screen it sits between two pictures the visitor
 * is meant to be comparing. It was a size larger and read as a third control
 * competing with "Try it on" — worse on a phone, where each card is only about
 * 165px wide and the chip was crossing a fifth of it.
 *
 * Still 24px tall, which is small but reachable; the 6px inset keeps it clear
 * of the card's corner radius.
 */
.ktr-d-replace {
	position: absolute;
	top: 6px;
	left: 6px;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: 24px;
	padding: 0 8px;
	border: 0;
	border-radius: 999px;
	background: rgba(0, 0, 0, 0.62);
	color: #fff;
	font-size: 10px;
	font-weight: 600;
	line-height: 1;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	white-space: nowrap;
	cursor: pointer;
}

.ktr-d-replace:hover {
	background: rgba(0, 0, 0, 0.85);
	color: #fff;
}

/*
 * The one action on the confirm screen, so it takes the full width of the
 * footer rather than sitting as a pill in the middle of it. It used to be the
 * whole of a step and was padded like it (22px/40px); in a footer under the
 * pictures that height was just a slab.
 */
.ktr-d-go {
	width: 100%;
	max-width: 340px;
	border: 0;
	border-radius: var(--ktr-radius, 3px);
	padding: 15px 28px;
	background: var(--ktr-accent);
	color: #fff;
	font-size: 17px;
	font-weight: 700;
	letter-spacing: 0.2px;
	/*
	 * In px, and NOT inherited. On the result screen this button's height is
	 * subtracted from the picture's height budget (--ktr-d-cta), so it has to
	 * be a number this stylesheet knows. Left to inherit, it took Neve's body
	 * leading of ~1.7 — a 59px button against a 50px budget, and the column
	 * overflowed its slot by 9px on her site while looking correct anywhere
	 * else. 20 + 15 + 15 = exactly 50.
	 */
	line-height: 20px;
	cursor: pointer;
	box-shadow: 0 4px 14px rgba(239, 98, 197, 0.35);
}

.ktr-d-go:hover:not(:disabled) {
	background: var(--ktr-accent-hover);
	transform: translateY(-1px);
}

.ktr-d-go:disabled {
	background: #cfcfcf;
	box-shadow: none;
	cursor: not-allowed;
}

.ktr-d-gonote {
	margin: 0;
	font-size: 12px;
	color: var(--ktr-muted);
}

/* The allowance note, as opposed to the "upload a photo first" nudge. Darker
   and wider: it explains a disabled button rather than prompting the next
   step, and it carries a full sentence from the server. */
.ktr-d-gonote-blocked {
	max-width: 34ch;
	color: #272626;
	text-align: center;
	line-height: 1.4;
}

/* The generating percentage — the indeterminate spinner it replaced said
   nothing about how far along a 30-second wait was. Drawn by progressRing()
   in tryon.js; the arc's dash offset is the only thing that animates. */
.ktr-d-progress {
	position: relative;
	width: 96px;
	height: 96px;
}

.ktr-d-progress svg {
	width: 100%;
	height: 100%;
	/* 0% is the top of the circle, not the right. */
	transform: rotate(-90deg);
}

.ktr-d-progresstrack {
	stroke: var(--ktr-accent);
	opacity: 0.15;
}

.ktr-d-progressarc {
	stroke: var(--ktr-accent);
	transition: stroke-dashoffset 100ms linear;
}

.ktr-d-progresspct {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-family: var(--ktr-d-font, inherit);
	color: #272626;
	line-height: 1;
}

.ktr-d-progresspct b {
	font-size: 25px;
	font-weight: 700;
}

/* The sign, set smaller and raised against the number's cap height. Nudged up
   from CENTRE, not aligned to the box's top: these two are flex items of the
   96px ring box, so `align-self: flex-start` sent the % to the top of the ring
   rather than to the top of the number. */
.ktr-d-progresspct i {
	font-style: normal;
	font-size: 13px;
	position: relative;
	top: -6px;
	margin-left: 1px;
	opacity: 0.7;
}

/* Respect a reduced-motion preference: the number still counts, the arc just
   jumps to each value instead of easing there. */
@media (prefers-reduced-motion: reduce) {
	.ktr-d-progressarc {
		transition: none;
	}
}

.ktr-d-loadtitle {
	margin: 0;
	font-size: 15px;
	color: #272626;
}

.ktr-d-error {
	margin: 0;
	font-size: 14px;
	color: #272626;
}

.ktr-d-turnstile:empty {
	display: none;
}

/*
 * The mount used when the confirm screen's one isn't on screen (regenerating
 * from the result). It must be IN the document and rendered — a Turnstile widget
 * in a detached or display:none container never fires its callback — so it's
 * parked off-screen rather than hidden.
 */
.ktr-d-turnstile-float {
	position: absolute;
	left: -9999px;
	top: 0;
	width: 300px;
	height: 65px;
	overflow: hidden;
}

/*
 * Retry and Download, riding the picture's bottom-right corner — the same
 * place the storefront puts them.
 *
 * They spent a while in a row of their own under the result, which was honest
 * about not covering the try-on but cost 50px of a screen that now also
 * carries a shelf and a Shop button. The corner they sit in is the one part
 * of a 9:16 portrait that is reliably floor.
 *
 * DOM order is Retry then Download, so this is a plain `flex-end` rather than
 * a row-reverse: visual order and tab order stay the same, and Download lands
 * outermost in the corner.
 *
 * Both are pinned to --ktr-d-action-h. The icon is a circle sized by that and
 * the pill is sized by padding alone, so without it they came out different
 * heights and sat on a ragged baseline.
 */
.ktr-d-resultactions {
	--ktr-d-action-h: 36px;
	position: absolute;
	left: 0;
	right: 0;
	bottom: 12px;
	z-index: 2;
	display: flex;
	justify-content: flex-end;
	align-items: center;
	gap: 8px;
	padding: 0 12px;
	flex-wrap: wrap;
}

.ktr-d-pill {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	height: var(--ktr-d-action-h, 36px);
	padding: 0 16px;
	border: 0;
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.95);
	color: #272626;
	font-size: 12px;
	font-weight: 600;
	line-height: 1;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}

/* Spent allowance — see .ktr-d-btn:disabled. Opacity rather than a flat grey:
   this one sits on top of the result image, where a solid fill reads as a
   second card rather than a disabled control. */
.ktr-d-pill:disabled {
	opacity: 0.5;
	box-shadow: none;
	cursor: not-allowed;
}

/* Icon-only action, matching the storefront's round download button. */
.ktr-d-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--ktr-d-action-h, 36px);
	height: var(--ktr-d-action-h, 36px);
	flex: none;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.85);
	color: #fff;
	cursor: pointer;
	box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

.ktr-d-icon:hover,
.ktr-d-icon:focus {
	background: #000;
	color: #fff;
}

.ktr-d-icon svg {
	display: block;
}

/*
 * Download on a mouse, "press and hold" on a finger — never both.
 *
 * The proxy link is the right answer on a desktop, where a click puts a file
 * in the Downloads folder. On a phone it cannot be: `download` is ignored
 * cross-origin, so the anchor opens a tab and hands the save back to the
 * browser's share sheet — while the platform gesture, long-press the picture
 * and "Add to Photos", does it in one press without leaving the post.
 *
 * The switch is `pointer: coarse` rather than a width query: what decides this
 * is whether there is a finger on the glass, not how wide the glass is. A
 * touch laptop resolves as `fine` for its primary pointer and keeps the
 * button, which is the right call — it has a Downloads folder.
 */
.ktr-d-savehint {
	display: none;
}

@media (pointer: coarse) {
	.ktr-d-icon-download {
		display: none;
	}

	.ktr-d-savehint {
		display: block;
		flex: none;
		width: 100%;
		margin: 0;
		text-align: center;
		font-size: 12px;
		font-weight: 600;
		line-height: 1.4;
		color: var(--ktr-ink, #272626);
	}

	/*
	 * ...and the column's height budget has to pay for it, exactly as it does
	 * for the button: its 17px line plus the 12px gap above it. Only here —
	 * on a fine pointer the line is `display: none` and owes nothing.
	 */
	.ktr-d-resultinner {
		--ktr-d-cta: 29px;
	}

	.ktr-d-resultinner.has-cta {
		--ktr-d-cta: 91px;
	}
}

/*
 * The last line on the result screen.
 *
 * `flex: none` and no `margin-top: auto` — the frame above it is the flexible
 * box and already takes the slack; pushing from here would only fight it.
 */
.ktr-d-disclaimer {
	flex: none;
	margin: 0;
	text-align: center;
	font-size: 11px;
	line-height: 1.4;
	color: #8a8a8a;
}

/* -------------------------------------------------- modal, small screens -- */

/*
 * On a phone the modal is as tall as its content, and no taller.
 *
 * It used to be a flat `height: 100dvh`. That is right for the screens whose
 * picture fills the viewport, and wrong for the confirm screen, where two 9:16
 * cards side by side are limited by WIDTH — half a phone each — and come out
 * about 300px tall inside a 760px box. The slack went above and below them, so
 * the two pictures floated in the middle with a band of white between them and
 * the title.
 *
 * **This is why the card sizing changes here.** Everywhere else a card is
 * `min(100cqw, 100cqh * 9/16)` measured against its slot, which needs the
 * panel to have a definite height — and a panel sized by its content does not.
 * Worse, `container-type: size` implies `contain: size`, so a size container
 * inside an auto-height panel reports zero and every card collapses. So on
 * narrow screens the containers are turned off and the same `min()` is
 * expressed against the VIEWPORT instead: --ktr-d-pic is how tall a picture
 * may be here, which is 100dvh less this modal's own chrome above and below
 * it. The arithmetic is per screen because the chrome is — the confirm screen
 * has no heading and the result screen has a toggle and a button.
 *
 * `100dvh`, not `100vh`: on iOS Safari `vh` is the tall viewport with the
 * address bar retracted, so a `100vh` panel puts its footer — the Try it on
 * button — under the toolbar until you scroll, and a fixed panel does not
 * scroll.
 *
 * There is no scroll-snap here any more, and no `min-height` on anything. The
 * layout that this replaced needed both: its four steps were full-height
 * sections under `scroll-snap-type: y mandatory`, which comes with the rule
 * that every section must be at least as tall as the snap viewport or the
 * scroll springs back and the sheet becomes unscrollable. One screen at a time
 * has nothing to snap between.
 */
@media screen and (max-width: 782px) {
	.ktr-d-panel {
		/*
		 * Anchored to the BOTTOM, and `top: auto` is what makes the height
		 * come from the content at all. A fixed box with both `top` and
		 * `bottom` set resolves `height: auto` by filling the space between
		 * them — which is how the first attempt at this measured 737px on
		 * every screen, content be damned. Only one of the two may be set.
		 *
		 * Bottom rather than centred because centring a content-height box
		 * needs `top: 50%` and a transform, and a transform on the panel makes
		 * it the containing block for any fixed descendant. There are none
		 * today — the crop overlay is appended to <body>, not to this — but
		 * the sheet growing from the edge the thumb is nearest is the better
		 * answer anyway, and it needs no such caveat.
		 */
		left: 0;
		right: 0;
		top: auto;
		bottom: 0;
		width: 100%;
		height: auto;
		max-height: 100dvh;
		margin: 0;
		/* Rounded where it meets the page, square where it meets the edge. */
		border-radius: 18px 18px 0 0;
		/*
		 * A safety valve, not a scrolling layout. The budgets below are
		 * arithmetic, and arithmetic can be beaten — a two-line quota message
		 * from the server, a theme that gives the title a taller line. If the
		 * content does exceed the viewport it should be reachable rather than
		 * clipped, which is what `overflow: hidden` would do.
		 */
		overflow-y: auto;
	}

	.ktr-d-head {
		height: 52px;
		padding: 0 16px;
	}

	.ktr-d-title {
		font-size: 20px;
	}

	/*
	 * The column sizes to its content now, so nothing may grow into slack.
	 *
	 * `.ktr-d-screen` is deliberately NOT in this list. The body is a ROW
	 * flex container, so `flex: none` on the screen means shrink-to-fit its
	 * max-content WIDTH — which is the confirm pair at its natural size, 660px
	 * of it, overflowing a 390px phone and taking the picture-width cap with
	 * it (100% of a box that wide is not 100% of the phone). It keeps `flex:
	 * 1` and fills the row; its height comes from stretch, and the body's from
	 * the screen's content.
	 */
	.ktr-d-body,
	.ktr-d-pair {
		flex: none;
	}

	.ktr-d-body {
		overflow: visible;
	}

	.ktr-d-screen {
		padding: 12px 16px 16px;
	}

	/*
	 * Not a size container here — see the note above. Left as one it would
	 * `contain: size`, report zero height inside the auto-height panel, and
	 * collapse every card to nothing.
	 */
	.ktr-d-frame {
		container-type: normal;
		flex: none;
	}

	/*
	 * How tall a picture may be on each screen: the viewport, less this
	 * modal's own chrome around it. Each is the sum of what that screen puts
	 * above and below the picture — head 52, screen padding 28, a 12px gap
	 * between each pair of children, heading 26, tabs 32, caption block 21,
	 * footer 54 — plus 4px of slack so a rounding difference cannot clip.
	 */
	.ktr-d-body-upload .ktr-d-screen {
		--ktr-d-pic: calc(100dvh - 124px);
	}

	.ktr-d-body-confirm .ktr-d-screen {
		--ktr-d-pic: calc(100dvh - 172px);
	}

	.ktr-d-body-working .ktr-d-screen {
		--ktr-d-pic: calc(100dvh - 84px);
	}

	/* 228 + the disclaimer's two 16px lines and the 12px gap above it. It
	   wraps at every phone width, so two is what it costs, not one. */
	.ktr-d-body-result .ktr-d-screen {
		--ktr-d-pic: calc(100dvh - 272px);
	}

	/*
	 * The same `min()` the container-query rules express, against the
	 * viewport. The fallback matters: a screen whose stage class this
	 * stylesheet does not know still gets a sane picture rather than a
	 * zero-width one.
	 */
	.ktr-d-card,
	.ktr-d-empty,
	.ktr-d-cardwrap,
	.ktr-d-cellinner,
	.ktr-d-resultinner {
		width: min(100%, calc(var(--ktr-d-pic, 55dvh) * 9 / 16));
	}

	/* ...and inside those two wrappers the card fills, as it does elsewhere. */
	.ktr-d-cellinner > .ktr-d-card,
	.ktr-d-cellinner > .ktr-d-cardwrap,
	.ktr-d-resultinner > .ktr-d-cardwrap {
		width: 100%;
	}

	/*
	 * The pair goes edge to edge. On a 360px phone the two 9:16 cards are
	 * ~160px each, which is already the narrowest they can be and still show
	 * a garment — the screen's side padding is what has to give.
	 */
	.ktr-d-pair {
		gap: 8px;
	}

	/* Each cell is half the row, so the cap above is a cap and the width is
	   what actually binds. `min-width: 0` lets it shrink to that. */
	.ktr-d-cell {
		flex: 1 1 0;
		min-width: 0;
	}
}

/* ---------------------------------------------------------- selfie crop -- */

.ktr-d-cropwrap {
	position: fixed;
	inset: 0;
	z-index: 1000000; /* above the drawer */
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 20px;
	background: rgba(0, 0, 0, 0.7);

	/*
	 * The belt to the JS clamp's braces. Even with the stage sized to fit,
	 * a very short viewport (landscape phone, or a browser with a tall
	 * toolbar) can leave the panel taller than the screen — and centred flex
	 * children overflow BOTH ends, so the buttons would be unreachable with
	 * nothing to scroll.
	 */
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* Centred when it fits, top-aligned when it doesn't -- a centred child that
   overflows loses its top edge, which is where the picture is. */
@media (max-height: 700px) {
	.ktr-d-cropwrap {
		align-items: flex-start;
	}
}

.ktr-d-crop {
	/*
	 * The crop dialog is appended to <body>, not into the panel, so it
	 * inherits none of the drawer's tokens — its Use / Cancel buttons were
	 * resolving --ktr-d-font to nothing and falling back to her body serif
	 * while the drawer behind them was sans. Same three tokens the panel
	 * declares, same reason.
	 */
	--ktr-accent: #ef62c5;
	--ktr-accent-hover: #d957b1;
	--ktr-sans: "Open Sans", Arial, Helvetica, sans-serif;
	--ktr-d-font: var(--ktr-sans);
	font-family: var(--ktr-d-font);
	max-width: 340px;
	width: 100%;
	padding: 16px;
	border-radius: 12px;
	background: #fff;
	color: #272626;
	text-align: center;
}

.ktr-d-crophint {
	margin: 0 0 10px;
	font-size: 12px;
	color: #7a7a7a;
}

.ktr-d-crop-stage {
	position: relative;
	margin: 0 auto;
	overflow: hidden;
	border-radius: var(--ktr-radius, 3px);
	background: #1d2327;
	touch-action: none;
	user-select: none;
}

.ktr-d-crop-img {
	position: absolute;
	inset: 0;
	display: block;
	pointer-events: none;
}

.ktr-d-crop-frame {
	position: absolute;
	box-sizing: border-box;
	border: 2px solid #fff;
	box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
	cursor: move;
	touch-action: none;
}

/* Inside the frame: a 9:16 selfie fills the stage, and a handle hanging off
   the corner would be clipped by the stage's overflow. */
.ktr-d-crop-handle {
	position: absolute;
	right: 4px;
	bottom: 4px;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: #fff;
	border: 2px solid var(--ktr-accent);
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
	cursor: nwse-resize;
	touch-action: none;
}

/* A grip mark, so it reads as draggable rather than as a dot -- the same
   corner chevron the editor's handle carries (see admin.css); this port had
   shipped the bare circle. */
.ktr-d-crop-handle::after {
	content: '';
	position: absolute;
	inset: 5px;
	border-right: 2px solid var(--ktr-accent);
	border-bottom: 2px solid var(--ktr-accent);
}

.ktr-d-cropactions {
	display: flex;
	justify-content: center;
	gap: 8px;
	margin-top: 12px;
	flex-wrap: wrap;
}

/*
 * The hint line only exists on a touch device, so only there does the result
 * screen owe it height: 256 plus its own 16px line and the 12px gap above it.
 * After the width block above, so it wins over the rule there.
 */
@media screen and (max-width: 782px) and (pointer: coarse) {
	.ktr-d-body-result .ktr-d-screen {
		--ktr-d-pic: calc(100dvh - 300px);
	}
}
