/* ---------------------------------------------------------------------------
   Swipe rows (swipe.js): the controls every horizontal row shares, the
   testimonial carousel, the portfolio gallery row.
   Linked once per page by _swipe-assets.php, from the partials that use it.
   The brand strip and the featured band keep their row layout in
   showcase.css (head), so nothing there waits on this sheet.
   Radius reads var(--radius) with an 8px fallback.
--------------------------------------------------------------------------- */

[data-swipe]{position:relative;--swipe-fg:#191917}
.dark [data-swipe],[data-theme=dark] [data-swipe],[data-swipe][data-theme=dark]{--swipe-fg:#faf9f5}
[data-theme=light] [data-swipe]{--swipe-fg:#191917}

/* a live row can be grabbed; a drag never selects text or lifts an image */
.swipe-live>[data-swipe-view],.swipe-live[data-swipe-view]{cursor:grab}
.is-dragging [data-swipe-view],.is-dragging[data-swipe-view]{cursor:grabbing;-webkit-user-select:none;user-select:none}
.is-dragging [data-swipe-view] a,.is-dragging [data-swipe-view] img{pointer-events:none}
/* snap off while a hand or a glide owns the position, or every frame would snap back */
.is-dragging [data-swipe-view],.is-gliding [data-swipe-view]{scroll-snap-type:none!important;scroll-behavior:auto!important}
/* a held marquee stands still while it is dragged, stepped or resting after */
[data-swipe=marquee].is-held [data-swipe-view]>*{animation-play-state:paused!important}

/* ---------- controls ---------- */
.swipe-nav{display:none}
.swipe-live>.swipe-nav{display:flex}
.swipe-nav--over{position:absolute;inset:0;z-index:3;align-items:center;justify-content:space-between;
  padding-inline:8px;pointer-events:none}
.swipe-nav--below{position:absolute;left:0;right:0;bottom:0;height:44px;align-items:center;justify-content:center;gap:8px}
/* the bar's room is held whether or not the script runs, so nothing moves when it does */
[data-swipe-nav=below]{padding-bottom:68px}
/* Eric 2026-10-11: no box, no border, no fill, on every row. A soft chevron,
   quiet at rest and full on hover, press and keyboard focus; its 44px target
   is invisible around it, and keyboard focus still draws a ring. */
.swipe-btn{pointer-events:auto;flex:none;display:inline-grid;place-items:center;width:44px;height:44px;margin:0;padding:0;
  border:0;border-radius:var(--radius,8px);background:none;color:var(--swipe-fg);opacity:.55;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  transition:opacity var(--dur-2,180ms) var(--ease-out,ease)}
.swipe-btn svg{display:block;width:26px;height:26px}
.swipe-btn[aria-disabled=true]{opacity:.18;cursor:default}
.swipe-btn:focus-visible,.swipe-dot:focus-visible{outline:2px solid var(--accent,#d97757);outline-offset:-2px}
.swipe-btn:not([aria-disabled=true]):focus-visible,.swipe-btn:not([aria-disabled=true]):active{opacity:1}
@media (hover:hover){.swipe-btn:not([aria-disabled=true]):hover{opacity:1}}

.swipe-dots{display:flex;align-items:center}
.swipe-dot{display:grid;place-items:center;width:24px;height:44px;margin:0;padding:0;border:0;background:none;
  color:var(--swipe-fg);cursor:pointer;-webkit-tap-highlight-color:transparent}
.swipe-dot::before{content:"";width:8px;height:8px;border-radius:50%;background:currentColor;opacity:.24;
  transition:opacity var(--dur-2,180ms),transform var(--dur-2,180ms) var(--ease-out,ease)}
.swipe-dot[aria-current=true]::before{opacity:1;transform:scale(1.25)}
.swipe-count{display:none;min-width:5.5ch;text-align:center;font:500 var(--fs-small,14px)/1 var(--body);
  font-variant-numeric:tabular-nums;color:var(--swipe-fg)}
.swipe-many .swipe-dots{display:none}
.swipe-many .swipe-count{display:block}

/* ---------- testimonial carousel (_quote-carousel.php) ----------
   A native scroller of equal-height cards: three across from 1001px, two to
   641px, one with the next one peeking on a phone. The scroller runs to the
   screen's edges on a phone so the peek is real, not cut by the page margin. */
.qswipe .quotes{display:flex;gap:22px;max-width:none;margin:0;overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;scrollbar-width:none;padding-block:2px 4px}
.qswipe .quotes::-webkit-scrollbar{display:none}
.qswipe .quotes:focus-visible{outline:2px solid var(--accent,#d97757);outline-offset:6px}
.qswipe .quote{flex:0 0 calc((100% - 44px) / 3);min-width:0;scroll-snap-align:start}
@media (max-width:1000px){.qswipe .quote{flex-basis:calc((100% - 22px) / 2)}}
@media (max-width:640px){
  .qswipe .quotes{margin-inline:calc(-1 * var(--pad,20px));padding-inline:var(--pad,20px);scroll-padding-inline:var(--pad,20px)}
  .qswipe .quote{flex-basis:86%}
}
/* the first chevron lines up with the quote text, its 44px target reaching into the margin */
.qswipe .swipe-nav--below{justify-content:flex-start;left:-12px}
/* Strip lanes (brand strip, /work Row C, footer): from 900px the chevrons sit
   at the lane's ends, inside the edge fade, where no logo is legible yet; they
   clear the fixed side rail (58px) from 1280px. */
@media (min-width:1280px){.bstrip-lane>.swipe-nav--over{padding-left:66px}}

/* ---------- phones and small tablets (Eric 2026-10-11) ----------
   No chevrons under 900px: a finger swipes. The dots stay where a row has them
   (featured band, testimonials), on a shorter bar; a row without dots keeps no
   bar at all. A small hand over the row's top-right corner says it moves: it
   sits in the gap above the row, never on a card, and only while the row
   actually has somewhere to go (.swipe-live). */
.swipe-hint{display:none}
@media (max-width:899px){
  .swipe-btn,.swipe-nav--over{display:none!important}
  [data-swipe-nav=below]:not([data-swipe-dots]){padding-bottom:0}
  [data-swipe-nav=below][data-swipe-dots]{padding-bottom:52px}
  [data-swipe-nav=below]:not([data-swipe-dots])>.swipe-nav{display:none}
  .qswipe .swipe-nav--below{left:-8px}
  .qswipe .swipe-count{min-width:0;padding-left:8px;text-align:left}
  /* the footer strip's thin padding would clip the hint (the strip contains its paint) */
  .bstrip--footer{padding-top:32px}
  .swipe-live>.swipe-hint{display:block;position:absolute;z-index:2;right:var(--swipe-hint-r,0);bottom:100%;
    width:20px;height:20px;margin-bottom:4px;color:var(--swipe-fg);opacity:.55;pointer-events:none}
  .swipe-hint svg{display:block;width:20px;height:20px}
  .bstrip-lane{--swipe-hint-r:var(--pad,20px)}
  .swipe-hint.is-nudge svg{animation:swipe-nudge 1.6s var(--ease-out,ease) .3s 1 both}
}
@keyframes swipe-nudge{0%,100%{transform:none}25%{transform:translateX(-3px)}55%{transform:translateX(3px)}80%{transform:translateX(-1px)}}

/* about five lines, then Read more; the cut only exists once the script can undo it */
.quote .clamp-on{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:5;line-clamp:5;overflow:hidden}
.quote.is-open .clamp-on{display:block;-webkit-line-clamp:none;line-clamp:none;overflow:visible}
.q-more{align-self:flex-start;min-height:44px;margin:-14px 0 -10px;padding:0;border:0;background:none;cursor:pointer;
  font:600 var(--fs-small,14px)/1 var(--body);color:inherit;text-decoration:underline;text-decoration-thickness:1px;
  text-underline-offset:5px;-webkit-tap-highlight-color:transparent}
.q-more[hidden]{display:none}
.q-more:focus-visible{outline:2px solid var(--accent,#d97757);outline-offset:3px;border-radius:2px}

/* the rating line and, beside it, the ask */
.q-rate{display:flex;flex-wrap:wrap;align-items:center;gap:10px 24px;margin-top:26px}
.q-rate .q-google{margin:0}
.q-rate a.q-write{display:inline-flex;align-items:center;gap:8px;min-height:44px;margin:0;padding:0 18px;
  border:1px solid color-mix(in srgb,currentColor 30%,transparent);border-radius:var(--radius-pill,999px);font:500 var(--fs-small,14px)/1 var(--body);
  color:var(--ink,#191917);text-decoration:none;-webkit-tap-highlight-color:transparent;
  transition:background-color var(--dur-2,180ms) var(--ease-out,ease),color var(--dur-2,180ms) var(--ease-out,ease)}
.dark .q-rate a.q-write{color:var(--paper,#faf9f5)}
@media (hover:hover){.q-rate a.q-write:hover{background:color-mix(in srgb,currentColor 7%,transparent);border-color:color-mix(in srgb,currentColor 45%,transparent)}}
.q-rate a.q-write:active{background:color-mix(in srgb,currentColor 12%,transparent)}
.q-rate a.q-write:focus-visible{outline:2px solid var(--accent,#d97757);outline-offset:2px}

/* ---------- portfolio gallery as a row (_gallery.php, 'row' => true) ---------- */
.rgal--row .rgal-grid{display:flex;gap:16px;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;
  scrollbar-width:none;padding-block:2px 6px}
.rgal--row .rgal-grid::-webkit-scrollbar{display:none}
/* one row height, each tile as wide as its picture's shape (--a, set inline) */
.rgal--row{--rg-h:clamp(200px,22vw,300px)}
.rgal--row .rgal-t,.rgal--row .rgal-t:first-child{flex:0 0 auto;grid-column:auto;width:calc(var(--rg-h) * var(--a,1.6));scroll-snap-align:start}
.rgal--row .rgal-t .rgal-ph,.rgal--row .rgal-t:first-child .rgal-ph{aspect-ratio:var(--a,1.6)}
.rgal--row .rgal-ph--fit{background:linear-gradient(165deg,rgba(25,25,23,.025),rgba(25,25,23,.07))}
.rgal--row .rgal-ph--fit img,.rgal--row .rgal-ph--fit video{object-fit:contain}
.dark .rgal--row .rgal-ph--fit,[data-theme=dark] .rgal--row .rgal-ph--fit{background:linear-gradient(165deg,rgba(240,238,230,.03),rgba(240,238,230,.08))}
@media (max-width:767px){
  .rgal--row{--rg-h:min(46vw,220px)}
  .rgal--row .rgal-grid{margin-inline:calc(-1 * var(--pad,20px));padding-inline:var(--pad,20px);scroll-padding-inline:var(--pad,20px)}
}

@media (prefers-reduced-motion:reduce){.swipe-btn,.swipe-dot::before,.q-rate a.q-write{transition:none}.swipe-hint svg{animation:none!important}}
html.reduce-motion .swipe-btn,html.reduce-motion .swipe-dot::before,html.reduce-motion .q-rate a.q-write{transition:none}
html.reduce-motion .swipe-hint svg{animation:none!important}
.swipe-sr{position:absolute;top:0;left:0;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
.q-write{position:relative}
