/* Page styles. Shared header/nav/footer/scroll-top chrome now lives in
   ../chrome.css — do not re-add it here. */

a { color: inherit; }
/* Text-link gradient hover: solid white at rest, brand gradient slides in on hover */
/* ≤1440 down to the 1024 stack: first column ~60%, links block ~40%, with Explore & Listen each half of that block (no cramming — there's plenty of room) */
/* Hero logo: large, shrinks on scroll */
/* Intro width: narrower centered content column */
.ts-intro { max-width: calc(var(--maxw) * 0.75); margin-left: auto; margin-right: auto; }
@media (max-height: 820px) {
    .ts-herosec { min-height: auto !important; justify-content: flex-start !important; padding-top: 13rem !important; padding-bottom: 5rem !important; }
  }
@keyframes tsFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes tsQuoteIn { from { opacity: 0; } to { opacity: 1; } }
/* ---- Responsive (base styles are inline; these only override at breakpoints) ---- */
.ts-push { transition: transform 0.55s var(--ease-out); }
.ts-push:hover { transform: scale(0.9); }
@media (max-width: 900px) {
    .ts-cta-row { flex-direction: column !important; text-align: center; gap: var(--space-sm) !important; }
    .ts-cta-row > div { flex: 1 1 auto !important; }
  }
@media (max-width: 1024px) {
    .ts-nav-desktop { display: none !important; }
    .ts-nav-mobile { display: flex !important; }
    .ts-2col { grid-template-columns: 1fr !important; gap: 44px !important; }
    .ts-herosec { padding-top: 10.5rem !important; }
    .ts-events-grid { grid-template-columns: repeat(2, 1fr) !important; }
    .ts-sec { padding-top: 84px !important; padding-bottom: 84px !important; }
  }
@media (max-width: 1024px) {
    .ts-footer-grid { flex-direction: column !important; align-items: center !important; text-align: center !important; gap: 2.5rem !important; }
    .ts-footer-grid .ts-fbrand, .ts-footer-grid .ts-fright { flex: 1 1 auto !important; width: 100% !important; }
    .ts-footer-grid .ts-flist { align-items: center !important; }
    .ts-footer-grid .ts-flist a { width: auto !important; }
    .ts-frow { gap: 1.5rem !important; }
    .ts-footer-grid .ts-fsocial { justify-content: center !important; }
    .ts-fcopy { text-align: center !important; }
    .ts-cta-row { padding-bottom: calc(var(--section-y) + 2.5rem) !important; }
  }
@media (max-width: 1024px) {
    .ts-fsocial { flex-direction: column !important; }
  }
@media (max-width: 560px) {
    .ts-flogo { width: 320px !important; height: auto !important; }
    .ts-fseam { height: clamp(10px, 1vw, 14px) !important; top: -4px !important; }
    .ts-events-grid { grid-template-columns: 1fr !important; }
    .ts-pad { padding-left: 20px !important; padding-right: 20px !important; }
    .ts-testimonial-quote { min-height: 9em !important; }
  }
@media (max-width: 1440px) { .ts-intro { max-width: calc(var(--maxw) * 0.80); } }
@media (max-width: 1024px) { .ts-intro { max-width: calc(var(--maxw) * 0.85); } }
@media (max-width: 768px)  { .ts-intro { max-width: calc(var(--maxw) * 0.90); } }
@media (max-width: 480px)  { .ts-intro { max-width: 100%; } }
/* BugHerd #10 — show the full static box (icon + text) on ANY device without hover.
   Was gated on max-width:1024px only, so iPads (1180-1366px) fell through to the
   hover-only behaviour and tapping did nothing. hover:none / pointer:coarse catch
   every touch device regardless of width; the width query is kept for small windows. */
@media (max-width: 1024px), (hover: none), (pointer: coarse) {
    .ts-vp { width: 100% !important; height: auto !important; }
    .ts-vp > .ts-vp-text { position: static !important; opacity: 1 !important; transform: none !important; }
  }
/* hamburger */
/* About value-prop equalizer bars */
.ts-vp > svg, .ts-vp > span { position: relative; z-index: 1; }
/* About: thin-line gradient waveform along the BOTTOM of the section; gets punchier when hovering any value-prop box */
.ts-about-eq { position: absolute; left: 0; right: 0; bottom: 0; height: 5.5rem; z-index: 0; display: flex; align-items: flex-end; gap: 0.06rem; padding: 0 var(--section-x); opacity: 0.72; overflow: hidden; pointer-events: none; transition: opacity 0.4s var(--ease-out); }
@keyframes ts-punch { 0%,100% { transform: scaleY(0.32); } 20% { transform: scaleY(1); } 45% { transform: scaleY(0.5); } 70% { transform: scaleY(0.92); } 85% { transform: scaleY(0.4); } }
.ts-about-eq.ts-eq-loud { opacity: 0.88; }
@media (max-width: 1024px) { .ts-about-eq { padding-left: 0 !important; padding-right: 0 !important; } }
.ts-about-eq.ts-eq-loud .ts-about-eq-bar { animation-name: ts-punch; animation-duration: 0.75s !important; }
/* Reviews stage-light spotlights: constant gentle sweep */
@keyframes ts-spotL { 0%,100% { transform: translateY(-3%) rotate(-9deg); } 50% { transform: translateY(6%) rotate(7deg); } }
@keyframes ts-spotR { 0%,100% { transform: translateY(0%) rotate(8deg); } 50% { transform: translateY(-16%) rotate(-8deg); } }
@media (prefers-reduced-motion: reduce) { .ts-spot { animation: none !important; } }
/* Gallery cascade reveal */
.ts-gal-item { opacity: 0; transform: translateY(1.6rem) scale(0.985); transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out); }
.ts-gal-item.ts-gal-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .ts-gal-item { opacity: 1 !important; transform: none !important; } }
/* About value-prop column: fade in from the right when scrolled into view */
.ts-vp-grid { opacity: 0; transform: translateX(30vw); transition: opacity 2.4s var(--ease-out), transform 2.4s var(--ease-out); }
.ts-vp-grid.ts-vp-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .ts-vp-grid { opacity: 1 !important; transform: none !important; } }
/* About value-prop boxes: a small icon box at rest; the box grows on hover to reveal the wrapping text. The column track keeps its original width. */
/* rest: icon-square box; hover: box grows to full column width (identical to the original box), text fades/slides in and wraps normally */
.ts-vp { width: 6rem; height: 6rem; max-width: 100%; align-self: flex-start; justify-self: start; }
.ts-vp:hover { width: 100%; }
/* opaque boxes: use a glow (not brightness) to show the focused box */
.ts-vp:hover, .ts-vp:focus-within { box-shadow: 0 0 0 1px rgba(210, 79, 242, 0.25), 0 12px 38px rgba(186, 0, 235, 0.35) !important; }
/* text is absolutely positioned so it never reflows the box as it grows; it fades in from the left after a short delay */
.ts-vp > .ts-vp-text { position: absolute; top: 50%; left: 6.25rem; right: 1.5rem; display: block; text-align: left; opacity: 0; transform: translateY(-50%) translateX(-1.25rem); transition: opacity 0.15s var(--ease-out), transform 0.2s var(--ease-out); }
/* BugHerd #1 — the box animates width over 0.7s and the text is absolutely
   positioned with left/right offsets, so its available width changes for the whole
   0.7s. Revealing at 0.3s meant the reflow was visible: a word jumped to a second
   line mid-animation. Delay now matches the box's 0.7s width transition, so all
   reflow happens while the text is still at opacity 0. */
.ts-vp:hover .ts-vp-text { opacity: 1; transform: translateY(-50%); transition: opacity 0.45s var(--ease-out) 0.7s, transform 0.5s var(--ease-out) 0.7s; }
@media (prefers-reduced-motion: reduce) { .ts-vp-text { transition: none; } }
/* Occasion: one-shot zoom-out — starts larger, eases to normal once in view (not scroll-linked) */
.ts-events-zoom { transform: scale(1.16); transition: transform 1.3s var(--ease-out); }
.ts-events-zoom.ts-ez-in { transform: none; }
@media (max-width: 768px) {
    #events .ts-events-zoom { transform: none !important; opacity: 0; transition: opacity 1s var(--ease-out); }
    #events .ts-events-zoom.ts-ez-in { opacity: 1 !important; }
  }
@media (prefers-reduced-motion: reduce) { .ts-events-zoom { transform: none !important; } }
/* Packages photos: fade in from the right */
.ts-pkg-photos { opacity: 0; transform: translateX(30vw); transition: opacity 1.6s var(--ease-out), transform 1.6s var(--ease-out); }
.ts-pkg-photos.ts-pp-in { opacity: 1; transform: none; }
/* FAQ: soft fade in */
.ts-soft-fade { opacity: 0; transform: translateY(1rem); transition: opacity 1.1s var(--ease-out), transform 1.1s var(--ease-out); }
.ts-soft-fade.ts-soft-in { opacity: 1; transform: none; }
/* Footer columns: staggered fade in up */
/* ≤1024 (stacked footer): first column fades in first, the other two fade up together */
@media (prefers-reduced-motion: reduce) { .ts-pkg-photos, .ts-soft-fade, .ts-foot-col { opacity: 1 !important; transform: none !important; } }
/* ============================================================
   Static-port additions (state that the DC runtime used to
   re-render is now expressed as classes toggled by home.js)
   ============================================================ */
/* Packages slider — stacked copy blocks, one visible */
.ts-pkg-copy { grid-area: 1 / 1; opacity: 0; pointer-events: none; transition: opacity 0.5s var(--ease-out); }
.ts-pkg-copy.is-active { opacity: 1; pointer-events: auto; }
/* Packages slider — slides: active gets shade + gradient frame, inactive dims */
.ts-pkg-slide .ts-slide-active-fx { position: absolute; inset: 0; z-index: 2; opacity: 0; transition: opacity 0.4s var(--ease-out); }
.ts-pkg-slide .ts-slide-inactive-fx { position: absolute; inset: 0; z-index: 2; background: rgba(0,0,0,0.5); backdrop-filter: grayscale(1); -webkit-backdrop-filter: grayscale(1); opacity: 1; transition: opacity 0.4s var(--ease-out); }
.ts-pkg-slide.is-active .ts-slide-active-fx { opacity: 1; }
.ts-pkg-slide.is-active .ts-slide-inactive-fx { opacity: 0; }
/* Slider dots (packages + reviews) */
.ts-dot { width: 0.5rem; height: 0.5rem; border-radius: var(--radius-pill); border: none; cursor: pointer; background: rgba(255,255,255,0.28); transition: width var(--dur-med) var(--ease-out); }
.ts-dot.is-active { width: 1.875rem; background: var(--gradient-brand-soft); }
/* Testimonials — stacked quotes, one visible */
.ts-tquote { grid-area: 1 / 1; margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--fs-testimonial); line-height: 1.5; color: #fff; opacity: 0; pointer-events: none; transition: opacity 1.2s var(--ease-out); }
.ts-tquote.is-active { opacity: 1; pointer-events: auto; }
/* FAQ accordion */
.ts-faq-item { border-top: 2px solid var(--purple-950); }
.ts-faq-item > button .ts-faq-q { color: var(--purple-950); transition: color 0.4s var(--ease-out); }
.ts-faq-item.is-open > button .ts-faq-q { color: var(--purple-600); }
.ts-faq-icon { transform: rotate(0deg); transition: transform 0.5s var(--ease-out); }
.ts-faq-item.is-open .ts-faq-icon { transform: rotate(135deg); }
.ts-faq-a { display: none; padding: 0 0 1.5rem 0; }
.ts-faq-item.is-open .ts-faq-a { display: block; animation: tsFadeIn 0.3s var(--ease-out) both; }
/* Gallery — three static layouts swapped by viewport width
   (the runtime re-rendered 5 / 3 / 2 columns on resize) */
.ts-gallery-3, .ts-gallery-2 { display: none !important; }
@media (max-width: 1400px) {
  .ts-gallery-5 { display: none !important; }
  .ts-gallery-3 { display: flex !important; }
}
@media (max-width: 560px) {
  .ts-gallery-3 { display: none !important; }
  .ts-gallery-2 { display: flex !important; }
}
.ts-gal-col-off { margin-top: 56px; }
/* Lightbox */
.ts-lightbox { display: none; position: fixed; inset: 0; z-index: 100; background: rgba(3,0,24,0.92); backdrop-filter: blur(6px); align-items: center; justify-content: center; padding: 40px; cursor: zoom-out; }
.ts-lightbox.is-open { display: flex; animation: tsFadeIn 0.25s var(--ease-out) both; }

/* BugHerd #12 — mobile: tighten the gap between the photo marquee and the FAQ
   section. Both sections carry inline padding of var(--section-y), so the gap was
   double that (~8rem at phone widths). !important is required to beat the inline
   style, matching how the other overrides in this file work. */
@media (max-width: 768px) {
  #gallery { padding-bottom: 2rem !important; }
  #faq { padding-top: 2.5rem !important; }
}

/* BugHerd #11 (rev 3) — packages slider order at <=1024px:
     copy -> VIEW ALL PACKAGES -> arrows -> slider cards -> dots

   rev 1/2 tried `display: contents` to lift the controls out of the left grid
   column. It collapsed the wrapper to zero height in Chrome, so it is abandoned.

   This version needs no exotic layout: .ts-pkg-2col stays the stacked 1fr grid it
   already becomes at <=1024, and its children fall in DOM order
   (left column -> cards -> mobile dots). Only two things are needed:
     1. inside the left column, order the CTA above the arrows;
     2. swap which dots list is visible.
   The mobile dots are a second [data-pkg-dot] list after the cards; the slider JS
   binds all of them via querySelectorAll, so both stay in sync, and only one is
   ever displayed. */
@media (max-width: 1024px) {
  .ts-pkg-2col .ts-pkg-left { display: flex; flex-direction: column; align-items: stretch; }
  .ts-pkg-2col .ts-pkg-left > * { order: 1; }
  .ts-pkg-2col .ts-pkg-left > .ts-pkg-cta { order: 2; align-self: flex-start; margin: 2rem 0 0.75rem; }
  .ts-pkg-2col .ts-pkg-left > .ts-pkg-controls { order: 3; margin: 0 0 1.75rem; }
  .ts-pkg-2col .ts-pkg-controls .ts-pkg-dots { display: none !important; }
  .ts-pkg-2col > .ts-pkg-dots-m { display: flex !important; margin: 1.75rem 0 0; }
  /* Zero the shared .ts-2col 44px gap for this section so the space above and below
     the cards is set purely by the two margins above — equal, and tighter. */
  .ts-pkg-2col.ts-2col { gap: 0 !important; }
}

/* BugHerd #11 — narrow-width refinements.

   <=664px: .ts-pkg-copy uses `grid-area: 1/1`, so all six descriptions stack in one
   cell and the block is always as tall as the LONGEST one. As the text wraps to more
   lines at narrow widths that reserved space grows, which reads as a big gap above the
   button. The reserved space is deliberate (it stops the layout jumping when you change
   package), so trim the button's own top margin instead of fighting it. */
@media (max-width: 664px) {
  .ts-pkg-2col .ts-pkg-left > .ts-pkg-cta { margin-top: 0.5rem; }
}

/* <=500px: one card at a time instead of two. The JS measures slide width rather than
   assuming 2-up, so this flex-basis change is all that is needed. */
@media (max-width: 500px) {
  .ts-pkg-2col .ts-pkg-slide { flex: 0 0 100% !important; }
}

/* Promo video — 75% width on desktop, full width from mobile down.
   Vertical padding is deliberately lighter than var(--section-y): this band is a
   single element, so full section rhythm reads as too much air. At <=768px it
   tightens to var(--section-x) so top/bottom match the side gutter. */
.ts-video-frame { max-width: 75%; margin-left: auto; margin-right: auto; }
#promo-video { padding: clamp(2.5rem, 4vw, 4rem) 0; }
@media (max-width: 768px) {
  .ts-video-frame { max-width: 100%; }
  #promo-video { padding: var(--section-x) 0; }
}
/* Testimonials — long quotes clamp to 3 lines with Read more (rev 2026-10-06, 3 lines per Jesica 2026-10-07) */
.ts-tquote { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; }
.ts-tquote.is-clamped { -webkit-mask-image: linear-gradient(180deg, #000 62%, transparent 100%); mask-image: linear-gradient(180deg, #000 62%, transparent 100%); }
.ts-tquote.is-expanded { display: block; -webkit-line-clamp: unset; line-clamp: none; -webkit-mask-image: none; mask-image: none; }
.ts-tmore { display: inline-flex; align-items: center; gap: 0.4rem; margin: -0.5rem auto 1.5rem; padding: 0.25rem 0; border: 0; background: none; cursor: pointer; font-family: var(--font-body); font-weight: 700; font-size: var(--fs-other-4); letter-spacing: 0.1em; text-transform: uppercase; color: #fff; text-decoration: underline; text-decoration-color: var(--purple-300); text-underline-offset: 0.3em; transition: color var(--dur-med) var(--ease-out); }
.ts-tmore:hover { color: var(--purple-300); }
.ts-tmore:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.ts-tmore[hidden] { display: none; }
/* rev 2026-10-07: measure each quote at its own height; old quote leaves instantly so it can't flash under the new one */
.ts-tquote { align-self: start; transition: none; }
.ts-tquote.is-active { transition: opacity 1.2s var(--ease-out); }
/* rev 2026-10-07: 11 reviews — keep arrows round; drop the dots on small phones */
#reviews .ts-arrowbtn { flex-shrink: 0; aspect-ratio: 1 / 1; }
@media (max-width: 480px) { #reviews [data-t-dot] { display: none; } }
/* rev 2026-10-07: the old 560px min-height made the box taller than the 3-line clamp (lines showed under the …) */
@media (max-width: 560px) { .ts-tquote.ts-testimonial-quote { min-height: 0 !important; } }
/* rev 2026-10-07: show ~3.6 lines (line-height 1.5) so the 4th line peeks through the fade; no ellipsis */
.ts-tquote { display: block; -webkit-line-clamp: unset; line-clamp: none; max-height: 5.4em; }
.ts-tquote.is-clamped { -webkit-mask-image: linear-gradient(180deg, #000 55%, transparent 100%); mask-image: linear-gradient(180deg, #000 55%, transparent 100%); }
.ts-tquote.is-expanded { max-height: none; }
/* rev 2026-10-07: ≤768px show 4 lines + 5th peeking (4.6 × 1.5 line-height); ≤480px 6 lines + 7th peeking */
@media (max-width: 768px) {
  .ts-tquote { max-height: 6.9em; }
  .ts-tquote.is-clamped { -webkit-mask-image: linear-gradient(180deg, #000 68%, transparent 100%); mask-image: linear-gradient(180deg, #000 68%, transparent 100%); }
}
@media (max-width: 480px) {
  .ts-tquote { max-height: 9.9em; }
  .ts-tquote.is-clamped { -webkit-mask-image: linear-gradient(180deg, #000 78%, transparent 100%); mask-image: linear-gradient(180deg, #000 78%, transparent 100%); }
}
