/* ============================================================================
   Dar Yazya — component classes
   ----------------------------------------------------------------------------
   Derived from the design system's own SIZES / VARIANTS / TONES maps, which the
   React components previously applied as inline styles with hover held in JS
   state. Expressing them here instead means:
     · the PHP partials and the React islands render the same appearance from
       one source, and cannot drift
     · hover, focus and press work before hydration, and without JS at all
     · prefers-reduced-motion in tokens/motion.css finally governs everything
   Every value below traces to a token. No literal colours except the two the
   design system itself hardcodes (#FFFFFF in the inverse skins).
   ========================================================================== */

/* ---------------------------------------------------------------- Button -- */
.dy-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  border:1px solid transparent;border-radius:var(--radius-control);
  font-family:var(--font-body);font-weight:var(--weight-medium);line-height:1;
  text-transform:uppercase;text-decoration:none;text-underline-offset:.28em;
  white-space:nowrap;cursor:pointer;
  transition:var(--transition-color),transform var(--dur-fast) var(--ease-standard);
}
.dy-btn--full{display:flex;width:100%}

/* sizes */
.dy-btn--sm{height:var(--control-h-sm);padding:0 var(--space-4);font-size:var(--size-caption);letter-spacing:.06em}
.dy-btn--md{height:var(--control-h);padding:0 var(--space-6);font-size:var(--size-body-sm);letter-spacing:.05em}
.dy-btn--lg{height:var(--control-h-lg);padding:0 var(--space-8);font-size:var(--size-body);letter-spacing:.045em}

/* variants */
.dy-btn--primary{background:var(--oxblood-700);color:var(--text-on-brand);border-color:var(--oxblood-700)}
.dy-btn--primary:hover{background:var(--oxblood-600);border-color:var(--oxblood-600)}

.dy-btn--secondary{background:transparent;color:var(--oxblood-700);border-color:var(--oxblood-700)}
.dy-btn--secondary:hover{background:var(--oxblood-50);border-color:var(--oxblood-700)}

.dy-btn--quiet{background:var(--sand-100);color:var(--neutral-900);border-color:var(--sand-100)}
.dy-btn--quiet:hover{background:var(--sand-200);border-color:var(--sand-200)}

.dy-btn--ghost{background:transparent;color:var(--neutral-900);border-color:transparent}
.dy-btn--ghost:hover{background:var(--neutral-100);border-color:transparent}

.dy-btn--inverse{background:var(--sand-100);color:var(--oxblood-700);border-color:var(--sand-100)}
.dy-btn--inverse:hover{background:#FFFFFF;border-color:#FFFFFF}

.dy-btn--link{height:auto;padding:0;background:transparent;color:var(--text-link);border-color:transparent}
.dy-btn--link:hover{background:transparent;color:var(--text-link-hover);text-decoration:underline}

/* shape + state */
.dy-btn--pill{border-radius:var(--radius-pill)}
.dy-btn:active:not(:disabled){transform:scale(var(--press-scale))}
.dy-btn:disabled,.dy-btn[aria-disabled="true"]{
  background:var(--state-disabled-bg);color:var(--state-disabled-fg);
  border-color:transparent;cursor:not-allowed;transform:none;
}
.dy-btn--link:disabled,.dy-btn--link[aria-disabled="true"]{background:transparent}

/* ------------------------------------------------------------------ Icon -- */
/* Lucide glyphs painted with CSS mask so they inherit currentColor. The URL is
   the one genuinely per-instance value, passed as --dy-icon. */
.dy-icon{
  display:inline-block;flex:0 0 auto;
  width:var(--dy-icon-size,20px);height:var(--dy-icon-size,20px);
  background-color:currentColor;
  -webkit-mask-image:var(--dy-icon);mask-image:var(--dy-icon);
  -webkit-mask-size:contain;mask-size:contain;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
}

/* ----------------------------------------------------------------- Badge -- */
.dy-badge{
  display:inline-flex;align-items:center;gap:var(--space-1);
  height:24px;padding:0 var(--space-3);border-radius:var(--radius-pill);
  font-family:var(--font-body);font-weight:var(--weight-semibold);
  font-size:var(--size-caption);line-height:1;
  letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;
}
.dy-badge--sm{height:20px;padding:0 var(--space-2);font-size:var(--size-micro)}

.dy-badge--neutral{background:var(--neutral-100);color:var(--neutral-700)}
.dy-badge--neutral.dy-badge--solid{background:var(--neutral-800);color:var(--sand-100)}
.dy-badge--brand{background:var(--oxblood-50);color:var(--oxblood-700)}
.dy-badge--brand.dy-badge--solid{background:var(--oxblood-700);color:var(--sand-100)}
.dy-badge--rose{background:var(--rose-50);color:var(--rose-700)}
.dy-badge--rose.dy-badge--solid{background:var(--rose-500);color:#FFFFFF}
.dy-badge--olive{background:var(--olive-50);color:var(--olive-700)}
.dy-badge--olive.dy-badge--solid{background:var(--olive-500);color:#FFFFFF}
.dy-badge--ochre{background:var(--ochre-100);color:var(--ochre-600)}
.dy-badge--ochre.dy-badge--solid{background:var(--ochre-500);color:#FFFFFF}
.dy-badge--danger{background:var(--state-danger-soft);color:var(--oxblood-600)}
.dy-badge--danger.dy-badge--solid{background:var(--state-danger);color:#FFFFFF}

/* ------------------------------------------------------------------ Card -- */
.dy-card{
  border:1px solid transparent;border-radius:var(--radius-card);
  transition:var(--transition-lift);
}
.dy-card--pad-none{padding:0}
.dy-card--pad-sm{padding:var(--card-pad-sm)}
.dy-card--pad-md{padding:var(--card-pad)}
.dy-card--pad-lg{padding:var(--card-pad-lg)}

.dy-card--plain{background:transparent}
.dy-card--outlined{background:var(--surface-card);border-color:var(--border-hairline)}
.dy-card--raised{background:var(--surface-raised);box-shadow:var(--shadow-md)}
.dy-card--sunken{background:var(--surface-sunken)}
.dy-card--photo{background:var(--surface-photo)}
.dy-card--inverse{background:var(--surface-inverse);color:var(--text-on-inverse)}

.dy-card--interactive:hover{
  box-shadow:var(--shadow-lift);
  transform:translateY(var(--rise-hover));
}

/* ------------------------------------------------------------ IconButton -- */
.dy-iconbtn{
  position:relative;display:inline-grid;place-items:center;padding:0;
  border:1px solid transparent;border-radius:var(--radius-circle);
  cursor:pointer;background:transparent;
  transition:var(--transition-color);
}
.dy-iconbtn--square{border-radius:var(--radius-control)}
.dy-iconbtn--sm{width:34px;height:34px;--dy-icon-size:16px}
.dy-iconbtn--md{width:42px;height:42px;--dy-icon-size:19px}
.dy-iconbtn--lg{width:50px;height:50px;--dy-icon-size:22px}

.dy-iconbtn--ghost{color:var(--neutral-900)}
.dy-iconbtn--ghost:hover{background:var(--neutral-100)}
.dy-iconbtn--outline{color:var(--oxblood-700);border-color:var(--border-default)}
.dy-iconbtn--outline:hover{background:var(--oxblood-50)}
.dy-iconbtn--solid{color:var(--text-on-brand);background:var(--oxblood-700)}
.dy-iconbtn--solid:hover{background:var(--oxblood-600)}
.dy-iconbtn--inverse{color:var(--sand-100)}
.dy-iconbtn--inverse:hover{background:rgba(243,235,223,.16)}

.dy-iconbtn:disabled{color:var(--state-disabled-fg);background:transparent;cursor:not-allowed}

.dy-iconbtn__badge{
  position:absolute;top:1px;inset-inline-end:-1px;
  min-width:17px;height:17px;padding:0 4px;
  display:grid;place-items:center;border-radius:var(--radius-pill);
  background:var(--oxblood-700);color:var(--sand-100);
  font:var(--weight-semibold) var(--size-micro)/1 var(--font-body);
}

/* ------------------------------------------------------------------- Tag -- */
.dy-tag{
  display:inline-flex;align-items:center;gap:var(--space-2);
  height:32px;padding:0 var(--space-4);
  background:transparent;color:var(--neutral-700);
  border:1px solid var(--border-default);border-radius:var(--radius-pill);
  font-family:var(--font-body);font-weight:var(--weight-medium);
  font-size:var(--size-caption);line-height:1;letter-spacing:.02em;
  cursor:default;transition:var(--transition-color);
}
.dy-tag--interactive{cursor:pointer}
.dy-tag--interactive:hover{background:var(--sand-100)}
.dy-tag--selected,.dy-tag[aria-pressed="true"]{
  background:var(--oxblood-700);color:var(--sand-100);border-color:var(--oxblood-700);
}
.dy-tag--selected:hover,.dy-tag[aria-pressed="true"]:hover{background:var(--oxblood-700)}
.dy-tag[aria-disabled="true"],.dy-tag:disabled{color:var(--state-disabled-fg);cursor:not-allowed}
.dy-tag__remove{display:grid;place-items:center;cursor:pointer;opacity:.7;--dy-icon-size:13px}
.dy-tag__remove:hover{opacity:1}

/* ======================================================================== */
/* Motion, drawn from the brand's own three motifs                          */
/* ------------------------------------------------------------------------ */
/* The design system is prescriptive about this: movement is slow and weighted,
   "like setting a heavy bowl down". Reveals are a fade plus a 6–12px rise, and
   slide-ins from the side and parallax are ruled out. Everything below stays
   inside that, and takes its shape from the scallop, the lattice screen and the
   endless butterfly line rather than inventing a new vocabulary.

   All of it is off under prefers-reduced-motion — that preference asks for no
   motion, not slower motion. */

/* -- 1. The reveal: fade and rise ---------------------------------------- */
.dy-reveal{
  opacity:0;
  transform:translateY(10px);
  transition:opacity var(--dur-slow) var(--ease-standard),
             transform var(--dur-slow) var(--ease-out);
  transition-delay:var(--dy-reveal-delay,0ms);
}
.dy-reveal.is-revealed{opacity:1;transform:none}

/* -- 2. The scallop draws itself ----------------------------------------- */
/* The awning arc from the shopfront mark, wiped into view along the writing
   direction rather than faded — the one place a directional movement belongs,
   because the arc IS a line that travels. */
.dy-divider--scallop.dy-reveal{
  transform:none;
  opacity:1;
  clip-path:inset(0 100% 0 0);
  transition:clip-path var(--dur-deliberate) var(--ease-out);
}
[dir="rtl"] .dy-divider--scallop.dy-reveal{clip-path:inset(0 0 0 100%)}
.dy-divider--scallop.dy-reveal.is-revealed{clip-path:inset(0 0 0 0)}

/* -- 3. The lattice drifts ----------------------------------------------- */
/* The carved gypsum window screen, moving about as fast as shade does. One
   full cycle is the scale of the weave, so it never visibly restarts. */
@keyframes dy-lattice-drift{
  from{background-position:0 0,0 0}
  to{background-position:var(--lattice-scale) var(--lattice-scale),
                         calc(var(--lattice-scale) * -1) var(--lattice-scale)}
}
.dy-pattern--lattice .dy-pattern__texture,
.dy-pattern--lattice-inverse .dy-pattern__texture{
  animation:dy-lattice-drift 120s linear infinite;
}

/* -- 4. The endless line ------------------------------------------------- */
/* The butterfly repeat is a single unbroken line that never terminates, close
   to the endless-knot carved on the Bahla mosque door. It breathes rather than
   travels: opacity alone, over two minutes, so it reads as light changing on a
   wall rather than as an animation.

   It breathes around --dy-texture-opacity, which each tone sets to its OWN
   resting value. An earlier version animated between --pattern-opacity and
   1.9x that — but --pattern-opacity is 0.07, a token for a faint background
   wash, and the butterfly artwork rests at full strength. The pattern went to
   seven percent and read as gone. */
@keyframes dy-pattern-breathe{
  0%,100%{opacity:var(--dy-texture-opacity,1)}
  50%{opacity:calc(var(--dy-texture-opacity,1) * .82)}
}
.dy-pattern--oxblood .dy-pattern__texture,
.dy-pattern--cream .dy-pattern__texture,
.dy-pattern--ink .dy-pattern__texture{
  animation:dy-pattern-breathe 140s var(--ease-in-out) infinite;
}

@media (prefers-reduced-motion:reduce){
  .dy-reveal,
  .dy-divider--scallop.dy-reveal{
    opacity:1;transform:none;clip-path:none;transition:none;
  }
  .dy-pattern__texture{animation:none !important}
}

/* --------------------------------------------------------------- Divider -- */
.dy-divider{height:1px;width:100%;background:var(--border-hairline);border:0}
.dy-divider--strong{background:var(--border-strong)}
.dy-divider--inverse{background:var(--border-inverse)}
.dy-divider--vertical{display:inline-block;width:1px;height:auto;align-self:stretch}

/* The scallop echoes the awning in the shopfront mark. Once or twice a page. */
.dy-divider--scallop{
  --dy-scallop-size:14px;
  height:var(--dy-scallop-size);width:100%;background:none;
  background-image:radial-gradient(circle at 50% 100%,
    transparent calc(var(--dy-scallop-size) - 1px),
    var(--dy-scallop-ink,var(--sand-400)) calc(var(--dy-scallop-size) - 1px),
    var(--dy-scallop-ink,var(--sand-400)) var(--dy-scallop-size),
    transparent var(--dy-scallop-size));
  background-size:calc(var(--dy-scallop-size) * 2) var(--dy-scallop-size);
  background-repeat:repeat-x;background-position:center bottom;
}
.dy-divider--scallop.dy-divider--inverse{--dy-scallop-ink:var(--rose-400)}

/* Labelled rule */
.dy-divider-labelled{display:flex;align-items:center;gap:var(--space-4)}
.dy-divider-labelled__line{flex:1;height:1px;background:var(--border-hairline)}
.dy-divider-labelled__label{
  font:var(--type-eyebrow);letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase;color:var(--text-muted);
}
.dy-divider-labelled--inverse .dy-divider-labelled__line{background:var(--border-inverse)}
.dy-divider-labelled--inverse .dy-divider-labelled__label{color:var(--text-on-inverse-muted)}

/* --------------------------------------------------------------- Stepper -- */
/* QuantityStepper. Numerals sit in --font-mono and stay Western even in Arabic
   copy, as prices and dimensions do (readme.md § 2). */
.dy-stepper{
  display:inline-flex;align-items:center;
  border:1px solid var(--border-default);border-radius:var(--radius-control);
  height:var(--control-h);
}
.dy-stepper--sm{height:var(--control-h-sm)}
.dy-stepper button{
  display:grid;place-items:center;
  width:var(--control-h-sm);height:100%;
  background:transparent;border:0;color:var(--neutral-900);cursor:pointer;
  transition:var(--transition-color);
}
.dy-stepper button:hover:not(:disabled){background:var(--neutral-100)}
.dy-stepper button:disabled{color:var(--state-disabled-fg);cursor:not-allowed}
.dy-stepper__value{
  min-width:2.5ch;padding-inline:var(--space-2);text-align:center;
  font-family:var(--font-mono);font-weight:var(--weight-medium);
  font-size:var(--size-body-sm);
}

/* ----------------------------------------------------- Scrim and drawer -- */
/* The 2px scrim behind a modal is the only blur in the system (readme.md § 3.3). */
.dy-scrim{
  position:fixed;inset:0;z-index:var(--z-overlay);
  background:var(--overlay-scrim);backdrop-filter:blur(2px);
}

/* --z-modal, NOT --z-drawer.
   The scrim is --z-overlay (400) and --z-drawer is 300, so a drawer on
   --z-drawer sits UNDERNEATH its own scrim: the panel is visible but every
   click lands on the scrim instead, which reads exactly like a frozen page.
   The design system's own basket drawer uses --z-modal for the panel. */
.dy-drawer{
  position:fixed;inset-block:0;inset-inline-end:0;z-index:var(--z-modal);
  width:min(420px,100vw);
  display:flex;flex-direction:column;
  background:var(--surface-card);box-shadow:var(--shadow-xl);
  /* The drawer goes full-width on a phone (readme.md § 3.6). */
}
.dy-drawer[hidden]{display:none}

.dy-drawer__head{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-4);padding:var(--space-6);
  border-bottom:1px solid var(--border-hairline);
}
.dy-drawer__empty,.dy-drawer__error{
  padding:var(--space-8) var(--space-6);
  font:var(--type-body);color:var(--text-muted);
}
.dy-drawer__error{color:var(--state-danger)}

.dy-drawer__list{
  flex:1;overflow-y:auto;margin:0;padding:0;list-style:none;
}
.dy-drawer__line{
  display:flex;gap:var(--space-4);align-items:flex-start;
  padding:var(--space-5) var(--space-6);
  border-bottom:1px solid var(--border-hairline);
}
.dy-drawer__thumb{
  width:72px;height:96px;object-fit:cover;flex:0 0 auto;
  background:var(--surface-photo);border-radius:var(--radius-sm);
}
.dy-drawer__meta{display:flex;flex-direction:column;gap:var(--space-3);flex:1;min-width:0}
.dy-drawer__name{
  font:var(--weight-regular) var(--size-body-sm)/1.35 var(--font-display);
  color:var(--text-primary);text-decoration:none;
}
.dy-drawer__right{display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-2)}

.dy-drawer__foot{
  padding:var(--space-6);border-top:1px solid var(--border-hairline);
  display:flex;flex-direction:column;gap:var(--space-4);
}
.dy-drawer__subtotal{display:flex;align-items:baseline;justify-content:space-between;gap:var(--space-4)}

@media (max-width:560px){
  .dy-drawer{width:100vw}
}

/* ---------------------------------------------------------------- Dialog -- */
.dy-dialog{
  position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);
  z-index:var(--z-modal);
  width:min(560px,calc(100vw - var(--space-8)));
  max-height:calc(100vh - var(--space-16));
  display:flex;flex-direction:column;
  background:var(--surface-card);border-radius:var(--radius-card);
  box-shadow:var(--shadow-xl);
}
.dy-dialog__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-4);
  padding:var(--space-6);border-bottom:1px solid var(--border-hairline);
}
.dy-dialog__body{padding:var(--space-6);overflow-y:auto;font:var(--type-body);color:var(--text-secondary)}
.dy-dialog__body table{width:100%;border-collapse:collapse}
.dy-dialog__body th,.dy-dialog__body td{
  padding:var(--space-3) var(--space-2);text-align:start;
  border-bottom:1px solid var(--border-hairline);font:var(--type-body-sm);
}
.dy-dialog__body th{font-weight:var(--weight-medium);color:var(--text-primary)}

/* ----------------------------------------------------------------- Toast -- */
.dy-toast-host{
  position:fixed;z-index:var(--z-toast);
  inset-block-end:var(--space-6);inset-inline-end:var(--space-6);
  display:flex;flex-direction:column;gap:var(--space-3);
  pointer-events:none;
}
.dy-toast{
  pointer-events:auto;
  display:flex;align-items:flex-start;gap:var(--space-3);
  min-width:300px;max-width:420px;
  padding:var(--space-4) var(--space-5);
  background:var(--surface-raised);
  border:1px solid var(--border-hairline);border-radius:var(--radius-card);
  box-shadow:var(--shadow-lg);
}
.dy-toast__icon{margin-top:1px}
.dy-toast--success .dy-toast__icon{color:var(--olive-500)}
.dy-toast--info .dy-toast__icon{color:var(--sand-600)}
.dy-toast--notice .dy-toast__icon{color:var(--ochre-500)}
.dy-toast--danger .dy-toast__icon{color:var(--state-danger)}
.dy-toast__body{flex:1;display:flex;flex-direction:column;gap:2px}
.dy-toast__title{font:var(--weight-medium) var(--size-body-sm)/1.35 var(--font-body);color:var(--text-primary)}
.dy-toast__desc{font:var(--type-caption);color:var(--text-secondary)}

@media (max-width:560px){
  .dy-toast-host{inset-inline:var(--space-4);inset-block-end:var(--space-4)}
  .dy-toast{min-width:0;max-width:none}
}

/* ------------------------------------------------------------- Accordion -- */
.dy-accordion{border-top:1px solid var(--border-hairline)}
.dy-accordion__item{border-bottom:1px solid var(--border-hairline)}
.dy-accordion__trigger{
  width:100%;display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-4);padding:var(--space-5) 0;
  background:transparent;border:0;cursor:pointer;text-align:start;
  font:var(--weight-medium) var(--size-body)/1.3 var(--font-body);
  color:var(--text-primary);
}
.dy-accordion__chevron{color:var(--sand-600)}
/* 0fr → 1fr animates to the content's real height without measuring it. */
.dy-accordion__panel{
  display:grid;grid-template-rows:0fr;
  transition:grid-template-rows var(--dur-base) var(--ease-standard);
}
.dy-accordion__panel--open{grid-template-rows:1fr}
.dy-accordion__clip{overflow:hidden}
.dy-accordion__content{
  padding-bottom:var(--space-5);
  font:var(--type-body);color:var(--text-secondary);max-width:var(--measure-prose);
}

/* ------------------------------------------------------------ MediaFrame -- */
/* The arch is the threshold shape. Its crown curves away immediately, so
   nothing may hang in the top corners — overlays go on the straight bottom
   edge (readme.md § 3.3). */
.dy-media{
  position:relative;overflow:hidden;margin:0;
  aspect-ratio:var(--aspect-product);
  border-radius:var(--radius-xs);
  box-shadow:var(--shadow-inset-media);
  display:grid;place-items:center;padding:var(--space-8);
}
.dy-media--arch{border-radius:var(--radius-arch-soft)}
.dy-media--rounded{border-radius:var(--radius-media)}
.dy-media--circle{border-radius:var(--radius-circle)}

.dy-media--clay{background:var(--surface-photo)}
.dy-media--warm{background:var(--surface-photo-warm)}
.dy-media--rose{background:var(--surface-photo-rose)}
.dy-media--olive{background:var(--surface-photo-olive)}
.dy-media--sand{background:var(--sand-100)}
.dy-media--oxblood{background:var(--oxblood-700)}

/* cover, not contain.
   The design system frames a cut-out product on a branded ground, which wants
   contain plus padding. The client's photography is already shot on its own
   pale ground, so contain rendered a white box inside the arch — a frame
   within a frame. cover lets the photograph BE the arch. */
.dy-media img{width:100%;height:100%;object-fit:cover;transition:var(--transition-media)}

/* ---- hover cycling -------------------------------------------------------
   A cross-fade, not a slide. The design system is explicit: movement is slow
   and weighted, "no slide-ins from the side, no parallax", and nothing
   bounces. Cycling the frames by fading between them shows the same three
   photographs without contradicting that.
   700ms matches the product-image hover the design system already specifies —
   slow enough to notice. */
.dy-media__frame{
  position:absolute;inset:0;
  width:100%;height:100%;object-fit:cover;
  opacity:0;
  transition:opacity var(--dur-deliberate) var(--ease-standard);
  pointer-events:none;
}
.dy-media__frame--on{opacity:1}
/* The base photograph fades out only when a later frame is showing, so the
   frame is never briefly empty. */
.dy-media--cycles.is-cycling > img:first-of-type{opacity:0}

/* One hairline tick per photograph, on the STRAIGHT BOTTOM EDGE — the arch's
   crown curves away and clips anything in its top corners. */
.dy-media__ticks{
  position:absolute;inset-inline:0;bottom:var(--space-4);
  display:flex;gap:var(--space-2);justify-content:center;
  opacity:0;transition:opacity var(--dur-base) var(--ease-standard);
  pointer-events:none;
}
.dy-media--cycles:hover .dy-media__ticks{opacity:1}
.dy-media__tick{
  width:18px;height:2px;border-radius:var(--radius-pill);
  background:rgba(42,37,33,.22);
  transition:background-color var(--dur-base) var(--ease-standard);
}
.dy-media__tick--on{background:var(--oxblood-700)}

@media (prefers-reduced-motion:reduce){
  /* Do not cycle at all — a reduced-motion preference is not a request for
     slower animation, it is a request for none. */
  .dy-media__frame{transition:none}
  .dy-media__ticks{display:none}
}
/* Only an empty frame keeps the inset; a real photograph goes edge to edge. */
.dy-media:has(img){padding:0}
.dy-media--zoom:hover img{transform:scale(1.04)}
.dy-media--empty{padding:0}
.dy-media--empty .dy-eyebrow{opacity:.8;color:var(--sand-600)}
.dy-media--oxblood.dy-media--empty .dy-eyebrow{color:var(--rose-300)}

/* --------------------------------------------------------------- Gallery -- */
.dy-gallery{display:flex;gap:var(--space-4);align-items:flex-start}
.dy-gallery__rail{display:flex;flex-direction:column;gap:var(--space-3);flex:0 0 auto}
.dy-gallery__thumb{
  width:72px;height:96px;padding:0;overflow:hidden;cursor:pointer;
  background:var(--surface-photo);
  border:1px solid transparent;border-radius:var(--radius-sm);
  transition:var(--transition-color);
}
.dy-gallery__thumb--on{border-color:var(--oxblood-700)}
.dy-gallery__thumb img{width:100%;height:100%;object-fit:cover}
.dy-gallery__main{flex:1;min-width:0}
.dy-gallery__nav{
  position:absolute;inset-inline:var(--space-4);bottom:var(--space-4);
  display:flex;justify-content:space-between;
}
@media (max-width:700px){
  .dy-gallery{flex-direction:column-reverse}
  .dy-gallery__rail{flex-direction:row;overflow-x:auto}
}

/* ------------------------------------------------------- Buy / swatches -- */
.dy-buy{display:flex;flex-direction:column;gap:var(--space-6)}
.dy-buy__attr{display:flex;flex-direction:column;gap:var(--space-3)}
.dy-buy__attr-value{
  text-transform:none;letter-spacing:0;
  color:var(--text-primary);font:var(--type-body-sm);
}
.dy-buy__actions{display:flex;gap:var(--space-4);align-items:center;flex-wrap:wrap}
.dy-buy__add{flex:1;min-width:200px}

/* Swatches are circles of real material colour — never gradients. */
.dy-swatches{display:flex;gap:var(--space-3);flex-wrap:wrap}
.dy-swatch{
  width:34px;height:34px;padding:3px;
  background:transparent;cursor:pointer;
  border:1px solid transparent;border-radius:var(--radius-circle);
  transition:var(--transition-color);
}
.dy-swatch--on{border-color:var(--oxblood-700)}
.dy-swatch:disabled{opacity:.4;cursor:not-allowed}
.dy-swatch__dot{
  display:block;width:100%;height:100%;border-radius:var(--radius-circle);
  box-shadow:inset 0 0 0 1px rgba(42,37,33,.12);
}
/* A size or finish with no colour falls back to a labelled pill. */
.dy-swatch:has(.dy-swatch__text){
  width:auto;height:var(--control-h-sm);padding:0 var(--space-4);
  border-radius:var(--radius-pill);border-color:var(--border-default);
}
.dy-swatch__text{font:var(--weight-medium) var(--size-caption)/1 var(--font-body)}
.dy-swatch--on:has(.dy-swatch__text){
  background:var(--oxblood-700);color:var(--sand-100);border-color:var(--oxblood-700);
}

/* --------------------------------------------------------------- Select -- */
.dy-select{
  height:var(--control-h-sm);padding:0 var(--space-4);
  background:var(--surface-card);color:var(--text-primary);
  border:1px solid var(--border-default);border-radius:var(--radius-control);
  font:var(--type-body-sm);cursor:pointer;
}

/* -------------------------------------------------------------- Filters -- */
.dy-filters{display:flex;flex-direction:column;gap:var(--space-4);margin-block-end:var(--space-8)}
.dy-filters__row{display:flex;gap:var(--space-3);align-items:center;flex-wrap:wrap}
.dy-filters__row--chips{padding-top:var(--space-2);border-top:1px solid var(--border-hairline)}
.dy-filters__count{font-family:var(--font-mono);font-size:var(--size-micro);opacity:.7}
.dy-filters__sort{display:flex;align-items:center;gap:var(--space-3);margin-inline-start:auto}

/* -------------------------------------------------------- SectionHeading -- */
.dy-section-heading{display:flex;flex-direction:column;gap:var(--space-6)}
.dy-section-heading--split{
  flex-direction:row;align-items:flex-end;justify-content:space-between;flex-wrap:wrap;
}
.dy-section-heading__text{display:flex;flex-direction:column;gap:var(--space-3)}
.dy-section-heading--center{align-items:center}
.dy-section-heading--center .dy-section-heading__text{align-items:center;text-align:center}
.dy-section-heading__eyebrow{color:var(--sand-600)}
.dy-section-heading__title{max-width:20ch}
.dy-section-heading__lead{
  font:var(--type-lead);color:var(--text-secondary);max-width:var(--measure-lead);
}
/* The trailing link in a split heading ("All pieces →"). It sits on the
   baseline of the title rather than centred against the whole block. */
.dy-section-heading__action{flex:0 0 auto;padding-block-end:var(--space-1)}
.dy-section-heading--inverse .dy-section-heading__eyebrow{color:var(--rose-300)}
.dy-section-heading--inverse .dy-section-heading__title{color:var(--sand-100)}
.dy-section-heading--inverse .dy-section-heading__lead{color:var(--text-on-inverse);opacity:.92}

/* ---------------------------------------------------------- AssuranceRow -- */
.dy-assurance{
  list-style:none;margin:0;padding:0;
  display:grid;gap:var(--space-4);
  grid-template-columns:repeat(auto-fit,minmax(200px,1fr));
}
.dy-assurance--stack{grid-template-columns:1fr}
.dy-assurance__item{display:flex;gap:var(--space-3);align-items:flex-start}
.dy-assurance__icon{margin-top:2px;color:var(--sand-600)}
.dy-assurance__body{display:flex;flex-direction:column;gap:2px}
.dy-assurance__label{font:var(--weight-medium) var(--size-body-sm)/1.35 var(--font-body);color:var(--text-primary)}
.dy-assurance__detail{font:var(--type-caption);color:var(--text-secondary)}
.dy-assurance--inverse .dy-assurance__icon{color:var(--sand-400)}
.dy-assurance--inverse .dy-assurance__label{color:var(--sand-100)}
.dy-assurance--inverse .dy-assurance__detail{color:var(--text-on-inverse)}

/* ----------------------------------------------------------------- Quote -- */
.dy-quote{margin:0;display:flex;flex-direction:column;gap:var(--space-5);max-width:28ch}
.dy-quote__mark{color:var(--sand-500)}
.dy-quote__text{
  font:var(--weight-light) var(--size-h3)/1.4 var(--font-display);
  letter-spacing:-.008em;color:var(--text-primary);
}
.dy-quote--lg .dy-quote__text{font:var(--weight-light) var(--size-h2)/1.32 var(--font-display)}
.dy-quote__foot{display:flex;flex-direction:column;gap:2px}
.dy-quote__cite{
  font-style:normal;
  font:var(--weight-medium) var(--size-body-sm)/1.3 var(--font-body);
  color:var(--text-primary);
}
.dy-quote__role{font:var(--type-caption);color:var(--text-muted)}
.dy-quote--inverse .dy-quote__mark{color:var(--rose-300)}
.dy-quote--inverse .dy-quote__text,.dy-quote--inverse .dy-quote__cite{color:var(--sand-100)}
.dy-quote--inverse .dy-quote__role{color:var(--rose-300)}

/* ---------------------------------------------------------- PatternPanel -- */
/* Behind headings and imagery only, never behind a paragraph. The lattice tone
   is the exception — it is quiet enough for a short block of text. */
.dy-pattern{position:relative;isolation:isolate}
.dy-pattern__texture{position:absolute;inset:0;z-index:0;pointer-events:none}
.dy-pattern__content{position:relative;z-index:1;display:block}

.dy-pattern--oxblood{background:var(--oxblood-700);color:var(--text-on-inverse)}
.dy-pattern--cream{background:var(--sand-50);color:var(--text-primary)}
.dy-pattern--ink{background:var(--surface-photo);color:var(--text-primary)}

/* --dy-pattern-image is set inline only when the artwork is present, so the
   panel degrades to flat brand colour rather than a broken image. */
.dy-pattern--oxblood .dy-pattern__texture,
.dy-pattern--cream .dy-pattern__texture,
.dy-pattern--ink .dy-pattern__texture{
  background-image:var(--dy-pattern-image,none);
  background-size:auto var(--pattern-scale);
  background-repeat:repeat;
  /* Each tone's resting opacity. The breathe animation modulates around this
     rather than replacing it, so a tone never loses its intended weight. */
  opacity:var(--dy-texture-opacity,1);
}
.dy-pattern--oxblood .dy-pattern__texture{--dy-texture-opacity:1}
.dy-pattern--cream .dy-pattern__texture{--dy-texture-opacity:1}
.dy-pattern--ink .dy-pattern__texture{--dy-texture-opacity:.28}

/* Lattice — the diagonal screen of a carved gypsum window. Drawn in CSS. */
.dy-pattern--lattice,.dy-pattern--lattice-inverse{background:var(--sand-50);color:var(--text-primary)}
.dy-pattern--lattice-inverse{background:var(--oxblood-700);color:var(--text-on-inverse)}
.dy-pattern--lattice .dy-pattern__texture,
.dy-pattern--lattice-inverse .dy-pattern__texture{
  --dy-lattice-line:rgba(110,82,60,.13);
  background-image:
    repeating-linear-gradient(45deg,var(--dy-lattice-line) 0 1px,transparent 1px var(--lattice-scale)),
    repeating-linear-gradient(-45deg,var(--dy-lattice-line) 0 1px,transparent 1px var(--lattice-scale));
}
.dy-pattern--lattice-inverse .dy-pattern__texture{--dy-lattice-line:rgba(222,203,182,.20)}

/* ----------------------------------------------------------- ProductCard -- */
.dy-product-card{display:flex;flex-direction:column;gap:var(--space-4)}
.dy-product-card__media{position:relative;display:block;text-decoration:none}
.dy-product-card__media:hover{text-decoration:none}

/* The arch crown curves away, so the badge sits on the STRAIGHT BOTTOM EDGE,
   and fades out when the quick-add slides up over it. */
.dy-product-card__badge{
  position:absolute;bottom:var(--space-4);inset-inline-start:var(--space-4);
  opacity:1;transition:opacity var(--dur-fast) var(--ease-standard);
}
.dy-product-card__media:hover .dy-product-card__badge{opacity:0}

.dy-product-card__add{
  position:absolute;inset-inline:var(--space-4);bottom:var(--space-4);
  opacity:0;transform:translateY(6px);
  transition:opacity var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-out);
}
.dy-product-card__media:hover .dy-product-card__add,
.dy-product-card__add:focus-visible{opacity:1;transform:translateY(0)}

.dy-product-card__body{display:flex;flex-direction:column;gap:var(--space-1)}
.dy-product-card__name{
  font:var(--weight-regular) var(--size-h4)/1.25 var(--font-display);
  color:var(--text-primary);text-decoration:none;
}
.dy-product-card__material{font:var(--type-caption);color:var(--text-muted);letter-spacing:.02em}
.dy-product-card__price{margin-top:var(--space-1)}

/* A touch device has no hover, so the quick-add must not be unreachable. */
@media (hover:none){
  .dy-product-card__add{opacity:1;transform:none;position:static;margin-top:var(--space-3)}
  .dy-product-card__badge{opacity:1}
}

/* ------------------------------------------------------------------ Logo -- */
/* Sizes derive from --dy-logo-size so the lockup scales as one unit, the way
   Logo.jsx computes them from its `size` prop. */
.dy-logo{
  display:inline-flex;align-items:center;
  gap:calc(var(--dy-logo-size,48px) * .32);
}
.dy-logo__mark{height:var(--dy-logo-size,48px);width:auto;border-radius:var(--radius-circle)}
.dy-logo__name{
  font-family:var(--font-display);font-weight:var(--weight-regular);
  font-size:calc(var(--dy-logo-size,48px) * .46);line-height:1.15;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--oxblood-700);white-space:nowrap;
}
.dy-logo--on-dark .dy-logo__name{color:var(--sand-100)}
/* Arabic must stay joined and is never all-caps, and Amiri sits smaller. */
[dir="rtl"] .dy-logo__name{
  font-size:calc(var(--dy-logo-size,48px) * .54);
  letter-spacing:0;text-transform:none;
}

/* ----------------------------------------------------------------- Forms -- */
/* Element selectors as well as classes, so WooCommerce's own checkout, account
   and review forms inherit the design without each being overridden by hand. */
.dy-field{display:flex;flex-direction:column;gap:var(--space-2)}
.dy-field__label,
.woocommerce form label:not(.woocommerce-form__label-for-checkbox){
  font:var(--type-eyebrow);letter-spacing:var(--ls-eyebrow);
  text-transform:uppercase;color:var(--text-secondary);
}
[dir="rtl"] .dy-field__label,
[dir="rtl"] .woocommerce form label{text-transform:none}

.dy-field__required{color:var(--oxblood-500)}

.dy-input,
.woocommerce form input[type="text"],
.woocommerce form input[type="email"],
.woocommerce form input[type="tel"],
.woocommerce form input[type="password"],
.woocommerce form input[type="number"],
.woocommerce form input[type="search"],
.woocommerce form select,
.woocommerce form textarea{
  width:100%;height:var(--control-h);padding:0 var(--space-4);
  background:var(--surface-card);color:var(--text-primary);
  border:1px solid var(--border-default);border-radius:var(--radius-control);
  font:var(--type-body);
  transition:var(--transition-color);
}
.woocommerce form textarea,.dy-textarea{height:auto;min-height:120px;padding:var(--space-3) var(--space-4)}

.dy-input:focus,
.woocommerce form input:focus,
.woocommerce form select:focus,
.woocommerce form textarea:focus{
  outline:none;border-color:var(--oxblood-700);box-shadow:var(--focus-ring);
}
.dy-input::placeholder,.woocommerce form input::placeholder{color:var(--text-muted)}

.dy-input--error,.woocommerce form .woocommerce-invalid input{border-color:var(--state-danger)}
.dy-field__hint{font:var(--type-caption);color:var(--text-muted)}
.dy-field__error{font:var(--type-caption);color:var(--state-danger)}

/* Checkbox and radio: a hairline box that fills oxblood when checked. No
   custom SVG tick — accent-color keeps the native control accessible. */
.dy-check,
.woocommerce form input[type="checkbox"],
.woocommerce form input[type="radio"]{
  width:18px;height:18px;margin:0;
  accent-color:var(--oxblood-700);
  cursor:pointer;
}
.dy-check-row{display:flex;align-items:center;gap:var(--space-3)}
.dy-check-row label{font:var(--type-body-sm);color:var(--text-primary);text-transform:none;letter-spacing:0}

/* WooCommerce buttons inherit the brand button rather than Woo's own skin. */
.woocommerce a.button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce #respond input#submit{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
  height:var(--control-h);padding:0 var(--space-6);
  background:var(--oxblood-700);color:var(--text-on-brand);
  border:1px solid var(--oxblood-700);border-radius:var(--radius-control);
  font-family:var(--font-body);font-weight:var(--weight-medium);
  font-size:var(--size-body-sm);line-height:1;
  letter-spacing:.05em;text-transform:uppercase;text-decoration:none;cursor:pointer;
  transition:var(--transition-color),transform var(--dur-fast) var(--ease-standard);
}
.woocommerce a.button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover{background:var(--oxblood-600);border-color:var(--oxblood-600)}
.woocommerce a.button:active,
.woocommerce button.button:active{transform:scale(var(--press-scale))}

/* Woo notices, in the brand's tones rather than its own green/blue/red. */
.woocommerce-message,.woocommerce-info,.woocommerce-error{
  list-style:none;margin:0 0 var(--space-6);padding:var(--space-4) var(--space-5);
  border:1px solid var(--border-hairline);border-radius:var(--radius-card);
  font:var(--type-body-sm);
}
.woocommerce-message{background:var(--olive-50);color:var(--olive-700)}
.woocommerce-info{background:var(--sand-50);color:var(--neutral-700)}
.woocommerce-error{background:var(--state-danger-soft);color:var(--oxblood-600)}
