/*
 * Omdegan storefront.
 *
 * The design system arrives as React components with inline styles. Here each of those
 * components becomes one class block using the same tokens, so markup stays semantic and
 * the values stay traceable back to the source design.
 *
 * Sections
 *   1. Base and layout
 *   2. Controls        button, icon button, field, choice, switch
 *   3. Surfaces        card, badge, tag, notice, toast, dialog
 *   4. Commerce        price, tier ladder, quantity, lead time, minimum bar, date picker
 *   5. Product         row, card, rails
 *   6. Chrome          header, footer, mobile tab bar
 *   7. WooCommerce     template overrides
 */

/* ------------------------------------------------------------------ 1. Base */

/*
 * Every box measures its own border and padding.
 *
 * Fourteen rules used to say this one at a time and twelve that needed it did not, so a
 * control asking for --control-h rendered two pixels taller than the token it named, and
 * the one place that added padding as well as a border came out eighteen pixels over.
 * Nothing in the theme ever wanted content-box; it was only ever the default nobody had
 * turned off.
 *
 * The inherit/initial pair lets a component opt out for a subtree without every
 * descendant having to opt back in. Nothing does today.
 */
html {
	box-sizing: border-box;
	direction: rtl;

	/*
	 * The scrollbar's room is kept whether or not there is a scrollbar, so that stopping the
	 * page behind a dialog does not shift everything sideways under the scrim.
	 */
	scrollbar-gutter: stable;
}

/*
 * A dialog over the page is the only thing that scrolls while it is open.
 *
 * A modal <dialog> takes the click and the focus, and leaves the wheel alone: the page
 * behind it still scrolls, and its scrollbar sits outside the panel offering to move
 * something the scrim says is not available. Stopped here rather than in script, so it
 * holds for a dialog whichever code opened it and comes back on its own when it closes.
 */
html:has(dialog.omd-dialog[open]) { overflow: hidden; }

*,
*::before,
*::after {
	box-sizing: inherit;
}

/*
 * Inheritance does not reach into a <details>.
 *
 * Its children are slotted into the element's own shadow tree, so they inherit through a
 * slot the rule above cannot select, and land on the initial content-box however many
 * ancestors say otherwise. Measured: a div inside a plain div is border-box, the same div
 * inside a <details> is not. Re-anchoring at the fold puts the subtree back on inherit.
 */
details > * {
	box-sizing: border-box;
}

body {
	margin: 0;
	font: var(--type-body);
	color: var(--text-body);
	background: var(--surface-page);
	-webkit-font-smoothing: antialiased;
	text-wrap: pretty;
	/*
	 * Vazirmatn's ss01 draws Latin digits as Persian ones, and Arabic-Indic ones too. It
	 * changes the glyph, not the character: a figure still copies, parses and posts back
	 * as 1234, which is what every parser downstream expects, while reading as ۱۲۳۴.
	 *
	 * That covers the figures this theme never sees — WooCommerce's own strings, another
	 * plugin's output — without anyone having to remember to convert them.
	 */
	font-feature-settings: "ss01";
}

/*
 * Except where the digits are an identifier rather than a quantity. A coupon is typed
 * back by hand and read down a phone line, and a code shown as OMDEGAN۱۰ cannot be.
 */
input[name="coupon_code"],
code, kbd, samp { font-feature-settings: normal; }

h1, h2, h3, h4 {
	margin: 0;
	color: var(--text-strong);
}

a {
	color: var(--text-link);
	/* Persian dots and descenders need the rule pushed further off the baseline, and a
	   thickness the typeface chooses rather than one fixed in pixels. */
	text-decoration-thickness: from-font;
	text-underline-offset: 0.45em;
}

a:hover {
	color: var(--text-link-hover);
}

.omd-icon {
	display: block;
	flex: 0 0 auto;
}

/* Lucide draws these pointing left; in an RTL layout "forward" is the other way. */
.omd-icon--mirrored {
	transform: scaleX(-1);
}

button {
	font-family: inherit;
}

/*
 * A fieldset groups fields for a screen reader; the box around it is the browser's idea,
 * not this design's. One place remembered to turn it off and the next did not, and drew a
 * ruled box with its own padding and its own corners in the middle of a card.
 */
fieldset {
	border: 0;
	padding: 0;
	margin: 0;
	min-width: 0;
}

legend {
	padding: 0;
}

img {
	max-width: 100%;
	height: auto;
}

:focus-visible {
	outline: 2px solid var(--border-focus);
	outline-offset: 2px;
}

/* Hidden means hidden.
 *
 * The browser's own rule is a plain [hidden] { display: none }, which any class that sets
 * a display of its own outranks — so a control the theme styles as flex stays on screen
 * while every script on the page believes it is gone. That is a bug in the one state
 * nobody thinks to look at, and it is worth an !important to make impossible. */
[hidden] { display: none !important; }

@keyframes omd-spin { to { transform: rotate(360deg); } }
@keyframes omd-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes omd-fade { from { opacity: 0; } to { opacity: 1; } }

.omd-shell {
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	background: var(--surface-page);
}

.omd-container {
	width: 100%;
	max-width: var(--width-max);
	margin-inline: auto;
	padding-inline: var(--gutter-inline);
}

.omd-main {
	flex: 1;
	padding-block: var(--space-7) var(--space-13);
}

.omd-page-title {
	font: var(--type-h3);
	color: var(--text-strong);
	margin: 0 0 var(--space-2);
}

.omd-page-lede {
	font: var(--type-sm);
	color: var(--text-muted);
	margin: 0 0 var(--space-6);
	max-width: 62ch;
}

/* The margin is stated whole. This class lands on a p in one template and an h2 in
   another, and each brings a top margin of its own that pushes the label away from what
   it labels -- one em of it, which grew with the label when the type did. */
.omd-eyebrow {
	font: var(--type-label);
	color: var(--text-muted);
	margin: 0 0 var(--space-3);
}

/* Heading a card rather than a page section, where the design leaves a step more room
   between the label and what it labels. */
.omd-card > .omd-eyebrow { margin-bottom: var(--space-4); }

.omd-section {
	margin-top: var(--space-9);
}

.omd-section__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	margin-bottom: var(--space-4);
}

.omd-section__head h2 {
	font: var(--type-title);
	margin: 0;
}

/* The name and whatever counts it, kept together on the start side so the way to the
   rest stays pinned to the other. */
.omd-section__lead {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: var(--space-4);
	min-width: 0;
}

.omd-section__note { font: var(--type-sm); color: var(--text-faint); }

.omd-section__head--on-deal h2 { color: var(--text-on-deal); }
.omd-section__head--on-deal .omd-section__note { color: var(--text-on-deal-muted); }

.omd-screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.omd-skip-link:focus {
	position: fixed;
	inset-block-start: var(--space-3);
	inset-inline-start: var(--space-3);
	z-index: var(--z-toast);
	padding: var(--space-3) var(--space-5);
	background: var(--surface-card);
	border-radius: var(--radius-control);
	box-shadow: var(--shadow-3);
}

/* Hidden rails keep no visible bar; product rails keep a thin one, because it is the
   only cue that content continues sideways. */
.omd-scroll {
	scrollbar-width: none;
	-ms-overflow-style: none;
}

.omd-scroll::-webkit-scrollbar {
	height: 0;
	width: 0;
}

.omd-rail {
	display: flex;
	gap: var(--space-4);
	overflow-x: auto;
	padding-bottom: var(--space-3);
	scrollbar-width: thin;
	scrollbar-color: var(--sang-400) transparent;
}

.omd-rail::-webkit-scrollbar { height: 8px; }
.omd-rail::-webkit-scrollbar-track { background: transparent; }
.omd-rail::-webkit-scrollbar-thumb { background: var(--sang-400); border-radius: var(--radius-pill); }
.omd-rail::-webkit-scrollbar-thumb:hover { background: var(--sang-500); }

/*
 * A rail of product cards sizes its own columns rather than leaving it to the card. A
 * fixed card width left a rail of two cards sitting in the corner of an empty row; the
 * columns stretch to fill whatever is there, and start scrolling once there are enough
 * to need it.
 */
.omd-rail--cards {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: minmax(228px, 1fr);
	align-items: stretch;
	gap: var(--space-5);
}

@media (min-width: 901px) {
	.omd-rail--cards { grid-auto-columns: minmax(264px, 1fr); }
}

.omd-rail--on-deal { scrollbar-color: var(--border-on-deal) transparent; }
.omd-rail--on-deal::-webkit-scrollbar-thumb { background: var(--border-on-deal); }
.omd-rail--on-deal::-webkit-scrollbar-thumb:hover { background: var(--wash-on-color); }

/* -------------------------------------------------------------- 2. Controls */

.omd-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-3);
	font: var(--type-body-strong);
	height: var(--control-h);
	padding: 0 var(--space-7);
	border-radius: var(--radius-button);
	border: 1px solid transparent;
	cursor: pointer;
	transition: var(--transition-control), transform var(--dur-1) var(--ease-standard);
	white-space: nowrap;
	text-decoration: none;
	background: var(--anar-600);
	color: var(--text-inverse);
	/* Every other control in the kit measures --control-h border to border. Without this a
	   button was two pixels taller than the field beside it, and a --block one overflowed
	   its card by the width of its own padding. */
}

.omd-btn:active:not(:disabled) { transform: translateY(1px); }
.omd-btn:hover:not(:disabled) { background: var(--anar-700); color: var(--text-inverse); }

.omd-btn--sm { height: var(--control-h-sm); padding: 0 var(--space-5); font-size: var(--fs-sm); }
.omd-btn--lg { height: var(--control-h-lg); padding: 0 var(--space-9); font-size: var(--fs-body-lg); }
.omd-btn--block { width: 100%; }

.omd-btn--secondary { background: var(--shab-800); color: var(--text-inverse); }
.omd-btn--secondary:hover:not(:disabled) { background: var(--shab-900); }

.omd-btn--outline { background: transparent; color: var(--anar-700); border-color: var(--anar-600); }
.omd-btn--outline:hover:not(:disabled) { background: var(--anar-600); color: var(--text-inverse); }

.omd-btn--quiet {
	background: var(--sang-000);
	color: var(--text-strong);
	border-color: var(--border-hairline);
	box-shadow: var(--shadow-1);
}

.omd-btn--quiet:hover:not(:disabled) { background: var(--sang-100); color: var(--text-strong); border-color: var(--border-default); }

.omd-btn--ghost { background: transparent; color: var(--text-muted); }
.omd-btn--ghost:hover:not(:disabled) { background: var(--sang-200); color: var(--text-body); }

.omd-btn--danger { background: var(--state-danger); color: var(--text-inverse); }
.omd-btn--danger:hover:not(:disabled) { background: var(--anar-800); }

/*
 * The destructive one, in its quiet frame.
 *
 * For an action that takes something away but is not the main thing on the screen: a filled
 * danger button beside a row of ordinary ones shouts over all of them, and an operator who
 * sees red everywhere stops reading it.
 *
 * A compound rather than a third variant name, so the markup says what it is: the outline
 * button, in the danger colour. Two classes outrank either on its own, so it does not
 * matter which of them the stylesheet defines first.
 *
 * Hover tints rather than fills. Filling would put --text-inverse on --state-danger, and in
 * the dark theme that ramp end is a pale pink: light text on a light ground. The soft tint
 * is a real token in both themes and keeps the label the colour it already was.
 */
.omd-btn--outline.omd-btn--danger {
	background: transparent;
	color: var(--state-danger);
	border-color: var(--state-danger);
}

.omd-btn--outline.omd-btn--danger:hover:not(:disabled) {
	background: var(--state-danger-soft);
	color: var(--state-danger);
	border-color: var(--state-danger);
}

/* Quiet controls that sit on a coloured surface wash instead of changing hue. */
.omd-btn--on-color { background: transparent; color: var(--text-on-deal); }
.omd-btn--on-color:hover:not(:disabled) { background: var(--wash-on-color); color: var(--text-on-deal); }

/*
 * Unavailable, drawn by receding rather than by fading.
 *
 * An opacity fade dims the label as much as the fill, so a disabled primary button ended up
 * with white text at 45% on a pale red at 45%: quieter, but no easier to read and no clearer
 * about why it could not be pressed. These colours were picked instead, each part moving
 * toward the surface behind it — pale fill, soft border, light label — which is what actually
 * made the fade read as "off".
 *
 * After the variants, because a variant class and this carry the same weight and the last one
 * wins. Every variant already has a 1px border and the same height and padding, so the swap
 * never moves the button or its label by a pixel.
 */
.omd-btn:disabled,
.omd-btn[aria-disabled="true"] {
	background: var(--surface-disabled);
	color: var(--text-disabled);
	border-color: var(--border-disabled);
	box-shadow: none;
	cursor: not-allowed;
}

/* The ones with no fill of their own keep none. Filling them would make an unavailable ghost
   louder than an available one, which is backwards. */
.omd-btn--outline:disabled, .omd-btn--outline[aria-disabled="true"],
.omd-btn--ghost:disabled, .omd-btn--ghost[aria-disabled="true"],
.omd-btn--on-color:disabled, .omd-btn--on-color[aria-disabled="true"] { background: transparent; }

.omd-btn--ghost:disabled, .omd-btn--ghost[aria-disabled="true"],
.omd-btn--on-color:disabled, .omd-btn--on-color[aria-disabled="true"] { border-color: transparent; }

/*
 * A button waiting on the network.
 *
 * Written against aria-busy rather than a class of ours, because the attribute is the
 * thing a screen reader already understands and it cannot get out of step with what is
 * announced. Any button in the kit can take it, which is the point: the panel is going to
 * do more of its saving this way, and a second spinner invented for the next form is a
 * second spinner to keep in step with this one.
 *
 * The spinner stands where the icon was rather than beside it, so the button keeps its
 * width and the row under it does not jump. Whoever sets the attribute must disable the
 * button too; that is what actually stops a second submit, and the cursor here only says
 * so.
 *
 * Left spinning under prefers-reduced-motion. That setting is about motion that decorates
 * or disorients, and this is a small progress indicator in one corner of one control:
 * stopping it would leave a ring that reads as broken rather than as busy.
 */
.omd-btn[aria-busy="true"] { cursor: progress; }

.omd-btn[aria-busy="true"] > .omd-icon { display: none; }

.omd-btn[aria-busy="true"]::before {
	content: "";
	flex: none;
	width: 14px;
	height: 14px;
	border: 2px solid currentColor;
	border-top-color: transparent;
	border-radius: var(--radius-pill);
	animation: omd-spin 0.7s linear infinite;
}

/* Busy is not unavailable, and must not borrow its look. The button is genuinely disabled
   while it waits, which is what stops a second submit, but it should read as working, so it
   keeps its own colour one notch duller instead of receding. More specific than the disabled
   rules above, so it wins wherever both apply. */
.omd-btn[aria-busy="true"]:disabled,
.omd-btn[aria-busy="true"][aria-disabled="true"] {
	background: var(--anar-500);
	color: var(--text-inverse);
	border-color: transparent;
	box-shadow: none;
}

.omd-btn--secondary[aria-busy="true"]:disabled,
.omd-btn--secondary[aria-busy="true"][aria-disabled="true"] { background: var(--shab-700); }

.omd-btn--danger[aria-busy="true"]:disabled,
.omd-btn--danger[aria-busy="true"][aria-disabled="true"] { background: var(--anar-600); }

/* The only one that dulls its border and not its label. The label is the brand colour here
   rather than a fill, and "a notch duller" walks down the anar ramp — which in the dark theme
   walks toward the background instead of away from it, and put the text at roughly 2:1 on the
   card. The softened border and the spinner say "working" on their own. */
.omd-btn--outline[aria-busy="true"]:disabled,
.omd-btn--outline[aria-busy="true"][aria-disabled="true"] {
	background: transparent;
	color: var(--anar-700);
	border-color: var(--anar-400);
}

.omd-btn--quiet[aria-busy="true"]:disabled,
.omd-btn--quiet[aria-busy="true"][aria-disabled="true"] {
	background: var(--sang-000);
	color: var(--text-muted);
	border-color: var(--border-hairline);
	box-shadow: var(--shadow-1);
}

.omd-btn--ghost[aria-busy="true"]:disabled,
.omd-btn--ghost[aria-busy="true"][aria-disabled="true"] {
	background: transparent;
	color: var(--text-faint);
	border-color: transparent;
}

.omd-btn--on-color[aria-busy="true"]:disabled,
.omd-btn--on-color[aria-busy="true"][aria-disabled="true"] {
	background: transparent;
	color: var(--text-on-deal-muted);
	border-color: transparent;
}

.omd-iconbtn {
	position: relative;
	width: var(--control-h);
	height: var(--control-h);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-pill);
	border: 1px solid transparent;
	cursor: pointer;
	transition: var(--transition-control);
	background: var(--sang-000);
	color: var(--text-strong);
	border-color: var(--border-hairline);
	box-shadow: var(--shadow-1);
	text-decoration: none;
}

.omd-iconbtn:hover { background: var(--sang-100); border-color: var(--border-default); color: var(--text-strong); }

/* A field switched off by a rule elsewhere on the form: still there, still readable, and
   plainly not yours to type in. */
.omd-input:disabled {
	background: var(--sang-100);
	color: var(--text-faint);
	cursor: not-allowed;
}

/* Read-only, and it has to look it: a box that takes the caret and keeps its value is one
   somebody types into twice before believing it. Still readable, unlike disabled, because
   the value is the point of showing it. */
.omd-input[readonly] {
	background: var(--sang-100);
	cursor: default;
}
.omd-iconbtn--sm { width: var(--control-h-sm); height: var(--control-h-sm); }
.omd-iconbtn--lg { width: var(--control-h-lg); height: var(--control-h-lg); }

.omd-iconbtn--solid { background: var(--anar-600); color: var(--text-inverse); border-color: transparent; box-shadow: none; }
.omd-iconbtn--solid:hover { background: var(--anar-700); color: var(--text-inverse); border-color: transparent; }

.omd-iconbtn--dark { background: var(--shab-800); color: var(--text-inverse); border-color: transparent; box-shadow: none; }
.omd-iconbtn--dark:hover { background: var(--shab-900); color: var(--text-inverse); border-color: transparent; }

.omd-iconbtn--bare { background: transparent; color: var(--text-muted); border-color: transparent; box-shadow: none; }
.omd-iconbtn--bare:hover { background: var(--sang-200); color: var(--text-body); border-color: transparent; }

/* Same receding treatment as the buttons, and after the variants for the same cascade
   reason. The bare one keeps no fill, so an unavailable icon does not become the loudest
   thing in a toolbar. */
.omd-iconbtn:disabled {
	background: var(--surface-disabled);
	color: var(--text-disabled);
	border-color: var(--border-disabled);
	box-shadow: none;
	cursor: not-allowed;
}

.omd-iconbtn--bare:disabled { background: transparent; border-color: transparent; }

.omd-iconbtn__badge {
	position: absolute;
	top: -2px;
	inset-inline-start: -2px;
	min-width: 18px;
	height: 18px;
	padding: 0 5px;
	border-radius: var(--radius-pill);
	background: var(--anar-600);
	color: var(--text-inverse);
	font: var(--fw-bold) var(--fs-caps)/18px var(--font-core);
	text-align: center;
	box-shadow: 0 0 0 2px var(--sang-000);
}

.omd-field {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	min-width: 0;
}

.omd-field__label {
	font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core);
	color: var(--text-strong);
}

.omd-field__hint { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-field__error { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--state-danger); }

/* The star beside a label that has to be filled in. Hidden from a screen reader, which is
   told the same thing by the input's own required attribute. */
.omd-field__req { color: var(--state-danger); }

.omd-field__control {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	height: var(--control-h);
	padding: 0 var(--space-4);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	transition: var(--transition-control);
}

.omd-field__control:focus-within { border-color: var(--border-focus); box-shadow: var(--ring-focus); }
.omd-field__control--pill { border-radius: var(--radius-pill); padding-inline: var(--space-5); }
.omd-field__control--sm { height: var(--control-h-sm); }
.omd-field__control--invalid { border-color: var(--state-danger); }

.omd-field__control > input,
.omd-field__control > textarea {
	flex: 1;
	min-width: 0;
	border: 0;
	outline: none;
	background: transparent;
	font: var(--type-body);
	font-size: var(--fs-sm);
	color: var(--text-body);
}

.omd-field__control > .omd-icon { color: var(--text-faint); flex: none; }
.omd-field__suffix { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-faint); white-space: nowrap; }

.omd-select {
	position: relative;
	display: flex;
	align-items: center;
}

.omd-select > select {
	appearance: none;
	width: 100%;
	height: var(--control-h);
	padding: 0 var(--space-4);
	padding-inline-end: var(--space-9);
	font: var(--type-body);
	font-size: var(--fs-sm);
	color: var(--text-body);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-select > select:focus { outline: none; border-color: var(--border-focus); box-shadow: var(--ring-focus); }
.omd-select--sm > select { height: var(--control-h-sm); }

.omd-select__chevron {
	position: absolute;
	inset-inline-end: var(--space-4);
	pointer-events: none;
	color: var(--text-muted);
	display: flex;
}

.omd-choice {
	position: relative;
	display: inline-flex;
	align-items: flex-start;
	gap: var(--space-3);
	cursor: pointer;
}

/*
 * `.omd-radio` is the same control outside a `.omd-choice`. Some rows lay themselves out
 * (an address, a delivery mode) and need the input and its ring as their own children
 * rather than wrapped in one. It is the same component either way; a second radio drawn a
 * second way is how one screen ends up looking like a different site.
 *
 * Every wrapper that holds one of these is `position: relative`, and has to be. An
 * absolutely positioned box with no positioned ancestor is laid out against the page
 * itself, which also means no ancestor's `overflow` can clip it: inside a scroller like
 * the panel it lands wherever its row happens to be and stretches the document to reach
 * it, so a shell that is exactly one screen tall grows a second scrollbar.
 */
.omd-choice > input,
.omd-radio { position: absolute; opacity: 0; width: 0; height: 0; }

.omd-choice__box {
	width: 20px;
	height: 20px;
	flex: 0 0 auto;
	/* The radio's checked border is 2px where the resting one is 1px; without this the
	   box would grow by a pixel the moment it is selected. */
	display: grid;
	place-items: center;
	border-radius: var(--radius-check);
	border: 1px solid var(--border-default);
	background: var(--sang-000);
	color: var(--text-inverse);
	transition: var(--transition-control);
	margin-top: var(--nudge-optical);
}

/* Shared state first, then the two shapes override it. Order matters: the radio rules
   below have the same specificity as the generic checked rule, so they have to come
   after it to win. */
.omd-choice > input:checked + .omd-choice__box { background: var(--anar-600); border-color: var(--anar-600); }
.omd-choice > input:focus-visible + .omd-choice__box,
.omd-radio:focus-visible + .omd-choice__box { box-shadow: var(--ring-focus); }

/* Checkbox: the box fills and reveals its tick. */
.omd-choice__box > * { opacity: 0; }
.omd-choice > input:checked + .omd-choice__box > * { opacity: 1; }

/* Radio: a ring, not a filled tick. It keeps its card-white centre when selected and
   marks the choice with a brand dot, which is what the design system's Radio does. */
.omd-choice--radio .omd-choice__box,
.omd-radio + .omd-choice__box { border-radius: var(--radius-pill); position: relative; }

.omd-choice--radio .omd-choice__box > *,
.omd-radio + .omd-choice__box > * { display: none; }

.omd-choice--radio > input:checked + .omd-choice__box,
.omd-radio:checked + .omd-choice__box {
	background: var(--sang-000);
	border: 2px solid var(--anar-600);
}

/* Pinned to the centre rather than laid out, so nothing that ends up inside the box can
   shove the dot off it. */
.omd-choice--radio .omd-choice__box::after,
.omd-radio + .omd-choice__box::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 10px;
	height: 10px;
	border-radius: var(--radius-pill);
	background: var(--anar-600);
	opacity: 0;
	transition: opacity var(--dur-1) var(--ease-standard);
}

.omd-choice--radio > input:checked + .omd-choice__box::after,
.omd-radio:checked + .omd-choice__box::after { opacity: 1; }

/* Only drawn when the shop has more than one way to deliver, which it usually has not.
   The rate label carries its own price, so the row reads as one line either way. */
.omd-ship { position: relative; display: flex; align-items: center; gap: var(--space-2); cursor: pointer; }

/* Back on the design system's own values. These were darkened by hand while `--text-muted`
   was 3.87:1; the token now resolves to `--sang-650` and clears the bar on its own, so the
   override was a workaround for something fixed at the root. */
.omd-choice__label { font: var(--type-sm); color: var(--text-body); line-height: 1.7; }
.omd-choice__note { display: block; font-size: var(--fs-xs); color: var(--text-muted); }

/*
 * Unavailable, for both shapes.
 *
 * Last in the section on purpose: the radio rules above carry the same weight, so anything
 * meant to beat them has to come after them. The old rule faded the box alone with an
 * opacity, which left a full-contrast label sitting beside a ghost of a box and read as a
 * rendering fault rather than as a setting nobody may touch.
 *
 * A checked-but-disabled control keeps a pale brand hue rather than going the same grey as an
 * unchecked one. Whether the box is ticked is usually the only thing the reader wanted from
 * it, and losing that to say "you may not change this" answers a question nobody asked.
 */
.omd-choice:has(> input:disabled) { cursor: not-allowed; }

.omd-choice > input:disabled + .omd-choice__box,
.omd-radio:disabled + .omd-choice__box {
	background: var(--surface-disabled);
	border-color: var(--border-disabled);
}

.omd-choice > input:disabled:checked + .omd-choice__box {
	background: var(--fill-disabled-on);
	border-color: var(--fill-disabled-on);
	color: var(--mark-disabled-on);
}

/* The radio keeps its card-white centre when it is checked, disabled or not, because the ring
   and the dot are what say so. */
.omd-choice--radio > input:disabled:checked + .omd-choice__box,
.omd-radio:disabled:checked + .omd-choice__box {
	background: var(--sang-000);
	border: 2px solid var(--fill-disabled-on);
}

.omd-choice--radio > input:disabled:checked + .omd-choice__box::after,
.omd-radio:disabled:checked + .omd-choice__box::after { background: var(--mark-disabled-on); }

.omd-choice > input:disabled ~ .omd-choice__label,
.omd-choice > input:disabled ~ .omd-choice__label .omd-choice__note { color: var(--text-disabled); }

.omd-switch { position: relative; display: inline-flex; align-items: center; gap: var(--space-4); cursor: pointer; }
.omd-switch > input { position: absolute; opacity: 0; width: 0; height: 0; }

.omd-switch__track {
	width: 44px;
	height: 24px;
	flex: 0 0 auto;
	border-radius: var(--radius-pill);
	padding: 2px;
	background: var(--sang-400);
	display: flex;
	align-items: center;
	justify-content: flex-end;
	transition: background-color var(--dur-2) var(--ease-standard);
}

/* The same switch as a button or a link, for a toggle that acts on its own rather than
   waiting to be saved with a form around it. A link is for one that only changes what is
   being looked at, such as a filter, so it still works with the script blocked. Either
   carries its state in aria-checked, which is also what a screen reader reads, so there is
   one source of it rather than two. */
button.omd-switch { border: 0; background: none; padding: 0; font: inherit; }
a.omd-switch { text-decoration: none; color: inherit; }

.omd-switch > input:checked + .omd-switch__track,
.omd-switch[aria-checked="true"] .omd-switch__track { background: var(--anar-600); justify-content: flex-start; }

.omd-switch__label { font: var(--type-sm); color: var(--text-body); }

.omd-switch__thumb {
	width: 20px;
	height: 20px;
	border-radius: var(--radius-pill);
	background: var(--sang-000);
	box-shadow: var(--shadow-1);
	/* A grid so the busy spinner below sits in the middle of it without being laid out. */
	display: grid;
	place-items: center;
}

/*
 * Focus.
 *
 * The input is absolutely positioned at zero by zero, so the global :focus-visible outline
 * was drawing a box around nothing and this control had no visible focus at all. The ring is
 * relayed onto the track instead, which is the same trick .omd-choice and .omd-tick already
 * use for the same reason.
 *
 * The button form is focusable in its own right, so its outline is stood down in favour of
 * the same ring: one control should not announce focus two different ways depending on which
 * of its two shapes a screen happens to use.
 */
.omd-switch > input:focus-visible + .omd-switch__track,
button.omd-switch:focus-visible .omd-switch__track,
a.omd-switch:focus-visible .omd-switch__track { box-shadow: var(--ring-focus); }

button.omd-switch:focus-visible,
a.omd-switch:focus-visible { outline: none; }

/* Unavailable. The track still says which way it is set, in a pale brand hue rather than the
   same grey as an off one, because which way it is set is usually the only thing being read
   from it. */
.omd-switch:has(> input:disabled), button.omd-switch:disabled { cursor: not-allowed; }

.omd-switch > input:disabled + .omd-switch__track,
button.omd-switch:disabled .omd-switch__track { background: var(--surface-disabled); }

.omd-switch > input:disabled:checked + .omd-switch__track,
button.omd-switch:disabled[aria-checked="true"] .omd-switch__track { background: var(--fill-disabled-on); }

.omd-switch > input:disabled + .omd-switch__track .omd-switch__thumb,
button.omd-switch:disabled .omd-switch__thumb { background: var(--knob-disabled); box-shadow: none; }

.omd-switch > input:disabled ~ .omd-switch__label,
button.omd-switch:disabled .omd-switch__label { color: var(--text-disabled); }

/*
 * Waiting on the network, for a switch that saves itself.
 *
 * Same bargain as the button: whoever sets aria-busy must disable the control too, because
 * that is what stops a second write, and these rules only say so. They outrank the disabled
 * rules above, so the track keeps its real colour while it waits — a switch that greys out
 * mid-flight reads as having failed rather than as still going.
 */
.omd-switch[aria-busy="true"] { cursor: progress; }

.omd-switch[aria-busy="true"] > input:disabled + .omd-switch__track,
button.omd-switch[aria-busy="true"]:disabled .omd-switch__track { background: var(--sang-400); }

.omd-switch[aria-busy="true"] > input:disabled:checked + .omd-switch__track,
button.omd-switch[aria-busy="true"]:disabled[aria-checked="true"] .omd-switch__track { background: var(--anar-600); }

.omd-switch[aria-busy="true"] > input:disabled + .omd-switch__track .omd-switch__thumb,
button.omd-switch[aria-busy="true"]:disabled .omd-switch__thumb { background: var(--sang-000); box-shadow: var(--shadow-1); }

.omd-switch[aria-busy="true"] .omd-switch__thumb::after {
	content: "";
	width: 12px;
	height: 12px;
	border-radius: var(--radius-pill);
	border: 1.5px solid var(--sang-500);
	border-top-color: transparent;
	animation: omd-spin 0.7s linear infinite;
}

.omd-switch[aria-busy="true"] > input:checked + .omd-switch__track .omd-switch__thumb::after,
button.omd-switch[aria-busy="true"][aria-checked="true"] .omd-switch__thumb::after {
	border-color: var(--anar-600);
	border-top-color: transparent;
}

.omd-switch[aria-busy="true"] .omd-switch__label { color: var(--text-muted); }

/* -------------------------------------------------------------- 3. Surfaces */

/*
 * No clip on the plain card. Its content sits inside a padding, so nothing of its own ever
 * reaches a rounded corner, and a clip there is only felt by the things meant to escape:
 * a calendar hanging below the field that opened it, cut off at the card's edge.
 *
 * The flush variant is the one that needs it, since that is where a table runs to the edge
 * and would otherwise square off the corners it is sitting in.
 */
.omd-card {
	background: var(--surface-card);
	color: var(--text-body);
	border-radius: var(--radius-card);
	padding: var(--space-6);
	box-shadow: var(--shadow-2);
	transition: box-shadow var(--dur-2) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
}

.omd-card--flush { padding: 0; overflow: hidden; }
.omd-card--tight { padding: var(--space-4); }
.omd-card--loose { padding: var(--space-8); }
.omd-card--sunken { background: var(--surface-sunken); box-shadow: none; }
.omd-card--inverse { background: var(--surface-inverse); color: var(--text-inverse); }
.omd-card--overflow { overflow: visible; }

a.omd-card:hover, .omd-card--interactive:hover { box-shadow: var(--shadow-3); transform: translateY(-2px); }

.omd-card__head {
	display: flex;
	align-items: baseline;
	gap: var(--space-4);
	margin-bottom: var(--space-5);
}

.omd-card--flush > .omd-card__head { padding: var(--space-6) var(--space-6) 0; margin-bottom: var(--space-4); }
.omd-card__head h2, .omd-card__head h3 { font: var(--type-h3); color: inherit; }

/* A switch that governs the card it sits in goes to the far end of the head, and centred
   on itself rather than on the baseline the heading and its meta share. */
.omd-card__switch { margin-inline-start: auto; align-self: center; }
.omd-card__meta { font-size: var(--fs-xs); color: var(--text-muted); }

/* A card head aligns on the baseline, which is right for a heading beside a line of text
   and wrong for a badge: the pill has its own padding over a line-height of 1, so its
   baseline sits well below its middle and it hangs low against the heading. Centred
   instead, and laid out as flex so the span's own text strut cannot push it off either.
   Scoped to heads that actually hold one, so every plain-text meta stays where it was. */
.omd-card__head > .omd-card__meta:has(.omd-badge) {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	align-self: center;
}

.omd-card__action { margin-inline-start: auto; }

.omd-badge {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--fw-medium) var(--fs-caps)/1 var(--font-core);
	padding: var(--space-2) var(--space-4);
	border-radius: var(--radius-pill);
	white-space: nowrap;
	background: var(--sang-200);
	color: var(--sang-700);
}

.omd-badge--success { background: var(--state-success-soft); color: var(--sabz-700); }
.omd-badge--warning { background: var(--state-warning-soft); color: var(--zard-700); }
.omd-badge--danger  { background: var(--state-danger-soft); color: var(--anar-800); }
.omd-badge--brand   { background: var(--anar-100); color: var(--anar-800); }
.omd-badge--info    { background: var(--state-info-soft); color: var(--text-strong); }

.omd-tag {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--fw-regular) var(--fs-sm)/1 var(--font-core);
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-pill);
	cursor: pointer;
	background: var(--sang-000);
	color: var(--text-body);
	border: 1px solid var(--border-hairline);
	transition: var(--transition-control);
	white-space: nowrap;
	text-decoration: none;
}

/* A selected chip keeps its own hover. The plain one tints the text anar and outranks
   .omd-tag--selected, which would leave dark red on the near-black selected ground. */
.omd-tag:hover:not(.omd-tag--selected):not([aria-pressed="true"]) {
	color: var(--anar-700);
	border-color: var(--anar-300);
}

.omd-tag[aria-pressed="true"], .omd-tag--selected {
	background: var(--shab-800);
	color: var(--text-inverse);
	border-color: var(--shab-800);
}

/* The colour has to be restated: most chips are links, and the global a:hover outranks
   .omd-tag--selected, which is what put dark red text on the near-black selected chip. */
.omd-tag[aria-pressed="true"]:hover,
.omd-tag--selected:hover {
	background: var(--shab-900);
	border-color: var(--shab-900);
	color: var(--text-inverse);
}

.omd-tag--static { cursor: default; }

.omd-notice {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	background: var(--state-info-soft);
	color: var(--text-strong);
	border-radius: var(--radius-control);
	padding: var(--space-4) var(--space-5);
	font: var(--type-sm);
	line-height: 1.8;
}

.omd-notice--success { background: var(--state-success-soft); color: var(--sabz-700); }
.omd-notice--warning { background: var(--state-warning-soft); color: var(--zard-700); }
.omd-notice--danger  { background: var(--state-danger-soft); color: var(--anar-800); }
.omd-notice > .omd-icon { margin-top: var(--nudge-optical); flex: none; }
.omd-notice__body { flex: 1; min-width: 0; }
.omd-notice__title { font-weight: var(--fw-semibold); }
.omd-notice__action { flex: 0 0 auto; }

.omd-toast {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3);
	background: var(--shab-800);
	color: var(--text-inverse);
	border-radius: var(--radius-popover);
	padding: var(--space-4) var(--space-5);
	box-shadow: var(--shadow-4);
	font: var(--type-sm);
}

.omd-toast__icon { color: var(--on-dark-success); }
.omd-toast--info .omd-toast__icon { color: var(--on-dark-muted); }
.omd-toast--danger .omd-toast__icon { color: var(--on-dark-danger); }

.omd-toast-host {
	position: fixed;
	inset-inline: 0;
	bottom: var(--space-7);
	z-index: var(--z-toast);
	display: flex;
	justify-content: center;
	pointer-events: none;
	padding-inline: var(--space-5);
}

.omd-toast-host > * { pointer-events: auto; animation: omd-rise var(--dur-3) var(--ease-standard) both; }

.omd-dialog {
	border: 0;
	padding: 0;
	background: transparent;
	max-width: 100%;
	max-height: 100%;
}

.omd-dialog::backdrop { background: var(--scrim); }

.omd-dialog__panel {
	width: min(520px, calc(100vw - var(--space-8)));
	max-height: 88vh;
	overflow: auto;
	background: var(--surface-card);
	color: var(--text-body);
	border-radius: var(--radius-dialog);
	box-shadow: var(--shadow-4);
	padding: var(--space-7);
}

.omd-dialog__head {
	display: flex;
	align-items: flex-start;
	gap: var(--space-4);
	margin-bottom: var(--space-5);
}

.omd-dialog__head h2 { font: var(--type-h3); }
.omd-dialog__description { margin: var(--space-2) 0 0; font: var(--type-sm); color: var(--text-muted); line-height: 1.8; }
.omd-dialog__foot { display: flex; gap: var(--space-3); margin-top: var(--space-7); flex-wrap: wrap; }

/* Takes the slack so the close control sits at the far end of the heading row. */
.omd-dialog__close { margin-inline-start: auto; flex: none; }
.omd-dialog__body { font: var(--type-body); color: var(--text-body); margin: 0; text-wrap: pretty; }

/* A dialog that holds a form rather than a question: the picker, then what is worth saying
   about the file, then the answer. */
.omd-dialog__panel > form { display: flex; flex-direction: column; gap: var(--space-5); margin-top: var(--space-6); }

/* An offer sitting beside the thing it helps with: a blank file to fill in, next to the
   box it is about to be dropped into. */
.omd-dialog__aside {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	padding: var(--space-4);
	border-radius: var(--radius-control);
	background: var(--surface-page);
	font: var(--type-sm);
	color: var(--text-body);
}

.omd-dialog__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; text-wrap: pretty; }
.omd-dialog__pair { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* What a chosen file turned out to hold. Its own scroll, because a file of two hundred
   refusals must not push the button that answers it off the screen. */
.omd-dialog__report {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	max-height: 40vh;
	overflow-y: auto;
}

.omd-dialog__report .omd-eyebrow { margin: 0; }
.omd-dialog__report .omd-table { min-width: 0; }
.omd-dialog__panel > form > .omd-dialog__foot { margin-top: var(--space-2); }
.omd-dialog__panel--wide { width: min(680px, calc(100vw - var(--space-8))); }

/* -------------------------------------------------------------- 4. Commerce */

.omd-price {
	display: inline-flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-1);
}

/* Discount meta sits on its own line above the price. Inline, a discounted price is
   about twice the intrinsic width of an undiscounted one, which destabilises the grids
   the component sits in. */
.omd-price--inline { flex-direction: row; align-items: baseline; gap: var(--space-2); }

.omd-price__meta {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	white-space: nowrap;
}

.omd-price__off {
	font: var(--fw-semibold) var(--fs-xs)/1.6 var(--font-core);
	color: var(--anar-700);
	background: var(--anar-100);
	border-radius: var(--radius-pill);
	padding: var(--space-1) var(--space-3);
}

.omd-price__was { font: var(--fw-regular) var(--fs-xs)/1.4 var(--font-core); color: var(--price-strike); }
.omd-price__main { display: inline-flex; align-items: baseline; gap: var(--space-2); white-space: nowrap; }

.omd-price__value {
	font-family: var(--font-numeric);
	font-weight: var(--fw-bold);
	font-size: var(--fs-title);
	line-height: 1.3;
	color: var(--price-retail);
}

/* «رایگان» stands where a figure would: a word, so it takes the reading face rather than
   the numeric one, and the shop's own colour rather than a price's. */
.omd-price__value--free { font-family: var(--font-core); color: var(--price-wholesale); }

.omd-price--sm .omd-price__value { font-size: var(--fs-sm); }
.omd-price--lg .omd-price__value { font-size: var(--fs-h3); }
.omd-price--wholesale .omd-price__value { color: var(--price-wholesale); }
.omd-price--inverse .omd-price__value { color: var(--text-inverse); }

.omd-price__currency { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-faint); }
.omd-price__per { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-muted); }
.omd-price--inverse .omd-price__currency { color: var(--shab-200); }

.omd-tiers { display: flex; flex-direction: column; }

.omd-tiers__row {
	display: grid;
	grid-template-columns: 1fr auto auto;
	gap: var(--space-4);
	align-items: baseline;
	padding: var(--space-3);
	border-top: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-tiers__row--active { background: var(--anar-100); }
.omd-tiers__band { font: var(--type-sm); color: var(--text-body); }
.omd-tiers__price { font: var(--fw-semibold) var(--fs-sm)/1.5 var(--font-numeric); color: var(--text-strong); white-space: nowrap; }
.omd-tiers__extra { font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-core); color: var(--text-muted); white-space: nowrap; }
.omd-tiers__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); margin-top: var(--space-3); }

/* Sized by its contents wherever it is dropped. The buy box and the cart line are both
   stretching flex columns, and a stretched stepper spreads the two buttons to opposite
   ends of the card with the field pooling the slack between them. */
.omd-qty {
	display: inline-flex;
	flex-direction: column;
	align-items: flex-start;
	align-self: start;
	gap: var(--space-2);
}

.omd-qty__box {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-pill);
	padding: var(--space-1);
	background: var(--sang-000);
}

.omd-qty__step {
	width: 34px;
	height: 34px;
	display: grid;
	place-items: center;
	border: 0;
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-strong);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-qty__step:hover:not(:disabled) { background: var(--sang-200); }
.omd-qty__step:disabled { color: var(--sang-400); cursor: not-allowed; }

/* The bin the minus becomes at the floor, in the same colour a catalogue row's is. */
.omd-qty__step--out { color: var(--state-danger); }

/* While the change is in flight. The control keeps its size and stops taking presses, so
   a second tap on «+» cannot race the first one's answer back. */
.omd-qty[aria-busy="true"] { opacity: 0.6; }

/* The count and its unit read as one centred figure, and reserve the design's width
   between the two buttons so short units do not pull them together. */
.omd-qty__amount {
	display: inline-flex;
	align-items: baseline;
	justify-content: center;
	gap: var(--space-2);
	min-width: 62px;
}

.omd-qty__value {
	width: auto;
	min-width: 0;
	padding: 0;
	text-align: center;
	border: 0;
	background: transparent;
	font: var(--fw-semibold) var(--fs-sm)/1 var(--font-core);
	color: var(--text-strong);
}

.omd-qty__value:focus { outline: none; }
.omd-qty__unit { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-muted); }
.omd-qty__note {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-3);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}

/* Divides the two bounds without another word between them. */
.omd-qty__sep {
	width: 1px;
	align-self: stretch;
	margin-block: 2px;
	background: var(--border-hairline);
}
.omd-qty__note--warn { color: var(--zard-700); }

.omd-lead {
	display: inline-flex;
	align-items: flex-start;
	gap: var(--space-2);
	font: var(--fw-regular) var(--fs-xs)/1.8 var(--font-core);
	color: var(--text-muted);
}

.omd-lead--bumped { color: var(--zard-700); }
.omd-lead > .omd-icon { margin-top: var(--nudge-optical); flex: none; }

.omd-minorder { display: flex; flex-direction: column; gap: var(--space-3); }

.omd-minorder__head {
	display: flex;
	align-items: baseline;
	gap: var(--space-3);
	font: var(--type-sm);
}

.omd-minorder__state { color: var(--zard-700); font-weight: var(--fw-medium); }
.omd-minorder--met .omd-minorder__state { color: var(--sabz-700); }
.omd-minorder__amounts { margin-inline-start: auto; font-size: var(--fs-xs); color: var(--text-muted); }

.omd-minorder__track {
	height: 6px;
	border-radius: var(--radius-pill);
	background: var(--sang-200);
	overflow: hidden;
}

.omd-minorder__fill {
	height: 100%;
	border-radius: var(--radius-pill);
	background: var(--zard-500);
	transition: width var(--dur-3) var(--ease-standard);
}

.omd-minorder--met .omd-minorder__fill { background: var(--sabz-500); }
.omd-minorder__gap { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }

/*
 * The bookable window can cross a month boundary, so the picker shows one month and
 * offers a chip per month it reaches.
 *
 * The switch is a radio group, not a script. Each pane holds its month's radio, chip and
 * grid as siblings, so `:checked ~` pairs them without the stylesheet needing to know how
 * many months there are; `display: contents` then lifts the three into this flex row so
 * `order` can gather every chip above the one visible grid.
 */
.omd-datepicker { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-4) var(--space-2); }
.omd-datepicker__pane { display: contents; }
.omd-datepicker__tab { order: 0; }

.omd-datepicker__head { order: 1; flex: 0 0 100%; display: flex; align-items: baseline; gap: var(--space-3); }
.omd-datepicker__month { font: var(--fw-semibold) var(--fs-body)/1.4 var(--font-core); color: var(--text-strong); }
.omd-datepicker__note { font-size: var(--fs-xs); color: var(--zard-700); }

.omd-datepicker__grid {
	order: 2;
	flex: 0 0 100%;
	display: grid;
	grid-template-columns: repeat(7, 1fr);
	gap: var(--space-2);
}

.omd-datepicker__pick:not(:checked) ~ .omd-datepicker__head,
.omd-datepicker__pick:not(:checked) ~ .omd-datepicker__grid { display: none; }
.omd-datepicker__pick:checked ~ .omd-datepicker__tab {
	background: var(--shab-800);
	color: var(--text-inverse);
	border-color: var(--shab-800);
}

/* The chip is a label for a visually hidden radio, so the ring has to be relayed to it. */
.omd-datepicker__pick:focus-visible ~ .omd-datepicker__tab,
.omd-slot > input:focus-visible + .omd-tag { box-shadow: var(--ring-focus); }

.omd-datepicker__weekday {
	text-align: center;
	font: var(--type-caps);
	color: var(--text-faint);
	padding-bottom: var(--space-2);
}

.omd-datepicker__day {
	height: 38px;
	border-radius: var(--radius-control);
	cursor: pointer;
	border: 1px solid transparent;
	background: var(--sang-100);
	color: var(--text-body);
	font: var(--fw-medium) var(--fs-sm)/1 var(--font-core);
	transition: var(--transition-control);
}

/* The chosen day keeps its own hover: the plain one is a pale grey, and it outranks
   the selected background while leaving the inverse text behind, which turns the date
   the customer picked into white on near-white. */
.omd-datepicker__day:hover:not(:disabled):not([aria-pressed="true"]) { background: var(--sang-200); }
.omd-datepicker__day:disabled { background: transparent; color: var(--sang-400); cursor: not-allowed; }
.omd-datepicker__day[aria-pressed="true"],
.omd-datepicker__day[aria-current="date"] { background: var(--anar-600); border-color: var(--anar-600); color: var(--text-inverse); }
.omd-datepicker__day[aria-pressed="true"]:hover,
.omd-datepicker__day[aria-current="date"]:hover { background: var(--anar-700); border-color: var(--anar-700); }

/* As a link, for a picker that filters rather than posts. Same day, same box. */
a.omd-datepicker__day { display: grid; place-items: center; text-decoration: none; }
a.omd-datepicker__day:hover:not([aria-current="date"]) { background: var(--sang-200); color: var(--text-body); }

/*
 * The panel's day filter: one control in a bar, and a month when it is open.
 *
 * <details> is the browser's own disclosure, so open, close, escape and the keyboard order
 * come with it and nothing here needs a script. The month floats over the page rather than
 * pushing the list below it down by three hundred pixels every time it is opened.
 */
/* A flex box rather than a block: the summary is inline-flex, and in a block it would sit
   on a text baseline with three pixels of descender under it, which is enough to knock the
   control out of line with the fields beside it. */
.omd-daypicker { position: relative; display: inline-flex; }

/* As a field rather than a filter: it fills its column the way an input does, instead of
   shrinking to the width of the date that happens to be in it. */
.omd-field > .omd-daypicker { display: flex; }

.omd-field > .omd-daypicker > .omd-daypicker__now {
	flex: 1;
	justify-content: flex-start;
	/* The height of a field. The small one is for a filter bar, where it stands beside a
	   search pill rather than beside an input. */
	height: var(--control-h);
}

.omd-daypicker__now {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	height: var(--control-h-sm);
	padding: 0 var(--space-4);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-pill);
	font: var(--fw-regular) var(--fs-sm)/1 var(--font-core);
	color: var(--text-body);
	cursor: pointer;
	white-space: nowrap;
	list-style: none;
	transition: var(--transition-control);
}

.omd-daypicker__now::-webkit-details-marker { display: none; }
.omd-daypicker__now:hover { border-color: var(--border-strong); }
.omd-daypicker__now:focus-visible { border-color: var(--border-focus); box-shadow: var(--ring-focus); }
.omd-daypicker__now .omd-icon { color: var(--text-faint); }
.omd-daypicker[open] .omd-daypicker__now { border-color: var(--border-focus); }

.omd-daypicker__pop {
	position: absolute;
	z-index: var(--z-dropdown);
	inset-inline-start: 0;
	margin-top: var(--space-2);
	width: 320px;
	max-width: calc(100vw - 32px);
	padding: var(--space-5);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-3);
}

/* The month, with a way either side of it. Named buttons rather than arrows, so the row
   needs the name in the middle and the two steps pushed to the ends. */
.omd-daypicker .omd-datepicker__head { align-items: center; justify-content: space-between; }

/* Today, and the way out, on the row the calendar already ends with. */
.omd-daypicker__foot { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-4); }
.omd-daypicker__today { flex: 1; justify-content: center; }

.omd-slots { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* A time this cart cannot have. Struck through and dashed rather than removed, because a
   customer who cannot see the option cannot work out what to change to get it back; the
   label carries which product and why. */
.omd-slot--blocked .omd-tag {
	border-style: dashed;
	color: var(--text-faint);
	background: var(--surface-page);
	text-decoration: line-through;
	cursor: not-allowed;
}
.omd-slot--blocked .omd-tag:hover { color: var(--text-faint); border-color: var(--border-hairline); }


.omd-slot[hidden] { display: none; }


/* --------------------------------------------------------------- 5. Product */

/* The tile fills whatever box sizes it. Without this the empty state, which has no
   image to give it height, collapses to the height of its own caption. */
.omd-thumb {
	width: 100%;
	height: 100%;
	border-radius: var(--radius-thumb);
	overflow: hidden;
	display: grid;
	/* A definite track, so the image's height: 100% has something to resolve against. An
	   implicit row is sized by its content, which sends a square image back to its own
	   ratio and out through the bottom of the frame. */
	grid-template: minmax(0, 1fr) / minmax(0, 1fr);
	place-items: center;
	text-align: center;
	font: var(--fw-regular) var(--fs-xs)/1.35 var(--font-core);
	color: var(--text-faint);
}

/* The tinted plate belongs to the empty state, not to the frame: a packshot carries its
   own near-white ground, so a square of colour around it reads as a grey box the product
   sits in rather than as the product. Where :has is unsupported the empty tile loses its
   plate; the label still names it. */
.omd-thumb:not(:has(img)) {
	background: var(--sang-200);
	/* Keeps the label off the edges, and takes the usable width under it so it breaks
	   onto two centred lines in a thumbnail. A large tile still has room for one line. */
	padding: var(--space-2);
}

/*
 * Contained, not cropped. These are packshots: the label, the weight and the brand are
 * printed on the front, and filling the box cuts whichever edge does not fit.
 *
 * Bounded rather than sized. The thumb centres its child, so a percentage height has no
 * definite box to resolve against and falls back to the image's own ratio: width:100%
 * plus height:100% made a square image square again, overflowing the frame it was meant
 * to fit inside and getting clipped by it.
 */
.omd-thumb > img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* WooCommerce wraps the loop in ul.products, which the theme uses as a plain container.
   The float and percentage width are stated rather than assumed: WooCommerce's own
   layout sheet grids `ul.products li.product`, and so do plenty of extensions. */
.products {
	list-style: none;
	margin: 0;
	padding: 0;
	display: block;

	/*
	 * The rows arrange themselves by the width of the list they are in, not the width of
	 * the window. The same list is the whole page on the shop and a four-hundred-pixel
	 * column in an aside, and a row that only knew about the window would lay itself out
	 * for a desktop inside a column it does not fit.
	 */
	container-type: inline-size;
	container-name: rows;
}

.products > .omd-prow {
	float: none;
	width: auto;
	margin: 0;
	clear: none;
}

/* Four direct children on one line, restacked by area on a narrow screen. Named areas
   rather than a wrapper element: a wrapper would have to be display:contents to keep the
   grid flat, and that promotes its children into the wrong box. */
.omd-prow {
	display: grid;
	grid-template-columns: 64px minmax(0, 1.5fr) minmax(220px, 1fr) auto;
	grid-template-areas: "thumb body price action";
	gap: var(--space-5);
	align-items: center;
	padding: var(--space-5) var(--space-6);
	border-top: 1px solid var(--border-hairline);
	transition: background-color var(--dur-2) var(--ease-standard);
	/* Anchor for the stretched title link below. */
	position: relative;
}

.omd-prow:hover { background: var(--sang-100); cursor: pointer; }

/* Rows are separated by a rule, not fenced by one. The first row has nothing above it
   to be separated from, and the last has no rule below it either. */
.omd-prow:first-child { border-top: 0; }
.omd-prow__thumb { grid-area: thumb; display: block; width: 64px; height: 64px; }
.omd-prow__body { grid-area: body; min-width: 0; }
.omd-prow__price { grid-area: price; min-width: 0; }

/*
 * The wide row, which is what a row is until its list says otherwise. Both of these are
 * turned back over by the narrow arrangement further down, keyed to the width of the
 * list rather than of the window: this used to be a window-width media query, which in a
 * four-hundred-pixel column was answering about the wrong box.
 *
 * The wrapper steps aside so the grid still receives four items; its children carry their
 * own grid areas.
 */
.omd-prow__foot { display: contents; }

/*
 * One column width for every row. Each row is its own grid, so an auto-sized action
 * column takes the width of whatever button that row happens to carry: a row offering the
 * cart is wider than a row offering an add, and the prices beside them step in and out
 * down the list. The floor is the design's, and clears the longest label a row can show;
 * anything longer still widens its own row rather than being cut off.
 */
.omd-prow__action { min-width: 148px; }
/* Flush with the end of the row. The action column is the last thing in the row and the
   card's edge runs down beside it, so aligning there gives the column a straight outer
   edge; against the price it would only be ragged against a number that is itself set
   away from the track edge. */
.omd-prow__action { grid-area: action; display: flex; justify-content: flex-end; }
.omd-prow__price { position: relative; z-index: 1; pointer-events: none; }
.omd-prow__name { font: var(--type-body-strong); color: var(--text-strong); text-decoration: none; }

/* The title link covers the row, so clicking anywhere opens the product while the link
   itself remains the only focusable, labelled target. */
.omd-prow__name::after {
	content: "";
	position: absolute;
	inset: 0;
}

.omd-prow:focus-within { background: var(--sang-100); }

/* The action sits above the stretched link so it stays separately clickable. */
.omd-prow__action { position: relative; z-index: 1; }
.omd-prow__unit { font-size: var(--fs-xs); color: var(--text-muted); margin-top: var(--space-1); }

/*
 * A row that is already in the cart stops offering a button and shows the line instead:
 * how many, and the two ways to change it. One pill, so it reads as one control.
 */
/*
 * Both of a row's controls stand exactly --control-h tall, and the row reserves that much
 * whichever one it is showing. Otherwise a product going into the cart swapped a 32px
 * button for a 40px stepper and the row grew by eight pixels; on a phone the rows are
 * stacked, so every row below it moved under the finger that had just tapped.
 */
.omd-prow__action {
	/* Named here rather than inside the stepper, because the column reserves its width
	   from them and a child's custom property cannot be read by its parent. */
	--prow-step-btn: 34px;
	--prow-step-count: 40px;
	min-height: var(--control-h);
	align-items: center;
}

.omd-prow__action .omd-btn { height: var(--control-h); }

.omd-prow__step {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	margin: 0;
	min-height: var(--control-h);
	box-sizing: border-box;
	padding: var(--space-1);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-pill);
	background: var(--sang-000);
	transition: var(--transition-control);
}

/* Dimmed while the cart is being written, so the whole control reads as mid-change and
   not just the number in the middle of it. */
.omd-prow__step[aria-busy="true"] { opacity: 0.72; }

.omd-prow__step-btn {
	width: var(--prow-step-btn, 34px);
	height: var(--prow-step-btn, 34px);
	display: grid;
	place-items: center;
	border: 0;
	border-radius: var(--radius-pill);
	background: transparent;
	color: var(--text-strong);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-prow__step-btn:hover:not(:disabled) { background: var(--sang-200); }
.omd-prow__step-btn:active:not(:disabled) { background: var(--sang-300); }
.omd-prow__step-btn:disabled { color: var(--sang-400); cursor: not-allowed; }

/* At the minimum there is nothing to take away but the line itself, so the minus becomes
   a bin and says so in the colour of a thing that removes. */
.omd-prow__step-btn--out { color: var(--state-danger); }

.omd-prow__step-count {
	position: relative;
	min-width: var(--prow-step-count, 40px);
	display: grid;
	place-items: center;
	font: var(--fw-semibold) var(--fs-sm)/1 var(--font-core);
	color: var(--text-strong);
}

/* The same ring the buttons wear, in the place the number was: the count is what the
   request is about, so it is the count that spins. */
.omd-prow__step-count[aria-busy="true"] { color: transparent; }

.omd-prow__step-count[aria-busy="true"]::after {
	content: "";
	/* Laid over the number rather than replacing it, so the control keeps its width and
	   the row does not twitch every time the count changes. */
	position: absolute;
	inset: 0;
	margin: auto;
	width: 14px;
	height: 14px;
	border: 2px solid var(--text-muted);
	border-top-color: transparent;
	border-radius: var(--radius-pill);
	animation: omd-spin 0.7s linear infinite;
}

/* The add is a form of its own, so it needs no margin of its own inside the row. */
.omd-prow__add { margin: 0; }

/* Under the unit line, where the design put them. No z-index of their own: the title link
   is stretched over the row and these are not clickable, so a click on one should open the
   product like a click anywhere else in the row does. */
.omd-prow__badges { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.omd-prow__sold-out { font: var(--fw-medium) var(--fs-sm)/1.4 var(--font-core); color: var(--text-faint); }

/*
 * WooCommerce's own ajax add left the button where it was and appended a «view cart»
 * link beside it, and the rules that tamed both used to live here. The row no longer
 * uses that path: it posts to the theme's own handler and gets the stepper back, so
 * there is nothing left for those rules to match.
 */

/*
 * 677px, which is what this list measures when the window is 720: the width the rows used
 * to change shape at, back when they measured the window. Named in the list's own terms
 * so the shop stacks exactly where it always did, and a list in a column stacks because
 * it is narrow rather than because the window is.
 */
@container rows (max-width: 677px) {
	.omd-prow {
		grid-template-columns: 60px minmax(0, 1fr);
		grid-template-areas:
			"thumb body"
			".     foot";
		row-gap: var(--space-4);
		align-items: end;
		padding: var(--space-5);
	}

	.omd-prow__thumb { width: 60px; height: 60px; align-self: start; }

	/* The name starts level with the top of the picture. Left to the row's own end
	   alignment it sat on the picture's baseline instead, which only shows on a product
	   with neither a unit line nor a badge: a name hanging at the bottom of an empty
	   sixty pixels. */
	.omd-prow__body { align-self: start; }

	/* Price and action share one line that is allowed to wrap. A long price used to run
	   under the button; now the button drops beneath it and stays at the end of the row. */
	.omd-prow__foot {
		grid-area: foot;
		display: flex;
		flex-wrap: wrap;
		align-items: flex-end;
		justify-content: space-between;
		column-gap: var(--space-4);
		row-gap: var(--space-3);
		min-width: 0;
	}

	/*
	 * One width whichever control the row is showing, the same idea as the wide layout's
	 * action column above. The price and the control share a line that is allowed to
	 * wrap, and the stepper is wider than the add button: without this, a product going
	 * into the cart pushed the control onto a second line and every row below it moved
	 * under the finger that had just tapped. Sized from the stepper, which is the wider.
	 */
	.omd-prow__action {
		margin-inline-start: auto;
		min-width: calc(var(--prow-step-btn) * 2 + var(--prow-step-count) + var(--space-1) * 4 + 2px);
	}
}

.omd-pcard {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	background: var(--surface-card);
	border-radius: var(--radius-card);
	padding: var(--space-4);
	box-shadow: var(--shadow-2);
	transition: box-shadow var(--dur-2) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
	text-decoration: none;
}

.omd-pcard:hover { box-shadow: var(--shadow-3); transform: translateY(-2px); }

.omd-pcard__name {
	font: var(--type-body-strong);
	color: var(--text-strong);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	min-height: 2.9em;
	line-height: 1.45;
}

.omd-pcard__unit { font-size: var(--fs-xs); color: var(--text-muted); }
.omd-pcard__action { margin-top: var(--space-2); }

.omd-grid {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	list-style: none;
	margin: 0;
	padding: 0;
}

.omd-deal {
	background: var(--surface-deal);
	border-radius: var(--radius-card);
	padding: var(--space-7);
}

/*
 * The picture is the only part of a card that grows with the column. Name, unit, badges,
 * price and control cost the same however wide the card is, so air between them that a
 * laptop can spend is air a phone cannot. None of those parts goes, because each is
 * something a wholesale buyer decides on; the space between them does, and the picture
 * takes back what the padding gives up.
 */
.omd-tile {
	position: relative;
	background: var(--surface-card);
	border-radius: var(--radius-card);
	padding: var(--space-3);
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

.omd-tile__name {
	font: var(--type-body-strong);
	line-height: 1.35;
	color: var(--text-strong);
	text-decoration: none;
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
	/* A product name can be one unbroken word longer than the card. Breaking it is ugly;
	   letting it push the rail's column wider is worse, because every column follows. */
	overflow-wrap: anywhere;
}

/* Tight, not the page's reading line-height: that one is meant for paragraphs and this is
   one line that never becomes two. */
.omd-tile__unit { font-size: var(--fs-xs); line-height: var(--lh-tight); color: var(--text-muted); }
.omd-tile__badges { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.omd-tile__sold-out { font: var(--fw-medium) var(--fs-body)/1.4 var(--font-core); color: var(--text-faint); }

/* The price and its ladder are one block, pushed to the foot of the card so the prices
   line up across a rail whatever the names above them did. */
.omd-tile__money {
	margin-top: auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

/* The name link covers the card, so clicking anywhere but the control opens the product
   while the link itself remains the only focusable, labelled target. Same arrangement as
   a catalogue row, for the same reason: the control below is a form, and a form cannot
   live inside a link. */
.omd-tile__name::after {
	content: "";
	position: absolute;
	inset: 0;
}

.omd-tile__action {
	position: relative;
	z-index: 1;
	display: flex;
}

.omd-tile__action > * { flex: 1; }

/*
 * Both of a card's controls stand exactly --control-h tall, the way a row's do. Otherwise
 * a product going into the cart swaps a 32px button for a 40px stepper and every card in
 * the rail grows by eight pixels under the finger that pressed it.
 */
.omd-tile__action {
	--prow-step-btn: 34px;
	--prow-step-count: 40px;
	min-height: var(--control-h);
	align-items: center;
}

.omd-tile__action .omd-btn { width: 100%; justify-content: center; height: var(--control-h); }

/* Stretched across a card, the stepper puts a button at each end and gives the count the
   room between them. Left packed together the three of them sat in a corner of a pill
   that ran the width of the card, which reads as a control that failed to lay itself
   out rather than one filling the space on purpose. */
.omd-tile__action .omd-prow__step { justify-content: space-between; }
.omd-tile__action .omd-prow__step-count { flex: 1; }

/*
 * The count once a script has made it typeable. It is the pill's own number, so it keeps
 * the pill's type and colour and brings no box of its own; the ring belongs to the pill,
 * which is the control a person sees.
 */
.omd-prow__step-value {
	width: 100%;
	min-width: 0;
	border: 0;
	padding: 0;
	background: transparent;
	text-align: center;
	font: inherit;
	color: inherit;
	appearance: textfield;
}

.omd-prow__step-value:focus { outline: none; }
.omd-prow__step:focus-within { border-color: var(--border-focus); box-shadow: var(--ring-focus); }

/* The cards in a row are already the same height. The slack has to go somewhere, and
   left to itself it went into the picture: a tile with a one-line name grew a taller
   frame than its neighbours and pushed everything below it down. It goes under the price
   instead, which lines the prices up along the bottom of the row. */
.omd-tile__shot {
	--cover-ratio: 1;
	flex: none;
	border-radius: var(--radius-thumb);
}

/* Stacked rungs, in the panel's price editor. */
.omd-ladder {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

/* The saving a quantity earns, beside the price that earns it. */
.omd-ladder__off {
	font: var(--fw-semibold) var(--fs-micro)/1.6 var(--font-core);
	color: var(--anar-700);
	background: var(--anar-100);
	border-radius: var(--radius-pill);
	padding: 0 var(--space-2);
	white-space: nowrap;
}

/*
 * The same ladder on a row, which has one line for it rather than a column. Inline rather
 * than a flex row: on a phone the sentence wraps, and as a flex item the badge could not
 * follow it onto the last line and dropped to one of its own.
 */
.omd-ladder-line {
	margin-top: var(--space-2);
	font: var(--fw-medium) var(--fs-xs)/1.5 var(--font-core);
	color: var(--price-wholesale);
}

.omd-ladder-line .omd-ladder__off {
	display: inline-block;
	margin-inline-start: var(--space-2);
	padding: var(--space-1) var(--space-3);
}

/*
 * The categories are a grid, not a rail. There are few enough of them to show at once,
 * and a rail put half of them behind a gesture on the one screen where a visitor is still
 * deciding what the shop even sells.
 */
.omd-cat-grid {
	display: grid;
	gap: var(--space-5);
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (min-width: 901px) {
	.omd-cat-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

.omd-cat {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-4);
	text-decoration: none;
}

.omd-cat__disc {
	width: 100%;
	aspect-ratio: 1;
	border-radius: var(--radius-pill);
	background: var(--sang-200);
	display: grid;
	place-items: center;
	overflow: hidden;
	color: var(--text-faint);
	transition: box-shadow var(--dur-2) var(--ease-standard);
}

.omd-cat:hover .omd-cat__disc { box-shadow: var(--shadow-3); }

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

.omd-cat__disc > img { width: 100%; height: 100%; object-fit: cover; }

/* The fallback icon keeps its share of the disc instead of a fixed 22px. The disc is as
   wide as its column now, and a fixed icon sat in the middle of it as a speck beside the
   categories that do have a picture. */
.omd-cat__disc > .omd-icon { width: 30%; height: auto; }
.omd-cat__label { font: var(--type-sm); color: var(--text-body); text-align: center; text-wrap: pretty; }

.omd-fact {
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-1);
	padding: var(--space-6);
	display: flex;
	gap: var(--space-4);
	align-items: flex-start;
	color: var(--text-muted);
}

/* Was an inline grid on the section itself; a class now, because the section is built
   from settings and can come out with one card or three. */
.omd-promises {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.omd-fact__title { font: var(--type-body-strong); color: var(--text-strong); }
.omd-fact__body { font: var(--type-sm); color: var(--text-muted); margin-top: var(--space-1); }

/*
 * The home page is a stack of separate offers, and they stand further apart than the
 * sections of an ordinary page. One gap on the stack rather than a margin on each part,
 * because which parts are on the page at all depends on the catalogue: a rail cannot
 * know what it follows, or whether anything does.
 */
.omd-home {
	display: flex;
	flex-direction: column;
	gap: var(--space-11);
}

.omd-home > .omd-section { margin-top: 0; }

/* What the shop writes rather than sells: the recipes and the blog, side by side, and
   each on its own when the other has nothing to show yet. */
.omd-homecols {
	display: grid;
	gap: var(--space-7);
}

@media (min-width: 901px) {
	.omd-homecols {
		grid-auto-flow: column;
		grid-auto-columns: minmax(0, 1fr);
	}
}

.omd-hlist {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
}

/*
 * The two columns are read as one block, so their cards are the same height: 80px, which
 * is a 56px thumbnail plus its padding. Both names take one line and are cut rather than
 * wrapped, because a name long enough to wrap is what would break the alignment, and two
 * columns that almost line up read as a mistake.
 */
.omd-hrec {
	display: grid;
	grid-template-columns: 56px minmax(0, 1fr) auto;
	align-items: center;
	gap: var(--space-4);
	height: 80px;
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	padding: var(--space-4);
	text-decoration: none;
}

.omd-hrec__thumb {
	width: 56px;
	height: 56px;
	border-radius: var(--radius-thumb);
	overflow: hidden;
	background: var(--sang-200);
}

.omd-hrec__thumb img { width: 100%; height: 100%; object-fit: cover; }

.omd-hrec__body {
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
	min-width: 0;
}

.omd-hrec__name {
	font: var(--type-body-strong);
	color: var(--text-strong);
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.omd-hrec__meta { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }

.omd-hrec__price {
	font: var(--fw-bold) var(--fs-sm)/1.3 var(--font-numeric);
	color: var(--text-strong);
	white-space: nowrap;
}

.omd-hpost {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: var(--space-1);
	height: 80px;
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	padding: var(--space-4) var(--space-5);
	text-decoration: none;
}

.omd-hpost__title {
	font: var(--type-body-strong);
	color: var(--text-strong);
	display: -webkit-box;
	-webkit-line-clamp: 1;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.omd-hpost__meta { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }

.omd-hrec,
.omd-hpost { transition: box-shadow var(--dur-2) var(--ease-standard); }

.omd-hrec:hover,
.omd-hpost:hover { box-shadow: var(--shadow-3); }

@media (prefers-reduced-motion: reduce) {
	.omd-hrec,
	.omd-hpost { transition: none; }
}

/*
 * The banner frame, whether one picture or a slider of them. Both are .omd-cover: the
 * picture whole, over a blurred copy of itself. What is put here is laid out by somebody
 * and usually has words in it, so a crop does not take a dull corner off a photograph, it
 * takes the end off a sentence.
 */
.omd-hero {
	--cover-ratio: 1870 / 840;
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
}

/*
 * Home banner slider.
 *
 * The track is the slider. A scroll container with one snap point per banner already
 * carries a finger, a trackpad and a scrollbar, so the markup is a working carousel
 * before app.js runs. What a scroll container cannot do is say which banner you are on
 * or move to the next by itself, and that is all the arrows and dots are for; they stay
 * out of sight until the script has wired them, so nothing dead is ever on screen.
 */
.omd-slider {
	position: relative;
	border-radius: var(--radius-card);
	overflow: hidden;
	box-shadow: var(--shadow-2);
	background: var(--sang-200);
}

.omd-slider__track {
	display: flex;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	/* A swipe that runs past the last banner is a swipe at the slider, not at the page
	   behind it, and on a phone that difference is the browser's back gesture. */
	overscroll-behavior-inline: contain;
}

.omd-slider__slide {
	flex: 0 0 100%;
	scroll-snap-align: start;
	/* The frame .omd-hero draws, so a shop that has one of each does not have the page
	   change height between them. */
	--cover-ratio: 1870 / 840;
}

/* The link is the layer the picture is measured against, so it has to be the positioned
   one; .omd-cover img fills whichever of the two it finds itself inside. */
.omd-slider__link { position: absolute; inset: 0; display: block; }

.omd-slider__nav,
.omd-slider__dots {
	display: none;
	position: absolute;
	inset-block-end: var(--space-4);
}

.omd-slider--ready .omd-slider__nav,
.omd-slider--ready .omd-slider__dots { display: flex; }

.omd-slider__nav {
	inset-inline-start: var(--space-4);
	gap: var(--space-2);
}

.omd-slider__dots {
	inset-inline: 0;
	justify-content: center;
	gap: var(--space-2);
	/* The strip runs the width of the banner; only the dots themselves may take a click,
	   or the middle of a linked banner would stop being clickable. */
	pointer-events: none;
}

/*
 * Literal white, not a token: these sit on somebody's photograph rather than on a surface
 * the theme chose, and following the ramp into the dark theme would put grey dots on the
 * same bright picture.
 */
.omd-slider__dot {
	pointer-events: auto;
	width: 8px;
	height: 8px;
	padding: 0;
	border: 0;
	cursor: pointer;
	border-radius: var(--radius-pill);
	background: rgba(255, 255, 255, .55);
	box-shadow: 0 1px 3px rgba(0, 0, 0, .35);
	transition: width var(--dur-3) var(--ease-standard), background-color var(--dur-2) var(--ease-standard);
}

.omd-slider__dot:hover { background: rgba(255, 255, 255, .8); }

/* The one you are on stretches into a capsule rather than changing colour alone: a size
   difference survives being read at a glance over a busy picture. */
.omd-slider__dot[aria-current="true"] {
	width: 26px;
	background: #FFFFFF;
}

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

@media (max-width: 600px) {
	.omd-slider__nav,
	.omd-slider__dots { inset-block-end: var(--space-3); }

	.omd-slider__nav {
		inset-inline-start: var(--space-3);
		gap: var(--space-1);
	}

	.omd-slider__nav .omd-iconbtn { width: var(--control-h); height: var(--control-h); }
}

.omd-breadcrumb {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--type-sm);
	color: var(--text-muted);
	margin-bottom: var(--space-5);
	flex-wrap: wrap;
}

.omd-breadcrumb a { color: var(--text-link); }

/* Announcement ---------------------------------------------------------- */

/*
 * A strip above everything for something the shop needs said on every page. Closing it is
 * a checkbox, so it works with JavaScript off; app.js writes the cookie that keeps it
 * closed on the next page.
 */
.omd-alert { background: var(--surface-inverse); color: var(--text-inverse); }
.omd-alert:has(.omd-alert__switch:checked) { display: none; }

.omd-alert__inner {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding-block: var(--space-3);
}

.omd-alert__icon { flex: none; display: flex; color: var(--on-dark-warning); }

/* Takes the slack, so the link and the close sit at the end whether or not there is a
   link at all. */
.omd-alert__text {
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	column-gap: var(--space-3);
	row-gap: var(--space-1);
}

.omd-alert__title { font: var(--fw-medium) var(--fs-sm)/1.7 var(--font-core); color: var(--text-inverse); }
.omd-alert__body { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--on-dark-muted); }

.omd-alert__cta {
	flex: none;
	font: var(--fw-medium) var(--fs-xs)/1 var(--font-core);
	color: var(--text-inverse);
	white-space: nowrap;
}

.omd-alert__cta:hover { color: var(--on-dark-muted); }

.omd-alert__close {
	flex: none;
	width: var(--control-h-sm);
	height: var(--control-h-sm);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-pill);
	color: var(--on-dark-muted);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-alert__close:hover { background: var(--shab-700); color: var(--text-inverse); }
.omd-alert__switch:focus-visible + .omd-alert__inner .omd-alert__close { box-shadow: var(--ring-focus); }


/* ---------------------------------------------------------------- 6. Chrome */

/*
 * WordPress's admin bar is fixed over the top of the page for a logged-in visitor, and
 * anything that sticks to the top of the viewport has to clear it. Zero for everyone
 * else, so the arithmetic below is the same either way.
 */
:root { --admin-bar-h: 0px; }
body.admin-bar { --admin-bar-h: 32px; }

@media screen and (max-width: 782px) {
	body.admin-bar { --admin-bar-h: 46px; }
}

/* Under 600px WordPress stops fixing the bar and lets it scroll away with the page. */
@media screen and (max-width: 600px) {
	body.admin-bar { --admin-bar-h: 0px; }
}

:root { --header-h: 64px; }

.omd-header {
	position: sticky;
	top: var(--admin-bar-h);
	z-index: var(--z-sticky);
	background: var(--surface-card);
	border-bottom: 1px solid var(--border-hairline);
}

/* A sticky header with a z-index is its own stacking context, which traps the drawer
   inside it: the panel would sit under the tab bar however high its own z-index went.
   Lift the whole header, and only while the menu is actually open. */
.omd-header:has(.omd-drawer[open]) { z-index: var(--z-dialog); }

.omd-header__inner {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding-block: var(--space-3);
	/* Stated once: anything else that has to clear the sticky header measures from here. */
	min-height: var(--header-h);
}

.omd-brand { display: flex; align-items: center; gap: var(--space-3); flex: none; text-decoration: none; }
.omd-brand__mark { display: block; border-radius: var(--radius-pill); }
.omd-brand__name { font: var(--fw-semibold) var(--fs-title)/1.2 var(--font-core); color: var(--text-strong); }

.omd-nav { display: flex; align-items: center; gap: var(--space-6); margin-inline-start: var(--space-6); }
.omd-nav ul { display: flex; align-items: center; gap: var(--space-6); list-style: none; margin: 0; padding: 0; }
.omd-nav a { font: var(--type-sm); color: var(--text-body); text-decoration: none; }
.omd-nav a:hover, .omd-nav .current-menu-item > a { color: var(--text-brand); }

.omd-header__search { flex: 1; min-width: 0; max-width: 420px; margin-inline-start: auto; }
.omd-header__tools { display: flex; align-items: center; gap: var(--space-2); flex: none; }

/* The shop before it opens ------------------------------------------------ */

/* Pushed to the far end of the bar, where the cart and the account sit on the open
   shop's header. */
.omd-closed__state { margin-inline-start: auto; flex: none; }

/* Centred in whatever room is left rather than sitting under the header: there is
   nothing else on the page for it to be above. The shell is already a full-height
   column, so filling the middle of it is all this takes -- and the footer stays at the
   bottom of a tall screen instead of floating halfway up it. */
.omd-main:has(.omd-closed) { display: flex; padding-block: 0; }

.omd-closed {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-5);
	text-align: center;
	padding-block: var(--space-10);
}

/* A measure short enough to read in one go, on every line of it. */
.omd-closed > * { max-width: 44ch; }

.omd-closed__mark { display: block; border-radius: var(--radius-pill); }
.omd-closed__eyebrow { font: var(--type-caps); letter-spacing: var(--ls-caps); color: var(--text-muted); margin: 0; }
.omd-closed__title { font: var(--type-h1); letter-spacing: var(--ls-display); color: var(--text-strong); margin: 0; text-wrap: pretty; }
.omd-closed__note { font: var(--fw-regular) var(--fs-body-lg)/var(--lh-body) var(--font-core); color: var(--text-body); margin: 0; text-wrap: pretty; }

/* Slide-in menu for screens without the inline nav ---------------------- */

.omd-drawer { flex: none; }

.omd-drawer__toggle {
	width: var(--control-h);
	height: var(--control-h);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-pill);
	color: var(--text-strong);
	cursor: pointer;
	transition: var(--transition-control);
	/* A summary is a list item by default, marker and all. */
	list-style: none;
}

.omd-drawer__toggle::-webkit-details-marker { display: none; }
.omd-drawer__toggle:hover { background: var(--sang-200); }
.omd-drawer__toggle:focus-visible { outline: 2px solid var(--border-focus); outline-offset: 2px; }

.omd-drawer__scrim {
	position: fixed;
	inset: 0;
	z-index: var(--z-dialog);
	background: var(--scrim);
	opacity: 0;
	pointer-events: none;
	transition: opacity var(--dur-2) var(--ease-standard);
}

.omd-drawer[open] .omd-drawer__scrim { opacity: 1; pointer-events: auto; }

.omd-drawer__panel {
	position: fixed;
	/* Starts below the admin bar, which is fixed over everything for a logged-in visitor. */
	inset-block: var(--admin-bar-h) 0;
	inset-inline-start: 0;
	z-index: calc(var(--z-dialog) + 1);
	width: min(320px, 84vw);
	padding: var(--space-6);
	background: var(--surface-card);
	box-shadow: var(--shadow-4);
	overflow-y: auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-6);
	/* The panel is pinned to the inline start, which is the right in this RTL layout, so
	   it hides by moving right. Transforms are physical, not logical. */
	transform: translateX(100%);
	transition: transform var(--dur-3) var(--ease-standard);
}

.omd-drawer[open] .omd-drawer__panel { transform: none; }

/* The drawer opens over the page and hides the header, so it says whose site this is
   before it says where you can go. */
.omd-drawer__brand {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding-bottom: var(--space-6);
	border-bottom: 1px solid var(--border-hairline);
	text-decoration: none;
}

.omd-drawer__brand img { display: block; border-radius: var(--radius-pill); flex: none; }
.omd-drawer__brand-name { font: var(--fw-semibold) var(--fs-title)/1.2 var(--font-core); color: var(--text-strong); }

.omd-drawer__nav { display: flex; flex-direction: column; gap: var(--space-1); }

.omd-drawer__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}

.omd-drawer__list a {
	display: block;
	padding: var(--space-4) var(--space-4);
	border-radius: var(--radius-control);
	font: var(--type-body-strong);
	color: var(--text-body);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-drawer__list a:hover { background: var(--sang-100); color: var(--text-brand); }
.omd-drawer__list .current-menu-item > a { background: var(--surface-brand-soft); color: var(--anar-800); }

/* A <details> renders its panel for the first time in the same style recalculation that
   slides it in, and a transition has nothing to move from, so the first open used to
   appear with no animation at all. app.js pins the closed position with this state, lets
   the browser lay the panel out there, then releases it. */
.omd-drawer.is-opening .omd-drawer__panel { transform: translateX(100%); }
.omd-drawer.is-opening .omd-drawer__scrim { opacity: 0; }

/* And the other way: [open] going stops the panel being rendered at once, so it used to
   slide in and then simply vanish. This state holds [open] on for the length of the
   slide. Out is quicker than in: opening is the thing you asked for, closing is
   something you want over with. */
.omd-drawer.is-closing .omd-drawer__panel {
	transform: translateX(100%);
	transition-duration: var(--dur-2);
}

.omd-drawer.is-closing .omd-drawer__scrim {
	opacity: 0;
	pointer-events: none;
	transition-duration: var(--dur-1);
}

@media (prefers-reduced-motion: reduce) {
	.omd-drawer__panel,
	.omd-drawer__scrim { transition: none; }
}

.omd-footer {
	background: var(--surface-inverse);
	color: var(--text-inverse);
	padding-block: var(--space-9);
}

.omd-footer__grid {
	display: grid;
	gap: var(--space-8);
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.omd-footer__brand { font: var(--fw-semibold) var(--fs-title)/1.2 var(--font-core); color: var(--text-inverse); }
.omd-footer p { font: var(--type-sm); color: var(--shab-300); margin: var(--space-3) 0 0; max-width: 40ch; }
.omd-footer__head { font: var(--type-label); color: var(--shab-300); margin-bottom: var(--space-4); }
/* Foreground on the footer comes from the shab ramp, not sang. This surface stays dark
   in both themes ("the shab ramp does not invert, it lifts"), so a sang tone would
   invert to near-black here and vanish against it. */
.omd-footer__list { display: flex; flex-direction: column; gap: var(--space-3); font: var(--type-sm); color: var(--shab-200); list-style: none; margin: 0; padding: 0; }
.omd-footer__list a { color: var(--shab-200); text-decoration: none; display: flex; align-items: center; gap: var(--space-2); }
.omd-footer__list a:hover { color: var(--text-inverse); }

.omd-footer__intro { display: flex; flex-direction: column; gap: var(--space-5); }

/* An outlined chip rather than bare text: on a dark footer it reads as the tappable
   thing it is, and the outline keeps it from competing with the brand above it. */
.omd-footer__phone {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3);
	align-self: flex-start;
	padding: var(--space-3) var(--space-5);
	border: 1px solid var(--shab-600);
	border-radius: var(--radius-control);
	color: var(--text-inverse);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-footer__phone:hover {
	background: var(--wash-on-color);
	border-color: var(--shab-500);
	color: var(--text-inverse);
}

.omd-footer__phone-text { display: flex; flex-direction: column; gap: 2px; }
.omd-footer__phone-number { font: var(--fw-semibold) var(--fs-body)/1.3 var(--font-numeric); color: var(--text-inverse); }
.omd-footer__phone-note { font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-core); color: var(--shab-300); }

/* Two related groups sharing one column. */
.omd-footer__stack { display: flex; flex-direction: column; gap: var(--space-7); }

.omd-footer__seal { display: flex; align-items: flex-start; }

.omd-footer__base {
	margin-top: var(--space-9);
	padding-top: var(--space-6);
	border-top: 1px solid var(--border-on-deal);
	text-align: center;
}

/* The generic footer paragraph is capped at 40ch for the about blurb. The copyright is a
   single centred line, so it opts out of that measure rather than centring inside it.
   Qualified with the element to outrank `.omd-footer p`, which would otherwise keep the
   measure and leave the line centred inside a narrow box pinned to one side. */
.omd-footer p.omd-footer__copyright {
	font: var(--type-sm);
	color: var(--shab-300);
	max-width: none;
	margin: 0;
}

/*
 * eNAMAD trust seal.
 *
 * The artwork is dark ink on a transparent ground, and this footer is dark in both
 * themes, so it needs a light card behind it to be legible at all. In the dark theme
 * that card would itself be dark, so the image is inverted instead.
 *
 * Sized in CSS as well as in the width and height attributes because the seal is served
 * from inside Iran only: abroad it never loads, and the reserved box keeps the footer
 * from reflowing around a gap.
 */
.omd-enamad {
	display: inline-block;
	flex: none;
	line-height: 0;
	padding: var(--space-3);
	border-radius: var(--radius-2);
	background: var(--sang-000);
}

.omd-enamad img {
	display: block;
	width: 125px;
	height: 136px;
	object-fit: contain;
}

[data-theme="dark"] .omd-enamad {
	background: transparent;
	padding: 0;
}

[data-theme="dark"] .omd-enamad img {
	filter: invert(1);
}

.omd-tabbar {
	position: sticky;
	bottom: 0;
	z-index: var(--z-sticky);
	background: var(--surface-card);
	border-top: 1px solid var(--border-hairline);
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	padding-block: var(--space-2);
	padding-inline: var(--gutter-inline);
}

.omd-tabbar__item {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-1);
	min-height: var(--tap-min);
	border: 0;
	background: transparent;
	color: var(--text-muted);
	text-decoration: none;
	font: var(--fw-medium) var(--fs-micro)/1.4 var(--font-core);
	cursor: pointer;
}

.omd-tabbar__item[aria-current="page"] { color: var(--text-brand); }

.omd-tabbar__badge {
	position: absolute;
	top: 2px;
	inset-inline-end: calc(50% - 22px);
	min-width: 18px;
	height: 18px;
	padding: 0 4px;
	border-radius: var(--radius-pill);
	background: var(--surface-brand);
	color: var(--text-inverse);
	font: var(--fw-semibold) var(--fs-micro)/18px var(--font-numeric);
	text-align: center;
}

/* Article bodies arrive as stored HTML from the editor, so they cannot be styled inline. */
.omd-post h2 { font: var(--type-title); color: var(--text-strong); margin: var(--space-7) 0 var(--space-3); }
.omd-post p { margin: 0 0 var(--space-4); text-wrap: pretty; }

/* A page on the default template has nothing beside it: no sidebar like an article, and
   no layout of its own like about or contact. Left alone its text would run the full
   1216px column, which is not a line anybody can read back to the start of.

   720px rather than the design system's --measure-prose, which is 64ch. `ch` is the width
   of a zero and the 60-to-75 character rule it encodes is a Latin one; Persian glyphs in
   Vazirmatn are narrower, so 64ch renders here at 576px and fits far more than 64
   characters while leaving most of the column empty. 720 is still inside the 864px an
   article gets beside its sidebar. The token is left as the design has it.

   A page that is not prose — a wide table, a gallery — takes the full-width template
   instead, and simply does not get this class. */
.omd-page__prose { max-width: 720px; }
.omd-post ul, .omd-post ol { margin: 0 0 var(--space-5); padding-inline-start: var(--space-6); display: flex; flex-direction: column; gap: var(--space-2); }
.omd-post li { text-wrap: pretty; }

.omd-post blockquote {
	margin: var(--space-6) 0;
	padding: var(--space-5);
	background: var(--sang-100);
	border-radius: var(--radius-card);
	font: var(--fw-medium) var(--fs-body)/1.8 var(--font-core);
	color: var(--text-strong);
}

.omd-post a { color: var(--text-brand); }

.omd-post img { border-radius: var(--radius-card); }

@media (max-width: 900px) {
	.omd-nav { display: none; }

	/* The tab bar owns this destination on narrow screens; two of them in one viewport
	   is a duplicate, not a shortcut. */
	.omd-header__account { display: none; }
}

@media (min-width: 901px) {
	.omd-tabbar,
	.omd-drawer { display: none; }
}

/*
 * The filter rail follows the reader down, the way the narrow screen's toolbar does.
 *
 * A catalogue is scrolled a long way and the filters are what change it, so a rail left
 * at the top means scrolling back to the beginning to narrow a price band. Only where the
 * rail is a rail: below 900px it is a bottom sheet, which is already always reachable.
 *
 * Same measured header height the toolbar uses, plus a gap so it does not sit against the
 * hairline, and gated on the same class so a page with the script blocked leaves the rail
 * in the flow rather than pinning it under a header whose height nobody measured.
 *
 * The height cap and the scrolling belong to the card, not to the rail around it. A
 * sticky column taller than the viewport pins its own bottom out of reach and the button
 * that applies the filters is down there, so something has to scroll; but a scroll
 * container clips what its children paint outside it, and putting it on the rail cut the
 * card's own shadow off square on all four sides. A box's shadow is painted outside its
 * border box and is not clipped by its own overflow, so the card can carry both.
 */
@media (min-width: 901px) {
	.omd-header-measured .omd-catalog__rail {
		position: sticky;
		top: calc(var(--admin-bar-h) + var(--omd-header-h, 0px) + var(--space-4));
	}

	.omd-header-measured .omd-catalog__rail > .omd-card {
		max-height: calc(100vh - var(--admin-bar-h) - var(--omd-header-h, 0px) - var(--space-8));
		overflow-y: auto;
		overscroll-behavior: contain;
	}
}

/* Below this the header cannot hold brand, menu, search and cart on one line without
   starving the search field, so it becomes two rows:

     brand ............................ cart
     menu  search .........................

   The cart is pushed to the far end of its row rather than left huddled against the
   brand, and the menu button leads the search row it shares. */
@media (max-width: 480px) {
	.omd-header__inner { flex-wrap: wrap; row-gap: var(--space-1); }

	.omd-brand { order: 1; }

	.omd-header__tools { order: 2; margin-inline-start: auto; }

	/* Flexbox breaks lines from the items' own widths, before auto margins are resolved,
	   so an auto margin alone would not guarantee the split. A zero-height item that
	   demands a full line does. */
	.omd-header__inner::after {
		content: "";
		order: 3;
		flex-basis: 100%;
		height: 0;
	}

	.omd-drawer { order: 4; }

	.omd-header__search {
		order: 5;
		/* Basis zero, not auto: sized from its own content the field is wider than the
		   space beside the menu button at 320px, and wraps to a third row. */
		flex: 1 1 0;
		min-width: 0;
		max-width: none;
		margin-inline-start: 0;
	}
}

/* ----------------------------------------------------------- 7. WooCommerce */

.woocommerce-notices-wrapper:empty { display: none; }

.woocommerce-message,
.woocommerce-info,
.woocommerce-error {
	list-style: none;
	margin: 0 0 var(--space-5);
	padding: var(--space-4) var(--space-5);
	border-radius: var(--radius-control);
	font: var(--type-sm);
	line-height: 1.8;
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
	text-align: start;
	background: var(--state-info-soft);
	color: var(--text-strong);
}

.woocommerce-message { background: var(--state-success-soft); color: var(--sabz-700); }
.woocommerce-error { background: var(--state-danger-soft); color: var(--anar-800); }
.woocommerce-error li { list-style: none; }

/*
 * WooCommerce writes the button before the sentence it belongs to, which reads as a
 * command with an explanation trailing off it. `order` puts it last without touching the
 * markup, and the auto margin takes the slack so the sentence starts at the edge the
 * language starts at and the button sits at the far end.
 */
.woocommerce-message .button,
.woocommerce-info .button,
.woocommerce-error .button {
	order: 1;
	margin-inline-start: auto;
}

/*
 * Once the line wraps there is no slack for that auto margin to take, and the button ends
 * up alone against the far edge with the text starting at the other one. Stacking puts it
 * back under the sentence it belongs to; it is still last, which is what "at the end"
 * means in a column.
 */
@media (max-width: 600px) {
	.woocommerce-message,
	.woocommerce-info,
	.woocommerce-error {
		flex-direction: column;
		align-items: flex-start;
	}

	.woocommerce-message .button,
	.woocommerce-info .button,
	.woocommerce-error .button { margin-inline-start: 0; }
}

/* A notice is usually the first thing on a page, but not always. Wherever one follows
   other content it needs its own room rather than sitting against whatever is above it.
   WooCommerce wraps notices in a container, so the notice itself is always its parent's
   first child; the container is the thing that has a sibling above it. */
.woocommerce-notices-wrapper:not(:first-child) { margin-top: var(--space-6); }

.woocommerce-message:not(:first-child),
.woocommerce-info:not(:first-child),
.woocommerce-error:not(:first-child) { margin-top: var(--space-5); }

/* A price field says what it means without a placeholder doing the work: the unit stays
   put while you type, and the "from"/"to" reads as one sentence across the two rows. */
.omd-affix {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	height: var(--control-h-sm);
	padding-inline: var(--space-4);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	transition: var(--transition-control);
	cursor: text;
}

.omd-affix:hover { border-color: var(--border-strong); }
.omd-affix:focus-within { border-color: var(--border-focus); box-shadow: var(--ring-focus); }

.omd-affix__lead,
.omd-affix__trail { flex: none; user-select: none; white-space: nowrap; }
.omd-affix__lead { font: var(--fw-medium) var(--fs-sm)/1 var(--font-core); color: var(--text-muted); }
.omd-affix__trail { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-faint); }

.omd-affix input {
	flex: 1;
	min-width: 0;
	border: 0;
	outline: none;
	background: transparent;
	font: var(--type-body);
	font-size: var(--fs-sm);
	color: var(--text-body);
}

/* The shortcuts run off the edge of the rail on purpose: a row that ends flush looks
   complete, and there are more than fit. */
.omd-presets {
	gap: var(--space-2);
	/* Exactly the tight card's padding, so the rail spans its full width and no further. */
	margin-inline: calc(var(--space-4) * -1);
	padding-inline: var(--space-4);
}

.omd-presets > .omd-tag { flex: none; }

/* Catalogue ------------------------------------------------------------- */

.omd-catalog {
	display: grid;
	gap: var(--space-7);
	grid-template-columns: 260px minmax(0, 1fr);
	align-items: start;
}

/* The opener, the scrim and the way out belong to the sheet, so none of them show while
   the rail is a rail. */
.omd-filters__open,
.omd-filters__scrim,
.omd-filters__close { display: none; }

@media (max-width: 900px) {
	.omd-catalog { grid-template-columns: minmax(0, 1fr); }

	/*
	 * The rail becomes a bottom sheet. Driven by a checkbox and `:has()` rather than a
	 * script: filters that need JavaScript to open are filters a phone cannot use. A
	 * bottom sheet rather than a side one because the side is where the nav drawer comes
	 * from, and two panels arriving from the same edge read as the same panel.
	 */
	.omd-catalog__rail {
		position: fixed;
		inset-inline: 0;
		bottom: 0;
		z-index: calc(var(--z-dialog) + 1);
		max-height: 85vh;
		overflow-y: auto;
		overscroll-behavior: contain;
		border-start-start-radius: var(--radius-card);
		border-start-end-radius: var(--radius-card);
		box-shadow: var(--shadow-4);
		background: var(--surface-card);
		transform: translateY(100%);
		transition: transform var(--dur-3) var(--ease-standard);
	}

	.omd-catalog:has(.omd-filters__switch:checked) .omd-catalog__rail { transform: none; }

	.omd-filters__scrim {
		display: block;
		position: fixed;
		inset: 0;
		z-index: var(--z-dialog);
		background: var(--scrim);
		opacity: 0;
		pointer-events: none;
		transition: opacity var(--dur-2) var(--ease-standard);
	}

	.omd-catalog:has(.omd-filters__switch:checked) .omd-filters__scrim { opacity: 1; pointer-events: auto; }

	/* A sheet the page scrolls behind reads as broken, and `:has()` can say so without JS. */
	body:has(.omd-filters__switch:checked) { overflow: hidden; }

	.omd-filters__open { display: inline-flex; }

	.omd-filters__close {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: var(--control-h);
		height: var(--control-h);
		margin-inline-start: auto;
		border-radius: var(--radius-pill);
		color: var(--text-muted);
		cursor: pointer;
		transition: var(--transition-control);
	}

	.omd-filters__close:hover { background: var(--sang-200); color: var(--text-strong); }

	/* The sheet already has its own edge; the card inside it should not draw a second. */
	.omd-catalog__rail .omd-card { border: 0; box-shadow: none; background: transparent; }
}

/* The switch is visually hidden, so its focus ring has to be relayed to the button. */
.omd-filters__switch:focus-visible + .omd-filters__open { box-shadow: var(--ring-focus); }

.omd-filters__count {
	display: inline-grid;
	place-items: center;
	min-width: 18px;
	height: 18px;
	padding-inline: 5px;
	border-radius: var(--radius-pill);
	background: var(--surface-brand);
	color: var(--text-inverse);
	font: var(--fw-semibold) var(--fs-micro)/1 var(--font-numeric);
}

@media (prefers-reduced-motion: reduce) {
	.omd-catalog__rail,
	.omd-filters__scrim { transition: none; }
}

.omd-catalog__toolbar {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	flex-wrap: wrap;
	margin-bottom: var(--space-4);
}

.omd-catalog__sort { min-width: 190px; margin-inline-start: auto; }

/*
 * On a phone the filter and the sort follow the list down.
 *
 * A catalogue is scrolled a long way, and the two controls that change what is in it were
 * left at the top of it: deciding to sort by price after ten rows meant scrolling back to
 * the beginning to say so. Only on narrow screens, because the wide layout keeps the
 * filters in a rail beside the list where they never leave the screen anyway.
 *
 * It sits directly under the header, whose height app.js measures and publishes: the
 * header is one row wide and two narrow, and it grows again when the admin bar is there.
 * The class is app.js saying it has measured, so with the script blocked the bar simply
 * scrolls away as it always did rather than pinning itself behind the header.
 *
 * The bleed and the padding give the bar a background that reaches the edges of the
 * screen; without it the rows would show through the gutters as they passed underneath.
 */
@media (max-width: 900px) {
	.omd-header-measured .omd-catalog__toolbar {
		position: sticky;
		top: calc(var(--admin-bar-h) + var(--omd-header-h, 0px));
		/* Below the header and far below the filter sheet, both of which must cover it. */
		z-index: 10;
		background: var(--surface-page);
		margin-inline: calc(var(--gutter-inline) * -1);
		padding: var(--space-3) var(--gutter-inline);
		border-bottom: 1px solid var(--border-hairline);
	}
}

.woocommerce-ordering { margin: 0; }

.woocommerce-ordering select,
.omd-blog__sort select {
	appearance: none;
	width: 100%;
	height: var(--control-h-sm);
	padding-inline: var(--space-4) var(--space-8);
	font: var(--type-sm);
	color: var(--text-body);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	cursor: pointer;
	transition: var(--transition-control);
	/* Chevron drawn as a background so the control needs no wrapper element. */
	background-image: linear-gradient(45deg, transparent 50%, var(--text-muted) 50%),
		linear-gradient(135deg, var(--text-muted) 50%, transparent 50%);
	background-position: calc(var(--space-5) - 3px) 50%, var(--space-5) 50%;
	background-size: 6px 6px, 6px 6px;
	background-repeat: no-repeat;
}

.woocommerce-ordering select:focus,
.omd-blog__sort select:focus { outline: none; border-color: var(--border-focus); box-shadow: var(--ring-focus); }
.omd-filters { display: flex; flex-direction: column; gap: var(--space-6); }
.omd-filters__group { display: flex; flex-direction: column; gap: var(--space-3); }
/*
 * Horizontal rails run to the edge of the viewport rather than stopping at the page
 * gutter. Cut off cleanly at the gutter, a rail looks like a finished row; carried past
 * the edge, the half-visible chip is the only thing telling the reader to scroll.
 */
.omd-rail-bleed {
	margin-inline: calc(-1 * var(--gutter-inline));
	padding-inline: var(--gutter-inline);
}

.omd-chips { display: flex; gap: var(--space-3); overflow-x: auto; padding-bottom: var(--space-3); margin-bottom: var(--space-5); }
.omd-chips > * { flex: none; }

.omd-empty { padding: var(--space-10) var(--space-6); text-align: center; }
.omd-empty__title { font: var(--type-body-strong); color: var(--text-strong); }
.omd-empty__body { font: var(--type-sm); color: var(--text-muted); margin: var(--space-2) 0 var(--space-5); }

.woocommerce-pagination { margin-top: var(--space-7); }

.woocommerce-pagination ul {
	display: flex;
	gap: var(--space-2);
	list-style: none;
	margin: 0;
	padding: 0;
	justify-content: center;
}

.woocommerce-pagination a,
.woocommerce-pagination span {
	display: grid;
	place-items: center;
	min-width: var(--control-h);
	height: var(--control-h);
	padding-inline: var(--space-3);
	border-radius: var(--radius-control);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	color: var(--text-body);
	text-decoration: none;
	font: var(--fw-medium) var(--fs-sm)/1 var(--font-numeric);
}

.woocommerce-pagination .current { background: var(--shab-800); border-color: var(--shab-800); color: var(--text-inverse); }

/* Single product -------------------------------------------------------- */

/*
 * Named areas rather than a column count, because the buy box sits in a different place
 * in the reading order on each width. Beside the whole left column on a desktop; on a
 * phone between the name and the description, so the price and the button are the next
 * thing after the picture rather than a scroll past everything the shop wants to say.
 */
.omd-product {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 400px;
	grid-template-areas:
		"top   card"
		"rest  card"
		"after card";
	column-gap: var(--space-8);
	row-gap: var(--space-7);
	align-items: start;
}

.omd-product__top { grid-area: top; display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
.omd-product__rest { grid-area: rest; display: flex; flex-direction: column; gap: var(--space-7); min-width: 0; }
/*
 * The column the buy box stands in, and whatever else belongs beside the price: the
 * recipe for this dish, where there is one. A wrapper rather than two children of the
 * grid, because two items given the same named area are drawn on top of each other.
 */
.omd-product__buy {
	grid-area: card;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}
.omd-product__after { grid-area: after; min-width: 0; }

@media (max-width: 1000px) {
	.omd-product {
		grid-template-columns: minmax(0, 1fr);
		grid-template-areas:
			"top"
			"card"
			"rest"
			"after";
		row-gap: var(--space-6);
	}
}

/* The buy box follows the reader down: the description, the specifications and the price
   ladder are long, and the price and the button should not be a scroll away from them.
   Only where there are two columns; stacked, a sticky box would cover the page. */
@media (min-width: 1001px) {
	.omd-product__buy {
		position: sticky;
		/*
		 * Measured from the viewport, so everything fixed above has to be counted in: the
		 * admin bar, the header, and the header's hairline.
		 *
		 * Spelled out rather than kept in a variable of its own. A var() inside a custom
		 * property is resolved where that property is declared, so a --clearance defined
		 * on :root would bake in the logged-out --admin-bar-h and inherit that answer down
		 * however the body overrides it.
		 */
		top: calc(var(--admin-bar-h) + var(--header-h) + 1px + var(--space-5));
		align-self: start;
	}
}
/*
 * Square, like the design. Packshots are shot square, so a square frame is filled edge
 * to edge and the corner radius rounds the picture itself. A frame of another ratio
 * letterboxes the image and drops its own sharp-cornered block inside the rounded one.
 *
 * Capped and pushed to the start edge beside the buy box, full width and centred once
 * the two columns stack: at that point it is the top of the page, not a column of it.
 */
.omd-product__hero {
	aspect-ratio: 1;
	border-radius: var(--radius-card);
	max-width: 420px;
	margin-inline-end: auto;
	/* Its own scale for the empty state. The thumbnail size is set for a 60px tile; at
	   420px the same label reads as a caption that has lost its picture. */
	font: var(--type-sm);
}

@media (max-width: 1000px) {
	.omd-product__hero { max-width: none; margin-inline: auto; }
}

/*
 * More than one picture of the same thing: one on the stage, the rest as a strip under it.
 *
 * The switch is a radio group, not a script. Every shot is in the markup and the checked
 * one is the one showing, which means the picture still changes on a page whose
 * JavaScript never arrived — the same trade the delivery slots and the month tabs make.
 * `:has()` is only ever asked to hide, so a browser without it shows the first shot and a
 * strip that does nothing rather than an empty frame.
 */
.omd-shots { display: flex; flex-direction: column; gap: var(--space-4); max-width: 420px; margin-inline-end: auto; }

.omd-shots__stage {
	position: relative;
	aspect-ratio: 1;
	border-radius: var(--radius-card);
	overflow: hidden;
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
}

.omd-shots__shot { margin: 0; position: absolute; inset: 0; }
.omd-shots__shot img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* Only the chosen one. Stacked rather than swapped so every shot is a real image the
   browser has already fetched, and the change costs nothing when it happens. */
.omd-shots__shot + .omd-shots__shot { opacity: 0; }

.omd-shots:has(.omd-shots__pick:nth-of-type(1):checked) .omd-shots__shot[data-shot="0"],
.omd-shots:has(.omd-shots__pick:nth-of-type(2):checked) .omd-shots__shot[data-shot="1"],
.omd-shots:has(.omd-shots__pick:nth-of-type(3):checked) .omd-shots__shot[data-shot="2"],
.omd-shots:has(.omd-shots__pick:nth-of-type(4):checked) .omd-shots__shot[data-shot="3"],
.omd-shots:has(.omd-shots__pick:nth-of-type(5):checked) .omd-shots__shot[data-shot="4"],
.omd-shots:has(.omd-shots__pick:nth-of-type(6):checked) .omd-shots__shot[data-shot="5"] { opacity: 1; z-index: 1; }

.omd-shots:has(.omd-shots__pick:nth-of-type(2):checked) .omd-shots__shot[data-shot="0"],
.omd-shots:has(.omd-shots__pick:nth-of-type(3):checked) .omd-shots__shot[data-shot="0"],
.omd-shots:has(.omd-shots__pick:nth-of-type(4):checked) .omd-shots__shot[data-shot="0"],
.omd-shots:has(.omd-shots__pick:nth-of-type(5):checked) .omd-shots__shot[data-shot="0"],
.omd-shots:has(.omd-shots__pick:nth-of-type(6):checked) .omd-shots__shot[data-shot="0"] { opacity: 0; }

.omd-shots__strip { display: flex; gap: var(--space-3); flex-wrap: wrap; }

.omd-shots__thumb {
	width: 76px;
	height: 76px;
	flex: none;
	padding: 0;
	cursor: pointer;
	overflow: hidden;
	border-radius: var(--radius-thumb, var(--radius-control));
	border: 1px solid var(--border-hairline);
	background: var(--surface-card);
	transition: var(--transition-control);
}

.omd-shots__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.omd-shots__thumb:hover { border-color: var(--border-strong); }

/* The chosen thumbnail, marked the way the design marks it: a second, darker edge. */
.omd-shots__pick:nth-of-type(1):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="0"],
.omd-shots__pick:nth-of-type(2):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="1"],
.omd-shots__pick:nth-of-type(3):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="2"],
.omd-shots__pick:nth-of-type(4):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="3"],
.omd-shots__pick:nth-of-type(5):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="4"],
.omd-shots__pick:nth-of-type(6):checked ~ .omd-shots__strip .omd-shots__thumb[data-shot="5"] {
	border: 2px solid var(--shab-600);
}

.omd-shots__pick:focus-visible ~ .omd-shots__strip .omd-shots__thumb { box-shadow: var(--ring-focus); }

@media (max-width: 1000px) {
	.omd-shots { max-width: none; margin-inline: auto; }
}
.omd-product__title { font: var(--type-h2); color: var(--text-strong); margin: 0 0 var(--space-3); }
.omd-product__unit { font: var(--type-sm); color: var(--text-muted); }
.omd-product__desc { font: var(--fw-regular) var(--fs-body)/1.85 var(--font-core); color: var(--text-body); margin: 0; }

.omd-product__headline {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
}

.omd-share { flex: none; }

@media (max-width: 900px) {
	.omd-share--wide { display: none; }
}

@media (min-width: 901px) {
	.omd-share--narrow { display: none; }
}

.omd-specs { display: flex; flex-direction: column; }

.omd-specs__row {
	display: flex;
	gap: var(--space-4);
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border-hairline);
}

.omd-specs__key { font: var(--type-sm); color: var(--text-muted); min-width: 120px; }
.omd-specs__value { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-core); color: var(--text-strong); }

/* The form runs notice, quantity, button as three direct children with nothing between
   them. Spacing them here rather than in the template keeps it right whatever a hook
   adds in the middle. */
.omd-buybox form.cart { display: flex; flex-direction: column; gap: var(--space-5); }

.omd-buybox { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-buybox__base { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-buybox__saving { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-core); color: var(--text-brand); }
.omd-buybox__badges { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* What the quantity in the stepper comes to, ruled off from the controls above it. */
.omd-line {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	border-top: 1px solid var(--border-hairline);
	padding-top: var(--space-4);
}

.omd-line__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.omd-line__label { font: var(--type-body-strong); color: var(--text-body); }
.omd-line__total { font: var(--fw-semibold) var(--fs-body-lg)/1.3 var(--font-numeric); color: var(--text-strong); white-space: nowrap; }
.omd-line__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

.omd-buybox__schedule {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	border-top: 1px solid var(--border-hairline);
	padding-top: var(--space-5);
}

/* Cart and checkout ----------------------------------------------------- */

.omd-checkout-grid {
	display: grid;
	gap: var(--space-7);
	grid-template-columns: minmax(0, 1fr) 380px;
	align-items: start;
}

@media (max-width: 1000px) {
	.omd-checkout-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Both columns space their own cards, so a card must not carry a margin of its own: the
   gap would be added to it, and the first card in a column would start lower than the
   first card beside it. */
.omd-checkout-grid__aside,
.omd-checkout-grid__main {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}

.omd-cart-line {
	display: grid;
	grid-template-columns: 56px 1fr;
	gap: var(--space-4);
	padding: var(--space-5);
	border-top: 1px solid var(--border-hairline);
}

.omd-cart-line__thumb { display: block; width: 56px; height: 56px; }
.omd-cart-line__body { min-width: 0; }

.omd-cart-line__head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
}

.omd-cart-line__name { font: var(--type-body-strong); color: var(--text-strong); text-decoration: none; }
.omd-cart-line__unit { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-cart-line__tier { font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-core); color: var(--price-wholesale); margin-top: var(--space-2); }

.omd-cart-line__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-top: var(--space-4);
}

.omd-cart-line__total { font: var(--fw-bold) var(--fs-body)/1.3 var(--font-numeric); color: var(--text-strong); }
.omd-cart-line__each { font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-core); color: var(--text-muted); }
.omd-cart-line__warn { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--state-warning); margin-top: var(--space-2); }

.omd-totals { display: flex; flex-direction: column; gap: var(--space-3); }

.omd-totals__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	font: var(--type-sm);
	color: var(--text-body);
}

.omd-totals__row--muted { color: var(--text-muted); }
.omd-totals__row--discount { color: var(--text-brand); }
.omd-totals__rule { height: 1px; background: var(--border-hairline); margin: var(--space-2) 0; }
.omd-totals__label { font: var(--type-body-strong); color: var(--text-strong); }
.omd-totals__payable { font: var(--type-price); color: var(--text-strong); }
.omd-totals__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-totals__blocked { display: flex; flex-direction: column; gap: var(--space-2); }
.omd-totals__blocked p { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--state-danger); margin: 0; }

/* What a summary card hangs under its figures: the button that moves the customer on,
   whatever is holding that up, and the way back. The same stack on the cart, the delivery
   step and the review, so the three cards end the same way. */
.omd-totals__actions {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	margin-top: var(--space-5);
}

.omd-steps { display: flex; align-items: center; gap: var(--space-5); margin-bottom: var(--space-6); flex-wrap: wrap; }
.omd-steps__item { display: flex; align-items: center; gap: var(--space-3); }

.omd-steps__dot {
	width: 28px;
	height: 28px;
	border-radius: var(--radius-pill);
	display: grid;
	place-items: center;
	font: var(--fw-semibold) var(--fs-sm)/1 var(--font-numeric);
	background: var(--surface-sunken);
	color: var(--text-muted);
}

.omd-steps__item--active .omd-steps__dot { background: var(--surface-brand); color: var(--text-inverse); }
.omd-steps__label { font: var(--type-sm); color: var(--text-muted); }
.omd-steps__item--active .omd-steps__label { font: var(--type-body-strong); color: var(--text-strong); }

.omd-coupon { display: flex; gap: var(--space-3); align-items: flex-end; flex-wrap: wrap; }
.omd-coupon .omd-field { flex: 1; min-width: 180px; }

/* WooCommerce's own form markup, restyled rather than re-templated: these classes are
   emitted by core form field helpers we do not override. */

.woocommerce-billing-fields__field-wrapper,
.woocommerce-shipping-fields__field-wrapper,
.woocommerce-account-fields,
.woocommerce-address-fields__field-wrapper {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.form-row-wide, .form-row.notes { grid-column: 1 / -1; }

.form-row { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.form-row > label { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); }
.form-row .required { color: var(--state-danger); text-decoration: none; }

.form-row input[type="text"],
.form-row input[type="email"],
.form-row input[type="tel"],
.form-row input[type="password"],
.form-row input[type="number"],
.form-row textarea,
.form-row .select2-container .select2-selection,
.form-row select {
	width: 100%;
	min-height: var(--control-h);
	padding: var(--space-3) var(--space-4);
	font: var(--type-body);
	font-size: var(--fs-sm);
	color: var(--text-body);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	transition: var(--transition-control);
}

.form-row textarea { min-height: 96px; line-height: 1.8; resize: vertical; }

.form-row input:focus,
.form-row textarea:focus,
.form-row select:focus { outline: none; border-color: var(--border-focus); box-shadow: var(--ring-focus); }

.woocommerce-invalid input, .woocommerce-invalid select { border-color: var(--state-danger); }

#payment .wc_payment_methods {
	list-style: none;
	margin: 0 0 var(--space-5);
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
}

/* The design's Radio, which .omd-choice--radio already is: a ring with a dot, the name
   beside it, the explanation under the name. No box around the chosen one — these are a
   list of ways to pay, not a row of cards, and a border would make the unchosen ones look
   disabled. The room between them is the list's gap. */
.omd-pay { display: flex; flex-direction: column; gap: var(--space-1); }

/* The description, lined up under the name rather than under the ring: the ring is 20px
   and the gap beside it is --space-3, so the text starts where the name starts.
   Two selectors for one look: .omd-pay__note is a method that only explains itself and is
   always on screen, .payment_box is one with fields that WooCommerce folds away. */
#payment .omd-pay__note,
#payment .payment_box {
	margin: 0;
	padding-inline-start: calc(20px + var(--space-3));
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	color: var(--text-muted);
}

#payment :is(.omd-pay__note, .payment_box) p { margin: 0; }
#payment :is(.omd-pay__note, .payment_box) p + p { margin-top: var(--space-2); }
#payment .payment_box :is(input, select, textarea) { margin-top: var(--space-3); }

/* My account ------------------------------------------------------------ */

/*
 * The account page is one column with tabs across the top; see section 8. WooCommerce's
 * own two-column shell — a vertical nav beside the content — is gone with it, and so is
 * the grid that used to place them, which would otherwise drop .omd-account into the
 * 220px column the nav used to sit in.
 */

.woocommerce-orders-table,
.woocommerce-table--order-details,
.shop_table {
	width: 100%;
	border-collapse: collapse;
	background: var(--surface-card);
	border-radius: var(--radius-card);
	overflow: hidden;
	box-shadow: var(--shadow-2);
	font: var(--type-sm);
}

.shop_table th {
	text-align: start;
	font: var(--type-label);
	color: var(--text-muted);
	padding: var(--space-4) var(--space-5);
	background: var(--surface-sunken);
}

.shop_table td {
	padding: var(--space-4) var(--space-5);
	border-top: 1px solid var(--border-hairline);
	color: var(--text-body);
}

.shop_table tfoot th { background: transparent; font: var(--type-body-strong); color: var(--text-strong); }

.omdegan-order-delivery { margin-top: var(--space-7); }
.omdegan-order-delivery h2 { font: var(--type-title); margin-bottom: var(--space-2); }

/* Core WooCommerce controls --------------------------------------------- */

/*
 * The theme dequeues WooCommerce's stylesheet and templates the pages it cares about.
 * Everything WooCommerce still renders itself (place order, account forms, its own
 * buttons) would otherwise arrive unstyled, so core's classes are adopted here rather
 * than each screen being re-templated.
 */
.woocommerce .button,
.woocommerce button.button,
.woocommerce a.button,
.woocommerce input.button,
.woocommerce-page .button,
#place_order,
.wp-element-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-3);
	font: var(--type-body-strong);
	height: var(--control-h);
	padding: 0 var(--space-7);
	border-radius: var(--radius-button);
	border: 1px solid transparent;
	background: var(--anar-600);
	color: var(--text-inverse);
	cursor: pointer;
	white-space: nowrap;
	text-decoration: none;
	transition: var(--transition-control);
}

.woocommerce .button:hover,
.woocommerce button.button:hover,
.woocommerce a.button:hover,
.woocommerce input.button:hover,
#place_order:hover,
.wp-element-button:hover { background: var(--anar-700); color: var(--text-inverse); }

/* The same receding treatment as .omd-btn, kept in step with it by hand because this block is
   a copy of those values rather than a reuse of them. */
.woocommerce .button:disabled,
.woocommerce button.button:disabled,
.woocommerce input.button:disabled,
#place_order:disabled,
.wp-element-button:disabled {
	background: var(--surface-disabled);
	color: var(--text-disabled);
	border-color: var(--border-disabled);
	box-shadow: none;
	cursor: not-allowed;
}

#place_order { width: 100%; height: var(--control-h-lg); font-size: var(--fs-body-lg); }

/* Account, login, register and password reset forms. WooCommerce renders these whole,
   so they get the card the rest of the site puts a form in. */
.woocommerce-form-login,
.woocommerce-form-register,
.woocommerce-ResetPassword,
.woocommerce-EditAccountForm,
.woocommerce-address-fields {
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	padding: var(--space-7);
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	margin: 0 0 var(--space-6);
}

.woocommerce-account:not(.logged-in) .woocommerce {
	display: block;
	max-width: 460px;
	margin-inline: auto;
}

.omd-signin__head {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-3);
	margin-bottom: var(--space-7);
}

.omd-signin__mark { display: block; border-radius: var(--radius-pill); }
.omd-signin__title { font: var(--type-h3); color: var(--text-strong); margin: 0; text-align: center; }

/* ------------------------------------------------------------------ Sign in

   The shop's own steps: a number, a five-digit code, and a name if the number is new.
   The card is the theme's; what is particular here is the row of code boxes, which is
   the one place a form asks for one value in five inputs. */

.omd-auth {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}

.omd-auth__card {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	/* The focus ring on a code box sits outside its border, and the card clips. */
	overflow: visible;
}

.omd-auth__form {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	margin: 0;
}

.omd-auth__form .omd-field { margin: 0; }

/* Name and surname share a row until the row is too narrow to read them in. */
.omd-auth__pair {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
	gap: var(--space-4);
}

.omd-auth__lede { display: flex; flex-direction: column; gap: var(--space-2); }
.omd-auth__ledetitle { font: var(--type-body-strong); color: var(--text-strong); }
.omd-auth__lede p { font: var(--type-sm); color: var(--text-muted); margin: 0; text-wrap: pretty; }

.omd-auth__sent {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-3);
	flex-wrap: wrap;
}

.omd-auth__sentto { font: var(--type-sm); color: var(--text-body); margin: 0; }
.omd-auth__number { font: var(--fw-semibold) var(--fs-sm)/1.6 var(--font-numeric); color: var(--text-strong); }

/* Left to right on a right-to-left page, because the boxes hold a number and a number
   is read that way in Persian too. */
.omd-auth__code {
	display: flex;
	gap: var(--space-3);
	justify-content: center;
}

.omd-auth__box {
	flex: 0 1 56px;
	min-width: 0;
	height: 60px;
	box-sizing: border-box;
	text-align: center;
	font: var(--fw-bold) var(--fs-h3)/1 var(--font-numeric);
	color: var(--text-strong);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	outline: none;
	padding: 0;
	transition: var(--transition-control);
}

.omd-auth__box:focus {
	border-color: var(--border-focus);
	box-shadow: var(--ring-focus);
}

.omd-auth__resend {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-3);
	flex-wrap: wrap;
	margin: 0;
}

.omd-auth__wait { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

.omd-auth__legal {
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	color: var(--text-muted);
	margin: 0;
	text-align: center;
	text-wrap: pretty;
}

/* Under the card, not in it: these are other ways in, not part of the form above. */
.omd-auth__links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-3) var(--space-5);
	font: var(--type-sm);
}


/*
 * WooCommerce labels its form "ورود", which now repeats the heading above it. Only when
 * it is the only form on the page: with registration enabled the two headings are what
 * tell the columns apart, and WooCommerce nests them a level deeper.
 */
.woocommerce-account:not(.logged-in) .woocommerce > h2 { display: none; }

.woocommerce-account h2,
.woocommerce-ResetPassword > p:first-child { font: var(--type-h3); color: var(--text-strong); }

.woocommerce-form-login__rememberme,
.woocommerce-form__label-for-checkbox {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	font: var(--type-sm);
	color: var(--text-body);
}

/* WooCommerce puts the checkbox and the submit button in one row. Clear of each other,
   so a thumb reaching for the box on a phone cannot land on "sign in" instead. */
.woocommerce-form-login__rememberme { margin-bottom: var(--space-4); }

.woocommerce-form-login__rememberme input,
.woocommerce-form__label-for-checkbox input { width: 18px; height: 18px; accent-color: var(--anar-600); margin: 0; }

.woocommerce-LostPassword { font: var(--type-sm); margin: 0; }

/* WooCommerce's own two-column blocks (saved addresses). */
.woocommerce .col2-set {
	display: grid;
	gap: var(--space-6);
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.woocommerce .col2-set .col-1,
.woocommerce .col2-set .col-2 { min-width: 0; }

.woocommerce-privacy-policy-text p { font: var(--fw-regular) var(--fs-xs)/1.8 var(--font-core); color: var(--text-muted); }

/* WooCommerce's password reveal is an empty span that relied on its sprite for the icon.
   The sheet is dequeued, so the affordance is drawn here instead of dropped: desktop
   browsers offer no native reveal for a password field. */
.woocommerce .password-input { position: relative; display: block; }

.woocommerce .show-password-input {
	position: absolute;
	inset-inline-end: var(--space-3);
	top: 50%;
	transform: translateY(-50%);
	width: 28px;
	height: 28px;
	cursor: pointer;
	border-radius: var(--radius-1);
	background-image: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%237D8288%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22><path d=%22M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0%22/><circle cx=%2212%22 cy=%2212%22 r=%223%22/></svg>");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 18px 18px;
	opacity: .8;
}

.woocommerce .show-password-input:hover { opacity: 1; }
.woocommerce .show-password-input.display-password { opacity: 1; background-color: var(--sang-200); }

/* Prices rendered by WooCommerce itself (order tables, emails, widgets). */
.woocommerce-Price-amount { font-family: var(--font-numeric); font-weight: var(--fw-semibold); color: var(--price-retail); }
del .woocommerce-Price-amount { color: var(--price-strike); font-weight: var(--fw-regular); }
ins { text-decoration: none; }


/* An article that mentions this product, as a row you can read the whole of at a glance:
   what it is called, how long it takes, and that it is a link. */
.omd-reads { display: flex; flex-direction: column; gap: var(--space-3); }

.omd-read {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-5);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-read:hover { background: var(--sang-100); border-color: var(--border-default); }
.omd-read__icon { flex: none; color: var(--text-brand); display: flex; }
.omd-read__body { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }

.omd-read__title {
	font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-core);
	color: var(--text-link);
	/* Longhand, not `text-decoration: underline`: the shorthand resets the thickness to
	   auto, and the title would draw a heavier line than every other link on the site. */
	text-decoration-line: underline;
	text-decoration-thickness: from-font;
	text-underline-offset: 0.45em;
}

.omd-read__meta { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

.omd-read__go {
	margin-inline-start: auto;
	flex: none;
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--fw-medium) var(--fs-xs)/1 var(--font-core);
	color: var(--text-brand);
	white-space: nowrap;
}


/* --------------------------------------------------------------- 8. Account */

.omd-account__head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-bottom: var(--space-5);
}

.omd-account__title { font: var(--type-h3); color: var(--text-strong); margin: 0 0 var(--space-2); }
.omd-account__who { font: var(--type-sm); color: var(--text-muted); margin: 0; }
.omd-account__body { margin-top: var(--space-6); }
.omd-account__cta { margin: var(--space-5) 0 0; }

/* Tabs ------------------------------------------------------------------ */

.omd-tabs {
	display: flex;
	gap: var(--space-7);
	border-bottom: 1px solid var(--border-hairline);
	overflow-x: auto;
	scrollbar-width: none;
}

.omd-tabs::-webkit-scrollbar { height: 0; }

.omd-tabs__tab {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	white-space: nowrap;
	padding: 0 0 var(--space-4);
	font: var(--fw-regular) var(--fs-sm)/1.5 var(--font-core);
	color: var(--text-muted);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-tabs__tab:hover { color: var(--text-strong); }

/* An inset shadow rather than a border: a border on the selected tab alone would move
   every other tab up by its width. */
.omd-tabs__tab--on {
	font-weight: var(--fw-semibold);
	color: var(--shab-800);
	box-shadow: inset 0 -2px 0 var(--anar-600);
}

.omd-tabs__count { font-size: var(--fs-xs); color: var(--text-faint); }

/* Orders ---------------------------------------------------------------- */

.omd-orders { display: flex; flex-direction: column; gap: var(--space-4); }

/* A label on the run of cards below it, not a container around them: the pages cut every
   ten orders, which lands mid-month as often as not. */
.omd-orders__month {
	font: var(--fw-semibold) var(--fs-sm)/1.4 var(--font-core);
	color: var(--text-strong);
	margin: var(--space-3) 0 0;
}

.omd-orders__month:first-child { margin-top: 0; }

.omd-order { display: flex; flex-direction: column; gap: var(--space-4); }

.omd-order__head,
.omd-order__foot {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
}

.omd-order__foot { align-items: center; }
.omd-order__id { font: var(--type-body-strong); color: var(--text-strong); }
/* Two labelled facts that wrap apart from each other rather than running together. */
.omd-order__when {
	display: flex;
	flex-wrap: wrap;
	column-gap: var(--space-4);
	row-gap: var(--space-1);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}
.omd-order__total { font: var(--fw-bold) var(--fs-body)/1.3 var(--font-numeric); color: var(--text-strong); }
.omd-order__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.omd-order__more { border-top: 1px solid var(--border-hairline); padding-top: var(--space-4); }

/* Body colour, not the link red: this opens a fold on the card you are already on, and
   red here would put the loudest thing on the card on its least important control. */
.omd-order__summary {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	cursor: pointer;
	list-style: none;
	font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core);
	color: var(--text-strong);
}

/* Safari draws its own triangle from a pseudo-element that list-style cannot reach. */
.omd-order__summary::-webkit-details-marker { display: none; }

.omd-order__caret { color: var(--text-muted); transition: transform var(--dur-2) var(--ease-standard); }
.omd-order__more[open] .omd-order__caret { transform: rotate(180deg); }

.omd-order__items {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	margin-top: var(--space-4);
}

.omd-order__line { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.omd-order__name { font: var(--type-sm); color: var(--text-body); }
.omd-order__amount { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-numeric); color: var(--text-strong); white-space: nowrap; }
.omd-order__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; }

.omd-order__count { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-faint); }

.omd-orders__pages {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-top: var(--space-6);
}

/* A pager that pages one card's table rather than the page sits inside that card, which
   has no padding of its own for it to stand in. */
.omd-card--flush > .omd-orders__pages {
	margin-top: 0;
	padding: var(--space-4) var(--space-6) var(--space-6);
}

.omd-orders__range { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); margin: 0; }

.omd-pager { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* Square-ish rather than pill, so a row of numbers reads as a set of equal cells and the
   current one is a filled cell among them. */
.omd-pager__page {
	display: inline-grid;
	place-items: center;
	min-width: 34px;
	height: 34px;
	padding: 0 var(--space-3);
	border-radius: var(--radius-control);
	border: 1px solid var(--border-default);
	background: var(--surface-card);
	color: var(--text-body);
	font: var(--fw-medium) var(--fs-sm)/1 var(--font-numeric);
	text-decoration: none;
}

a.omd-pager__page:hover { border-color: var(--border-strong); color: var(--text-strong); }

.omd-pager__page--on {
	background: var(--anar-600);
	border-color: var(--anar-600);
	color: var(--text-inverse);
}

.omd-pager__gap { padding: 0 var(--space-1); color: var(--text-faint); font: var(--fw-regular) var(--fs-sm)/1 var(--font-core); }

/* The ends of the range keep their place in the row rather than disappearing, so the
   numbers do not shift sideways as you page through them. */
.omd-orders__pages [aria-disabled="true"] { opacity: 0.5; pointer-events: none; }

/* Operator panel -------------------------------------------------------- */

/* Its own shell, not the storefront's: a dark rail of screens down one side and the work
   filling the rest. The page's own header and footer are not loaded here.

   The shell is exactly the height of the window and does not scroll; the two columns
   scroll inside it, independently. Sticky would have done most of this, but the rail
   would still slide under the admin bar and lose its own bottom off the screen: the rail
   is not part of the reading, and reading should not move it.

   The window minus the admin bar, when there is one. WordPress publishes its height as a
   custom property and pushes the document down by the same amount; without the
   subtraction the shell is that much too tall and the whole page gains a scrollbar. */
.omd-panel {
	display: grid;
	grid-template-columns: 236px minmax(0, 1fr);
	/* The bar is a row of the shell rather than the first thing inside the work column.
	   It has to be, for the narrow layout: there the rail lies down between the bar and
	   the work, and a bar nested inside the work cannot have anything above it. */
	grid-template-rows: auto minmax(0, 1fr);
	height: calc(100vh - var(--wp-admin--admin-bar--height, 0px));
	overflow: hidden;
	background: var(--surface-page);
}

.omd-panel__side { grid-column: 1; grid-row: 1 / 3; }
.omd-panel__head { grid-column: 2; grid-row: 1; }
.omd-panel__main { grid-column: 2; grid-row: 2; }

.omd-panel__side {
	background: var(--surface-inverse);
	display: flex;
	flex-direction: column;
	gap: var(--space-7);
	padding: var(--space-6) var(--space-5);
	min-height: 0;
	/* Only when there are more screens than fit. Until then it never moves. */
	overflow-y: auto;
}

.omd-panel__brand { display: flex; flex-direction: column; }
.omd-panel__brand-name { font: var(--fw-bold) var(--fs-body)/1.3 var(--font-core); color: var(--text-inverse); }
.omd-panel__brand-sub { font: var(--fw-regular) var(--fs-caps)/1.4 var(--font-core); color: var(--shab-400); }

.omd-panel__nav { display: flex; flex-direction: column; gap: var(--space-1); }

.omd-panel__link {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-4);
	border: 0;
	border-radius: var(--radius-control);
	background: transparent;
	font: var(--fw-medium) var(--fs-sm)/1 var(--font-core);
	color: var(--shab-200);
	text-decoration: none;
	transition: background var(--dur-1) var(--ease-standard);
}

.omd-panel__link:hover { background: var(--shab-700); color: var(--text-inverse); }
.omd-panel__link--on, .omd-panel__link--on:hover { background: var(--shab-600); color: var(--text-inverse); }

.omd-panel__escape {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: 0;
	border: 0;
	background: transparent;
	font: var(--fw-medium) var(--fs-xs)/1.4 var(--font-core);
	color: var(--shab-300);
	text-decoration: none;
}

.omd-panel__escape:hover { color: var(--text-inverse); }

.omd-panel__foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--space-4); }


/* The one thing that scrolls. Its sticky header sticks to this box rather than to the
   window, which is why the header stays put while the work moves under it. */
.omd-panel__main { min-width: 0; min-height: 0; display: flex; flex-direction: column; overflow-y: auto; }

.omd-panel__head {
	background: var(--sang-000);
	border-bottom: 1px solid var(--border-hairline);
	padding: var(--space-4) clamp(16px, 3vw, 32px);
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
	position: sticky;
	top: 0;
	z-index: var(--z-sticky);
}

/* The screen's name takes the room, which is what pushes everything about the session to
   the far end of the bar. On the heading rather than on the first thing after it: the shop
   shell has a way to wp-admin there and the employer's has nothing, and the end of the bar
   is the end of the bar either way. */
.omd-panel__heading { display: flex; align-items: baseline; gap: var(--space-4); min-width: 0; margin-inline-end: auto; }
.omd-panel__title { font: var(--fw-semibold) var(--fs-title)/1.3 var(--font-core); color: var(--text-strong); margin: 0; }
.omd-panel__today { font: var(--type-sm); color: var(--text-muted); }
.omd-panel__wpadmin { font: var(--type-sm); }

/* Who is signed in. On the bar rather than at the foot of the rail: it is a fact about
   the session, not about the screen, and on a narrow layout the rail is a scrolling strip
   where a name has to be scrolled to before it can be read. */
.omd-panel__me {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	flex: none;
}

.omd-panel__avatar {
	width: 32px;
	height: 32px;
	flex: none;
	border-radius: var(--radius-pill);
	background: var(--shab-800);
	display: grid;
	place-items: center;
	font: var(--fw-semibold) var(--fs-xs)/1 var(--font-core);
	color: var(--text-inverse);
}

.omd-panel__me-body { display: flex; flex-direction: column; min-width: 0; }
.omd-panel__me-name { font: var(--fw-medium) var(--fs-xs)/1.4 var(--font-core); color: var(--text-strong); white-space: nowrap; }
.omd-panel__me-role { font: var(--fw-regular) var(--fs-micro)/1.4 var(--font-core); color: var(--text-muted); white-space: nowrap; }

/* Worded rather than drawn: a door with an arrow through it is a bad guess to get wrong. */
.omd-panel__out {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	flex: none;
	font: var(--fw-medium) var(--fs-xs)/1.4 var(--font-core);
	color: var(--text-muted);
	text-decoration: none;
	white-space: nowrap;
}

.omd-panel__out:hover { color: var(--text-strong); }

/* The containing block for anything inside a screen that positions itself absolutely and
   has no nearer anchor, a visually hidden label most often. Without it such a box is laid
   out against the page and escapes this scroller's clip. */
.omd-panel__body {
	position: relative;
	padding: var(--space-7) clamp(16px, 3vw, 32px) var(--space-11);
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	/*
	 * A measure, the way every other reading column here has one. Left to fill a wide
	 * desk a settings card becomes a short field with two feet of nothing beside it, and
	 * a table row so wide the eye loses which line it is on. Tables that genuinely need
	 * more scroll inside .omd-table__scroll and are unaffected.
	 */
	width: 100%;
	max-width: 1200px;
	margin-inline: auto;
}

.omd-panel__bar { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
/* Sizing only. What the search box looks like is .omd-field__control's job — the same
   pill the storefront's search wears, because it is the same control. */
.omd-panel__find { position: relative; flex: 1; min-width: 200px; max-width: 320px; margin: 0; }

/* A form that is one group of controls inside the bar rather than the bar itself, which is
   what a bar holding two forms, or a form beside a plain link, needs. A form is a block, so
   without this its fields stack down the page instead of sitting in the row. */
.omd-panel__group {
	display: flex;
	/* Bottoms, not middles: some of these rows mix a field that carries a written label
	   above it with a button that does not, and centred they sit at two different heights. */
	align-items: flex-end;
	gap: var(--space-3);
	flex: 1 1 auto;
	min-width: 200px;
	flex-wrap: wrap;
	margin: 0;
}

.omd-panel__bar > .omd-btn { margin-inline-start: auto; }

.omd-panel__empty { font: var(--type-sm); color: var(--text-muted); margin: 0; padding: var(--space-6); }

.omd-panel__back { display: inline-flex; align-items: center; gap: var(--space-2); font: var(--type-sm); text-decoration: none; color: var(--text-muted); }
.omd-panel__back:hover { color: var(--text-strong); }
.omd-panel__h2 { font: var(--type-h2); color: var(--text-strong); margin: 0; }
.omd-panel__lede { font: var(--type-sm); color: var(--text-muted); margin: 0; max-width: 66ch; }

.omd-panel__form { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-panel__grid { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* The grid spaces its own cells. A field written as a paragraph carries the browser's own
   margin as well, which starts it a line lower than the field beside it written as a div,
   and the row stops lining up for a reason nothing in the markup shows. */
.omd-panel__grid > .omd-field { margin: 0; }

/* And the step after it belongs to the grid rather than to whichever field happened to be
   a paragraph: a grid ends where its last row does, and what follows needs the same room
   between it and the fields that the fields have between themselves. */
.omd-panel__grid + * { margin-top: var(--space-5); }
.omd-panel__save { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.omd-panel__view { margin-inline-start: auto; font: var(--type-sm); }

/* Beside a refused save, saying what it is waiting for. */
.omd-panel__save-note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); text-wrap: pretty; }

/*
 * Whose file this is, above a form that is about something else. It reads rather than being
 * a field, so it is a tinted block rather than a bordered one: a border here would look like
 * one more input to tab into.
 */
.omd-panel__strip {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	padding: var(--space-4);
	border-radius: var(--radius-control);
	background: var(--surface-page);
}

.omd-panel__strip-body { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.omd-panel__strip-name { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-core); color: var(--text-strong); }
.omd-panel__strip-note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

/* Three ticks under a label, one per line. They are one word each, and a row of them reads
   as a sentence rather than as three separate answers.

   The card is a block with no gap of its own, so the block carries the step that separates
   it from the grid above and the summary below; without it the label sits on the last row
   of fields and the ticks on the notice. */
.omd-meals {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	margin-block: var(--space-6);
}

.omd-meals .omd-eyebrow { margin: 0; }

/* The SMS screen. A state card that says in one line whether customers can get in at
   all, then the account, then a way to prove it without asking one to try. */
.omd-smsstate__head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-3); }
.omd-smsstate__head h2 { font: var(--type-h4); color: var(--text-strong); margin: 0; }

/* Two choices with a sentence each, so they stand side by side only when both sentences
   still have room to be read. */
.omd-choices {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
	gap: var(--space-5);
}

.omd-panel__grid--top { margin-top: var(--space-5); }

/* Two choices of one word each. The grid above is for choices that carry a sentence, and
   used here it puts a single word at each end of a wide card. */
.omd-choices--inline { display: flex; flex-wrap: wrap; gap: var(--space-6); }

/*
 * The sending method asks for one field, so the other is not there to be filled in wrong.
 * Both stay in the form and keep their values; only the display is dropped. Written as a
 * hide rather than a show so that a browser without `:has()` falls back to both showing,
 * which is merely noisier, not broken.
 */
.omd-card:has(#omd-sms-way-pattern:checked) .omd-smsway--line,
.omd-card:has(#omd-sms-way-line:checked) .omd-smsway--pattern { display: none; }

.omd-smstest { display: flex; align-items: flex-end; gap: var(--space-4); flex-wrap: wrap; }
.omd-smstest .omd-field { margin: 0; }

/* The notices card. Its own line, its own words, and one block per message so the wording,
   what may go in it and what it costs are read together rather than looked up apart. */
.omd-notices:not(:has(#omd-notice-own:checked)) .omd-notices__own { display: none; }

.omd-notices__list { display: flex; flex-direction: column; gap: var(--space-5); }

.omd-notices__row {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	padding: var(--space-5);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-notices__head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
}

.omd-notices__row .omd-field { margin: 0; }
.omd-notices__text { resize: vertical; line-height: 1.85; }

.omd-notices__spec {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	color: var(--text-muted);
}

.omd-notices__cost { font-family: var(--font-numeric); white-space: nowrap; }

/* What the customer actually receives, tokens filled in. Set apart from the box above it
   because a preview that looks like an input is a preview somebody tries to type in. */
.omd-notices__preview {
	margin: 0;
	padding: var(--space-4);
	border-radius: var(--radius-control);
	background: var(--sang-100);
	font: var(--fw-regular) var(--fs-sm)/1.85 var(--font-core);
	color: var(--text-strong);
	text-wrap: pretty;
}

/* The versions list on the updates screen: a name, what it is at, and what that means. */
.omd-versions { display: flex; flex-direction: column; gap: var(--space-3); margin-block: var(--space-4); }

.omd-versions__row {
	display: flex;
	align-items: baseline;
	gap: var(--space-3) var(--space-4);
	flex-wrap: wrap;
	padding-top: var(--space-3);
	border-top: 1px solid var(--border-hairline);
}

.omd-versions__row:first-child { border-top: 0; padding-top: 0; }
.omd-versions__name { font: var(--type-body-strong); color: var(--text-strong); display: flex; align-items: baseline; gap: var(--space-3); }
.omd-versions__kind { font: var(--type-sm); color: var(--text-muted); }
.omd-versions__now { font: var(--fw-semibold) var(--fs-sm)/1.6 var(--font-numeric); color: var(--text-body); }
.omd-versions__state { margin-inline-start: auto; }
.omd-versions__code { display: block; margin-top: var(--space-2); font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-numeric); color: var(--text-body); word-break: break-all; }

/* A table, because this is a list of records with the same fields, which is the one
   shape a table is for. It scrolls inside its own box rather than widening the page. */
.omd-table__scroll { overflow-x: auto; }
.omd-table { width: 100%; min-width: 760px; border-collapse: collapse; }

.omd-table th,
.omd-table td {
	text-align: start;
	padding: var(--space-4) var(--space-5);
	border-top: 1px solid var(--border-hairline);
	font: var(--type-sm);
	color: var(--text-body);
	vertical-align: middle;
}

.omd-table thead th {
	border-top: 0;
	font: var(--fw-semibold) var(--fs-xs)/1.5 var(--font-core);
	color: var(--text-muted);
	white-space: nowrap;
}

/*
 * Stacked tables of one shape have to agree with each other, and a table that sizes itself
 * to its own rows does not: three meals of one menu drew three different grids, with «کد
 * غذا» in a different place each time. The columns that hold a number are given the width a
 * number needs and the name column takes whatever is left, so the three line up.
 *
 * Only while they are tables: under 700px each row becomes a block, and a width there would
 * be a narrow column in the middle of a card.
 */
@media (min-width: 701px) {
	/* Fixed layout, so the widths below are the whole answer: with automatic layout a long
	   dish name in one meal still pulled every column after it along, which is the thing
	   these tables are being asked not to do. The first column takes whatever is left. */
	.omd-table--menu,
	.omd-table--cook,
	.omd-table--cook-by { table-layout: fixed; }

	.omd-table--menu th:nth-child(2),
	.omd-table--menu td:nth-child(2) { width: 8rem; }

	.omd-table--menu th:nth-child(3),
	.omd-table--menu td:nth-child(3) { width: 12rem; }

	.omd-table--cook th:nth-child(2),
	.omd-table--cook td:nth-child(2) { width: 8rem; }

	/* Split by company the dish name still takes what is left, with the company beside it
	   and the count in the same place the summed table puts it. */
	.omd-table--cook-by th:nth-child(2),
	.omd-table--cook-by td:nth-child(2) { width: 16rem; }

	.omd-table--cook-by th:nth-child(3),
	.omd-table--cook-by td:nth-child(3) { width: 8rem; }
}

.omd-table__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-table__name a { color: inherit; text-decoration: none; }
.omd-table__name a:hover { color: var(--text-link-hover); }
.omd-table__money { font: var(--fw-semibold) var(--fs-sm)/1.5 var(--font-numeric); color: var(--text-strong); }

/* A price that is not currently being charged is still the price. Struck through and
   faint says "this, but not right now"; replacing it with a badge threw the number away
   and left nothing to compare when it comes back. */
.omd-table__money--off { color: var(--text-faint); text-decoration: line-through; }
/* A heading that sorts. It is a link, so it inherits the heading's own type rather than
   growing a second one, and the caret is what says which column the order is on: faint on
   the columns that could be, solid on the one that is. */
.omd-table__sort {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	color: inherit;
	font: inherit;
	text-decoration: none;
}

.omd-table__sort:hover { color: var(--text-strong); }
.omd-table__sort:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-1); }
.omd-table__sort > .omd-icon { opacity: 0.35; flex: none; }
.omd-table__sort:hover > .omd-icon { opacity: 0.7; }
.omd-table__sort--on { color: var(--text-strong); }
.omd-table__sort--on > .omd-icon { opacity: 1; }

/*
 * The buttons at the end of a row, and the end is where they sit.
 *
 * A table cell, and it has to be: `display: flex` here takes the cell out of the table's
 * column model, and then the row shrink-wraps its content and the whole table piles up
 * against the start edge with the slack left over at the far end. Measured on the staff
 * list: five columns adding up to 673px inside a table a thousand wide.
 *
 * So the cell stays a cell, asks for as little width as the table will give it, and its
 * contents line up inline against the far edge. The slack goes where it is useful, to the
 * columns that carry reading matter.
 */
.omd-table__acts {
	width: 1%;
	white-space: nowrap;
	text-align: end;
}

.omd-table__acts > * { vertical-align: middle; }
.omd-table__acts > * + * { margin-inline-start: var(--space-2); }
.omd-table__acts form { margin: 0; display: inline-flex; }
/* A second line under the cell it belongs to, never a tail running on from it. */
.omd-table__note { display: block; font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-core); color: var(--text-faint); }

/* Availability lives in its own row at the foot of the identity card, ruled off from the
   fields above it: what is true of the recipe on the site, not another thing to type. */
.omd-panel__sale {
	margin-top: var(--space-6);
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
}

/* A block whose answer is a field rather than a switch: wide enough to read a product's
   name in, and still willing to drop below the words on a narrow screen. */
.omd-panel__sale-pick { flex: 1 1 240px; max-width: 340px; min-width: 0; }

.omd-panel__sale-title { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); }
.omd-panel__sale-note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

/*
 * An ingredient the shop stocks: the same line, made a way to buy it.
 *
 * The name keeps the colour every other ingredient has and carries a small mark instead.
 * Underlining the ones that link would make a list where half the names are decorated
 * look like a list with mistakes in it; the mark says «this one is in the shop» without
 * changing how the list reads.
 */
.omd-rows__name--link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-width: 0;
	color: var(--text-body);
	text-decoration: none;
}

.omd-rows__name--link:hover { color: var(--text-link); }
.omd-rows__out { display: inline-flex; flex: none; color: var(--text-link); }

/* Repeating rows in a panel form: a line of fields with a control to drop the line. */
.omd-rowset { display: flex; flex-direction: column; gap: var(--space-3); }

/* Every field labelled, on every row. Repeating «ماده / مقدار / واحد» down the card is
   more words than a header row would be, but a header row is the first thing a narrow
   screen throws away — and a lone number box with no name is a guess. */
.omd-rowset__row {
	display: grid;
	grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.7fr) minmax(0, 0.9fr) auto;
	gap: var(--space-3);
	align-items: end;
}

.omd-rowset__field { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.omd-rowset__remove { align-self: end; }
.omd-rowset__add { margin: var(--space-4) 0 0; }

/* Whatever follows the add button belongs to the card rather than to the rows, and needs
   the room between them that says so. */
.omd-rowset__add + * { margin-top: var(--space-5); }

/* An ingredient, plus the shop's own version of it. The link is the widest of the four
   because a product's name is a sentence and the other three are a word each. */
.omd-rowset--recipe .omd-rowset__row {
	grid-template-columns: minmax(0, 1.6fr) minmax(0, 0.7fr) minmax(0, 0.8fr) minmax(0, 1.2fr) auto;
}

/* The picker's hint is its own line under the box, which in a row of fields aligned at
   the bottom would drag the other three down to meet it. What it would say is already in
   the box: the product's name, or nothing. */
.omd-rowset .omd-search .omd-field__hint { display: none; }

/* An hour, an hour and a factor: two short numbers and one that is read as a rate. */
.omd-rowset--bands .omd-rowset__row {
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr) auto;
}

/* Day, dish, count: the dish name is the long one and the count is two digits. */
.omd-rowset--plan .omd-rowset__row {
	grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.8fr) minmax(0, 0.6fr) auto;
}

/* ------------------------------------------------------------- Warehouses */

/* A switch that turns the rest of its card on and off. Dimmed rather than hidden, so what
   would apply is still readable; app.js marks it inert so the keyboard agrees with the eye,
   and the :has() rule below says the same thing on a page whose script never arrived. */
.omd-gated { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-gated__body { display: flex; flex-direction: column; gap: var(--space-5); transition: opacity var(--dur-2) var(--ease-standard); }
.omd-gated--off > .omd-gated__body,
.omd-gated:has([data-omd-gate]:not(:checked)) > .omd-gated__body { opacity: 0.45; pointer-events: none; }

/* One vehicle, and whether this warehouse has it. A row rather than a checkbox list: the
   capacity beside the name is what an operator decides on, and it needs room. */
.omd-vans { display: flex; flex-direction: column; gap: var(--space-3); }

/* The same rows on the product screen, saying which depots stock the thing. */
.omd-supply { display: flex; flex-direction: column; gap: var(--space-3); }

.omd-supply__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
}

.omd-supply__head .omd-eyebrow { margin: 0; }
.omd-supply__none { margin: 0; font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--state-danger); }

.omd-van {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	background: var(--sang-100);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-van--on { border-color: var(--border-default); background: var(--surface-card); }
.omd-van__name { display: flex; align-items: center; gap: var(--space-3); min-width: 0; font: var(--type-body-strong); color: var(--text-strong); }
.omd-van__name > .omd-icon { flex: none; color: var(--text-muted); }
.omd-van__spec { margin-inline-start: auto; font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); }
.omd-van__switch { flex: none; }

/* The zone table: a province, a city, and the control that drops the row. */
.omd-zone { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-zone__table { display: flex; flex-direction: column; }

.omd-zone__head,
.omd-zone__row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 32px;
	gap: var(--space-4);
	align-items: center;
}

.omd-zone__head {
	font: var(--type-caps);
	letter-spacing: var(--ls-caps);
	color: var(--text-muted);
	padding-bottom: var(--space-3);
	border-bottom: 1px solid var(--border-hairline);
}

.omd-zone__row { padding: var(--space-4) 0; border-bottom: 1px solid var(--border-hairline); }
.omd-zone__cell { min-width: 0; font: var(--type-sm); color: var(--text-muted); }
.omd-zone__cell--strong { font: var(--type-body-strong); color: var(--text-strong); }
.omd-zone__x { justify-self: end; }
.omd-zone__empty { margin: 0; font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--state-danger); }
.omd-zone__error { margin: 0; font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--state-danger); }

/* Province, city and the button, on one row while there is room for three. */
.omd-zone__add {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
	gap: var(--space-3);
	align-items: end;
}

.omd-zone__field { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.omd-zone__field .omd-field { margin: 0; }

/* Three controls on one line, all the same height.
   A select sizes itself from min-height plus its padding and lands a couple of pixels
   above the others, which is invisible on its own and obvious in a row. Stated once here
   rather than left to two different rules to agree by accident; the pickers carry a label
   and the button does not, which is why the row aligns on «end» rather than stretching. */
.omd-zone__add .omd-input,
.omd-zone__go .omd-btn { height: var(--control-h); min-height: var(--control-h); }
.omd-zone__go { margin: 0; }

/* The delete sits at the far end, away from the save it must not be mistaken for. */
.omd-panel__save--split > form:first-child { margin-inline-end: auto; }

/* ----------------------------------------------------- Finding a place */

/* The search that opens above a map. A control rather than a button: it stands where an
   input would and says what is in it, which is what the design draws. */
.omd-placefind { display: block; margin-bottom: var(--space-3); }

.omd-placefind__trigger {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	width: 100%;
	height: 40px;
	padding: 0 var(--space-4);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	background: var(--surface-card);
	color: var(--text-muted);
	font: var(--type-sm);
	text-align: start;
	cursor: text;
	transition: var(--transition-control);
}

.omd-placefind__trigger:hover { border-color: var(--shab-600); }
.omd-placefind__trigger:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.omd-placefind__trigger > .omd-icon { flex: none; }
.omd-placefind__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.omd-placefind__label--set { color: var(--text-strong); }

.omd-placefind__list {
	display: flex;
	flex-direction: column;
	margin: var(--space-4) 0 0;
	padding: 0;
	list-style: none;
	max-height: 324px;
	overflow: auto;
}

.omd-placefind__option {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-3);
	border-bottom: 1px solid var(--border-hairline);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-placefind__option:hover { background: var(--sang-100); }
.omd-placefind__option:focus-visible { outline: none; box-shadow: var(--ring-focus); border-radius: var(--radius-control); }
.omd-placefind__body { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.omd-placefind__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-placefind__where { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-placefind__note { margin: var(--space-5) 0 0; font: var(--type-sm); color: var(--text-muted); }

/*
 * A sheet on a phone, a panel on a desktop. The dialog element is the same either way;
 * only where it sits and which corners are round change.
 *
 * Every dialog, not a modifier one of them happened to carry. A phone is where a centred
 * box with a margin on all four sides reads worst, and a dialog added next year should not
 * have to remember to ask for the behaviour every other one has.
 */
@media (max-width: 640px) {
	.omd-dialog { margin: auto auto 0; width: 100%; max-width: 100%; }

	.omd-dialog .omd-dialog__panel {
		width: 100%;
		max-height: 86vh;
		border-radius: var(--radius-dialog) var(--radius-dialog) 0 0;
		padding: var(--space-6);
	}
}

/* Sides of a box, and the weight limit beside them. One measurement in several parts, so
   they stay on one line until there is genuinely no room, rather than breaking at the
   panel grid's usual width. */
.omd-panel__grid--three { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
.omd-panel__grid--four { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

/* A second job inside one card, ruled off from the first. Two halves of one thing — the
   key, and proving the key works — belong under one heading; a card each would put a
   title between them and read as two subjects. */
.omd-card__part {
	margin-top: var(--space-6);
	padding-top: var(--space-6);
	border-top: 1px solid var(--border-hairline);
}

/* A save that belongs to one card. Divided off, so it reads as the end of this section
   rather than as one more control inside it, and set at the far end where the primary
   save at the foot of the page also sits. */
.omd-card__save {
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-top: var(--space-5);
	padding-top: var(--space-4);
	border-top: 1px solid var(--border-hairline);
}

/* A line of explanation in that row belongs to the card, not to the button: it takes the
   slack and reads from the start, rather than crowding the thing it is explaining. */
.omd-card__save > .omd-panel__lede { margin-inline-end: auto; }

/* A control that is the subject of the row rather than a note about it: the file picker a
   button acts on stands beside that button and takes the slack, the way the sentence does
   when the row is only being explained. */
.omd-card__save > .omd-card__save-fill { flex: 1 1 220px; margin-inline-end: auto; }

/* A card that is only this row has nothing above to be ruled off from, and the rule then
   draws a line across the top of nothing with the row squeezed under it. */
.omd-card > .omd-card__save:only-child {
	margin-top: 0;
	padding-top: 0;
	border-top: 0;
}

/*
 * The worked example: a dialog on a desk, a bottom sheet on a phone.
 *
 * A checkbox rather than <dialog>, because this one has to survive a round trip. Working
 * out a price is a read, so the form inside is a GET and the answer arrives as a fresh
 * page; a dialog opened by showModal() would be closed again by that very reload. The
 * checkbox comes back ticked from the server and the sheet reopens with the answer in it.
 *
 * Hidden on the unchecked state rather than shown on the checked one, and by a plain
 * sibling combinator: where none of this applies the panel is simply on the page, which
 * is noisier than intended and still usable.
 */
.omd-probe { display: flex; margin-bottom: var(--space-6); }

.omd-probe__switch:not(:checked) ~ .omd-probe__scrim,
.omd-probe__switch:not(:checked) ~ .omd-probe__panel { display: none; }

.omd-probe__scrim {
	position: fixed;
	inset: 0;
	z-index: var(--z-dialog);
	background: var(--scrim);
	cursor: pointer;
}

.omd-probe__panel {
	position: fixed;
	/* Inset on every side with auto margins is how a fixed box centres itself without
	   knowing which way the page runs. */
	inset: 0;
	margin: auto;
	z-index: calc(var(--z-dialog) + 1);
	width: min(620px, calc(100vw - var(--space-8)));
	height: fit-content;
	max-height: 88vh;
	overflow: auto;
	overscroll-behavior: contain;
	background: var(--surface-card);
	border-radius: var(--radius-dialog);
	box-shadow: var(--shadow-4);
	padding: var(--space-7);
}

/* The switch is visually hidden, so its focus ring has to be relayed to the button. */
.omd-probe__switch:focus-visible + .omd-probe__open { box-shadow: var(--ring-focus); }

/* Four short numbers, then the button on a line of its own. A grid rather than a wrapping
   row, so the fields sit in even columns instead of leaving the button stranded beside
   whichever one happened to be last. */
.omd-probe__ask {
	display: grid;
	/* Two by two rather than auto-fit: four columns squeeze these labels onto two lines
	   each, and auto-fit at a readable width leaves the fourth field on a row by itself. */
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--space-4);
	align-items: end;
	margin-bottom: var(--space-6);
}

.omd-probe__ask .omd-field { margin: 0; max-width: none; }
.omd-probe__ask > .omd-btn { grid-column: 1 / -1; justify-self: start; }

@media (max-width: 900px) {
	/* Up from the bottom edge, the way the catalogue's filters arrive. The side is where
	   the nav drawer comes from, and two panels from one edge read as the same panel. */
	.omd-probe__panel {
		inset: auto 0 0 0;
		margin: 0;
		width: auto;
		max-height: 85vh;
		border-radius: var(--radius-card) var(--radius-card) 0 0;
		padding: var(--space-6);
	}

	/* A sheet the page scrolls behind reads as broken, and `:has()` can say so without JS. */
	body:has(.omd-probe__switch:checked) { overflow: hidden; }
}

/* The delivery calculation on the checkout, which only the shop's own people see. Marked
   off so nobody mistakes it for something a customer is looking at. */
.omd-estimate { border: 1px dashed var(--border-default); box-shadow: none; }
.omd-estimate .omd-totals__row { font: var(--type-sm); }

/* Which vehicle takes the basket, one row each, in the order they are tried. The chosen
   one is outlined rather than only badged: the eye finds the box before it finds a word. */
.omd-picks { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); }

.omd-picks__row {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
	padding: var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	background: var(--surface-card);
}

.omd-picks__row--on { border-color: var(--surface-brand); background: var(--surface-brand-soft); }
.omd-picks__name { font: var(--type-label); color: var(--text-strong); min-width: 6rem; }
.omd-picks__detail { font: var(--type-sm); color: var(--text-muted); flex: 1; min-width: 0; }

/* The banners editor. One row per banner: its picture, where it goes, and where it sits. */
.omd-slides { display: flex; flex-direction: column; gap: var(--space-4); }

.omd-slide {
	display: grid;
	grid-template-columns: 200px minmax(0, 1fr) auto;
	gap: var(--space-4);
	align-items: start;
	padding: var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	background: var(--surface-sunken);
}

.omd-slide__figure { display: flex; flex-direction: column; gap: var(--space-3); }

/* The link runs long and the time is two digits, so they share a line rather than each
   taking one. */
.omd-slide__fields {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 150px;
	gap: var(--space-4);
	align-items: start;
}

.omd-slide__thumb {
	aspect-ratio: 1870 / 840;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
	border: 1px dashed var(--border-default);
	border-radius: var(--radius-thumb);
	background: var(--surface-card);
	color: var(--text-faint);
}

/* Dashed while it is a space for a picture; solid once it holds one. */
.omd-slide__thumb:has(img) { border-style: solid; border-color: var(--border-hairline); }
.omd-slide__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.omd-slide__tools { display: flex; gap: var(--space-2); }

/* Categories -------------------------------------------------------------- */

/* A form with a column of things to type and a column of things to know: what is filed
   here, what it will look like, and the button that commits it. */
.omd-panel__split { display: grid; gap: var(--space-5); grid-template-columns: minmax(0, 1fr); align-items: start; }
.omd-panel__split-main,
.omd-panel__split-side { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }

/* Two columns only once there is room for two. The work column is some 300px narrower
   than the window, so a breakpoint set on the window has to allow for that: below this
   the side column was taking a third of what was left and the form beside it had to wrap
   every field it had. Measured: at this width the main column still fits two fields
   across the data card, rail included, which is the narrowest it is worth splitting at. */
@media (min-width: 1240px) {
	.omd-panel__split { grid-template-columns: minmax(0, 1fr) 300px; }

	/* What is true of this thing and the button that commits it, kept in view down a long
	   form. The panel's work column is the scroller it sticks inside. */
	.omd-panel__split-side { position: sticky; top: var(--space-6); }
}

.omd-panel__count { font: var(--type-sm); color: var(--text-muted); }
.omd-panel__foot-note {
	margin: 0;
	padding: var(--space-5) var(--space-6) var(--space-6);
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	color: var(--text-muted);
}

.omd-table__slug { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); }

/* The tree, flattened. Depth is an indent on the link rather than a nest of tables, so a
   child reads as filed under the row above it without the row losing its columns. */
.omd-cat-row {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3);
	color: inherit;
	text-decoration: none;
	margin-inline-start: calc(var(--depth, 0) * var(--space-6));
}

.omd-cat-row:hover { color: var(--text-link-hover); }

.omd-cat-row__mark {
	width: 28px;
	height: 28px;
	flex: none;
	display: grid;
	place-items: center;
	overflow: hidden;
	border-radius: var(--radius-pill);
	background: var(--sang-100);
	color: var(--text-body);
}

.omd-cat-row__mark img { width: 100%; height: 100%; object-fit: cover; }

.omd-catmedia { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-catmedia__modes { display: flex; gap: var(--space-6); flex-wrap: wrap; }
.omd-catmedia__part { border-top: 1px solid var(--border-hairline); padding-top: var(--space-5); }

/* Which half shows is a checked radio away, with no script in it. The hidden half is
   still posted, so switching to the icon for a season keeps the photograph. */
.omd-catmedia:has([name="category_media"][value="image"]:checked) .omd-catmedia__part--icon,
.omd-catmedia:has([name="category_media"][value="icon"]:checked) .omd-catmedia__part--image { display: none; }

.omd-catmedia__part--image { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.omd-catmedia__pick { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }

.omd-catmedia__thumb {
	width: 72px;
	height: 72px;
	flex: none;
	display: grid;
	place-items: center;
	overflow: hidden;
	border-radius: var(--radius-control);
	border: 1px dashed var(--border-default);
	background: var(--sang-100);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}

/* Dashed while it is a space for a picture; solid once it holds one. */
.omd-catmedia__thumb:has(img) { border-style: solid; border-color: var(--border-hairline); }
.omd-catmedia__thumb img { width: 100%; height: 100%; object-fit: cover; }

/* The recipe editor's picture. A frame the shape the recipe is actually drawn in, so what
   an operator lines up here is what the listing and the recipe page will show. */
.omd-recmedia { display: flex; align-items: flex-start; gap: var(--space-5); flex-wrap: wrap; }
.omd-recmedia__side { display: flex; flex-direction: column; gap: var(--space-3); }
.omd-recmedia__acts { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* An .omd-cover, so the shape, the contained picture and the blurred plate behind it are
   the storefront's and not a second opinion about them. What is left here is the frame. */
.omd-recmedia__thumb {
	--cover-ratio: 1;
	width: 224px;
	flex: none;
	border-radius: var(--radius-control);
	border: 1px dashed var(--border-default);
	background: var(--sang-100);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}

/* Dashed while it is a space for a picture; solid once it holds one. */
.omd-recmedia__thumb:has(img) { border-style: solid; border-color: var(--border-hairline); background: var(--sang-200); }

.omd-icontiles { display: flex; gap: var(--space-3); flex-wrap: wrap; margin: var(--space-3) 0; }
.omd-icontile { cursor: pointer; }
.omd-icontile > input { position: absolute; opacity: 0; width: 0; height: 0; }

.omd-icontile__box {
	width: 44px;
	height: 44px;
	display: grid;
	place-items: center;
	border-radius: var(--radius-control);
	border: 1px solid var(--sang-400);
	background: var(--surface-card);
	color: var(--text-body);
	transition: var(--transition-control);
}

/* Two pixels of brand where there was one of hairline. The box keeps its size because the
   border grows inward, which is what box-sizing already does for it. */
.omd-icontile > input:checked + .omd-icontile__box { border: 2px solid var(--shab-600); color: var(--text-strong); }
.omd-icontile > input:focus-visible + .omd-icontile__box { box-shadow: var(--ring-focus); }
.omd-icontile:hover .omd-icontile__box { background: var(--sang-100); }

.omd-catpreview {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	margin-top: var(--space-3);
	padding: var(--space-5);
	background: var(--sang-100);
	border-radius: var(--radius-control);
}

.omd-catpreview__disc {
	width: 44px;
	height: 44px;
	flex: none;
	display: grid;
	place-items: center;
	overflow: hidden;
	border-radius: var(--radius-pill);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	color: var(--text-body);
}

.omd-catpreview__disc img { width: 100%; height: 100%; object-fit: cover; }
.omd-catpreview__body { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.omd-catpreview__label { font: var(--type-body-strong); color: var(--text-strong); }
.omd-catpreview__url { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); overflow-wrap: anywhere; }

/* Products ---------------------------------------------------------------- */

/* The listing's bar carries a search, two filters and an apply button, then the two
   buttons that leave the list. The plain bar pushes a lone button to the far side, which
   is wrong for a row that ends in a pair of them: --middle cancels that and the group
   below takes the far edge itself. */
.omd-panel__pick { flex: none; min-width: 160px; margin: 0; }
.omd-panel__apply { flex: none; }
.omd-panel__bar-end { margin-inline-start: auto; display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* A product carries four unrelated sets of settings. They are grouped behind a rail of
   radio buttons rather than stacked, so the two anybody touches daily are not at the
   bottom of a very long page — and because they are radios and not a script, every field
   stays in the form whichever rail is showing. */
.omd-pdata { display: grid; grid-template-columns: minmax(0, 1fr); }

.omd-pdata__rail {
	display: flex;
	gap: var(--space-2);
	overflow-x: auto;
	padding: var(--space-4) var(--space-5);
	background: var(--sang-100);
	border-bottom: 1px solid var(--border-hairline);
	scrollbar-width: none;
}

.omd-pdata__rail::-webkit-scrollbar { height: 0; }
.omd-pdata__tab { flex: none; cursor: pointer; }
.omd-pdata__tab > input { position: absolute; opacity: 0; width: 0; height: 0; }

.omd-pdata__tab-box {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	white-space: nowrap;
	padding: var(--space-4);
	border-radius: var(--radius-control);
	font: var(--type-sm);
	color: var(--text-muted);
	transition: var(--transition-control);
}

.omd-pdata__tab > input:checked + .omd-pdata__tab-box {
	background: var(--surface-card);
	color: var(--text-strong);
	box-shadow: var(--shadow-1);
}

.omd-pdata__tab > input:focus-visible + .omd-pdata__tab-box { box-shadow: var(--ring-focus); }
.omd-pdata__tab:hover .omd-pdata__tab-box { color: var(--text-strong); }

.omd-pdata__body { padding: var(--space-6); min-width: 0; }
.omd-pdata__part { display: flex; flex-direction: column; gap: var(--space-5); }

/* Hide the part whose own radio is not the checked one. One rule per part, and still a
   hide rather than a "hide all, then show the checked one", so a browser without :has()
   shows every part stacked, which is a long form rather than a broken one. */
.omd-pdata:has([name="product_view"]:checked:not([value="general"])) .omd-pdata__part--general,
.omd-pdata:has([name="product_view"]:checked:not([value="stock"])) .omd-pdata__part--stock,
.omd-pdata:has([name="product_view"]:checked:not([value="ship"])) .omd-pdata__part--ship,
.omd-pdata:has([name="product_view"]:checked:not([value="comments"])) .omd-pdata__part--comments,
.omd-pdata:has([name="product_view"]:checked:not([value="time"])) .omd-pdata__part--time,
.omd-pdata:has([name="product_view"]:checked:not([value="tiers"])) .omd-pdata__part--tiers,
.omd-pdata:has([name="product_view"]:checked:not([value="specs"])) .omd-pdata__part--specs { display: none; }

/* The rail stands up as soon as the card is wider than a phone. Not tied to the split
   above it: while that is stacked the card has the whole work column, which is wider than
   it gets once the side column is beside it. */
@media (min-width: 620px) {
	.omd-pdata { grid-template-columns: 196px minmax(0, 1fr); }

	.omd-pdata__rail {
		flex-direction: column;
		gap: var(--space-1);
		overflow-x: visible;
		padding: var(--space-5);
		border-bottom: 0;
		border-inline-end: 1px solid var(--border-hairline);
	}
}

/* Counting stock and not counting it are two different screens. The one that does not
   apply is hidden rather than greyed out: a number the shop works out for itself is not
   a field with a value you may not change, it is not a field. */
.omd-pstock { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-pstock__uncounted { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }

/* The switch row was written to close a card, so it carries the rule that separates it
   from the fields above. On a tab it is the first thing there and has nothing to divide. */
.omd-pdata__part > .omd-panel__sale:first-child {
	margin-top: 0;
	padding-top: 0;
	border-top: 0;
}

.omd-pdata__part--stock:has([name="product_manage_stock"]:checked) .omd-pstock__uncounted,
.omd-pdata__part--stock:not(:has([name="product_manage_stock"]:checked)) .omd-pstock__counted { display: none; }

/* The day count and what it means, under the two numbers it overrules. */
.omd-pship__day { display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-start; }

/* The rungs belong to one of the two pricing models. Hidden rather than greyed out under
   the other: they are not fields you may not fill in, they are fields that do not apply.
   Still in the form, so choosing the ladder again finds them as they were left. */

/*
 * The custom rules only mean anything under the custom mode, so they are hidden the same
 * way every other conditional block on this screen is: as a rule that hides, never one
 * that shows. A browser without `:has()` then shows the fields rather than losing them.
 */
.omd-pdata__part--time:not(:has([name="product_delivery_mode"][value="custom"]:checked)) .omd-ptime { display: none; }

.omd-ptime { display: flex; flex-direction: column; gap: var(--space-2); }

/* Tick chips. Not `.omd-chips`, which is the storefront's horizontal category rail: this
   one wraps, because a week and a day of hours do not belong on a scrollbar. */
.omd-ticks { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-5); }
.omd-tick { cursor: pointer; }
.omd-tick > input { position: absolute; opacity: 0; width: 0; height: 0; }

.omd-tick__box {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	border: 1px solid var(--border-default);
	background: var(--surface-card);
	color: var(--text-body);
	border-radius: var(--radius-pill);
	padding: var(--space-2) var(--space-4);
	font: var(--fw-medium) var(--fs-sm)/1.4 var(--font-core);
	transition: var(--transition-control);
}

.omd-tick:hover .omd-tick__box { border-color: var(--border-strong); }
.omd-tick > input:focus-visible + .omd-tick__box { box-shadow: var(--ring-focus); }

.omd-tick > input:checked + .omd-tick__box {
	border-color: var(--anar-600);
	background: var(--anar-100);
	color: var(--anar-700);
}

/* A span the shop no longer cuts a slot at. Dashed, because it is still in force and
   still ticked: it is out of the schedule, not out of the rule. */
.omd-tick--gone .omd-tick__box { border-style: dashed; }
.omd-tick__note { font: var(--fw-regular) var(--fs-xs)/1 var(--font-core); color: var(--text-muted); }

.omd-input--code { font-family: var(--font-core); direction: ltr; text-align: left; }

/* A short list of what a switch actually does, above the switch. Not a notice: a notice
   is about a state, and these are true whether the thing is on or off. */
.omd-facts { list-style: none; margin: 0 0 var(--space-5); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }

.omd-facts > li {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	font: var(--type-sm);
	color: var(--text-body);
	text-wrap: pretty;
}

.omd-facts > li > .omd-icon { flex: none; color: var(--text-muted); margin-top: 2px; }


/* The read-back of what the hours settings produce. A row per weekday, so a day that
   generates nothing is a gap in a list rather than an absence nobody notices. */
.omd-hours { display: flex; flex-direction: column; gap: var(--space-2); }

.omd-hours__row {
	display: grid;
	grid-template-columns: 7rem minmax(0, 1fr);
	gap: var(--space-4);
	align-items: baseline;
	padding: var(--space-3) 0;
	border-bottom: 1px solid var(--border-hairline);
}

.omd-hours__row:last-child { border-bottom: 0; }
.omd-hours__day { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); }
.omd-hours__slots { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.omd-hours__slot {
	font: var(--fw-regular) var(--fs-xs)/1.4 var(--font-numeric);
	color: var(--text-body);
	background: var(--surface-sunken);
	border-radius: var(--radius-pill);
	padding: var(--space-1) var(--space-3);
}

@media (max-width: 560px) {
	.omd-hours__row { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
}


/* Notifications. The two cards at the top answer different questions, so they sit side by
   side rather than stacked: where the messages go, and when they are allowed to arrive. */
.omd-notify__top {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
	gap: var(--space-6);
	align-items: start;
	margin-bottom: var(--space-6);
}

/* A grid rather than columns. Each card is a form now, and multicol can split one across
   a column break: a form whose save button lands in the next column is a form nobody
   presses. align-items:start keeps a short card short instead of stretching it to its
   neighbour. */
.omd-notify__groups {
	display: grid;
	gap: var(--space-6);
	grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
	align-items: start;
}

/* «ذخیره نشده», held in the layout whether or not it is showing, so a card does not
   twitch the moment somebody touches a switch. */
.omd-ncard__mark { visibility: hidden; }
.omd-ncard.is-dirty .omd-ncard__mark { visibility: visible; }

.omd-notify__summary {
	font: var(--type-sm);
	color: var(--text-muted);
	border-top: 1px solid var(--border-hairline);
	padding-top: var(--space-4);
	margin: var(--space-5) 0 0;
}

/* The buttons under a card's content, side by side. Each is its own form, because each
   posts a different command, and a form is a block: without this they stack. */
.omd-card__acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); margin-top: var(--space-5); }

.omd-bale { display: flex; align-items: flex-start; gap: var(--space-4); margin-top: var(--space-4); }
.omd-bale__title { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.omd-bale__mark {
	width: 44px;
	height: 44px;
	flex: none;
	border-radius: var(--radius-pill);
	background: var(--surface-sunken);
	display: grid;
	place-items: center;
	color: var(--text-body);
}

.omd-bale__who { min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.omd-bale__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-bale__meta { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }
.omd-bale__meta [dir="ltr"] { unicode-bidi: isolate; display: inline-block; }

/* The connect steps. Numbered because the order matters: the code is useless until the
   conversation with the bot exists.
   Named for the card rather than for what it is, because .omd-steps is already the
   checkout's «مرحله ۱ / ۲» indicator, which centres its items — this one inherited that
   and sat indented from the card's edge until it got a name of its own. */
.omd-bale__steps { list-style: none; counter-reset: omd-step; margin: var(--space-4) 0; padding: 0; display: flex; flex-direction: column; align-items: stretch; gap: var(--space-4); }

.omd-bale__steps > li {
	counter-increment: omd-step;
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	font: var(--type-sm);
	color: var(--text-body);
}

.omd-bale__steps > li::before {
	content: counter(omd-step);
	width: 22px;
	height: 22px;
	flex: none;
	border-radius: var(--radius-pill);
	background: var(--surface-sunken);
	display: grid;
	place-items: center;
	font: var(--fw-semibold) var(--fs-xs)/1 var(--font-numeric);
	color: var(--text-strong);
}

.omd-code {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	flex-wrap: wrap;
	background: var(--surface-sunken);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	padding: var(--space-3) var(--space-4);
	margin-bottom: var(--space-3);
}

.omd-code__value {
	font: var(--fw-semibold) var(--fs-body)/1.4 var(--font-numeric);
	letter-spacing: 0.06em;
	color: var(--text-strong);
	unicode-bidi: isolate;
}

.omd-code__acts { margin-inline-start: auto; display: flex; align-items: center; gap: var(--space-2); }

.omd-quiet { border-top: 1px solid var(--border-hairline); padding-top: var(--space-5); margin-top: var(--space-5); }
.omd-quiet__range { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }

/* One event: what it is on one side, whether you want it on the other.
   Never wrapped. These cards live in narrow columns, and a row that lets its controls
   drop underneath turns a scannable list of switches into a stack of paragraphs with
   switches buried in it — the label is what gives way instead, because a label reading
   over two lines still reads. */
.omd-nrow {
	display: flex;
	align-items: flex-start;
	gap: var(--space-4);
	padding: var(--space-4) 0;
	border-bottom: 1px solid var(--border-hairline);
}

/*
 * The line above the save button belongs to what is above it, not to the button.
 *
 * Every row keeps its own bottom border, the last one included, and the button's block
 * draws none. Owned that way round, the padding that keeps the last line of text off the
 * hairline lives inside the same box as the hairline, so the two cannot drift apart, and
 * there is no rule that has to know which row happens to be last.
 *
 * The schedule card has no rows, so its final block carries the line on the same terms.
 */
.omd-ncard .omd-card__save { border-top: 0; margin-top: 0; padding-top: var(--space-4); }
.omd-ncard .omd-quiet { border-bottom: 1px solid var(--border-hairline); padding-bottom: var(--space-4); }
.omd-nrow__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.omd-nrow__label { font: var(--type-body-strong); color: var(--text-strong); text-wrap: pretty; }
.omd-nrow__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); text-wrap: pretty; }

/* Fixed, so every switch in a card lines up down one edge whatever its label runs to. */
.omd-nrow__side { flex: 0 0 auto; width: 120px; display: flex; flex-direction: column; gap: var(--space-3); align-items: flex-end; }
/* The column is 120px and «خلاصه پایان روز» is the longest thing that goes in it, so the
   type and the padding are cut to fit rather than the words. A select that truncates its
   own label is a control that hides which of three answers is selected. */
.omd-nrow__mode { width: 100%; }
.omd-nrow__mode > select { font-size: var(--fs-xs); padding-inline: var(--space-3) var(--space-7); }
.omd-nrow__mode .omd-select__chevron { inset-inline-end: var(--space-3); }
.omd-nrow__fixed { font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

@media (max-width: 520px) {
	.omd-nrow__side { width: 104px; }
}

/* Same trick for the quiet window: no point offering an hour range that is switched off. */
.omd-quiet:has([name="notify_quiet"]:not(:checked)) .omd-quiet__range { display: none; }


.omd-pdata__part--tiers:not(:has([name="product_pricing"][value="tiers"]:checked)) .omd-ladder { display: none; }

.omd-switch--block { display: flex; }

/*
 * A switch with the sentence that explains it, and a stack of them.
 *
 * Grouped rather than spaced by hand, because the spacing is the grouping: a hint sitting
 * as far from its own switch as from the next one explains whichever control the eye
 * happens to land on. Tight to the switch above it, loose from the switch below.
 */
.omd-toggles { display: flex; flex-direction: column; gap: var(--space-6); margin-top: var(--space-5); }
.omd-toggle { display: flex; flex-direction: column; gap: var(--space-2); align-items: flex-start; }

/* The same measure every other explanation on the panel gets. A caveat run across the
   whole width of a wide card is a line the eye loses its place in. */
.omd-toggle .omd-field__hint { max-width: 66ch; line-height: 1.7; }

/* Shelves a product can sit on, indented the way the categories list is. Boxes rather
   than one choice: a product belongs on as many as it belongs on. */
.omd-shelves {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	max-height: 240px;
	overflow-y: auto;
	padding: var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-shelves__row { padding-inline-start: calc(var(--depth, 0) * var(--space-5)); }

/* Two numbers and a way to drop the rung. The ingredient rowset's four columns next door
   are tuned for a different row and leave the price the narrowest field on this one. */
.omd-pdata__part--tiers .omd-rowset__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto; }

/* A title and what it says. The value is the longer of the two on nearly every row. */
.omd-pdata__part--specs .omd-rowset__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr) auto; }

/* The two controls at the end of a row are one press each and belong side by side. They
   are inline rather than wrapped in a flex row, so `white-space: nowrap` on the cell is
   what keeps them on one line however squeezed the column is. */

/* The pictures, framed the way the catalogue frames them: square, so what is judged here
   is what the shop will show.

   The cap is on this, not on the card. The card is a card and fills its column like every
   other one; what must not run away is the square inside it, which stacked would become a
   picture of the product the size of the product. */
.omd-pshots { display: flex; flex-direction: column; gap: var(--space-4); max-width: 260px; }
.omd-pshots .omd-field__hint { margin: 0; }

.omd-pshots__stage {
	position: relative;
	width: 100%;
	aspect-ratio: 1;
	display: grid;
	place-items: center;
	overflow: hidden;
	border: 1px dashed var(--sang-400);
	border-radius: var(--radius-control);
	background: var(--sang-100);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}

.omd-pshots__stage:has(img) { border-style: solid; border-color: var(--border-hairline); background: var(--sang-000); }

/* Contained, not cropped. This is the frame somebody is judging a photograph in, and a
   picture cropped to fit hides the very edges they are checking. */
.omd-pshots__stage img { width: 100%; height: 100%; object-fit: contain; }

.omd-pshots__badge {
	position: absolute;
	top: var(--space-3);
	inset-inline-start: var(--space-3);
	padding: 3px 9px;
	border-radius: var(--radius-pill);
	background: var(--shab-800);
	color: var(--text-inverse);
	font: var(--type-caps);
}

.omd-pshots__strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
.omd-pshots__cell { position: relative; display: block; }

.omd-pshots__thumb {
	width: 100%;
	aspect-ratio: 1;
	padding: 0;
	cursor: pointer;
	overflow: hidden;
	display: block;
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-thumb, var(--radius-control));
	background: var(--sang-000);
	transition: var(--transition-control);
}

.omd-pshots__thumb img { width: 100%; height: 100%; object-fit: contain; display: block; }
.omd-pshots__cell--on .omd-pshots__thumb { border: 2px solid var(--shab-600); }
.omd-pshots__thumb:focus-visible { box-shadow: var(--ring-focus); }

/* Hung off the corner, on its own white ground, so it stays findable over a dark
   photograph and over a pale one. */
.omd-pshots__drop {
	position: absolute;
	top: -7px;
	inset-inline-start: -7px;
	background: var(--surface-card);
	border-radius: var(--radius-pill);
	box-shadow: var(--shadow-2);
}

.omd-pshots__add {
	width: 100%;
	aspect-ratio: 1;
	padding: 0;
	cursor: pointer;
	display: grid;
	place-items: center;
	border: 1px dashed var(--sang-400);
	border-radius: var(--radius-thumb, var(--radius-control));
	background: var(--sang-100);
	color: var(--text-muted);
	transition: var(--transition-control);
}

.omd-pshots__add:hover { border-color: var(--border-strong); color: var(--text-strong); }
.omd-pshots__acts { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* A field that searches ---------------------------------------------------- */

.omd-search__box { position: relative; display: block; }

/* In a filter bar: as wide as the search beside it elsewhere in the panel, and no wider. */
.omd-search--sm { flex: 1 1 200px; max-width: 280px; min-width: 0; }

.omd-search__list {
	position: absolute;
	inset-inline: 0;
	top: calc(100% + var(--space-2));
	z-index: var(--z-dropdown);
	margin: 0;
	padding: 0;
	list-style: none;
	max-height: 220px;
	overflow-y: auto;
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	box-shadow: var(--shadow-3);
}

.omd-search__option {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-5);
	font: var(--type-sm);
	color: var(--text-strong);
	cursor: pointer;
	transition: var(--transition-control);
}

/* One highlight for both ways of arriving: hover moves the same cursor the arrow keys do,
   so there is never a second row that also looks chosen. */
.omd-search__option.is-active { background: var(--sang-100); }
.omd-search__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); flex: none; }

.omd-search__empty {
	padding: var(--space-4) var(--space-5);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
}

/* Orders ---------------------------------------------------------------- */

/* The listing's controls: a search, a filter and two buttons. The plain bar pushes a lone
   button to the far side, which is right for "new recipe" and wrong for a row that ends
   in apply and clear. */
.omd-panel__bar--middle > .omd-btn { margin-inline-start: 0; }

/* Label and value, where the value is a sentence rather than a sum. Two columns rather
   than the row list's space-between: pushed to opposite edges of a wide card, a label and
   its value stop being a pair and the eye has to travel to match them up. */
.omd-panel__facts { margin-top: var(--space-5); }

.omd-panel__facts .omd-rows__row {
	display: grid;
	grid-template-columns: 140px minmax(0, 1fr);
	align-items: baseline;
}

.omd-panel__facts .omd-rows__value {
	font: var(--type-sm);
	color: var(--text-strong);
	white-space: normal;
}

/* The action closes the card, ruled off from the facts it acts on. */
.omd-panel__facts + .omd-panel__save,
.omd-rows + .omd-panel__save {
	margin-top: var(--space-2);
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
}

/* The bill sits inside a card with no padding of its own, because the table above it
   needs to reach the card's edges and it does not. */
.omd-panel__totals { padding: 0 var(--space-6) var(--space-6); }

.omd-rows__row--strong .omd-rows__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-rows__row--strong .omd-rows__value { font: var(--fw-bold) var(--fs-body)/1.4 var(--font-numeric); }

/* A labelled field beside a button: the button lines up with the control, not with the
   middle of the label-and-control pair. */
.omd-panel__save--foot { align-items: flex-end; margin-top: var(--space-5); }

/* Dashboard ------------------------------------------------------------- */

/* Four figures across the top, and as many fewer as the width allows. Auto-fit rather
   than a fixed count: a tile narrower than its number is worse than a second row. */
.omd-stats {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

.omd-stat {
	background: var(--surface-card);
	border-radius: var(--radius-card);
	padding: var(--space-5) var(--space-6);
	box-shadow: var(--shadow-2);
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
}

.omd-stat__head { display: flex; align-items: center; gap: var(--space-3); color: var(--text-faint); }
.omd-stat__label { font: var(--type-caps); letter-spacing: var(--ls-caps); color: var(--text-muted); }

/* Baseline, so the unit sits on the number's feet rather than in the middle of it. */
.omd-stat__figure { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }

.omd-stat__value {
	font-family: var(--font-numeric);
	font-weight: var(--fw-bold);
	font-size: var(--fs-h3);
	line-height: 1.2;
	color: var(--text-strong);
}

.omd-stat__unit { font-size: var(--fs-xs); color: var(--text-faint); }
.omd-stat__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }

/* Colour only where the number is telling you something: takings that fell, and work
   that is late. A tile that shouts on every reading is a tile nobody reads. */
.omd-stat__note--warning { color: var(--zard-700); }
.omd-stat__note--danger { color: var(--anar-700); }

/* What the table left out, said rather than left to be assumed. */
.omd-table__more {
	margin: 0;
	padding: var(--space-4) var(--space-6);
	border-top: 1px solid var(--border-hairline);
	font: var(--type-sm);
	color: var(--text-muted);
}

/* Narrow, last: these override the panel rules above and same-specificity
   selectors are settled by order, not by being in a media query. */
@media (max-width: 900px) {
	/* The zone stops being a table: the column headings are the first thing a narrow
	   screen has no room for, and a row reads better as two stacked lines. */
	.omd-zone__head { display: none; }

	.omd-zone__row {
		grid-template-columns: minmax(0, 1fr) auto;
		gap: var(--space-2) var(--space-4);
		align-items: start;
	}

	.omd-zone__cell { grid-column: 1; }
	.omd-zone__x { grid-column: 2; grid-row: 1 / span 2; }

	/* The two pickers keep the row and the button takes its own line under them: an
	   «افزودن» squeezed beside two controls is a target nobody can hit. */
	.omd-zone__go { grid-column: 1 / -1; }
	.omd-zone__go .omd-btn { width: 100%; justify-content: center; }

	/* The capacity drops under the name rather than being squeezed between it and the
	   switch, which is what happens to it at 360px. */
	.omd-van { flex-wrap: wrap; }
	.omd-van__spec { margin-inline-start: 0; flex-basis: 100%; order: 3; }
	.omd-van__switch { margin-inline-start: auto; }

	/* One column, and the document scrolls again. A fixed-height shell on a phone fights
	   the browser's own chrome, which grows and shrinks as you scroll. */
	.omd-panel {
		grid-template-columns: 1fr;
		/* Three rows, named, and only the last of them elastic. A min-height taller than
		   the content has to put the slack somewhere, and two named rows for three items
		   left the strip holding it: the bar and the work sat at their own heights while
		   the chips floated in the middle of half a screen of empty ground. The work takes
		   it instead, which is the row that can use it. */
		grid-template-rows: auto auto minmax(0, 1fr);
		height: auto;
		min-height: 100vh;
		overflow: visible;
	}

	/* The rail lies down between the bar and the work, and stops being a rail: a light
	   strip of the shop's own chips, which is the control the customer already scrolls
	   through the categories with. Not sticky, because the bar above it is, and two
	   sticky bands stacked would have to agree on a height that changes as the bar wraps. */
	.omd-panel__head { grid-column: 1; grid-row: 1; }
	.omd-panel__side { grid-column: 1; grid-row: 2; }
	.omd-panel__main { grid-column: 1; grid-row: 3; overflow: visible; }

	.omd-panel__side {
		flex-direction: row;
		align-items: center;
		gap: var(--space-2);
		padding: var(--space-4) var(--space-5);
		background: var(--sang-000);
		border-bottom: 1px solid var(--border-hairline);
		overflow-x: auto;
		overflow-y: hidden;
		scrollbar-width: none;
		/* A grid item may exceed its track unless told otherwise, and this one would:
		   the strip would grow to fit every screen rather than let the rail scroll. */
		min-width: 0;
	}

	.omd-panel__side::-webkit-scrollbar { height: 0; }

	/* The shell says whose panel this is in the bar's own title; a second name at the head
	   of the strip would be a label that does nothing, in the space the screens need. */
	.omd-panel__brand { display: none; }

	/* Both groups dissolve into the strip, so their items are the strip's items: the
	   screens, and after them the one link that is not a screen. */
	.omd-panel__nav,
	.omd-panel__foot { display: contents; }

	/* Not a rail row any more but a chip: the same shape, border and selected ground the
	   shop puts its categories on, restated here rather than borrowed from .omd-tag. One
	   element with both classes worked only while the two rule sets happened to out-rank
	   each other, and a later edit to either would have decided it silently. */
	.omd-panel__link,
	.omd-panel__escape {
		flex: none;
		display: inline-flex;
		align-items: center;
		gap: var(--space-2);
		padding: var(--space-3) var(--space-5);
		border: 1px solid var(--border-hairline);
		border-radius: var(--radius-pill);
		background: var(--sang-000);
		font: var(--fw-regular) var(--fs-sm)/1 var(--font-core);
		color: var(--text-body);
		white-space: nowrap;
		transition: var(--transition-control);
	}

	.omd-panel__link:hover,
	.omd-panel__escape:hover {
		background: var(--sang-000);
		color: var(--anar-700);
		border-color: var(--anar-300);
	}

	.omd-panel__link--on,
	.omd-panel__link--on:hover {
		background: var(--shab-800);
		border-color: var(--shab-800);
		color: var(--text-inverse);
	}

	/* The one thing on the strip that is not a screen, after the screens. */
	.omd-panel__escape { order: 1; }

	/* The name takes a line of its own and the measurements share the next: three fields
	   and a button across a phone leaves each of them too narrow to read or to hit. */
	.omd-rowset__row {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
		row-gap: var(--space-3);
		padding-bottom: var(--space-5);
		/*
		 * Stronger than a hairline, and standing further off the fields either side. A row
		 * here is four labelled boxes stacked down the screen, so the eye has to be told
		 * where one ingredient ends; at a hairline's weight, five ingredients read as one
		 * long form.
		 */
		border-bottom: 1px solid var(--border-default);
	}

	.omd-rowset { gap: var(--space-5); }

	/* Nothing to divide after the last one. */
	.omd-rowset__row:last-child { padding-bottom: 0; border-bottom: 0; }

	.omd-rowset__field--name { grid-column: 1 / -1; }

	/* The shop's own version of the ingredient takes a line too, after the row rather than
	   inside it: a product's name is a sentence, and a third of a phone is not enough of a
	   box to read one in. */
	.omd-rowset__field--link { grid-column: 1 / -1; order: 1; }

	/* An ingredient row is five columns wide on a laptop, and that rule outranks the one
	   above by a class. Said again here, or the amount and the unit keep a fifth of a
	   phone each and the unit shows no unit. */
	.omd-rowset--recipe .omd-rowset__row {
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
	}

	/* Two hours side by side and the factor under them. The four-column band row is a
	   desktop shape, and squeezed onto a phone it leaves three number fields too narrow to
	   read what is in them. */
	.omd-rowset--bands .omd-rowset__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
	.omd-rowset--bands .omd-rowset__remove { justify-self: start; }

	/* The picture takes the width and the row reads downwards: three columns across a
	   phone leaves the link field too narrow to check a URL in. */
	.omd-slide { grid-template-columns: minmax(0, 1fr); }
	.omd-slide__fields { grid-template-columns: minmax(0, 1fr); }
	.omd-slide__tools { justify-content: flex-end; }

	/* A card's heading and its note stop competing for one line. */
	.omd-panel__body .omd-card__head { flex-wrap: wrap; }

	/*
	 * What the thing is and the button that commits it, above the form rather than under
	 * all of it. Stacked, the side column falls in below a form that can run several
	 * screens, and the save button goes with it.
	 *
	 * The column stops being a box so its cards become items of the split itself; only
	 * then can one of them be ordered ahead of the form while the other stays behind it.
	 */
	.omd-panel__split:has(.omd-panel__pub) .omd-panel__split-side { display: contents; }
	.omd-panel__pub { order: -1; }

	/*
	 * A strip that scrolls sideways says so, twice over.
	 *
	 * A slim bar, which is the cue every one of these was hiding — a row of chips or tabs
	 * cut off at the edge otherwise reads as a row that simply ends there.
	 *
	 * And a shadow at whichever end still has something past it, because a phone draws its
	 * scrollbar over the content and only while a finger is moving: the bar answers "how
	 * much more" for anyone who has already started scrolling, and the shadow answers "is
	 * there more" for everyone who has not. The covering gradients scroll with the content
	 * and the shadows do not, so each shadow uncovers itself as that end comes into view
	 * and hides again at the end of the run.
	 */
	.omd-scroll,
	.omd-tabs,
	.omd-chips,
	.omd-pdata__rail,
	.omd-panel__side {
		scrollbar-width: thin;
		scrollbar-color: var(--sang-400) transparent;

		--omd-strip-bg: var(--surface-page);
		background-image:
			linear-gradient(to right, var(--omd-strip-bg), transparent),
			linear-gradient(to left, var(--omd-strip-bg), transparent),
			linear-gradient(to right, rgba(0, 0, 0, .10), transparent),
			linear-gradient(to left, rgba(0, 0, 0, .10), transparent);
		background-position: left center, right center, left center, right center;
		background-size: 24px 100%, 24px 100%, 10px 100%, 10px 100%;
		background-repeat: no-repeat;
		background-attachment: local, local, scroll, scroll;
	}

	.omd-pdata__rail { --omd-strip-bg: var(--sang-100); }
	.omd-panel__side { --omd-strip-bg: var(--sang-000); }

	.omd-scroll::-webkit-scrollbar,
	.omd-tabs::-webkit-scrollbar,
	.omd-chips::-webkit-scrollbar,
	.omd-pdata__rail::-webkit-scrollbar,
	.omd-panel__side::-webkit-scrollbar { height: 3px; width: 3px; }

	.omd-scroll::-webkit-scrollbar-track,
	.omd-tabs::-webkit-scrollbar-track,
	.omd-chips::-webkit-scrollbar-track,
	.omd-pdata__rail::-webkit-scrollbar-track,
	.omd-panel__side::-webkit-scrollbar-track { background: transparent; }

	.omd-scroll::-webkit-scrollbar-thumb,
	.omd-tabs::-webkit-scrollbar-thumb,
	.omd-chips::-webkit-scrollbar-thumb,
	.omd-pdata__rail::-webkit-scrollbar-thumb,
	.omd-panel__side::-webkit-scrollbar-thumb { background: var(--sang-400); border-radius: var(--radius-pill); }
}


@media (max-width: 1060px) {
	/*
	 * The table stops being a table.
	 *
	 * Wider than the rest of the panel's layout, because the question is not «is this a
	 * phone» but «does a row still fit the column it is drawn in», and the work column is
	 * some 300px narrower than the window. The widest of these tables needs 1046px of window
	 * before its seven columns stop being squeezed; below that a row not made of columns
	 * reads better than one cut off at the edge.
	 *
	 * It has to switch, not merely scroll: a table's min-width does more than overflow its
	 * own scroller, it widens the whole layout viewport to accommodate it, and the panel then
	 * sits in a strip down one side of a much wider page.
	 *
	 * Each row becomes a card, and each cell carries the column name it lost with the header.
	 */
	.omd-table { min-width: 0; }
	.omd-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
	.omd-table,
	.omd-table tbody,
	.omd-table tr,
	.omd-table th,
	.omd-table td { display: block; }

	.omd-table tr {
		padding: var(--space-4) var(--space-5);
		border-top: 1px solid var(--border-hairline);
	}

	.omd-table th,
	.omd-table td { border: 0; padding: 0; }

	.omd-table__name { margin-bottom: var(--space-3); }

	.omd-table td[data-column] {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--space-4);
		padding: var(--space-2) 0;
	}

	.omd-table td[data-column]::before {
		content: attr(data-column);
		font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-core);
		color: var(--text-muted);
	}

	/* Still a row. Qualified by the table, because `.omd-table td` above is the more
	   specific selector and would win on that alone however this is ordered — the blanket
	   `display: block` would otherwise drop the delete form onto a line of its own, and
	   only for the rows that have one. */
	.omd-table td.omd-table__acts {
		display: flex;
		align-items: center;
		gap: var(--space-2);
		padding-top: var(--space-3);
	}
}

/* Where the bar stops fitting on one line.
   Left to itself it breaks wherever it runs out of room, and around 400px that happens to
   be between the way to wp-admin and who is signed in — exactly where the rule is, which
   then starts the second line as a mark against the edge with nothing before it. Measured
   rather than guessed: at 560px and above everything still sits on one line. Below it the
   screen's name takes the first line on purpose, and the rule keeps something on both
   sides of it all the way down. */
@media (max-width: 560px) {
	.omd-panel__heading { flex-basis: 100%; margin-inline-end: 0; }

	/*
	 * The rule, and only where it has something on both sides of it. On a wide bar the far
	 * end is separation enough; it earns its keep on the second line, between the way to
	 * wp-admin and who is signed in. The employer's bar has no wp-admin link, so it never
	 * draws one against the edge with nothing before it.
	 */
	.omd-panel__wpadmin + .omd-panel__me {
		padding-inline-start: var(--space-4);
		border-inline-start: 1px solid var(--border-hairline);
	}
}

/* Recipes --------------------------------------------------------------- */

/* Two columns on a wide screen, the access card beside the reading rather than under it,
   so the price is on screen while the summary is being read. */
.omd-recipe {
	display: grid;
	gap: var(--space-5);
	align-items: start;
}

@media (min-width: 900px) {
	/*
	 * The design's proportions: the reading column a little over half again as wide as
	 * the aside, rather than a fixed narrow strip. The aside carries a catalogue row now,
	 * and a row needs the room to be one.
	 */
	.omd-recipe {
		grid-template-columns: minmax(0, 1.6fr) minmax(300px, 1fr);
		gap: var(--space-7);
	}

	/* Follows the reader down, the way a product's buy box does and for the same reason:
	   the ingredients and the method are long, and the price and the button should not be
	   a scroll away from them. Two columns only; stacked it would cover the page. The
	   clearance is spelled out rather than kept in a variable, because a var() inside a
	   custom property resolves where that property is declared and would bake in the
	   logged-out admin bar height. */
	.omd-recipe__aside {
		position: sticky;
		top: calc(var(--admin-bar-h) + var(--header-h) + 1px + var(--space-5));
		align-self: start;
	}
}

.omd-recipe__main { display: flex; flex-direction: column; gap: var(--space-5); min-width: 0; }
.omd-recipe__aside {
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}

/*
 * Stacked, the price and the way to buy belong under the name rather than at the foot of
 * the page. On a phone the two columns become one, and left in source order a reader had
 * to scroll past the whole ingredients list and the method to find out what the recipe
 * costs. The column dissolves so its sections and the aside can be ordered together;
 * everything else keeps the order it was written in.
 */
@media (max-width: 899px) {
	.omd-recipe {
		display: flex;
		flex-direction: column;
		/* The grid above sets align-items: start to keep the aside from stretching down
		   the page. On the cross axis of a column that means "as narrow as your content",
		   which shrank every card away from the edges. */
		align-items: stretch;
	}

	.omd-recipe__main { display: contents; }
	.omd-recipe__main > * { order: 3; }
	.omd-recipe__main > .omd-recipe__intro { order: 1; }
	.omd-recipe__aside { order: 2; }
}

.omd-recipe__intro { display: flex; flex-direction: column; gap: var(--space-6); }

/* Wider than the blog's 16:9: a dish photographed on a table is a landscape, and the
   frame is doing less cropping work here than a packshot's does. */
/*
 * Square, and capped at the width a product's hero is capped at, so a recipe and a
 * product open the same way. The picture is still shown whole over a blurred copy of
 * itself: a recipe photograph is somebody's plated dish, and a square crop of a landscape
 * shot takes the ends off it.
 */
.omd-recipe__cover {
	--cover-ratio: 1;
	/* Stated, because the auto margin that pushes the frame to the start edge also stops
	   it stretching, and everything inside .omd-cover is positioned: left to its own
	   content the frame is zero wide, and an aspect ratio of zero width is no frame. */
	width: 100%;
	max-width: 420px;
	margin-inline-end: auto;
	border-radius: var(--radius-card);
	font: var(--type-sm);
}

@media (max-width: 1000px) {
	.omd-recipe__cover { max-width: none; margin-inline: auto; }
}

.omd-recipe__title { font: var(--type-h2); color: var(--text-strong); margin: 0 0 var(--space-3); }
.omd-recipe__summary { font: var(--fw-regular) var(--fs-body)/1.85 var(--font-core); color: var(--text-body); margin: 0; text-wrap: pretty; }
.omd-recipe__facts { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.omd-recipe__held { font: var(--type-sm); color: var(--text-muted); margin: 0; }
.omd-recipe__price { margin-bottom: var(--space-2); }
.omd-recipe__terms { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0 0 var(--space-4); }

.omd-servings {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-bottom: var(--space-4);
}

.omd-servings__label { font: var(--type-sm); color: var(--text-muted); }

/* A ruled list rather than a gapped one: an amount belongs to the name on its own line,
   and a rule says that better than white space. */
.omd-rows { display: flex; flex-direction: column; }

.omd-rows__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	padding: var(--space-4) 0;
	border-top: 1px solid var(--border-hairline);
}

.omd-rows > .omd-rows__row:first-child { border-top: 0; }

.omd-rows__name { font: var(--type-sm); color: var(--text-body); }
.omd-rows__value { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-numeric); color: var(--text-strong); white-space: nowrap; }

/* Named only by its place in the list. Faint, because it is a placeholder for something
   that is there rather than a thing in its own right. The withheld amount is faint on
   every row of a locked list, named or not: it is the same dash standing for the same
   thing. */
.omd-rows__row--held .omd-rows__name,
.omd-rows__value--held { color: var(--text-faint); }

/* The method is prose in .omd-post, which already carries the article's headings, lists
   and quotes. All this adds is the space between the card's heading and the writing. */
.omd-recipe__method { margin-top: var(--space-4); }

.omd-recipes {
	display: grid;
	gap: var(--space-5);
	grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.omd-rcard { display: flex; flex-direction: column; gap: var(--space-3); }
/* Square, the shape the listing gives every recipe. */
.omd-rcard__cover { --cover-ratio: 1; border-radius: var(--radius-thumb); }
.omd-rcard__title { font: var(--type-title); color: var(--text-strong); margin: 0; }
.omd-rcard__title a { color: inherit; text-decoration: none; }
.omd-rcard__title a:hover { color: var(--text-link-hover); }
.omd-rcard__summary { font: var(--type-sm); color: var(--text-muted); margin: 0; text-wrap: pretty; }
.omd-rcard__meta { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-faint); margin: 0; }

.omd-rcard__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-3);
	margin-top: auto;
	padding-top: var(--space-2);
}

.omd-rcard__price { font: var(--fw-bold) var(--fs-body)/1.3 var(--font-numeric); color: var(--text-strong); }

/* Address book ---------------------------------------------------------- */

.omd-addresses { display: flex; flex-direction: column; gap: var(--space-5); }

.omd-addresses__none {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-8) var(--space-5);
	text-align: center;
}

.omd-addresses__none-icon { color: var(--text-faint); display: flex; }
.omd-addresses__none-title { font: var(--type-body-strong); color: var(--text-strong); }
.omd-addresses__none-body { font: var(--type-sm); color: var(--text-muted); max-width: 42ch; text-wrap: pretty; margin: 0; }
.omd-addresses__add { margin: 0; }

.omd-addr { display: flex; flex-direction: column; gap: var(--space-3); }
.omd-addr__head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.omd-addr__label { font: var(--type-body-strong); color: var(--text-strong); }
.omd-addr__text { font: var(--fw-medium) var(--fs-sm)/1.7 var(--font-core); color: var(--text-strong); margin: 0; text-wrap: pretty; }
.omd-addr__meta { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-body); margin: 0; }

.omd-addr__geo,
.omd-addr__nogeo {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	margin: 0;
}

.omd-addr__geo { color: var(--state-success); }
.omd-addr__nogeo { color: var(--zard-700); }

.omd-addr__actions {
	display: flex;
	gap: var(--space-2);
	flex-wrap: wrap;
	align-items: center;
	margin-top: var(--space-2);
}

/* Each action is its own form, so it must not become its own block. */
.omd-addr__do { display: contents; }

/* Address form ---------------------------------------------------------- */

.omd-addrform { display: flex; flex-direction: column; gap: var(--space-5); }

.omd-addrform__grid,
.omd-profile__grid {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.omd-addrform__hint,
.omd-profile__hint {
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
	margin: 0 0 var(--space-3);
}

.omd-addrform__map { display: flex; flex-direction: column; gap: var(--space-2); }

.omd-addrform__foot {
	display: flex;
	gap: var(--space-3);
	flex-wrap: wrap;
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
}

/* Fields ---------------------------------------------------------------- */

.omd-field { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.omd-field__label { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); }
.omd-field__hint { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); margin: 0; }
.omd-field__error { font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-core); color: var(--state-danger); margin: 0; }

.omd-input {
	width: 100%;
	min-height: var(--control-h);
	padding: var(--space-3) var(--space-4);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	font: var(--type-body);
	font-size: var(--fs-sm);
	color: var(--text-strong);
	transition: var(--transition-control);
}

/*
 * A field that carries its unit inside the box.
 *
 * The border moves to the wrapper and the input goes bare inside it, rather than the unit
 * being floated over a padded field: padding sized for «کیلوگرم» is not padding sized for
 * «لیتر بر کیلومتر», and a suffix that overlaps the number it belongs to is worse than no
 * suffix at all. Laid out as a row, the two can never collide whatever the words are.
 */
.omd-unit {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	padding-inline-end: var(--space-4);
	background: var(--sang-000);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	transition: var(--transition-control);
}

.omd-unit:hover { border-color: var(--border-strong); }

/* The ring belongs to the whole control, since the whole control is what is focused. */
.omd-unit:focus-within { box-shadow: var(--ring-focus); border-color: var(--border-strong); }

.omd-unit .omd-input {
	flex: 1;
	min-width: 0;
	border: 0;
	background: transparent;
	box-shadow: none;
}

.omd-unit__label {
	flex: none;
	font: var(--fw-regular) var(--fs-xs)/1 var(--font-core);
	color: var(--text-muted);
	white-space: nowrap;
}

.omd-unit:has(.omd-input:disabled) { background: var(--sang-100); }
.omd-field--bad .omd-unit { border-color: var(--state-danger); }

.omd-input:hover { border-color: var(--border-strong); }
.omd-input:focus-visible { outline: none; box-shadow: var(--ring-focus); border-color: var(--border-strong); }
.omd-input::placeholder { color: var(--text-faint); }

.omd-textarea { min-height: 96px; resize: vertical; line-height: 1.7; }

select.omd-input { cursor: pointer; }

.omd-field--bad .omd-input { border-color: var(--state-danger); }

/* Map ------------------------------------------------------------------- */

.omd-mappick { display: flex; flex-direction: column; gap: var(--space-3); }

.omd-mappick__canvas {
	height: var(--omd-map-h, 260px);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	overflow: hidden;
	background: var(--sang-200);
	/* The pin is positioned against this box rather than drawn on the map. */
	position: relative;
	z-index: 0;
}

/* The picker sits inside a card that already draws the frame. */
.omd-mappick--fixed .omd-mappick__canvas { border: 0; border-radius: 0; }

.omd-mappick__pair {
	display: grid;
	gap: var(--space-3);
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

/* The picker's note changes as the marker moves, so it holds its line whether or not it
   has one; a fixed map only ever speaks to say the tiles failed, and reserving a band for
   a sentence that will not come leaves a gap under every map on the site. */
.omd-mappick__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); margin: 0; min-height: 1.6em; }
.omd-mappick--fixed .omd-mappick__note { min-height: 0; }
.omd-mappick--fixed .omd-mappick__note:empty { display: none; }

/* The marker is a div, so it carries no colour of its own. */
.omd-mappick__pin { color: var(--anar-600); }
.omd-mappick__pin svg { filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.35)); }

/* The picker's pin: still at the centre while the map moves under it, so the foot of the
   stem is the chosen point at any zoom. Outside Leaflet's layers and inert, because it is
   not on the map — the map is behind it. */
.omd-mappick__crosshair {
	position: absolute;
	/* Physical, not logical: the centre of a map is the centre of a map, and
	   inset-inline-start would put this against the right edge on an RTL page. */
	left: 50%;
	top: 50%;
	z-index: 500;
	display: flex;
	flex-direction: column;
	align-items: center;
	/* The foot sits on the centre: the whole pin is lifted by its own height. */
	transform: translate(-50%, -100%);
	pointer-events: none;
}

.omd-mappick__crosshair-head {
	width: 22px;
	height: 22px;
	border-radius: var(--radius-pill);
	background: var(--anar-600);
	border: 3px solid var(--sang-000);
	box-shadow: 0 2px 6px rgb(0 0 0 / 0.35);
}

.omd-mappick__crosshair-stem {
	width: 2px;
	height: 26px;
	background: var(--anar-600);
	box-shadow: 0 0 0 1px rgb(255 255 255 / 0.7);
}

/* «Put me where I am.» Physically top-right, which is the one corner Leaflet leaves
   alone: the zoom control takes the top-left and the attribution the bottom-right. */
.omd-mappick__locate {
	position: absolute;
	top: var(--space-3);
	right: var(--space-3);
	/* Above Leaflet's own controls, which sit in the high hundreds. */
	z-index: 1000;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	padding: 0;
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	background: var(--surface-card);
	color: var(--text-strong);
	cursor: pointer;
	box-shadow: var(--shadow-2);
	transition: var(--transition-control);
}

.omd-mappick__locate:hover:not(:disabled) { background: var(--sang-100); }
.omd-mappick__locate:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.omd-mappick__locate:disabled { cursor: progress; color: var(--text-faint); }

/* Turning while the phone looks for a fix, which takes long enough to need saying. Left
   turning under prefers-reduced-motion for the reason the submit button's ring is: that
   setting is about motion that decorates or disorients, and a stopped progress indicator
   reads as broken rather than as busy. */
.omd-mappick__locate--busy > svg { animation: omd-spin 1.1s linear infinite; }

/* Leaflet's own chrome, brought into the theme's type and RTL. */
.leaflet-container { font: var(--type-sm); }
.leaflet-control-attribution { font-size: var(--fs-micro); direction: ltr; }
.leaflet-control-zoom a { color: var(--text-strong); }

/* Profile --------------------------------------------------------------- */

.omd-profile { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-profile__pass .omd-eyebrow { margin: 0 0 var(--space-2); }

.omd-profile__foot {
	display: flex;
	gap: var(--space-3);
	flex-wrap: wrap;
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
}



/* One order, in full -------------------------------------------------------- */

.omd-orderback {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--type-sm);
	color: var(--text-muted);
	text-decoration: none;
	margin-bottom: var(--space-5);
}

.omd-orderback:hover { color: var(--text-strong); }

.omd-orderhead {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-5);
	flex-wrap: wrap;
	margin-bottom: var(--space-7);
}

.omd-orderhead__title { font: var(--type-h3); color: var(--text-strong); margin: 0 0 var(--space-2); }
.omd-orderhead__meta { font: var(--type-sm); color: var(--text-muted); margin: 0; }
.omd-orderhead__side { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

.omd-orderview {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 380px;
	gap: var(--space-7);
	align-items: start;
}

@media (max-width: 1000px) {
	.omd-orderview { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
}

.omd-orderview__main,
.omd-orderview__side {
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	min-width: 0;
}

/* Progress ------------------------------------------------------------- */

.omd-track__steps { list-style: none; margin: var(--space-5) 0 0; padding: 0; }

.omd-track__step {
	display: grid;
	grid-template-columns: 24px minmax(0, 1fr);
	column-gap: var(--space-4);
	position: relative;
	padding-bottom: var(--space-6);
}

.omd-track__step:last-child { padding-bottom: 0; }

/* The rail is drawn from the dot down, so the last step has none to draw. */
.omd-track__step:not(:last-child)::before {
	content: "";
	position: absolute;
	top: 18px;
	bottom: 0;
	inset-inline-start: 11px;
	width: 2px;
	background: var(--border-hairline);
}

.omd-track__step--done:not(:last-child)::before { background: var(--state-success); }

.omd-track__dot {
	width: 12px;
	height: 12px;
	margin: 6px auto 0;
	border-radius: var(--radius-pill);
	background: var(--surface-card);
	box-shadow: 0 0 0 2px var(--sang-400) inset;
}

.omd-track__step--done .omd-track__dot { background: var(--state-success); box-shadow: 0 0 0 2px var(--state-success) inset; }
.omd-track__step--current .omd-track__dot { background: var(--surface-brand); box-shadow: 0 0 0 2px var(--surface-brand) inset; }
.omd-track__step--stopped .omd-track__dot { background: var(--state-danger); box-shadow: 0 0 0 2px var(--state-danger) inset; }

.omd-track__body { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.omd-track__label { font: var(--fw-medium) var(--fs-body)/1.6 var(--font-core); color: var(--text-muted); }
.omd-track__at { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }

.omd-track__step--done .omd-track__label,
.omd-track__step--stopped .omd-track__label { color: var(--text-strong); }
.omd-track__step--current .omd-track__label { font: var(--type-body-strong); color: var(--text-strong); }

/* Items ---------------------------------------------------------------- */

/* The card is flush and the rows carry the padding, so each row's rule runs the full width
   of the card the way the catalogue's rows do — a divider that stops short of the edge
   reads as a mistake next to one that does not. */
.omd-orderitems__list { display: flex; flex-direction: column; }

/* Every item takes a rule, the first one included: that is what separates the list from
   the card's heading, so it is one rule doing two jobs rather than a heading border and a
   between-items border that have to be kept matching. */
.omd-orderitem {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-6);
	border-top: 1px solid var(--border-hairline);
}
.omd-orderitem__thumb { width: 56px; height: 56px; flex: none; }
.omd-orderitem__body { min-width: 0; flex: 1; }
.omd-orderitem__name { font: var(--type-body-strong); color: var(--text-strong); text-wrap: pretty; }
.omd-orderitem__name a { color: inherit; text-decoration: none; }

/*
 * The whole row opens the product, the way a catalogue row does: the name's link is
 * stretched over the row so the picture and the price are part of the target, while the
 * link itself stays the one focusable, labelled thing in it.
 *
 * Only where there is somewhere to go — a deleted or hidden product gets no hover and no
 * pointer, because there is nothing behind it to click.
 */
.omd-orderitem--linked { position: relative; transition: var(--transition-control); }
.omd-orderitem--linked:hover { background: var(--sang-100); }
.omd-orderitem--linked:focus-within { background: var(--sang-100); }

.omd-orderitem--linked .omd-orderitem__name a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.omd-orderitem__qty { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }
.omd-orderitem__total { font: var(--fw-semibold) var(--fs-body)/1.4 var(--font-numeric); color: var(--text-strong); white-space: nowrap; }

.omd-ordernote-view__body { font: var(--fw-regular) var(--fs-body)/1.85 var(--font-core); color: var(--text-body); margin: var(--space-3) 0 0; text-wrap: pretty; }

/* Facts and bill -------------------------------------------------------- */

.omd-orderfacts, .omd-orderbill { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-orderfacts__row { display: flex; flex-direction: column; gap: var(--space-1); }
.omd-orderfacts__label { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }
.omd-orderfacts__value { font: var(--type-sm); color: var(--text-strong); text-wrap: pretty; }
.omd-orderfacts__value--numeric { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-numeric); }

.omd-orderbill { gap: var(--space-3); }

.omd-orderbill__row {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	font: var(--type-sm);
	color: var(--text-muted);
}

.omd-orderbill__row span:last-child { font-family: var(--font-numeric); white-space: nowrap; }
.omd-orderbill__row--discount { color: var(--text-brand); }

.omd-orderbill__payable {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-4);
	padding-top: var(--space-4);
	margin-top: var(--space-2);
	border-top: 1px solid var(--border-hairline);
	font: var(--type-body-strong);
	color: var(--text-strong);
}

.omd-orderbill__payable span:last-child { font: var(--type-price); white-space: nowrap; }
.omd-orderbill__help { margin-top: var(--space-3); }


/* Order received ------------------------------------------------------------ */

/* The page stacks a confirmation card, whatever the gateway prints, the items and the
   note. It spaces them itself, so none of those may carry a margin of its own — the gap
   would be added to it, and the ones without a margin would hug. */
.omd-received {
	max-width: 560px;
	margin-inline: auto;
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}
.omd-received__card { display: flex; flex-direction: column; gap: var(--space-5); }

.omd-received__head { display: flex; align-items: center; gap: var(--space-4); }

.omd-received__tick {
	flex: none;
	width: 44px;
	height: 44px;
	border-radius: var(--radius-pill);
	background: var(--state-success-soft);
	color: var(--state-success);
	display: grid;
	place-items: center;
}

.omd-received__title { font: var(--type-title); color: var(--text-strong); }
.omd-received__id { font: var(--type-sm); color: var(--text-muted); }

.omd-received__rows {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
}

.omd-received__row { display: flex; align-items: baseline; gap: var(--space-4); }

.omd-received__label {
	flex: none;
	min-width: 104px;
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core);
	color: var(--text-muted);
}

.omd-received__value { font: var(--type-sm); color: var(--text-strong); text-wrap: pretty; }
.omd-received__value--money { font: var(--type-price); color: var(--text-strong); }
.omd-received__value--numeric { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-numeric); color: var(--text-strong); }

.omd-received__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; text-wrap: pretty; }
.omd-received__actions { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* The item table WooCommerce prints under the card, and anything a gateway adds. */
/*
 * A gateway's own instructions land between the card and the table — a bank transfer's
 * account details, or the reminder that cash is paid at the door. WooCommerce prints them
 * as a bare paragraph, so they get the frame the rest of the page has.
 */
.omd-received > p {
	font: var(--type-sm);
	color: var(--text-body);
	margin: 0;
	padding: var(--space-4) var(--space-5);
	background: var(--state-info-soft);
	border-radius: var(--radius-control);
	text-wrap: pretty;
}

.omd-orderwho { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-orderwho__address { font: var(--type-sm); color: var(--text-body); font-style: normal; line-height: 1.8; }
.omd-orderwho__contact { display: flex; gap: var(--space-5); flex-wrap: wrap; }

.omd-orderwho__line {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--type-sm);
	color: var(--text-muted);
	text-decoration: none;
}


/* What is about to be ordered ---------------------------------------------- */

.omd-confirm {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	padding-top: var(--space-4);
	margin-top: var(--space-4);
	border-top: 1px solid var(--border-hairline);
}

.omd-confirm__row { display: flex; align-items: baseline; gap: var(--space-4); }
.omd-confirm__label { flex: none; min-width: 62px; font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }
.omd-confirm__value { font: var(--type-sm); color: var(--text-strong); text-wrap: pretty; }

.omd-ordernote__hint { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0 0 var(--space-3); }
.omd-ordernote textarea { box-sizing: border-box; width: 100%; min-height: 96px; resize: vertical; }


/* Formal invoice ------------------------------------------------------------ */

.omd-invoice { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-invoice__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; }

.omd-invoice__who {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/*
 * The three fields belong to the tick, so they are not there until it is ticked. `:has()`
 * rather than a script: a customer who ticks the box must always get the fields, and the
 * checkout is the last page on which to depend on JavaScript having loaded.
 */
.omd-invoice:not(:has(.omd-invoice__ask input:checked)) .omd-invoice__who { display: none; }


/* Delivery address in the cart, delivery time at the checkout --------------- */

.omd-shipto { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-shipto__list { display: flex; flex-direction: column; gap: var(--space-3); }
.omd-shipto__none { font: var(--type-sm); color: var(--text-muted); margin: 0; text-wrap: pretty; }
.omd-shipto__add { margin: 0; }
.omd-shipto__next { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; }

.omd-shipto__row,
.omd-when__mode {
	position: relative;
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	padding: var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-shipto__row:hover,
.omd-when__mode:hover { border-color: var(--border-strong); }

/*
 * The chosen one is filled and its edge doubled, as the design has it. A tinted border
 * alone was too quiet for a list somebody scans on a phone before paying: the row that
 * decides where the order goes should be findable without reading any of them.
 *
 * The second edge is an inset shadow rather than a two-pixel border, so choosing a row
 * does not move the ones under it by a pixel.
 */
.omd-shipto__row--on,
.omd-when__mode:has(input:checked) {
	border-color: var(--surface-brand);
	box-shadow: inset 0 0 0 1px var(--surface-brand);
	background: var(--surface-brand-soft);
}

/* The row holds two things now: the whole address, which picks it, and a way out to
   correct it. The pick takes the slack so the edit sits at the far end. */
.omd-shipto__pick {
	position: relative;
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	flex: 1;
	min-width: 0;
	cursor: pointer;
}

.omd-shipto__edit { flex: none; align-self: center; }

/* An address saved before a map point was required. Kept on screen and greyed rather than
   hidden: an address that vanishes leaves a customer with no idea what happened, and the
   row still has to carry the way to fix it. */
.omd-shipto__row--unusable { opacity: 0.75; }
.omd-shipto__row--unusable .omd-shipto__pick { cursor: not-allowed; }

.omd-shipto__gap {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core);
	color: var(--state-danger);
}

.omd-addrform__map--bad { border-radius: var(--radius-control); box-shadow: 0 0 0 2px var(--state-danger); padding: var(--space-3); }


.omd-shipto__body,
.omd-when__body { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }

.omd-shipto__label,
.omd-when__title { font: var(--type-body-strong); color: var(--text-strong); }
/* Medium rather than regular, and given the looser leading the design moved it to: this
   is the line somebody actually checks before paying, not a caption under the nickname. */
.omd-shipto__text { font: var(--fw-medium) var(--fs-sm)/1.7 var(--font-core); color: var(--text-strong); text-wrap: pretty; }
/* `--text-body`, not the muted grey, because the design's own address row sets it that
   way: this is the line that separates one address, or one delivery time, from the next. */
.omd-shipto__meta,
.omd-when__hint { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-body); text-wrap: pretty; }

.omd-when__modes { display: flex; flex-direction: column; gap: var(--space-3); }

/*
 * The calendar belongs to the second option, so choosing the first puts it away. `:has()`
 * rather than a class the script toggles, which is what lets the whole step work with
 * JavaScript blocked.
 */
.omd-when__picker { margin-top: var(--space-5); }
.omd-when:has(.omd-when__modes input[value="soonest"]:checked) .omd-when__picker { display: none; }

.omd-checkout__shipto {
	font: var(--type-sm);
	color: var(--text-muted);
	margin: 0 0 var(--space-6);
	display: flex;
	gap: var(--space-3);
	flex-wrap: wrap;
}

/* The billing fields the cart already answered. Hidden, not removed: they still post. */
.omd-checkout__carried { display: none; }

/*
 * The coupon's own form. Forms do not nest, so it is declared beside the checkout form and
 * the card in the column joins its field and button to it with the `form` attribute. All
 * it carries itself is the nonce, so there is nothing here to show.
 */
.omd-coupon-form { display: none; }



/* ---------------------------------------------------------------- Users */

/* The name and what the account is, on one line: the badges answer «who is this» before
   the form underneath asks anything, so they read as part of the heading. */
.omd-uhead { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/*
 * One column until there is room for the facts to stand beside the form instead of under
 * it. Narrower than that, a side column is only the same cards further down the page.
 */
.omd-uedit { display: grid; gap: var(--space-5); align-items: start; }
.omd-uedit__main,
.omd-uedit__side { min-width: 0; display: flex; flex-direction: column; gap: var(--space-5); }
.omd-uedit__aside { margin: var(--space-5) 0 0; }

@media (min-width: 1180px) {
	.omd-uedit { grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr); }

	/* An account that does not exist yet has no history to stand beside the form, so the
	   form takes the width rather than leaving a column of nothing. */
	.omd-uedit--alone { grid-template-columns: minmax(0, 1fr); }

	/* Stacked once the column is narrow: two tiles side by side there leaves neither wide
	   enough for the money written in it. */
	.omd-stats--stack { grid-template-columns: 1fr; }
}

/* What cannot be typed over: when the account was made, when it was last used, and its
   number. Ruled off from the fields above, which can. */
.omd-ufacts {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3) var(--space-6);
	margin-top: var(--space-5);
	padding-top: var(--space-5);
	border-top: 1px solid var(--border-hairline);
	font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-numeric);
	color: var(--text-muted);
}

/* A value that is read rather than typed, standing where its input would have. */
.omd-ufact { font: var(--type-sm); color: var(--text-strong); }

.omd-utags { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.omd-utag {
	font: var(--fw-medium) var(--fs-xs)/1 var(--font-core);
	color: var(--text-body);
	background: var(--surface-sunken);
	border-radius: var(--radius-pill);
	padding: var(--space-2) var(--space-4);
}

/* The roles above the table that changes them: how many people hold each and how far it
   reaches, which is what somebody arrives at this screen wanting to know. */
.omd-uroles { display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }

.omd-urole {
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	padding: var(--space-5);
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

.omd-urole__head { display: flex; align-items: center; gap: var(--space-3); }
.omd-urole__head .omd-badge { margin-inline-start: auto; }
.omd-urole__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-urole__stat { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); }

.omd-umatrix__col { text-align: center; white-space: nowrap; }
.omd-umatrix__role { display: block; font: var(--fw-semibold) var(--fs-xs)/1.5 var(--font-core); color: var(--text-strong); }
.omd-umatrix__count { display: block; font: var(--fw-regular) var(--fs-micro)/1.6 var(--font-numeric); color: var(--text-muted); }

/* The group's name inside the table rather than above it. Outside, the reader would be
   counting rows to work out where the group ends. */
.omd-umatrix__group {
	text-align: start;
	font: var(--type-caps);
	letter-spacing: var(--ls-caps);
	color: var(--text-muted);
	background: var(--surface-sunken);
}

.omd-umatrix__lead { text-align: start; }
.omd-umatrix__label { display: block; font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-core); color: var(--text-strong); }
.omd-umatrix__note { display: block; font: var(--fw-regular) var(--fs-micro)/1.6 var(--font-core); color: var(--text-muted); }
.omd-umatrix__aside { display: block; }
.omd-umatrix__cell { text-align: center; }
.omd-umatrix__box { display: inline-flex; }

/* A save at the foot of a flush card has no padding of its own to sit in, and the sentence
   beside it belongs at the start of the line rather than shoved against the button. */
.omd-card--flush > .omd-card__save {
	margin-top: 0;
	padding: var(--space-4) var(--space-6) var(--space-6);
	justify-content: space-between;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
}


/* --------------------------------------------------------------- 9. Rails */

/*
 * app.js adds these once it knows a rail overflows, so the hand only appears over a row
 * that can actually move. Selection is suppressed for the length of the pull, or dragging
 * across chips paints them blue instead of scrolling.
 */
.omd-rail.is-draggable,
.omd-chips.is-draggable,
.omd-slider__track.is-draggable { cursor: grab; }

.omd-rail.is-dragging,
.omd-chips.is-dragging,
.omd-slider__track.is-dragging {
	cursor: grabbing;
	user-select: none;
}

/* The drag and the glide that follows it set scrollLeft every frame; smooth scrolling
   would animate towards each of those in turn and arrive at none of them. */
.omd-rail,
.omd-chips { scroll-behavior: auto; }


/* ----------------------------------------------------------------- 10. Blog */

.omd-blog__lede {
	font: var(--type-sm);
	color: var(--text-muted);
	margin: 0 0 var(--space-6);
	max-width: 62ch;
}

.omd-blog__grid {
	display: grid;
	gap: var(--space-6);
	grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

/*
 * Search and ordering share one form, so a filtered listing is a single shareable URL.
 *
 * No submit button, like the header's search: a form whose only text field is this one
 * submits on Enter by itself, and the select reloads the listing as it changes.
 */
.omd-blog__controls {
	display: flex;
	align-items: flex-end;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-bottom: var(--space-5);
}

/* Sized to the design rather than stretched: a field the width of the page invites a
   sentence, and this one takes a word. */
.omd-blog__search,
.omd-recipes__search { flex: 0 1 320px; min-width: 240px; }

/* The blog's row lines its controls up by their bottoms, because a labelled select and a
   search field are different heights and their labels sit at different heights too. The
   recipes row has no labels and nothing to line up that way: bottom-aligning a shorter
   button beside a taller field just drops the button four pixels. */
.omd-recipes__controls {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-bottom: var(--space-5);
}
.omd-blog__sort { flex: none; width: 180px; }

/* Same control as the catalogue's ordering, one size up: the design pairs it with a
   40px search field and a shorter select beside a taller field reads as a mistake. */
.omd-blog__sort select { height: var(--control-h); }

.omd-blog__count {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	margin-bottom: var(--space-6);
	font: var(--type-sm);
	color: var(--text-muted);
}


/*
 * The line above a post, everywhere one appears: its section, when it ran, how long it
 * takes, and on the article itself who wrote it. Wraps rather than truncates, because a
 * card two hundred and eighty pixels wide cannot always hold all of it on one line.
 */
.omd-pmeta {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-3);
	font: var(--fw-regular) var(--fs-micro)/1.5 var(--font-core);
	color: var(--text-muted);
}

/* A link, unlike the design's plain label, because a section name that filters the index
   is worth clicking. Undecorated so it still reads as part of the meta line. */
.omd-pmeta__cat { color: var(--text-brand); font-weight: var(--fw-medium); text-decoration: none; }
.omd-pmeta__cat:hover { text-decoration: underline; }

.omd-bcard {
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	overflow: hidden;
	display: flex;
	flex-direction: column;
}

/* The 16:9 frame a cover sits in, on the card and on the article alike.
   The picture is shown whole rather than cropped to the banner, and the space left over
   is filled by a blurred, darkened copy of the same file. Covers here are packshots on
   white: a crop takes the lid off the jar, and letterboxing against a flat plate leaves
   two grey bands beside every one of them. */
.omd-cover {
	position: relative;
	display: grid;
	place-items: center;
	background: var(--sang-200);
	/* A knob rather than a constant: the home banner frames the same way at its own shape,
	   and turning it here beats out-specifying the component from three files away. */
	aspect-ratio: var(--cover-ratio, 16 / 9);
	overflow: hidden;
	font: var(--type-sm);
	color: var(--text-faint);
	text-decoration: none;
}

/* Scaled up past the frame because a blur samples the transparent outside as well, which
   would otherwise show as a pale border on all four edges. */
.omd-cover__blur {
	position: absolute;
	inset: 0;
	background-size: cover;
	background-position: 50% 50%;
	filter: blur(20px) brightness(0.82) saturate(1.15);
	transform: scale(1.12);
}

/* Taken out of the grid flow, which is there to centre the empty-state label. As a grid
   item the picture sizes the row it is in, so `height: 100%` resolves against its own
   height and the frame's 16:9 never reaches it: it renders square and is clipped, which
   is a centre crop wearing `contain`'s name. */
.omd-cover img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
	display: block;
}

.omd-bcard__body {
	padding: var(--space-5);
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	flex: 1;
}

.omd-bcard__title { font: var(--type-title); color: var(--text-strong); margin: 0; }
.omd-bcard__title a { color: inherit; text-decoration: none; }
.omd-bcard__title a:hover { color: var(--text-link-hover); }
.omd-bcard__excerpt { font: var(--type-sm); color: var(--text-muted); margin: 0; text-wrap: pretty; }

/* Pushed to the foot of the card, so the buttons line up across a row of cards whose
   titles and excerpts run to different lengths. */
.omd-bcard__foot { margin-top: auto; padding-top: var(--space-4); display: flex; }

.omd-back { font: var(--type-sm); display: inline-block; margin-bottom: var(--space-4); }

.omd-article {
	display: grid;
	grid-template-columns: minmax(0, 1fr) 320px;
	gap: var(--space-8);
	align-items: start;
}

@media (max-width: 1000px) {
	.omd-article { display: flex; flex-direction: column; gap: var(--space-7); }
}

.omd-article__main { min-width: 0; }
.omd-article__title { font: var(--type-h2); color: var(--text-strong); margin: var(--space-3) 0 var(--space-4); text-wrap: pretty; }

.omd-article__lede {
	font: var(--fw-regular) var(--fs-body-lg)/1.8 var(--font-core);
	color: var(--text-muted);
	margin: 0 0 var(--space-6);
	text-wrap: pretty;
}

/* Same frame as the card's, at the article's radius. */
.omd-article__cover {
	border-radius: var(--radius-card);
	margin-bottom: var(--space-6);
}

.omd-article__aside { min-width: 0; display: flex; flex-direction: column; gap: var(--space-5); }

.omd-sidelist { display: flex; flex-direction: column; gap: var(--space-4); }
/* The whole row is the link. Pulled out by its own padding so the hover plate reaches
   the card's inner edge rather than floating inside it. */
.omd-sideitem {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	margin-inline: calc(var(--space-3) * -1);
	padding: var(--space-3);
	border-radius: var(--radius-control);
	text-decoration: none;
	color: inherit;
	transition: background-color var(--dur-1) var(--ease-standard);
}

.omd-sideitem:hover { background: var(--sang-100); color: inherit; }
.omd-sideitem__thumb { width: 56px; height: 56px; flex: none; }
.omd-sideitem__body { min-width: 0; flex: 1; }
.omd-sideitem__name { display: block; font: var(--type-body-strong); color: var(--text-strong); margin-bottom: 2px; }
.omd-sideitem__price { display: block; font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-numeric); color: var(--text-muted); }
.omd-sideitem__go { flex: none; color: var(--text-faint); display: flex; }

.omd-sidepost { text-decoration: none; display: block; }

/* Blocks, not inline spans: the meta belongs on a line of its own, and a span would run
   on from wherever the title happened to stop wrapping. */
.omd-sidepost__title { display: block; font: var(--type-body-strong); color: var(--text-strong); margin-bottom: 2px; }
.omd-sidepost:hover .omd-sidepost__title { color: var(--text-link-hover); }
.omd-sidepost__meta { display: block; font: var(--fw-regular) var(--fs-micro)/1.5 var(--font-core); color: var(--text-muted); }


/* ------------------------------------------------------ 11. About & contact */

.omd-about__headline { font: var(--type-h2); color: var(--text-strong); margin: 0 0 var(--space-4); max-width: 24ch; text-wrap: pretty; }
.omd-about__lede { font: var(--type-body); color: var(--text-body); margin: 0 0 var(--space-9); max-width: 62ch; text-wrap: pretty; }

.omd-about {
	display: grid;
	gap: var(--space-6);
	grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
	align-items: start;
	margin-bottom: var(--space-9);
}

.omd-about__main { min-width: 0; display: flex; flex-direction: column; gap: var(--space-7); }
.omd-about__aside { min-width: 0; }
.omd-about__title { font: var(--type-title); color: var(--text-strong); margin: 0 0 var(--space-4); }

.omd-contact {
	display: grid;
	gap: var(--space-6);
	grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
	align-items: start;
}

.omd-contact__col { min-width: 0; display: flex; flex-direction: column; gap: var(--space-4); }

/* No map set, so nothing goes in the second column. One column of the same width the
   details had, rather than a column and an empty half of the page beside it. */
.omd-contact--single { grid-template-columns: minmax(0, 1fr); max-width: 520px; }

@media (max-width: 900px) {
	.omd-about,
	.omd-contact { grid-template-columns: minmax(0, 1fr); }
}

/* An icon in a tile, a strong line, and a quieter one under it. Shared by what the shop
   sells and by how to reach it: the same shape carrying different facts. */
.omd-facts { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-facts__row { display: flex; gap: var(--space-4); align-items: flex-start; }

.omd-facts__icon {
	flex: none;
	/* The design's forty pixels are the whole tile, hairline included. */
	width: 40px;
	height: 40px;
	border-radius: var(--radius-control);
	background: var(--sang-100);
	border: 1px solid var(--border-hairline);
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--text-body);
}

.omd-facts__body { min-width: 0; }
.omd-facts__title { font: var(--fw-semibold) var(--fs-body)/1.5 var(--font-core); color: var(--text-strong); text-decoration: none; }
.omd-facts__title:hover { color: var(--text-link-hover); text-decoration: underline; }
.omd-facts__value { font: var(--fw-semibold) var(--fs-body)/1.5 var(--font-numeric); color: var(--text-strong); margin: var(--space-1) 0 0; text-decoration: none; display: block; }
a.omd-facts__value:hover { color: var(--text-link-hover); }
.omd-facts__note { font: var(--type-sm); color: var(--text-muted); margin: var(--space-1) 0 0; max-width: 52ch; text-wrap: pretty; }
.omd-facts__body > .omd-eyebrow { margin-bottom: 0; }

.omd-glance { display: flex; flex-direction: column; gap: var(--space-5); margin: 0; }
.omd-glance__value { font: var(--fw-bold) var(--fs-h3)/1.2 var(--font-numeric); color: var(--text-strong); }
.omd-glance__label { font: var(--type-sm); color: var(--text-muted); margin: var(--space-1) 0 0; }

/* The promises in the design are a check-marked list. Written as an ordinary list in the
   editor, so the marker is drawn rather than typed. */
.omd-about__body ul { list-style: none; padding-inline-start: 0; gap: var(--space-3); }
.omd-about__body ul li { display: flex; gap: var(--space-3); align-items: flex-start; }

.omd-about__body ul li::before {
	content: "";
	flex: none;
	width: 19px;
	height: 19px;
	margin-top: var(--nudge-optical);
	background: var(--text-brand);
	/*
	 * The same check the icon set draws, as a mask rather than a character: the marker
	 * has to be the brand colour while the text beside it is not, and content authored in
	 * the editor has nowhere to carry an icon of its own.
	 */
	-webkit-mask: var(--mark-check) center / contain no-repeat;
	mask: var(--mark-check) center / contain no-repeat;
}

.omd-cta {
	background: var(--surface-inverse);
	color: var(--text-inverse);
	border-radius: var(--radius-card);
	padding: var(--space-7);
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-5);
	align-items: center;
	justify-content: space-between;
}

.omd-cta__body { min-width: 0; }
.omd-cta__title { font: var(--fw-semibold) var(--fs-title)/1.3 var(--font-core); margin: 0; }
.omd-cta__note { font: var(--type-sm); color: var(--sang-300); margin: var(--space-2) 0 0; max-width: 52ch; text-wrap: pretty; }
.omd-cta__actions { display: flex; gap: var(--space-5); flex-wrap: wrap; align-items: center; }
.omd-cta__link { font: var(--type-sm); color: var(--sang-300); }
.omd-cta__link:hover { color: var(--text-inverse); }

/* Tall enough for a thumb, and equal width so two of them read as one control. */
.omd-social { display: flex; gap: var(--space-3); flex-wrap: wrap; }

.omd-social__link {
	flex: 1 1 140px;
	min-height: var(--tap-min);
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	border: 1px solid var(--border-default);
	border-radius: var(--radius-control);
	color: var(--text-strong);
	font: var(--type-sm);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-social__link:hover { background: var(--sang-100); color: var(--text-strong); border-color: var(--border-default); }

/* The map runs to the card's edge, so the card has to be the thing that rounds it: the
   canvas is Leaflet's own element and its corners are square. */
.omd-map { display: flex; flex-direction: column; overflow: hidden; }

.omd-map__foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding: var(--space-4) var(--space-5);
	border-top: 1px solid var(--border-hairline);
}

.omd-map__where { min-width: 0; }
.omd-map__name { font: var(--fw-semibold) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); margin: 0; }
.omd-map__address { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); margin: 0; }
.omd-map__foot .omd-btn { flex: none; }

/* The pay-for-order page borrows the checkout's review layout wholesale; all it needs of
   its own is the room under its heading that the review step sets inline. */
.omd-paytitle { margin-bottom: var(--space-6); }

/* ------------------------------------------------------- 12. Paying by card */

/* The list of ways to pay. One card each, and the whole card is the link: a chevron on
   its own is a small target for a row that is entirely about going somewhere. */
.omd-methods { display: grid; gap: var(--space-5); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: start; }

.omd-method {
	display: flex;
	align-items: flex-start;
	gap: var(--space-4);
	padding: var(--space-6);
	border-radius: var(--radius-card);
	background: var(--surface-card);
	box-shadow: var(--shadow-2);
	text-decoration: none;
	color: var(--text-body);
	transition: box-shadow var(--dur-2) var(--ease-standard), transform var(--dur-2) var(--ease-standard);
}

.omd-method:hover { box-shadow: var(--shadow-3); transform: translateY(-2px); }

.omd-method__mark {
	width: 40px;
	height: 40px;
	flex: none;
	display: grid;
	place-items: center;
	border-radius: var(--radius-control);
	background: var(--sang-100);
	color: var(--text-body);
}

.omd-method__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.omd-method__head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.omd-method__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-method__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); }
.omd-method__go { flex: none; color: var(--text-muted); margin-top: var(--nudge-optical); }

/* Settings that are switches rather than fields still want the form's rhythm. */
.omd-panel__stack { display: flex; flex-direction: column; gap: var(--space-5); margin-top: var(--space-5); }
.omd-field--short { max-width: 200px; }
.omd-field__hint--loose { max-width: 66ch; line-height: 1.7; }

/* A card or IBAN is read a group at a time, so it gets the room to be. */
.omd-input--wide { letter-spacing: .06em; }
.omd-account__nums { display: flex; flex-direction: column; gap: 2px; align-items: flex-start; min-width: 0; }
.omd-account__card { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-numeric); color: var(--text-strong); letter-spacing: .04em; white-space: nowrap; }
.omd-account__nums .omd-table__note { overflow-wrap: anywhere; }

/* The deposit, on both sides of the counter: the operator's card on the order screen and
   the customer's on their own order page are the same block of facts beside the same
   picture, so they are the same rules. */
.omd-deposit { display: flex; flex-direction: column; gap: var(--space-5); }
.omd-deposit .omd-card__head { margin-bottom: 0; }

.omd-deposit__body { display: flex; align-items: flex-start; gap: var(--space-5); flex-wrap: wrap; }

/* Portrait, because a bank's receipt is: a screenshot of a phone or a slip from a
   machine. A square would crop the amount off one and letterbox the other. */
.omd-deposit__shot {
	width: 104px;
	flex: none;
	aspect-ratio: 3 / 4;
	display: block;
	overflow: hidden;
	border-radius: var(--radius-control);
	border: 1px solid var(--border-hairline);
	background: var(--sang-200);
}

.omd-deposit__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The operator's version keeps the panel's ruled list, which is what the facts above it
   on the same screen already are. */
.omd-deposit__rows { flex: 1; min-width: 220px; }
.omd-deposit__rows .omd-rows__value { white-space: normal; text-align: end; }
.omd-rows__value--num { letter-spacing: .03em; }

/* The customer's stacks label over value instead: it sits in the narrow column beside
   their order, where two things on one line would wrap into a mess. */
.omd-deposit__facts { flex: 1; min-width: 200px; }
.omd-deposit__facts > div { display: flex; flex-direction: column; gap: 2px; padding-block: var(--space-2); }
.omd-deposit__label { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-deposit__value { font: var(--type-sm); color: var(--text-strong); text-wrap: pretty; }
.omd-deposit__value--num { font: var(--fw-medium) var(--fs-sm)/1.6 var(--font-numeric); letter-spacing: .03em; }
.omd-deposit__acts { display: flex; gap: var(--space-3); flex-wrap: wrap; margin: 0; }

.omd-deposit__pay { display: flex; flex-direction: column; gap: var(--space-4); }

.omd-deposit__amount { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-4); }
.omd-deposit__amount span { font: var(--type-caps); letter-spacing: var(--ls-caps); color: var(--text-muted); }
.omd-deposit__amount strong { font: var(--type-price); color: var(--text-strong); }

.omd-bank {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	padding: var(--space-4) var(--space-5);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-bank__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; }
.omd-bank__holder { font: var(--type-body-strong); color: var(--text-strong); }
.omd-bank__name { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

/* Big, spaced and left-to-right: this number is about to be typed into a banking app off
   the screen, and four tight groups of four is where a digit gets lost. */
.omd-bank__card { font: var(--fw-semibold) var(--fs-title)/1.4 var(--font-numeric); color: var(--text-strong); letter-spacing: .06em; }
.omd-bank__sheba { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); overflow-wrap: anywhere; }

.omd-deposit__form { display: flex; flex-direction: column; gap: var(--space-5); border-top: 1px solid var(--border-hairline); padding-top: var(--space-5); }
.omd-deposit__again > .omd-deposit__form { border-top: 0; padding-top: var(--space-4); }
.omd-deposit__again > summary { display: inline-flex; cursor: pointer; list-style: none; }
.omd-deposit__again > summary::-webkit-details-marker { display: none; }

/* The picker. The input covers the whole box rather than sitting in it, so a tap
   anywhere opens the camera — which on a phone is where most of these come from. */
.omd-drop {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	text-align: center;
	min-height: 120px;
	padding: var(--space-5);
	border: 1px dashed var(--sang-400);
	border-radius: var(--radius-control);
	background: var(--surface-card);
	cursor: pointer;
	transition: var(--transition-control);
}

.omd-drop:hover { background: var(--sang-100); border-color: var(--shab-600); }
.omd-drop:focus-within { border-color: var(--shab-600); box-shadow: var(--ring-focus); }
.omd-drop__input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.omd-drop__mark { color: var(--text-muted); display: flex; }
.omd-drop__name { font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); overflow-wrap: anywhere; }
.omd-drop__hint { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

/* Chosen. Said with colour as well as with the name, because the name alone is easy to
   miss on a box that otherwise looks exactly as it did. */
.omd-drop--filled { border-style: solid; border-color: var(--state-success); background: var(--sabz-100); }
.omd-drop--filled .omd-drop__mark { color: var(--state-success); }

/*
 * The same picker as one control in a row, for a file chosen beside other fields rather
 * than on a page of its own. A bare <input type="file"> renders as the browser's own
 * button and truncated «no file chosen», which belongs to no design at all; this is the
 * kit's own control with the input still covering it.
 */
.omd-drop--inline {
	flex-direction: row;
	justify-content: flex-start;
	text-align: start;
	/* The height of a field, because that is what it stands in a row of. */
	min-height: var(--control-h);
	height: var(--control-h);
	padding: 0 var(--space-5);
	/* The pill the fields beside it wear. A rounded rectangle among pills reads as a
	   different kind of control, which this is not: it is one field of three. */
	border-radius: var(--radius-pill);
	min-width: 0;
}

.omd-drop--inline .omd-drop__name {
	font-weight: var(--fw-regular);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	overflow-wrap: normal;
}

/* ------------------------------------------------------ 12. Numeric fields */

/*
 * A field that holds a quantity shows Persian numerals while holding Latin ones, so the
 * customer reads ۱۵۰۰۰۰۰ and the server parses 1500000 with nothing converted in between.
 *
 * Last in the file, and it has to be: form controls do not inherit font-feature-settings
 * (the UA resets their font properties wholesale), and the `font` shorthand resets it
 * again wherever one is used, which .omd-btn and .omd-qty__value both do. Anything
 * setting `font` on these fields after this point would silently turn it back off.
 */
input[inputmode="numeric"],
input[type="tel"] { font-feature-settings: "ss01"; }

/* ------------------------------------------------------------------ comments */

/* Five stars, in two colours. The filled and the empty one are the same shape, which is
   what makes a four look like a four rather than like a different icon. */
.omd-stars { display: inline-flex; gap: 2px; vertical-align: middle; }
.omd-stars__one { display: flex; color: var(--sang-400); }
.omd-stars__one--on { color: var(--anar-600); }
.omd-stars__one svg { fill: currentColor; }

.omd-comments { display: flex; flex-direction: column; gap: var(--space-6); }

/* Under an article the section needs separating from what it is about. Inside a card it
   does not: the card is the separation, and its padding is the same padding «مشخصات»
   sits in. So the space lives on the blog's own wrapper rather than on the component. */
.omd-comments-below { margin-top: var(--space-8); padding-top: var(--space-7); border-top: 1px solid var(--border-hairline); }
.omd-comments__head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.omd-comments__title { font: var(--type-title); color: var(--text-strong); margin: 0; }
.omd-comments__count { font: var(--fw-regular) var(--fs-sm)/1.6 var(--font-numeric); color: var(--text-muted); }
.omd-comments__write { margin-inline-start: auto; }

/* The rating summary: the average on one side, how the votes fell on the other. */
.omd-crate {
	display: grid;
	grid-template-columns: minmax(0, auto) minmax(0, 1fr);
	gap: var(--space-6);
	align-items: center;
	padding: var(--space-5) var(--space-6);
	background: var(--sang-100);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-card);
}

@media (max-width: 560px) {
	.omd-crate { grid-template-columns: 1fr; }
}

.omd-crate__score { display: flex; flex-direction: column; gap: var(--space-2); }
.omd-crate__avg { display: flex; align-items: baseline; gap: var(--space-2); }
.omd-crate__number { font: var(--fw-bold) var(--fs-h2)/1 var(--font-numeric); color: var(--text-strong); }
.omd-crate__of, .omd-crate__rated { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }

.omd-crate__bars { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.omd-crate__bar { display: flex; align-items: center; gap: var(--space-3); }
.omd-crate__label { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); min-width: 52px; }
.omd-crate__many { font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-body); min-width: 24px; }

.omd-crate__track {
	flex: 1;
	height: 6px;
	min-width: 60px;
	border-radius: var(--radius-pill);
	background: var(--sang-300);
	overflow: hidden;
}

.omd-crate__fill { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--anar-600); }

.omd-comments__none {
	padding: var(--space-7) var(--space-6);
	border: 1px dashed var(--border-default);
	border-radius: var(--radius-card);
	text-align: center;
}

.omd-comments__none-title { font: var(--type-body-strong); color: var(--text-strong); }
.omd-comments__none-note { font: var(--type-sm); color: var(--text-muted); margin: var(--space-2) 0 0; }

.omd-comments__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.omd-comment { display: flex; gap: var(--space-4); padding: var(--space-5) 0; }
.omd-comment + .omd-comment { border-top: 1px solid var(--border-hairline); }

.omd-comment__initial {
	width: 40px;
	height: 40px;
	flex: none;
	border-radius: var(--radius-pill);
	background: var(--sang-200);
	display: grid;
	place-items: center;
	font: var(--fw-semibold) var(--fs-sm)/1 var(--font-core);
	color: var(--text-body);
}

.omd-comment__body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: var(--space-2); }
.omd-comment__who { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.omd-comment__name { font: var(--type-body-strong); color: var(--text-strong); }
.omd-comment__date { font: var(--fw-regular) var(--fs-micro)/1.6 var(--font-numeric); color: var(--text-muted); }
.omd-comment__text { font: var(--fw-regular) var(--fs-sm)/1.85 var(--font-core); color: var(--text-body); max-width: 74ch; }
.omd-comment__text p { margin: 0 0 var(--space-3); }
.omd-comment__text p:last-child { margin-bottom: 0; }

/* The shop's answer, indented under what it answers so the two are never read as one. */
.omd-comment__reply {
	margin-top: var(--space-2);
	margin-inline-start: var(--space-6);
	padding: var(--space-4) var(--space-5);
	background: var(--sang-100);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-comment__reply-head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-2); }
.omd-comment__reply-tag { font: var(--type-caps); letter-spacing: var(--ls-caps); color: var(--text-brand); }
.omd-comment__reply-by { font: var(--fw-semibold) var(--fs-xs)/1.5 var(--font-core); color: var(--text-strong); }

.omd-comment__reply-mark {
	width: 26px;
	height: 26px;
	flex: none;
	display: grid;
	place-items: center;
	border-radius: var(--radius-pill);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	color: var(--text-brand);
}

.omd-comments__form { border-top: 1px solid var(--border-hairline); padding-top: var(--space-6); }
.omd-comments__form-title { font: var(--type-body-strong); color: var(--text-strong); margin: 0 0 var(--space-4); }
/* A field is a <p>, and the browser gives a <p> a margin the theme never resets. In a
   flex column that margin is added to the gap rather than collapsing into it, which put
   two lines of air between the stars and the box under them. */
.omd-cform { display: flex; flex-direction: column; gap: var(--space-4); }
.omd-cform .omd-field, .omd-cform .form-submit, .omd-cform p { margin: 0; }

.omd-comments__login {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-5);
	flex-wrap: wrap;
	padding: var(--space-5) var(--space-6);
	background: var(--sang-100);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-card);
}

.omd-comments__login-title { font: var(--type-body-strong); color: var(--text-strong); }
.omd-comments__login-note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: var(--space-2) 0 0; max-width: 56ch; }

/* The rating picker: five radios the stylesheet draws as stars.
   Written in reverse in the markup so that "this one and everything after it" is a
   sibling selector, which is the only direction CSS can look. */
.omd-rate { flex-direction: row; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.omd-rate__stars { display: flex; flex-direction: row-reverse; justify-content: flex-end; gap: var(--space-1); }

/* The reading beside the stars. One span per score, and the checked star picks which. */
.omd-rate__score { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric); color: var(--text-muted); }
.omd-rate__score > span { display: none; }
.omd-rate:has(#omd-rate-1:checked) .omd-rate__score > [data-omd-rate="1"],
.omd-rate:has(#omd-rate-2:checked) .omd-rate__score > [data-omd-rate="2"],
.omd-rate:has(#omd-rate-3:checked) .omd-rate__score > [data-omd-rate="3"],
.omd-rate:has(#omd-rate-4:checked) .omd-rate__score > [data-omd-rate="4"],
.omd-rate:has(#omd-rate-5:checked) .omd-rate__score > [data-omd-rate="5"] { display: inline; }
.omd-rate__input { position: absolute; opacity: 0; width: 0; height: 0; }
.omd-rate__star { display: flex; padding: var(--space-1); cursor: pointer; color: var(--sang-400); transition: var(--transition-control); }
.omd-rate__star svg { fill: currentColor; }
.omd-rate__input:checked ~ .omd-rate__star,
.omd-rate__star:hover,
.omd-rate__star:hover ~ .omd-rate__star { color: var(--anar-600); }
.omd-rate__input:focus-visible + .omd-rate__star { outline: 2px solid var(--shab-600); outline-offset: 2px; border-radius: var(--radius-control); }

/* Two panels on the product page, switched by the address bar rather than by a script.
   A panel shows when it is what the link pointed at, or when anything inside it is. */
.omd-ptabs__bar { display: flex; gap: var(--space-2); border-bottom: 1px solid var(--border-hairline); margin-bottom: var(--space-5); }

.omd-ptabs__tab {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-3) var(--space-4);
	margin-bottom: -1px;
	border-bottom: 2px solid transparent;
	font: var(--fw-medium) var(--fs-sm)/1.5 var(--font-core);
	color: var(--text-muted);
	text-decoration: none;
}

.omd-ptabs__tab:hover { color: var(--text-strong); }
.omd-ptabs__count { font: var(--fw-regular) var(--fs-micro)/1 var(--font-numeric); color: var(--text-faint); }
.omd-ptabs__panel { display: none; }
.omd-ptabs:not(.omd-ptabs--live) > .omd-ptabs__panel:target,
.omd-ptabs:not(.omd-ptabs--live) > .omd-ptabs__panel:has(:target) { display: block; }
.omd-ptabs:not(:has(:target)):not(.omd-ptabs--live) > .omd-ptabs__panel--lead { display: block; }

.omd-ptabs:not(:has(:target)):not(.omd-ptabs--live) .omd-ptabs__tab--specs,
.omd-ptabs:not(.omd-ptabs--live):has(.omd-ptabs__panel--lead:target) .omd-ptabs__tab--specs,
.omd-ptabs:not(.omd-ptabs--live):has(.omd-ptabs__panel--talk :target) .omd-ptabs__tab--talk {
	color: var(--text-strong);
	border-bottom-color: var(--anar-600);
}

/* With a script the panel is chosen by a class, so switching tabs moves nothing. The
   rules above still draw the page on arrival, which is what stops a link into a comment
   flashing the wrong tab before this takes over. */
.omd-ptabs--live > .omd-ptabs__panel--on { display: block; }
.omd-ptabs--live .omd-ptabs__tab--on { color: var(--text-strong); border-bottom-color: var(--anar-600); }

/* -------------------------------------------------------- comments, in the panel */

.omd-csetup { display: grid; gap: var(--space-6); grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); align-items: start; }
.omd-panel__count { font: var(--type-sm); color: var(--text-muted); margin-inline-start: auto; }

/* The queue's rows. A waiting one is tinted, so a screen full of them reads as work. */
.omd-crows { display: flex; flex-direction: column; }
.omd-crow { display: flex; gap: var(--space-4); padding: var(--space-5) var(--space-6); }
.omd-crow + .omd-crow { border-top: 1px solid var(--border-hairline); }
.omd-crow--waiting { background: var(--sang-100); }
.omd-crow__body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: var(--space-2); }
.omd-crow__subject { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.omd-crow__subject a { font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-core); color: var(--text-brand); text-decoration: none; }
.omd-crow__subject a:hover { text-decoration: underline; }
.omd-crow__text { font: var(--fw-regular) var(--fs-sm)/1.85 var(--font-core); color: var(--text-body); margin: 0; max-width: 78ch; text-wrap: pretty; }

.omd-crow__reply {
	padding: var(--space-4) var(--space-5);
	background: var(--surface-card);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
}

.omd-crow__reply-by { font: var(--fw-semibold) var(--fs-xs)/1.5 var(--font-core); color: var(--text-strong); }
.omd-crow__reply p { font: var(--fw-regular) var(--fs-sm)/1.8 var(--font-core); color: var(--text-body); margin: var(--space-2) 0 0; max-width: 70ch; }

.omd-crow__acts { display: flex; gap: var(--space-2); flex-wrap: wrap; margin-top: var(--space-2); }
.omd-crow__acts form { margin: 0; }

/* Answering and correcting open on the page rather than in a dialog: both are edits of a
   paragraph, and a page that answers without moving is easier to answer on. */
.omd-crow__more > summary {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	cursor: pointer;
	font: var(--fw-medium) var(--fs-xs)/1.6 var(--font-core);
	color: var(--text-muted);
	padding: var(--space-2) 0;
	list-style: none;
}

.omd-crow__more > summary::-webkit-details-marker { display: none; }
.omd-crow__more > summary:hover { color: var(--text-strong); }
.omd-crow__form { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-2); }
.omd-crow__form-foot { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }

/* What is waiting on a screen, beside its name in the rail. */
.omd-panel__link-count {
	margin-inline-start: auto;
	min-width: 20px;
	padding: 0 var(--space-2);
	border-radius: var(--radius-pill);
	background: var(--anar-600);
	color: var(--text-inverse);
	font: var(--fw-semibold) var(--fs-micro)/20px var(--font-numeric);
	text-align: center;
}

/* --------------------------------------------- 13. غذای سازمانی */

/*
 * The contract's menu, picked one meal at a time.
 *
 * Every meal's panel is in the page at once, because they are all part of one unsaved
 * form: the tabs are radios, so moving between meals is a paint rather than a request and
 * nothing typed is lost. The radio also means this still works with the script blocked,
 * which the search box above the list does not, so that box is hidden until it does.
 */
.omd-menupick {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	margin-top: var(--space-5);
}

.omd-menupick__tabs {
	display: inline-flex;
	align-self: flex-start;
	background: var(--surface-sunken);
	border-radius: var(--radius-pill);
	padding: var(--space-1);
}

.omd-menupick__tab {
	position: relative;
	display: inline-flex;
	align-items: center;
	padding: var(--space-3) var(--space-5);
	border-radius: var(--radius-pill);
	font: var(--fw-medium) var(--fs-xs)/1 var(--font-core);
	color: var(--text-muted);
	cursor: pointer;
	white-space: nowrap;
	transition: var(--transition-control);
}

.omd-menupick__tab > input { position: absolute; opacity: 0; width: 0; height: 0; }

/* The design system draws this pair as --sang-000 on --shab-800, which is the light theme
   written down: in the dark one that second rung is a near-black and the label lands on its
   own background at 1.2:1. The semantic pair says the same thing in both. */
.omd-menupick__tab:has(> input:checked) {
	background: var(--surface-card);
	color: var(--text-strong);
	box-shadow: var(--shadow-1);
}

.omd-menupick__tab:has(> input:focus-visible) { box-shadow: var(--ring-focus); }

.omd-menupick__tab-count { color: var(--text-faint); margin-inline-start: var(--space-2); }

/*
 * Which panel shows. A meal is a fixed set of three in the domain, and CSS cannot be
 * written to follow a value, so each one is named here; a fourth meal would add a line.
 */
.omd-menupick__panel { display: none; flex-direction: column; gap: var(--space-3); }

.omd-menupick:has(.omd-menupick__tab > input[value="breakfast"]:checked) .omd-menupick__panel[data-meal="breakfast"],
.omd-menupick:has(.omd-menupick__tab > input[value="lunch"]:checked) .omd-menupick__panel[data-meal="lunch"],
.omd-menupick:has(.omd-menupick__tab > input[value="dinner"]:checked) .omd-menupick__panel[data-meal="dinner"] {
	display: flex;
}

.omd-menupick__note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; text-wrap: pretty; }

.omd-menupick__find { max-width: 320px; }

/* Tall enough to be worth scrolling, short enough that the save button stays reachable. */
.omd-menupick__list {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	max-height: 440px;
	overflow-y: auto;
}

/*
 * The floor under a searched list, so the dialog keeps its size.
 *
 * The list and its «nothing found» note share this box rather than sitting one under the
 * other: the height is reserved once, and a search that finds nothing fills it instead of
 * collapsing it. Without that, every keystroke that narrowed the results to none shrank the
 * dialog to the height of one sentence and the box jumped out from under the pointer.
 *
 * The scroll moves here too. Two nested scrollers, one on the list and one on the box
 * around it, is a list that scrolls the wrong one first.
 */
.omd-menupick__pane {
	display: flex;
	flex-direction: column;
	/* Against the screen as well as in pixels, so a short window scrolls the list inside the
	   dialog rather than the dialog inside itself, which takes the search box and the buttons
	   off the top and bottom of it. */
	min-height: min(280px, 25vh);
	max-height: min(440px, 45vh);
	overflow-y: auto;
}

/*
 * A dialog whose body is a searched list scrolls in one place: the list.
 *
 * The panel is a column that cannot scroll, and the pane takes whatever room is left after
 * the heading and the search box, so a list too tall for that room shrinks rather than
 * pushing the panel past its own height. The panel used to scroll as well, which put a
 * second scrollbar beside the first and moved the search box off the top of the dialog.
 *
 * Hidden rather than relying on the numbers adding up: two scrollers inside each other is
 * the sort of thing that appears on one window size and not another, and the pane yielding
 * its room is what makes the outer one impossible rather than merely unlikely.
 */
.omd-dialog__panel:has(> .omd-menupick__pane) {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

.omd-dialog__panel > .omd-menupick__pane { flex: 1 1 auto; }

.omd-menupick__pane > .omd-menupick__list { max-height: none; overflow: visible; }

.omd-menupick__pane > .omd-panel__empty {
	flex: 1;
	display: grid;
	place-items: center;
	text-align: center;
	text-wrap: pretty;
}

.omd-menupick__row {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	padding: var(--space-3) var(--space-4);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	transition: var(--transition-control);
}

.omd-menupick__row:hover { background: var(--surface-page); }

.omd-menupick__pick { flex: 1; min-width: 0; }
.omd-menupick__price { flex: 0 0 150px; }

/* What was typed into the box, grouped. Its width is held whether or not there is anything
   in it, so the name beside it does not move a step every time a digit is added. */
.omd-menupick__said {
	flex: 0 0 auto;
	min-width: 110px;
	text-align: end;
	font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-numeric);
	color: var(--text-muted);
	white-space: nowrap;
}

@media (max-width: 720px) {
	.omd-menupick__row { flex-wrap: wrap; }
	.omd-menupick__price { flex-basis: 100%; }
}

/*
 * The programme: one card per date, seven to a row, a week at a time.
 *
 * Seven columns whatever the contract's dates are, so a weekday is always in the same
 * place and a menu can be read down a column. Days outside the contract are drawn and
 * greyed rather than left out, because a row with four cells in it reads as a week with
 * three days missing.
 *
 * It scrolls sideways under about a thousand pixels instead of reflowing. A week that
 * wrapped to two rows of three and a half would stop being a week, and this is a screen for
 * a desk: the phone case is the employer's read-only view, which is a list.
 */

.omd-week {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	margin-top: var(--space-5);
}

.omd-week__bar {
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
}

.omd-week__nav { display: flex; align-items: center; gap: var(--space-2); }
.omd-week__label { min-width: 200px; text-align: center; font: var(--type-sm); color: var(--text-strong); }
.omd-week__spacer { margin-inline-start: auto; }

.omd-week__scroll { overflow-x: auto; }

.omd-week__grid {
	display: grid;
	grid-template-columns: repeat(7, minmax(0, 1fr));
	gap: var(--space-3);
	min-width: 980px;
}

.omd-week[aria-busy="true"] .omd-week__grid { opacity: .5; pointer-events: none; }

.omd-week__day {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	min-height: 196px;
	padding: var(--space-3);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	background: var(--surface-card);
}

/* Not a service day: still a cell, visibly not one that takes food. */
.omd-week__day--shut {
	border-color: transparent;
	background: var(--surface-page);
}

.omd-week__day--away { opacity: .45; }

.omd-week__head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--space-2);
}

.omd-week__name { font: var(--fw-semibold) var(--fs-sm)/1.5 var(--font-core); color: var(--text-strong); }
.omd-week__date { font: var(--fw-regular) var(--fs-xs)/1.5 var(--font-numeric); color: var(--text-muted); }
.omd-week__note { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--text-muted); }
.omd-week__gap { font: var(--fw-regular) var(--fs-xs)/1.6 var(--font-core); color: var(--state-warning); }

.omd-week__items { display: flex; flex-direction: column; gap: var(--space-2); }

.omd-week__item {
	display: flex;
	align-items: flex-start;
	gap: var(--space-2);
	padding: var(--space-2) var(--space-3);
	border: 1px solid var(--border-hairline);
	border-radius: var(--radius-control);
	background: var(--surface-card);
}

.omd-week__item-body { flex: 1; min-width: 0; }
.omd-week__item-name { font: var(--fw-medium) var(--fs-xs)/1.5 var(--font-core); color: var(--text-strong); }
.omd-week__item-price { font: var(--fw-regular) var(--fs-micro)/1.5 var(--font-numeric); color: var(--text-muted); }

.omd-week__acts { margin-top: auto; display: flex; align-items: center; gap: var(--space-2); }

/* The rotation bar: pick a saved week, lay it over the whole contract, or keep this one. */
.omd-week__tpl {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	flex-wrap: wrap;
	padding: var(--space-3) var(--space-4);
	border-radius: var(--radius-control);
	background: var(--surface-page);
}

.omd-week__tpl[aria-busy="true"] { opacity: .6; pointer-events: none; }
.omd-week__tpl-pick { min-width: 220px; }

/* The label and the note carry a block element's own margins, which is what tips a row of
   controls off its centre line. The bar's alignment is its own business. */
.omd-week__tpl > * { margin: 0; }
.omd-week__tpl .omd-eyebrow { margin: 0; flex: none; }
.omd-week__tpl .omd-menupick__said { text-align: start; white-space: normal; }

/*
 * Two shells the operator panel does not have: the employer's, which borrows the panel's
 * own chrome wholesale, and the staff one, which does not.
 *
 * Staff read this on a phone in a canteen queue, so there is no rail: the nav is a row of
 * tags, the allowance sits in the header where it is being looked for, and the menu is
 * cards rather than a table because a table of two columns on a phone is a list with extra
 * rules drawn on it.
 */

.omd-staff {
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	background: var(--surface-page);
}

.omd-staff__head {
	position: sticky;
	top: var(--admin-bar-h, 0px);
	z-index: var(--z-sticky);
	background: var(--surface-card);
	border-bottom: 1px solid var(--border-hairline);
	padding: var(--space-3) var(--gutter-inline);
	display: flex;
	align-items: center;
	gap: var(--space-4);
	flex-wrap: wrap;
}

.omd-staff__brand { display: flex; flex-direction: column; }
.omd-staff__brand-name { font: var(--fw-semibold) var(--fs-sm)/1.3 var(--font-core); color: var(--text-strong); }
.omd-staff__brand-sub { font: var(--fw-regular) var(--fs-micro)/1.4 var(--font-core); color: var(--text-muted); }

.omd-staff__nav { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.omd-staff__tab {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	padding: var(--space-2) var(--space-4);
	border-radius: var(--radius-chip);
	border: 1px solid var(--border-hairline);
	background: var(--surface-card);
	color: var(--text-body);
	font: var(--fw-medium) var(--fs-sm)/1.4 var(--font-core);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-staff__tab:hover { background: var(--sang-100); border-color: var(--border-default); color: var(--text-strong); }

.omd-staff__tab--on,
.omd-staff__tab--on:hover { background: var(--shab-800); border-color: var(--shab-800); color: var(--text-inverse); }

/* The allowance, which is the number this whole panel exists to spend. */
.omd-staff__purse { margin-inline-start: auto; text-align: end; }
.omd-staff__purse-label { display: block; font: var(--type-caps); letter-spacing: var(--ls-caps); color: var(--text-muted); }
.omd-staff__purse-value { display: block; font: var(--fw-bold) var(--fs-sm)/1.3 var(--font-numeric); color: var(--text-strong); }
.omd-staff__purse-value--out { color: var(--state-danger); }

.omd-staff__out {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--control-h-sm);
	height: var(--control-h-sm);
	border-radius: var(--radius-pill);
	color: var(--text-muted);
	text-decoration: none;
	transition: var(--transition-control);
}

.omd-staff__out:hover { background: var(--sang-200); color: var(--text-body); }

.omd-staff__body {
	flex: 1;
	width: 100%;
	max-width: 1100px;
	margin: 0 auto;
	padding: var(--space-6) var(--gutter-inline) var(--space-11);
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
}

/* The days, as a strip that scrolls sideways rather than wrapping into a calendar. */
.omd-staff__days {
	display: flex;
	gap: var(--space-2);
	overflow-x: auto;
	padding-bottom: var(--space-2);
	scrollbar-width: none;
}

.omd-staff__days::-webkit-scrollbar { height: 0; }

.omd-staff__day-dow { font: var(--fw-medium) var(--fs-xs)/1.4 var(--font-core); }
.omd-staff__day-dom { font: var(--fw-bold) var(--fs-body)/1.2 var(--font-numeric); }
.omd-staff__day-month { font: var(--fw-regular) var(--fs-micro)/1.4 var(--font-core); opacity: .75; }

.omd-staff__day {
	flex: none;
	min-width: 74px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 2px;
	padding: var(--space-3) var(--space-4);
	border-radius: var(--radius-control);
	border: 1px solid var(--border-hairline);
	background: var(--surface-card);
	color: var(--text-body);
	text-decoration: none;
	white-space: nowrap;
	transition: var(--transition-control);
}

.omd-staff__day:hover { border-color: var(--border-default); color: var(--text-strong); }
.omd-staff__day--on,
.omd-staff__day--on:hover { background: var(--shab-600); border-color: var(--shab-600); color: var(--text-inverse); }

/* Who is looking, and what their group lets them have. */
.omd-staff__hello { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.omd-staff__greeting { font: var(--fw-semibold) var(--fs-h3)/1.3 var(--font-core); color: var(--text-strong); margin: 0; }
.omd-staff__group { font: var(--type-sm); color: var(--text-muted); }

.omd-staff__tab-count {
	min-width: 20px;
	padding: 0 var(--space-2);
	border-radius: var(--radius-pill);
	background: var(--surface-brand);
	color: var(--text-inverse);
	font: var(--fw-bold) var(--fs-micro)/20px var(--font-numeric);
	text-align: center;
}

/* The menu and what is beside it: the wallet and the cart on a desk, under the dishes on a
   phone, because a canteen queue is held one thumb at a time. */
.omd-staff__pick { display: grid; gap: var(--space-5); align-items: start; }

@media (min-width: 900px) {
	.omd-staff__pick { grid-template-columns: minmax(0, 1fr) 320px; }
	.omd-staff__aside { position: sticky; top: var(--space-6); }
}

.omd-staff__aside { display: flex; flex-direction: column; gap: var(--space-4); }

.omd-staff__purse-card { gap: var(--space-3); }
.omd-staff__purse-left { font: var(--fw-bold) var(--fs-h2)/1.2 var(--font-numeric); color: var(--text-strong); margin: 0; }
.omd-staff__purse-of { font: var(--type-sm); color: var(--text-muted); margin: 0; }

/* How much of the period's allowance has gone, as a line rather than a number to work out. */
.omd-staff__bar {
	display: block;
	height: 8px;
	border-radius: var(--radius-pill);
	background: var(--sang-200);
	overflow: hidden;
}

.omd-staff__bar-fill { display: block; height: 100%; background: var(--surface-brand); }
.omd-staff__bar-fill--out { background: var(--state-danger); }

/* In a 320px column a name, a price and a stepper on one line is three squeezed things.
   The name takes its own line and the other two share the one under it. The rows that are
   only a label and a number are left alone. */
.omd-staff__line { flex-wrap: wrap; }
.omd-staff__line .omd-rows__name { flex: 1 1 100%; }

/* The stepper is the shop's, and in a cart row it sits at the end of the line beside the
   price rather than filling it. */
.omd-staff__line .omd-qty { margin-inline-start: auto; }

.omd-staff__dish-head { display: flex; align-items: flex-start; gap: var(--space-3); justify-content: space-between; }
.omd-staff__dish-unit { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; }

/* The price and the thing to do about it, on one line at the foot of the card. */
.omd-staff__dish-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	flex-wrap: wrap;
	margin-top: auto;
}

.omd-staff__dish-foot form { margin: 0; }

/* «افزودن» stands where the stepper will stand once the dish is in the cart, so the card
   does not change shape under the thumb that pressed it. */
.omd-staff__dish-foot .omd-btn {
	min-width: 140px;
	min-height: 40px;
}

.omd-staff__menu {
	display: grid;
	gap: var(--space-4);
	grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

/* A floor under every card, so a grid of them is a grid rather than a row of tall cards
   over a row of short ones. A card that has more to say, a note or two lines of name,
   still grows; what the floor stops is the ones with nothing extra sitting half height. */
.omd-staff__dish {
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
	min-height: 9.5rem;
}
.omd-staff__dish-name { font: var(--fw-semibold) var(--fs-body)/1.5 var(--font-core); color: var(--text-strong); margin: 0; }
.omd-staff__dish-note { font: var(--fw-regular) var(--fs-xs)/1.7 var(--font-core); color: var(--text-muted); margin: 0; }

/* The price sits above the button and the button is the last thing in the card, so a row
   of cards whose names run to different lengths still lines its buttons up. */

/*
 * The staff door. The shop's own sign-in head above one centred card, and nothing else on
 * the page: whoever is looking at this has been given two things to type and no reason to
 * be offered a third.
 */
.omd-door {
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: var(--space-7) var(--space-5);
	box-sizing: border-box;
	background: var(--surface-page);
}

/* The head belongs to the card below it, so the space between them is the card's own
   rather than the one the shop's sign-in leaves under a head that stands alone. */
.omd-door .omd-signin__head { margin-bottom: var(--space-5); }

.omd-door__card {
	width: 100%;
	max-width: 420px;
	display: flex;
	flex-direction: column;
	gap: var(--space-5);
	background: var(--surface-card);
	border-radius: var(--radius-card);
	box-shadow: var(--shadow-2);
	padding: var(--space-7) var(--gutter-card);
	box-sizing: border-box;
}

.omd-door__form { display: flex; flex-direction: column; gap: var(--space-4); margin: 0; }

/* The other thing this door does, as a line under the button rather than a second form
   stacked beneath the first. */
.omd-door__other {
	align-self: center;
	font: var(--type-sm);
	color: var(--text-link);
	text-decoration: none;
	border-bottom: 1px solid currentColor;
}

.omd-door__other:hover { color: var(--text-link-hover); }

