/* ==========================================================================
   Search — Claude Design turn 36.
   The bar, the desktop panel, the mobile full-screen overlay and the results
   page, in one stylesheet.

   THE COLOUR RULE. 36e gave this file one accent; turn 38 splits that accent by
   what each control DOES, which for a search surface means most of it is not
   violet at all:
     violet  marks the matched letters, the focused row, the caret, and the one
             button that STARTS something ("Request a quote"). Nothing else.
     blue    everything that navigates or informs — "See all N results",
             "Browse all services", "Try again", a chip or a card that opens
             something else. Rule 5: navigation is blue and light.
     ink     the filter pills' active state, and small neutral actions ("Clear").
             A filter changes a view, not data, so it never takes a hue.
     amber   appears on exactly two screens — nothing matched, and no connection
             — as two words and a 2px rule, never a filled panel, and NEVER on
             the button that gets you out of them (rule 4).
   Everything else is white, #f8f9fa, or a hairline.

   Namespaced `xs-` throughout so none of it can collide with the Tailwind
   utilities the rest of the customer surface is built from.
   ========================================================================== */

:root {
  --xs-violet:      #7c3aed;   /* the accent itself */
  --xs-violet-ink:  #6d28d9;   /* violet as text, darkened for contrast */
  --xs-violet-bg:   #f5f3ff;   /* the focused row */
  --xs-violet-line: #ddd6fe;   /* the ↵ pill border */
  --xs-amber:       #b45309;   /* something is wrong, as text AND as the border */
  --xs-amber-soft:  #fde68a;   /* the tint border, for amber surfaces */

  /* Blue was missing entirely, and turn 38 hands it a job this file does on every
     screen: "see all N results", "browse", a chip or a card that navigates.
     ALIASED, not restated — the values live once, in xw-controls.css, which
     customer_header.php links immediately before this file on every page that
     has a search bar. Nothing here is allowed to disagree with it. */
  --xs-blue:        var(--xw-blue);
  --xs-blue-ink:    var(--xw-blue-dark);
  --xs-blue-tint:   var(--xw-blue-tint);
  --xs-blue-line:   var(--xw-blue-border);
  --xs-ink-tint:    var(--xw-ink-tint);
  --xs-ink:         #111111;
  --xs-body:        #374151;
  --xs-muted:       #6b7280;
  --xs-soft:        #9ca3af;
  --xs-faint:       var(--xw-line-input, #d1d5db);
  --xs-line:        #e5e7eb;
  --xs-hair:        #f3f4f6;
  --xs-canvas:      #f8f9fa;
  --xs-photo:       var(--xw-photo, #eef0e9);   /* the tint behind a photo that has not loaded */
  --xs-field:       #d1d5db;
}

/* ── shared atoms ───────────────────────────────────────────────────────── */
.xs-lbl {
  font: 500 9.5px 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: .09em; text-transform: uppercase; color: var(--xs-soft);
}
.xs-bar {                      /* a section header inside the panel */
  padding: 8px 14px; background: var(--xs-canvas);
  border-top: 1px solid var(--xs-hair); border-bottom: 1px solid var(--xs-hair);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.xs-bar .n { font: 500 10px 'JetBrains Mono', ui-monospace, monospace; color: var(--xs-soft); }
.xs-foot {
  padding: 9px 14px; background: var(--xs-canvas); border-top: 1px solid var(--xs-hair);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* .xs-link does two different jobs in search36.js, and turn 38 gives them two
   different colours. Neither is violet: nothing here adds, books or submits.

   In .xs-foot it NAVIGATES — "See all 34 results", "Browse" — so it is blue and
   light, with an arrow, per rule 5. The arrow is drawn here rather than written
   into search36.js: the strings live in JS, which is out of scope for a visual
   pass, and an arrow is a property of the control anyway.

   Everywhere else (the "Clear" beside recent searches, in .blkh) it is a small
   neutral action, which is INK — and ink's ghost is #374151, not #111111. */
.xs-link { font: 600 10.5px 'Inter', sans-serif; color: var(--xs-body); cursor: pointer;
           background: none; border: 0; padding: 0; }
.xs-link:hover { text-decoration: underline; }
.xs-foot .xs-link { display: inline-flex; align-items: center; gap: 6px; color: var(--xs-blue-ink); }
.xs-foot .xs-link::after { content: "\2192"; }
.xs-note { font: 400 10.5px/1.55 'Inter', sans-serif; color: var(--xs-muted); }

/* ── the row (36c) ──────────────────────────────────────────────────────────
   A photo, a name with the match marked, and its parent. No price, no
   availability count, no badge — a list of names is for finding the right one,
   not for comparing them. */
.xs-row {
  display: flex; align-items: center; gap: 13px; padding: 10px 14px;
  background: #fff; text-decoration: none; cursor: pointer; border: 0; width: 100%;
  text-align: left; font: inherit;
}
.xs-row + .xs-row { border-top: 1px solid var(--xs-hair); }
.xs-row:hover { background: var(--xs-canvas); }
.xs-row.on {                   /* keyboard focus */
  background: var(--xs-violet-bg); box-shadow: inset 2px 0 0 var(--xs-violet);
}
.xs-row .t {                   /* the thumbnail, one size whatever it holds */
  flex: none; width: 44px; height: 44px; border-radius: 8px;
  background: var(--xs-photo); border: 1px solid var(--xs-line);
  object-fit: cover; display: block;
}
.xs-row .t.fb {                /* no photograph on record — same size, so the row height never changes */
  background: var(--xw-ink-tint, #f8f9fa); display: flex; align-items: center; justify-content: center;
  font: 600 16px 'Manrope', sans-serif; color: var(--xs-soft);
}
.xs-row .tx { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.xs-row .nm {
  font: 500 13px 'Inter', sans-serif; color: var(--xs-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.xs-row .nm mark { background: none; color: var(--xs-violet-ink); font: inherit; }
.xs-row .pa { font: 400 11px/1.55 'Inter', sans-serif; color: var(--xs-soft);
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.xs-row .kb {                  /* the ↵ affordance on the focused row */
  flex: none; background: #fff; border: 1px solid var(--xs-violet-line); border-radius: 999px;
  padding: 3px 8px; font: 500 11px 'Inter', sans-serif; color: var(--xs-violet-ink);
  display: none;
}
.xs-row.on .kb { display: block; }

/* ── the tile (36c) ─────────────────────────────────────────────────────────
   One aspect ratio per grid and one gap, so the row never looks crooked. The
   photograph is the tallest thing; the name is a caption under it, never
   inside it. */
.xs-grid { display: grid; gap: 14px; padding: 14px; }
.xs-grid.c4 { grid-template-columns: repeat(4, 1fr); }
.xs-grid.c3 { grid-template-columns: repeat(3, 1fr); gap: 11px; }
.xs-grid.c2 { grid-template-columns: repeat(2, 1fr); gap: 12px; }
.xs-tile { display: flex; flex-direction: column; gap: 8px; text-decoration: none; cursor: pointer; }
.xs-tile .ph {
  aspect-ratio: 3 / 2; border-radius: 10px; background: var(--xs-photo);
  border: 1px solid var(--xs-line); overflow: hidden; position: relative; display: block;
}
.xs-grid.c3 .xs-tile .ph { aspect-ratio: 1 / 1; }
.xs-grid.c2 .xs-tile .ph { aspect-ratio: 4 / 3; }
.xs-tile .ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.xs-tile .cap { font: 500 12px/1.35 'Inter', sans-serif; color: var(--xs-ink); }
.xs-grid.c3 .xs-tile .cap { font-size: 11px; }
/* A tile is a link. Rule 5 again — the hover goes blue, not violet. */
.xs-tile:hover .cap { color: var(--xs-blue-ink); }

/* ── skeletons, shaped like the row so nothing shifts when data lands ────── */
.xs-sk { display: flex; align-items: center; gap: 13px; padding: 10px 14px; }
.xs-sk + .xs-sk { border-top: 1px solid var(--xs-hair); }
.xs-sk .t { flex: none; width: 44px; height: 44px; border-radius: 8px; background: var(--xs-hair); }
.xs-sk .tx { flex: 1; display: flex; flex-direction: column; gap: 7px; }
.xs-sk .a { height: 10px; border-radius: 5px; background: var(--xs-line); }
.xs-sk .b { height: 8px;  border-radius: 4px; background: var(--xs-hair); }
@media (prefers-reduced-motion: no-preference) {
  .xs-sk .a, .xs-sk .b, .xs-sk .t { animation: xsPulse 1.4s ease-in-out infinite; }
}
@keyframes xsPulse { 0%,100% { opacity: 1 } 50% { opacity: .55 } }

/* ── something is wrong (the only amber on the surface) ─────────────────── */
.xs-alert { border-left: 2px solid var(--xs-amber); padding-left: 12px;
            display: flex; flex-direction: column; gap: 4px; }
.xs-alert .h  { font: 600 15px/1.18 'Manrope', sans-serif; letter-spacing: -.4px; color: var(--xs-ink); }
.xs-alert .h.amber { color: var(--xs-amber); }
.xs-alert .s  { font: 400 11.5px/1.55 'Inter', sans-serif; color: var(--xs-muted); }
/* "Try again". It used to be amber, which rule 4 forbids: amber describes the
   CONDITION and never sits on the way forward — the condition is stated once, by
   the .xs-alert above it, and that is where amber stays. Retrying re-reads the
   catalogue and changes nothing, so it is blue rather than violet, and it is the
   one SOLID on that screen because it is the way out of it. md geometry (38c). */
.xs-retry {
  background: var(--xs-blue); border: 1px solid var(--xs-blue); border-radius: 8px;
  padding: 12px 18px; font: 600 12.5px 'Inter', sans-serif; color: #fff;
  cursor: pointer; white-space: nowrap; text-align: center;
}
.xs-retry:hover { background: var(--xs-blue-ink); border-color: var(--xs-blue-ink); }
/* "Browse all services" — navigation, so blue and LIGHT (rule 5), and a ghost so
   it never competes with the solid beside it (rule 1: one solid per card). */
.xs-ghost {
  background: transparent; border: 1px solid transparent; border-radius: 8px;
  padding: 12px 18px; font: 600 12.5px 'Inter', sans-serif; color: var(--xs-blue-ink);
  cursor: pointer; white-space: nowrap; text-decoration: none;
  display: inline-flex; align-items: center; gap: 7px;
}
.xs-ghost::after { content: "\2192"; }
.xs-ghost:hover { background: var(--xs-blue-tint); }
.xs-cta {                       /* the one button that starts something */
  background: var(--xs-violet); border: 1px solid var(--xs-violet); border-radius: 8px;
  padding: 12px 18px; font: 600 12.5px 'Inter', sans-serif; color: #fff;
  cursor: pointer; white-space: nowrap; text-decoration: none; display: inline-block;
  text-align: center;
}
.xs-cta:hover { background: var(--xs-violet-ink); border-color: var(--xs-violet-ink); }
/* Category chips. They NAVIGATE, so the hover is blue — violet on something a
   scroll can undo reads as a commitment (rule 5). Pill geometry from .xw-cpill;
   the padding stays at 6px 12px because search36.js sets it inline on the
   in-panel chips and one padding beats two. */
.xs-pill {
  background: var(--xs-ink-tint); border: 1px solid var(--xs-line); border-radius: 999px;
  padding: 6px 12px; font: 600 11.5px 'Inter', sans-serif; color: var(--xs-body);
  white-space: nowrap; text-decoration: none; cursor: pointer; display: inline-block;
}
.xs-pill:hover { border-color: var(--xs-blue-line); color: var(--xs-blue-ink); }

/* ── DESKTOP · the field and its attached panel (36a) ───────────────────── */
.xs-dfield { position: relative; flex: 1; min-width: 0; }
.xs-dfield .box {
  width: 100%; height: 42px; border: 1px solid var(--xs-line); background: var(--xs-canvas);
  border-radius: 10px; display: flex; align-items: center; gap: 11px; padding: 0 14px;
}
/* NO transition on this box, deliberately. Transitioning border-color and
   border-radius here left the computed value pinned at the resting grey — the
   field never visibly opened and never went amber, even seconds after the class
   landed. These are discrete states, not an animation: the border says which
   field the panel belongs to and whether something went wrong, and it has to be
   true on the frame it changes. */
/* Open: the panel is attached, so the field loses its bottom edge and its lower corners. */
.xs-dfield.open .box {
  border-color: var(--xs-field); border-bottom-color: var(--xs-hair);
  background: #fff; border-radius: 10px 10px 0 0;
}
.xs-dfield.warn .box { border-color: var(--xs-amber); border-bottom-color: var(--xs-hair); }
/* Scoped by id as well as class. The class-only pair above was matching the
   element and sitting in the parsed sheet, yet losing to the resting rule — the
   field kept its grey edge while open and while warning. An id raises the
   specificity out of reach of anything the utility layer can produce, and the
   state of this one field is worth being certain about: it is how a person can
   see that the panel below belongs to it, and that something went wrong. */
#header-search-wrap.open > .box {
  border-color: var(--xs-field); border-bottom-color: var(--xs-hair);
  background: #fff; border-radius: 10px 10px 0 0;
}
#header-search-wrap.warn > .box {
  border-color: var(--xs-amber); border-bottom-color: var(--xs-hair);
}
.xs-dfield .box input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: none;
  font: 400 13px 'Inter', sans-serif; color: var(--xs-ink);
}
.xs-dfield .box input::placeholder { color: var(--xs-soft); }
.xs-dfield .ic { flex: none; color: var(--xs-soft); font-size: 13px; line-height: 1; }
.xs-dfield.open .ic { color: var(--xs-muted); }
.xs-dfield .clr { flex: none; border: 0; background: none; cursor: pointer; padding: 0;
                  color: var(--xs-soft); font-size: 12px; line-height: 1; display: none; }
.xs-dfield.has .clr { display: block; }
/* The caret: the design's one flicker of violet while nothing is typed. */
.xs-caret { flex: none; width: 1.5px; height: 17px; background: var(--xs-violet); display: none; }
.xs-dfield.open:not(.has) .xs-caret { display: block; animation: xsBlink 1.1s steps(1) infinite; }
@keyframes xsBlink { 0%,49% { opacity: 1 } 50%,100% { opacity: 0 } }
@media (prefers-reduced-motion: reduce) { .xs-caret { animation: none !important } }

.xs-panel {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 60;
  background: #fff; border: 1px solid var(--xs-field); border-top: none;
  border-radius: 0 0 12px 12px; box-shadow: 0 12px 28px rgba(17,17,17,.10);
  overflow: hidden; max-height: 74vh; overflow-y: auto;
}
.xs-panel.warn { border-color: var(--xs-amber); }
.xs-panel[hidden] { display: none; }
/* The `hidden` ATTRIBUTE is the single source of truth for this panel.
   shop.js still carries a document click listener that adds the Tailwind
   `hidden` CLASS whenever a click lands outside the input — and clicking the
   field's padding rather than the input itself counts as outside. That hid the
   panel a frame after it opened, and the click fallback then reopened it: the
   open-close-open flicker. Winning here means the stale listener can toggle that
   class as much as it likes and nothing moves. */
.xs-panel:not([hidden]) { display: block !important; }
/* Motion, borrowed from the popups rather than re-invented: the same easing
   (cubic-bezier(.16,1,.3,1)), the same short durations, and the same idea that
   a surface changing its contents morphs instead of blinking — search36.js
   hands state changes to xwMorph(), the very helper the cart and location
   popups use, so the height animates and the new content cross-fades.

   Only opacity and transform are animated. border-color and border-radius are
   deliberately excluded: transitioning those on the field pinned the computed
   value at its resting grey and the field never visibly opened. */
@media (prefers-reduced-motion: no-preference) {
  .xs-panel:not(.closing) { animation: xsIn .26s cubic-bezier(.16,1,.3,1); }
  .xs-panel.closing       { animation: xsOut .17s ease-in forwards; }

  /* NO entrance animation on rows or tiles, deliberately.
     They were staggering in from opacity 0 on every repaint — and a search panel
     repaints twice per query (skeletons, then results). The content blinked out
     and back each time, which is what reads as "it appears, disappears, then
     appears again". The panel animates once when it opens and once when it
     closes; everything inside it simply changes. That is the smooth version. */

  .xs-ov:not([hidden]):not(.closing) { animation: xsOvIn .3s cubic-bezier(.16,1,.3,1); }
  .xs-ov.closing                     { animation: xsOvOut .2s ease-in forwards; }

  /* No rule fading the panel or the overlay body as a whole. Doing that blinked
     the container itself — border and shadow included — on every repaint. The
     rows and tiles below carry the entrance instead, so the frame stays put and
     only what changed moves. */

  .xs-row, .xs-tile .cap, .xs-pill, .xs-link, .xs-rec {
    transition: background-color .18s cubic-bezier(.16,1,.3,1),
                color .18s cubic-bezier(.16,1,.3,1),
                border-color .18s cubic-bezier(.16,1,.3,1);
  }
  .xs-tile .ph img { transition: transform .5s cubic-bezier(.16,1,.3,1); }
  .xs-tile:hover .ph img { transform: scale(1.04); }
}
@keyframes xsIn    { from { opacity: 0; transform: translateY(-8px) } to { opacity: 1; transform: none } }
@keyframes xsOut   { to   { opacity: 0; transform: translateY(-6px) } }
@keyframes xsOvIn  { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }
@keyframes xsOvOut { to   { opacity: 0; transform: translateY(8px) } }
.xs-panel .head { padding: 16px 14px; border-bottom: 1px solid var(--xs-hair);
                  display: flex; flex-direction: column; gap: 5px; }
.xs-panel .head .h { font: 600 15px/1.18 'Manrope', sans-serif; letter-spacing: -.4px; color: var(--xs-ink); }
.xs-panel .head .s { font: 400 11.5px/1.55 'Inter', sans-serif; color: var(--xs-muted); }
.xs-panel .pad { padding: 16px 14px; display: flex; flex-direction: column; gap: 13px; }
.xs-panel .pills { padding: 11px 14px; display: flex; gap: 8px; flex-wrap: wrap; }

/* recent rows in the desktop panel */
.xs-rec { display: flex; align-items: center; gap: 12px; padding: 10px 14px;
          background: none; border: 0; width: 100%; cursor: pointer; text-align: left; }
.xs-rec + .xs-rec { border-top: 1px solid var(--xs-hair); }
.xs-rec:hover { background: var(--xs-canvas); }
.xs-rec .g { flex: none; width: 26px; text-align: center; color: var(--xs-faint); font-size: 11px; }
.xs-rec .q { flex: 1; min-width: 0; font: 400 12.5px 'Inter', sans-serif; color: var(--xs-ink);
             white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.xs-rec .x { flex: none; color: var(--xs-faint); font-size: 11px; border: 0; background: none;
             cursor: pointer; padding: 2px 4px; line-height: 1; }
.xs-rec .x:hover { color: var(--xs-ink); }

/* the keyboard legend */
.xs-keys { display: flex; align-items: center; gap: 11px; }
.xs-keys .k { display: flex; align-items: center; gap: 5px; }
.xs-keys .cap {
  padding: 2px 5px; border-radius: 4px; border: 1px solid var(--xs-line); background: #fff;
  font: 600 8.5px 'JetBrains Mono', ui-monospace, monospace; color: var(--xs-soft);
}
.xs-keys .w { font: 400 9.5px/1.55 'Inter', sans-serif; color: var(--xs-soft); }
@media (max-width: 1100px) { .xs-keys { display: none } }

/* ── MOBILE · full-screen (36b) ─────────────────────────────────────────── */
.xs-ov { position: fixed; inset: 0; z-index: 90; background: #fff;
         display: flex; flex-direction: column; }
.xs-ov[hidden] { display: none; }
/* PHONES ONLY. The full-screen surface is a small-screen answer to a small
   screen; on a desktop the attached panel is the design and a page-covering
   overlay would be wrong. Belt and braces with the `md:hidden` utility on the
   element and the width guard in openSearchOverlay(): a !important here means
   no future stylesheet, and no stray call, can put it on a desktop. */
@media (min-width: 768px) { .xs-ov { display: none !important; } }
.xs-ov .top {
  flex: none; background: #fff; padding: 11px 14px; display: flex; align-items: center;
  gap: 11px; border-bottom: 1px solid var(--xs-hair);
  padding-top: calc(11px + env(safe-area-inset-top));
}
.xs-ov .back { flex: none; border: 0; background: none; cursor: pointer; padding: 4px 6px;
               color: var(--xs-ink); font-size: 20px; line-height: 1; }
.xs-ov .field {
  flex: 1; min-width: 0; height: 42px; border: 1.5px solid var(--xs-line);
  background: var(--xs-canvas); border-radius: 999px; display: flex; align-items: center;
  gap: 10px; padding: 0 15px;
}
.xs-ov.typing .field { border-color: var(--xs-violet); background: #fff; }
.xs-ov.warn   .field { border-color: var(--xs-amber);  background: #fff; }
.xs-ov .field input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: none;
  font: 400 13.5px 'Inter', sans-serif; color: var(--xs-ink);
}
.xs-ov .field input::placeholder { color: var(--xs-soft); }
.xs-ov .ic { flex: none; color: var(--xs-soft); font-size: 13px; line-height: 1; }
.xs-ov.typing .ic, .xs-ov.warn .ic { color: var(--xs-muted); }
.xs-ov .clr { flex: none; border: 0; background: none; cursor: pointer; padding: 0;
              color: var(--xs-soft); font-size: 13px; line-height: 1; display: none; }
.xs-ov.has .clr { display: block; }
.xs-ov .body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
               padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
.xs-ov .sect { padding: 16px 14px; display: flex; flex-direction: column; gap: 18px; }
.xs-ov .blk { display: flex; flex-direction: column; gap: 10px; }
.xs-ov .blk + .blk { border-top: 1px solid var(--xs-hair); padding-top: 16px; }
.xs-ov .blkh { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.xs-ov .xs-grid { padding: 0; }
/* the mobile recents list sits flush, not inside a panel */
.xs-ov .xs-rec { padding: 8px 0; }
.xs-ov .xs-rec .g { width: 22px; }
.xs-ov .xs-row { padding: 11px 14px; }
.xs-ov .xs-row .t { width: 46px; height: 46px; }
.xs-ov .xs-row .kb { display: none !important; }   /* no keyboard on a phone */

/* ── the results page (36d) ─────────────────────────────────────────────── */
.xr { max-width: 1160px; margin: 0 auto; padding: 26px 32px 40px;
      display: flex; flex-direction: column; gap: 20px; }
.xr-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; }
.xr-h1 { font: 600 28px/1.18 'Manrope', sans-serif; letter-spacing: -1px; color: var(--xs-ink); margin: 0; }
.xr-sub { font: 400 12px/1.55 'Inter', sans-serif; color: var(--xs-muted); margin: 7px 0 0; }
/* ── the filters: counted pills, INK active (38d) ──────────────────────────
   All / Services / Categories filter cards that are already in the DOM — no
   request, no reload, and pressing another undoes it. That is a view change,
   which 38d gives an ink fill; violet would have promised a commitment the
   control cannot make. The count is a mono glyph in a muted colour rather than
   more of the label, so "Services" and "12" do not read as one word. */
.xr-filters { display: flex; gap: 8px; flex-wrap: wrap; }
.xr-f {
  display: inline-flex; align-items: center; gap: 7px;
  border-radius: 999px; padding: 8px 14px; background: var(--xs-ink-tint); border: 1px solid var(--xs-line);
  font: 600 11.5px 'Inter', sans-serif; color: var(--xs-body); white-space: nowrap; cursor: pointer;
}
.xr-f .n { font: 600 9.5px 'JetBrains Mono', ui-monospace, monospace; color: var(--xs-soft); }
.xr-f.on { background: var(--xs-ink); border-color: var(--xs-ink); color: #fff; }
.xr-f.on .n { color: rgba(255,255,255,.7); }
.xr-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.xr-card { display: flex; flex-direction: column; gap: 9px; text-decoration: none; }
.xr-card .ph {
  aspect-ratio: 4 / 3; border-radius: 10px; background: var(--xs-photo);
  border: 1px solid var(--xs-line); overflow: hidden; display: flex;
  align-items: center; justify-content: center;
  font: 600 17px 'Manrope', sans-serif; color: var(--xs-faint);
}
.xr-card .ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
.xr-card .nm { font: 600 13px/1.35 'Inter', sans-serif; color: var(--xs-ink); }
.xr-card .pa { font: 400 10.5px/1.55 'Inter', sans-serif; color: var(--xs-soft); }
.xr-card:hover .nm { color: var(--xs-blue-ink); }   /* a result card navigates — rule 5 */
.xr-tail {
  border-top: 1px solid var(--xs-hair); padding-top: 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
}
.xr-tail .h { font: 600 16px/1.18 'Manrope', sans-serif; letter-spacing: -.45px; color: var(--xs-ink); }
.xr-tail .s { font: 400 11.5px/1.55 'Inter', sans-serif; color: var(--xs-muted); margin-top: 4px; }
.xr-empty { display: flex; flex-direction: column; gap: 16px; }

@media (max-width: 1023px) {
  .xr { padding: 18px 16px 32px; }
  .xr-cards { grid-template-columns: repeat(3, 1fr); gap: 14px; }
}
@media (max-width: 640px) {
  .xr { padding: 14px; gap: 14px; }
  .xr-h1 { font-size: 20px; letter-spacing: -.7px; }
  .xr-sub { font-size: 11px; }
  .xr-cards { grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .xr-card .nm { font-size: 12px; }
  .xr-card .pa { font-size: 10px; }
  .xr-tail { flex-direction: column; align-items: stretch; }
  /* 38f: anything page-level on a phone is lg and full width. */
  .xr-tail .xs-cta { width: 100%; padding: 15px 22px; font-size: 14px; }
  /* Same rule as .xs-cta above: these two ARE the page on an empty or failed
     search, so full width alone is half the rule — page-level on a phone is lg
     (15px 22px / 600 14px), not md at full width. */
  .xr-empty .xs-ghost, .xr-empty .xs-retry {
    width: 100%; justify-content: center; text-align: center;
    padding: 15px 22px; font-size: 14px; gap: 9px;
  }
}
