/* ==========================================================================
   Travelogue — components

   Everything here is scoped under .tg. See base.css for why that matters.
   ========================================================================== */


/* --------------------------------------------------------------------------
   Buttons

   Fill is --tg-red-ink, not the logo red. White on the logo red measures
   3.99:1 and fails AA; the two are indistinguishable to the eye. tokens.css
   has the full note.
   -------------------------------------------------------------------------- */

.tg-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--tg-2);
  min-height: 48px;
  padding: 0 var(--tg-5);
  border: 1.5px solid transparent;
  border-radius: var(--tg-r-sm);
  font-family: var(--tg-body);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--tg-dur-fast) var(--tg-ease),
    border-color var(--tg-dur-fast) var(--tg-ease),
    color var(--tg-dur-fast) var(--tg-ease);
}

@media (min-width: 1024px) {
  .tg-btn { min-height: 52px; }
}

.tg-btn--primary {
  background: var(--tg-red-ink);
  border-color: var(--tg-red-ink);
  color: #fff;
}

.tg-btn--primary:hover {
  background: var(--tg-red-deep);
  border-color: var(--tg-red-deep);
  color: #fff;
}

.tg-btn--ghost {
  background: transparent;
  border-color: var(--tg-red-ink);
  color: var(--tg-red-ink);
}

.tg-btn--ghost:hover {
  background: var(--tg-red-wash);
  color: var(--tg-red-deep);
}

.tg-btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tg-3);
  margin-top: var(--tg-6);
}


/* --------------------------------------------------------------------------
   Chalkboard - DESIGN.md section 1

   The hostel keeps a physical board of the week's activities in the common
   area and rewrites it weekly. This is the site's signature element and the
   strongest conversion signal it has: "I arrive Tuesday, there is a tour
   Wednesday 9am."

   The only dark band on an otherwise white page. Nothing else on the site may
   use this treatment.
   -------------------------------------------------------------------------- */

.tg .tg-week__head { display: grid; gap: var(--tg-4); }

/* Narrow label column beside one large statement, as the reference does it.
   The label column stays empty below its badge on purpose: that white space is
   what makes the statement read as a statement. */
@media (min-width: 1024px) {
  .tg .tg-week__head {
    grid-template-columns: minmax(0, 1fr) minmax(0, 3fr);
    gap: var(--tg-6);
    align-items: start;
  }
}

/* base.css sets `.tg.tg p { margin: 0 0 var(--tg-5) }` at (0,3,1), which beats
   anything with two classes and no element and silently zeroes a top margin.
   Matched here so source order decides, components.css being the later file. */
.tg.tg .tg-week__statement > p { margin: var(--tg-4) 0 0; }

/* Photograph and board are one object, not two boxes with a gap between them.
   The container clips, so the corners belong to the pair rather than to either
   half of it. */
.tg .tg-week__card {
  margin-top: var(--tg-7);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  border-radius: var(--tg-r-lg);
  overflow: hidden;
  box-shadow: var(--tg-card-shadow);
}

@media (min-width: 1024px) {
  .tg .tg-week__card { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
}

/* At lg the figure has no intrinsic height: it stretches to the row, which the
   board's content defines, and the photograph is positioned against that.
   `min-height: 100%` does not work here, the percentage resolves against an
   auto row and computes to zero. */
.tg .tg-week__figure {
  margin: 0;
  position: relative;
  aspect-ratio: 16 / 10;
}

@media (min-width: 1024px) { .tg .tg-week__figure { aspect-ratio: auto; } }

.tg .tg-week__figure img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The group stands slightly below the Murugan statue, so bias the crop up to
     keep both the people and the landmark in frame at tall aspect ratios. */
  object-position: 50% 35%;
}

.tg .tg-board {
  display: flex;
  flex-direction: column;
  padding: var(--tg-6) var(--tg-5) 0;
  background: var(--tg-board);
  color: var(--tg-chalk);
  /* Faint tooth so it reads as a surface, not a dark div. */
  background-image:
    radial-gradient(circle at 20% 30%, rgba(255, 255, 255, .022) 0 1px, transparent 1px),
    radial-gradient(circle at 70% 65%, rgba(255, 255, 255, .018) 0 1px, transparent 1px);
  background-size: 7px 7px, 11px 11px;
}

@media (min-width: 640px) { .tg .tg-board { padding: var(--tg-7) var(--tg-6) 0; } }

.tg.tg .tg-board__list { margin: 0; max-width: none; flex: 1; }

/* Activity on its own line with the day and the time beneath it: the
   reference's big value over a small label, with the time carried to the right
   where a schedule wants it. Markup order stays day, activity, time, which is
   what a <dl> group requires. */
.tg .tg-board__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "what what"
    "day  time";
  gap: 6px var(--tg-4);
  padding-block: var(--tg-4);
  /* One border family only, and none on the last row. A rule above and below
     every row is the spec-table look the pre-flight check bans. */
  border-bottom: 1px solid rgba(255, 255, 255, .13);
}

@media (min-width: 640px) { .tg .tg-board__row { padding-block: var(--tg-5); } }


.tg .tg-board__row:last-child { border-bottom: 0; }

.tg .tg-board__row dd { margin: 0; max-width: none; }

.tg .tg-board__what {
  grid-area: what;
  font-family: var(--tg-display);
  font-size: var(--tg-h3);
  font-weight: 600;
  line-height: 1.2;
  color: var(--tg-chalk);
}

.tg .tg-board__what a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 255, 255, .26);
  transition: border-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-board__what a:hover { border-bottom-color: var(--tg-chalk); }

.tg .tg-board__day {
  grid-area: day;
  margin: 0;
  font-family: var(--tg-display);
  font-size: var(--tg-eyebrow);
  font-weight: 700;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: var(--tg-chalk-dim);
  white-space: nowrap;
}

.tg .tg-board__time {
  grid-area: time;
  font-size: var(--tg-small);
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--tg-chalk-dim);
  white-space: nowrap;
}

/* Between sm and the two-column card the panel spans the whole page, and a day
   pinned left with its time pinned right leaves the pair 500px apart, reading
   as a departures board. They sit together there instead. */
@media (min-width: 640px) and (max-width: 1023px) {
  .tg .tg-board__row {
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: var(--tg-4);
  }

  .tg .tg-board__time { text-align: left; }
}

/* The reference closes its panel with a bar in the opposite tone, flush to the
   panel edges. Ours inverts it: a light bar closing a dark panel. Bleeding it
   back out through the padding is what makes it a bar rather than a button
   sitting in a box. */
.tg .tg-board__cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tg-4);
  margin: var(--tg-6) calc(var(--tg-5) * -1) 0;
  padding: var(--tg-3) var(--tg-3) var(--tg-3) var(--tg-5);
  background: var(--tg-surface);
  color: var(--tg-ink);
  font-family: var(--tg-display);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
}

@media (min-width: 640px) {
  .tg .tg-board__cta {
    margin-inline: calc(var(--tg-6) * -1);
    padding: var(--tg-4) var(--tg-4) var(--tg-4) var(--tg-6);
    font-size: 17px;
  }
}

.tg .tg-board__go {
  display: grid;
  place-items: center;
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-ink);
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-board__go svg {
  width: 19px;
  height: 19px;
  stroke: #fff;
  transition: transform var(--tg-dur) var(--tg-ease);
}

@media (hover: hover) {
  .tg .tg-board__cta:hover .tg-board__go { background: var(--tg-red-ink); }
  .tg .tg-board__cta:hover .tg-board__go svg { transform: translateX(3px); }
}

.tg.tg p.tg-week__note {
  margin: var(--tg-5) 0 0;
  font-size: var(--tg-small);
  color: var(--tg-ink-3);
}

/* The one animation on the page.

   Justification, per the taste-skill motion rule: the rows write themselves in
   the way the physical board is chalked each week. It is storytelling, not
   decoration, and nothing else on the site animates on scroll like this.

   Initial state is declared in CSS rather than set by JS so there is no flash
   of un-offset content. Without JS, or under reduced motion, rows are simply
   visible. */
/* Never clip the element being observed.

   These rows used to carry .tg-reveal and hide themselves with
   `clip-path: inset(0 100% 0 0)`. That collapses the intersection rectangle to
   nothing, so the IntersectionObserver whose whole job was to un-clip them
   reported them as not visible and never fired: the rows stayed clipped for
   ever, and the panel rendered as an empty dark box. A fresh observer put on
   one of those rows, sitting 655px down a 1250px viewport, returned
   `isIntersecting: false` at ratio 0.

   The list is observed instead and the wipe is applied to its children, so the
   observed box is never the clipped box. Anything that hides with opacity or
   transform is safe either way; clip-path and anything else that zeroes the
   box must go on a child. */
@media (prefers-reduced-motion: no-preference) {
  .tg-js .tg-board__list.tg-stagger > .tg-board__row {
    transform: none;
    clip-path: inset(0 100% 0 0);
  }

  .tg-js .tg-board__list.tg-stagger.is-in > .tg-board__row {
    clip-path: inset(0 0 0 0);
    transition:
      opacity .35s var(--tg-ease) calc(var(--i) * .11s),
      clip-path .7s var(--tg-ease) calc(var(--i) * .11s);
  }
}


/* --------------------------------------------------------------------------
   More than a place to sleep — three cards

   After the reference's "who it's for" block: three cards, each a photograph
   with frosted pills laid over it and an icon, a heading and a line beneath.
   The outer two fan apart as the section arrives and close again as it leaves.

   The reference gives every card a "Learn more" link. Ours are not links: the
   three have nowhere individual to go, and a row that looks navigable and is
   not is the same fault as its filter tabs.
   -------------------------------------------------------------------------- */

.tg .tg-who { overflow-x: clip; }

.tg .tg-who__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.tg.tg .tg-who__head h2 { margin: var(--tg-5) 0 0; max-width: 18ch; }

.tg.tg .tg-who__head .tg__lead {
  max-width: 58ch;
  margin: var(--tg-4) 0 0;
  text-wrap: pretty;
}

.tg .tg-who__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-5);
  margin-top: var(--tg-7);
}

@media (min-width: 1024px) {
  .tg .tg-who__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--tg-4); }
}

.tg .tg-who__card {
  display: flex;
  flex-direction: column;
  padding: 14px;
  border-radius: var(--tg-r-md);
  background: var(--tg-surface);
  border: 1px solid var(--tg-line);
  box-shadow: var(--tg-card-shadow);
}

.tg .tg-who__media {
  position: relative;
  height: 340px;
  padding: var(--tg-3);
  border-radius: 18px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

@media (min-width: 640px) { .tg .tg-who__media { height: 400px; } }

.tg .tg-who__media--start { justify-content: flex-start; }
.tg .tg-who__media--end   { justify-content: flex-end; }

/* --pos is set per card in the markup. The lounge photograph puts its subject
   in the bottom third under a lens flare and needs a much lower crop than the
   other two, which is a property of the photograph, not of the layout. */
.tg .tg-who__media img {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
}

.tg .tg-who__pills {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--tg-2);
}

/* Frosted rather than sat on a scrim: the pills have to stay legible over a
   dark lounge, a bright wall of paper and a lit reception desk, and a scrim
   dark enough for all three would flatten every photograph underneath it. */
.tg .tg-who__pill {
  display: flex;
  align-items: center;
  gap: var(--tg-2);
  max-width: 230px;
  padding: 9px 13px;
  border-radius: 14px;
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 4px 16px rgba(20, 20, 20, .16);
  font-family: var(--tg-display);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--tg-ink);
}

.tg .tg-who__dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-red);
}

.tg .tg-who__foot { padding: var(--tg-4) var(--tg-2) var(--tg-2); }

.tg .tg-who__row {
  display: flex;
  align-items: center;
  gap: var(--tg-3);
}

.tg .tg-who__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-red-wash);
}

.tg .tg-who__icon svg { width: 17px; height: 17px; stroke: var(--tg-red-ink); }

.tg.tg .tg-who__foot h3 { margin: 0; font-size: 19px; }

.tg.tg .tg-who__sub {
  margin: var(--tg-2) 0 0;
  padding-left: calc(34px + var(--tg-3));
  font-size: 14px;
  line-height: 1.45;
  color: var(--tg-ink-2);
}

/* The fan.

   The reference scrubs rotation from scroll progress with framer-motion and
   reverses it on the way out. Native scroll-driven animation does the same,
   and the transform origin is the inner bottom corner so the cards hinge
   towards each other rather than drifting.

   Only from lg, where the three sit side by side: stacked, a rotation is just
   a crooked card. */
@media (min-width: 1024px) {
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .tg .tg-who__card {
        animation: linear both;
        animation-timeline: view();
        animation-range: entry 0% exit 100%;
      }

      .tg .tg-who__card:nth-child(1) {
        transform-origin: bottom right;
        animation-name: tg-fan-left;
      }

      .tg .tg-who__card:nth-child(2) { animation-name: tg-fan-mid; }

      .tg .tg-who__card:nth-child(3) {
        transform-origin: bottom left;
        animation-name: tg-fan-right;
      }
    }
  }
}

@keyframes tg-fan-left {
  0%, 100% { transform: translate(26px, 16px) rotate(0deg); }
  42%, 74% { transform: translate(0, 0) rotate(-5deg); }
}

@keyframes tg-fan-right {
  0%, 100% { transform: translate(-26px, 16px) rotate(0deg); }
  42%, 74% { transform: translate(0, 0) rotate(5deg); }
}

/* Translation only, never scale.
   A scaled layer is rasterised once and then stretched, which softens the
   photograph inside it. The hero had exactly this fault earlier in the build
   and the fix there was the same: move the box, do not resize it. */
@keyframes tg-fan-mid {
  0%, 100% { transform: translateY(16px); }
  42%, 74% { transform: translateY(-8px); }
}

/* The fan needs room underneath it. A five degree rotation drops the outer
   corner of a 560px card by about 25px, the closed state adds another 16px of
   translate, and the facts list was sitting inside both: the first item spent
   the whole animation behind the left-hand card. */

/* --------------------------------------------------------------------------
   Closing pill

   A centred dark pill, as the reference closes its card grid with. Without it
   the section stops dead after three cards: the head is centred, the cards are
   a wide three-up, and nothing holds the bottom.
   -------------------------------------------------------------------------- */

.tg .tg-cta-row {
  display: flex;
  justify-content: center;
  margin-top: var(--tg-7);
}

.tg .tg-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--tg-4);
  padding: 8px 8px 8px var(--tg-6);
  border-radius: var(--tg-r-pill);
  background: var(--tg-ink);
  color: #fff;
  font-family: var(--tg-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: var(--tg-track-tight);
  text-decoration: none;
  box-shadow: var(--tg-card-shadow);
}

.tg .tg-pill__go {
  display: grid;
  place-items: center;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface);
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-pill__go svg {
  width: 19px;
  height: 19px;
  stroke: var(--tg-ink);
  transition: transform var(--tg-dur) var(--tg-ease), stroke var(--tg-dur-fast) var(--tg-ease);
}

@media (hover: hover) {
  .tg .tg-pill:hover .tg-pill__go { background: var(--tg-red-ink); }
  .tg .tg-pill:hover .tg-pill__go svg { stroke: #fff; transform: translateX(3px); }
}

.tg .tg-pill:active { transform: scale(.98); }


/* --------------------------------------------------------------------------
   Rooms — the reference's tour-package layout

   One full-width card per room, photograph on one side and a panel on the
   other, sides alternating. The cards are sticky, so each comes to rest under
   the one before it and the stack builds as you scroll. That is the whole
   effect and it costs one property.
   -------------------------------------------------------------------------- */

.tg .tg-rooms__stack {
  display: flex;
  flex-direction: column;
  gap: var(--tg-5);
  margin-top: var(--tg-7);
}

.tg .tg-room {
  position: sticky;
  top: var(--tg-5);
  z-index: calc(var(--i) + 1);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  border-radius: 32px;
}

@media (min-width: 768px) { .tg .tg-room { top: 80px; } }

@media (min-width: 1024px) {
  .tg .tg-room { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Sides alternate down the stack, as the reference alternates them. */
  .tg .tg-room:nth-child(even) .tg-room__media { order: 2; }
}

.tg .tg-room__media {
  min-height: 300px;
  border-radius: 32px;
  overflow: hidden;
  background: var(--tg-surface-2);
}

@media (min-width: 1024px) { .tg .tg-room__media { min-height: 450px; } }

/* No hover zoom on card images: it is on our own do-not-ship list, and here it
   would fight a photograph that is already the size of half the page. */
.tg .tg-room__media img,
.tg .tg-room__media .tg-ph {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 0;
}

.tg .tg-room__panel {
  display: flex;
  flex-direction: column;
  padding: var(--tg-6);
  border-radius: 32px;
  background: var(--tg-surface-2);
}

@media (min-width: 768px) { .tg .tg-room__panel { padding: var(--tg-7); } }

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

.tg.tg .tg-room__top h3 {
  margin: 0;
  font-size: 26px;
  font-weight: 600;
  letter-spacing: var(--tg-track-tight);
}

@media (min-width: 768px) { .tg.tg .tg-room__top h3 { font-size: 28px; } }

.tg .tg-room__no {
  flex: none;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--tg-ink-3);
}

.tg.tg .tg-room__blurb {
  margin: var(--tg-4) 0 0;
  max-width: 44ch;
  color: var(--tg-ink-2);
}

.tg.tg ul.tg-room__rows {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: var(--tg-5) 0 0;
  padding: 0;
  list-style: none;
}

.tg .tg-room__rows li {
  display: flex;
  align-items: center;
  gap: var(--tg-2);
  font-size: 15px;
  font-weight: 500;
  color: var(--tg-ink);
}

.tg .tg-room__rows svg {
  flex: none;
  width: 17px;
  height: 17px;
  stroke: var(--tg-red-ink);
  stroke-width: 2;
}

/* Where the reference puts a two-by-two of specifications and a price. Both
   are still with the client, so the block names what it is waiting for. */
.tg .tg-room__spec {
  margin-top: var(--tg-6);
  padding-top: var(--tg-5);
  border-top: 1px solid var(--tg-line);
}

.tg .tg-room__foot { margin-top: auto; padding-top: var(--tg-6); }

/* --------------------------------------------------------------------------
   Location — the reference's featured destinations block

   Full bleed and dark, the active card's photograph blurred behind everything,
   a statement column on the left and a card carousel on the right that runs off
   the right edge.

   This is the second full-width dark band on the page, after the map. DESIGN.md
   section 1 is updated to match rather than the layout bent to fit it: the
   chalkboard remains the only dark panel inside a light section.
   -------------------------------------------------------------------------- */

.tg .tg-near {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--tg-board);
  color: var(--tg-chalk);
}

.tg .tg-near__bgs {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* The signature effect: the incoming photograph grows out of a card.
   It mounts at about a third of size, offset to the right where the cards are,
   with a card's corner radius, and expands to full bleed over 0.9s on the
   reference's own curve. Not a blur and not a cross-fade: the picture appears
   to come out of the card that was clicked.

   Every layer stays mounted and the outgoing one holds until the new one has
   covered it, so the panel never blanks between two photographs. */
.tg .tg-near__bg {
  position: absolute;
  inset: 0;
  opacity: 0;
  overflow: hidden;
  border-radius: 32px;
  transform: scale(.35) translate(28%, 4%);
  transition:
    opacity .65s var(--tg-ease),
    transform .9s var(--tg-ease),
    border-radius .9s var(--tg-ease);
}

.tg .tg-near__bg.is-on {
  opacity: 1;
  border-radius: 0;
  transform: none;
  z-index: 1;
}

/* Darkened, not blurred. The reference keeps the photograph readable at
   brightness .55 and lets two gradient scrims carry the contrast. */
.tg .tg-near__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(.55) contrast(1.08);
}

/* Two scrims, as the reference layers them: one from the left so the statement
   column stays legible, one from the bottom so the cards sit on something. */
.tg .tg-near__bgs::after,
.tg .tg-near__bgs::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}

.tg .tg-near__bgs::after {
  background: linear-gradient(to right, rgba(20, 20, 20, .92) 0%, rgba(20, 20, 20, .55) 50%, rgba(20, 20, 20, 0) 100%);
}

.tg .tg-near__bgs::before {
  background: linear-gradient(to top, rgba(20, 20, 20, .82) 0%, rgba(20, 20, 20, 0) 45%, rgba(20, 20, 20, .32) 100%);
}

.tg .tg-near__inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-7);
  padding-inline: var(--tg-gutter);
}

@media (min-width: 1024px) {
  .tg .tg-near__inner {
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: var(--tg-8);
    align-items: center;
    padding-left: max(var(--tg-gutter), calc((100vw - var(--tg-w-content)) / 2 + var(--tg-gutter)));
    padding-right: 0;
  }
}

/* --- the statement column ------------------------------------------------ */

.tg .tg-near__eyebrow {
  display: flex;
  align-items: center;
  gap: var(--tg-3);
  font-family: var(--tg-display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: var(--tg-red-on-dark);
}

/* The rule the reference sets before its label. */
.tg .tg-near__eyebrow::before {
  content: "";
  width: 26px;
  height: 2px;
  background: var(--tg-red);
}

.tg.tg .tg-near h2 {
  margin: var(--tg-4) 0 0;
  max-width: 16ch;
  color: var(--tg-chalk);
}

.tg.tg .tg-near__lead {
  margin: var(--tg-4) 0 0;
  max-width: 40ch;
  color: var(--tg-chalk-dim);
}

/* The place block changes with the carousel; the heading above it does not. */
/* All six panes share one grid cell, so the block is exactly as tall as the
   tallest of them and nothing below it gets overlapped. This was a 96px
   min-height over absolutely positioned panes, which was right until the
   panes grew a walking time and started overflowing it. */
.tg .tg-near__panes {
  display: grid;
  margin-top: var(--tg-6);
}

.tg .tg-near__pane {
  grid-area: 1 / 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity .3s var(--tg-ease);
}

.tg .tg-near__pane.is-on { opacity: 1; pointer-events: auto; }

/* Typed in, character by character, after the photograph has finished
   expanding. --c carries the reference's own index arithmetic,
   wordIndex * 5 + charIndex, which is what makes the reveal travel along the
   line instead of fading the whole block at once. */
/* The word cannot break; the characters inside it can be animated. */
.tg .tg-near__pane .tg-w { display: inline-block; white-space: nowrap; }
.tg .tg-near__pane .tg-w span { display: inline-block; }

@media (prefers-reduced-motion: no-preference) {
  .tg .tg-near__pane.is-on h3 .tg-w span,
  .tg .tg-near__pane.is-on p .tg-w span {
    animation: tg-type .2s ease-out both;
  }

  .tg .tg-near__pane.is-on h3 .tg-w span { animation-delay: calc(.55s + var(--c) * .012s); }
  .tg .tg-near__pane.is-on p .tg-w span { animation-delay: calc(.85s + var(--c) * .006s); }
}

@keyframes tg-type {
  from { opacity: 0; transform: translateX(-6px); filter: blur(3px); }
  to   { opacity: 1; transform: none; filter: none; }
}

.tg.tg .tg-near__pane h3 {
  margin: 0;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: var(--tg-track-tight);
  color: var(--tg-chalk);
}

.tg.tg .tg-near__pane p {
  margin: var(--tg-2) 0 0;
  max-width: 34ch;
  color: var(--tg-chalk-dim);
}

.tg .tg-near__walk {
  margin-top: var(--tg-5);
  padding-top: var(--tg-4);
  border-top: 1px solid rgba(255, 255, 255, .16);
  max-width: 260px;
}

/* The value beside the Walk label: a real time for the places the client has
   given one for, and the LOC-MIN placeholder for the ones nobody has timed. */
.tg .tg-near__walk-val {
  display: block;
  font-size: var(--tg-small);
  color: #fff;
}

.tg .tg-near__walk-key {
  display: block;
  font-family: var(--tg-display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: var(--tg-red-on-dark);
  margin-bottom: var(--tg-2);
}

.tg.tg .tg-near__pin {
  display: flex;
  align-items: center;
  gap: var(--tg-2);
  margin: var(--tg-4) 0 0;
  font-size: var(--tg-small);
  color: var(--tg-chalk-dim);
}

.tg .tg-near__pin svg { width: 16px; height: 16px; stroke: var(--tg-red); flex: none; }

/* --- the carousel -------------------------------------------------------- */

.tg.tg .tg-near__rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: clamp(190px, 54vw, 260px);
  gap: var(--tg-4);
  margin: -14px 0 0;
  /* The top padding is the whole reason the selected card has round corners.
     overflow-x: auto makes overflow-y compute to auto as well, so the rail is
     a scrollport on both axes, and the selected card's translateY(-10px) lift
     put its top 10px outside it. The scrollport cut that strip off, which took
     the 24px top corners with it and left a flat edge: square at the top,
     round at the bottom, on one card out of six with identical CSS.

     The negative margin above cancels the padding, so nothing moves. */
  padding: 14px var(--tg-gutter) var(--tg-3) 0;
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

.tg .tg-near__rail::-webkit-scrollbar { display: none; }

.tg .tg-near__card { scroll-snap-align: start; min-width: 0; }

.tg .tg-near__card button {
  display: block;
  position: relative;
  width: 100%;
  text-align: left;
  cursor: pointer;
  opacity: .58;
  transition: opacity var(--tg-dur) var(--tg-ease), transform var(--tg-dur) var(--tg-ease);
}

/* The shadow is on the button, not on the media, so that it is drawn around
   the whole lifted card rather than inside the media's overflow clip. */
.tg .tg-near__card button {
  border-radius: var(--tg-r-md);
  box-shadow: 0 18px 46px rgba(0, 0, 0, .45);
}

.tg .tg-near__card.is-on button { opacity: 1; transform: translateY(-10px); }

@media (hover: hover) { .tg .tg-near__card button:hover { opacity: 1; } }

.tg .tg-near__media {
  display: block;
  position: relative;
  aspect-ratio: 29 / 46;
  border-radius: var(--tg-r-md);
  overflow: hidden;
  background: rgba(255, 255, 255, .06);
}

.tg .tg-near__media img,
.tg .tg-near__media .tg-ph {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
}

.tg .tg-near__cap {
  border-bottom-left-radius: var(--tg-r-md);
  border-bottom-right-radius: var(--tg-r-md);
}

/* Inside the card, over a gradient, as the reference has it. */
.tg .tg-near__cap {
  display: block;
  position: absolute;
  inset: auto 0 0;
  z-index: 1;
  padding: 44px var(--tg-4) var(--tg-4);
  background: linear-gradient(to top, rgba(0, 0, 0, .9), rgba(0, 0, 0, .3) 45%, transparent);
}

.tg .tg-near__cap strong {
  display: block;
  font-family: var(--tg-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: var(--tg-track-tight);
  color: var(--tg-chalk);
}

.tg .tg-near__cap span {
  display: block;
  margin-top: 2px;
  font-size: var(--tg-small);
  color: var(--tg-chalk-dim);
}

.tg .tg-near__foot {
  display: flex;
  align-items: center;
  gap: var(--tg-4);
  margin-top: var(--tg-5);
  padding-right: var(--tg-gutter);
}

.tg .tg-near__nav { display: flex; gap: var(--tg-2); }

.tg .tg-near__btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--tg-r-pill);
  border: 1px solid rgba(255, 255, 255, .25);
  background: transparent;
  cursor: pointer;
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-near__btn svg { width: 18px; height: 18px; stroke: var(--tg-chalk); }

.tg .tg-near__btn--next { background: var(--tg-chalk); border-color: var(--tg-chalk); }
.tg .tg-near__btn--next svg { stroke: var(--tg-board); }

@media (hover: hover) {
  .tg .tg-near__btn:hover { background: rgba(255, 255, 255, .14); }
  .tg .tg-near__btn--next:hover { background: var(--tg-red-ink); border-color: var(--tg-red-ink); }
  .tg .tg-near__btn--next:hover svg { stroke: #fff; }
}

.tg .tg-near__dots { display: flex; gap: 6px; flex: 1; }

.tg .tg-near__dot {
  width: 26px;
  height: 3px;
  border-radius: 2px;
  background: rgba(255, 255, 255, .22);
  transition: background-color var(--tg-dur) var(--tg-ease), width var(--tg-dur) var(--tg-ease);
}

.tg .tg-near__dot.is-on { width: 40px; background: var(--tg-red); }

.tg .tg-near__more {
  flex: none;
  font-size: var(--tg-small);
  color: var(--tg-chalk);
  text-decoration: underline;
  text-underline-offset: .18em;
}

/* The walking-minutes placeholder sits on a dark ground here. */
.tg .tg-near .tg-ph--line {
  background: rgba(255, 255, 255, .05);
  border-color: rgba(255, 255, 255, .22);
}

/* --------------------------------------------------------------------------
   Closing call to action — the reference's CTA block

   One very large centred statement with a letter replaced by an icon badge and
   a word in italic serif, prints pinned at an angle, a rotated sticker, a
   dashed route across the ground, and a dark pill.

   White, not the reference's cream: the client asked for white and no warm
   paper ground on day one, and it is in DESIGN.md 2.
   -------------------------------------------------------------------------- */

.tg .tg-cta {
  position: relative;
  overflow: hidden;
  text-align: center;
  background: var(--tg-surface-2);
}

/* The dashed route. Meaningful here rather than decorative: the section above
   it is about everything being a walk away. */
.tg .tg-cta__path {
  position: absolute;
  inset: 0;
  z-index: 0;
  color: var(--tg-line);
  pointer-events: none;
}

.tg .tg-cta__path svg { width: 100%; height: 100%; }

.tg .tg-cta__inner { position: relative; z-index: 2; }

.tg.tg .tg-cta__title {
  margin: 0 auto;
  max-width: 16ch;
  font-size: clamp(2.5rem, 7.2vw, 5.25rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.035em;
}

/* The badge that stands in for a letter. Sized in ems so it scales with the
   heading rather than needing a value per breakpoint. */
/* The hostel's own mark standing in for the letter o. Relative to this
   stylesheet, so it keeps working when the site stops being travelogue.local.
   The red stays underneath: if the file ever goes missing the word still
   reads, with a plain red o instead of a hole. */
.tg .tg-cta__pin {
  display: inline-block;
  width: .82em;
  height: .82em;
  margin: 0 .02em;
  vertical-align: -.08em;
  border-radius: var(--tg-r-pill);
  background: var(--tg-red-ink) url(../img/logo.png) center / cover no-repeat;
  box-shadow: 0 6px 18px rgba(225, 16, 25, .28);
}

/* One word in italic serif, as the reference sets its last word. A system
   serif: a whole webfont for two words is not a trade worth making. */
.tg .tg-cta__serif {
  font-family: ui-serif, Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 400;
}

.tg .tg-cta__row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--tg-4);
  margin-top: var(--tg-7);
}

@media (min-width: 640px) { .tg .tg-cta__row { flex-direction: row; justify-content: center; gap: var(--tg-5); } }

.tg.tg .tg-cta__alt {
  font-family: var(--tg-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--tg-ink);
  text-decoration: underline;
  text-underline-offset: .2em;
}

/* Prints pinned at an angle, as the reference stacks its photographs. Hidden
   below lg, where a 260px stack over a 40px heading is only clutter. */
.tg .tg-cta__prints { display: none; }

@media (min-width: 1100px) {
  .tg .tg-cta__prints {
    display: block;
    position: absolute;
    top: var(--tg-7);
    right: 4%;
    z-index: 1;
    width: 230px;
    height: 290px;
    pointer-events: none;
  }

  .tg .tg-cta__print {
    position: absolute;
    inset: 0;
    border: 5px solid #fff;
    border-radius: 20px;
    overflow: hidden;
    background: #fff;
    box-shadow: 0 14px 40px rgba(20, 20, 20, .18);
    transform: rotate(var(--r)) translateY(calc(var(--k) * -6px));
  }

  .tg .tg-cta__print:last-child { box-shadow: 0 20px 50px rgba(20, 20, 20, .24); }

  .tg .tg-cta__print img { width: 100%; height: 100%; object-fit: cover; }
}

/* The sticker. The reference's is an invented boarding pass; this one carries
   the real Google rating, which is the strongest thing the page can say at the
   moment somebody is deciding. */
.tg .tg-cta__stamp { display: none; }

@media (min-width: 1100px) {
  .tg .tg-cta__stamp {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    position: absolute;
    left: 5%;
    bottom: var(--tg-7);
    z-index: 1;
    width: 148px;
    padding: var(--tg-3);
    border: 2px dashed var(--tg-red-ink);
    border-radius: 12px;
    background: var(--tg-surface);
    transform: rotate(-11deg);
    pointer-events: none;
  }

  .tg .tg-cta__stamp-top {
    font-family: var(--tg-display);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: var(--tg-track-wide);
    text-transform: uppercase;
    color: var(--tg-red-ink);
  }

  .tg .tg-cta__stamp-big {
    font-family: var(--tg-display);
    font-size: 34px;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--tg-ink);
  }

  .tg .tg-cta__stamp-sub {
    font-size: 11px;
    letter-spacing: var(--tg-track-wide);
    text-transform: uppercase;
    color: var(--tg-ink-3);
  }
}

/* --------------------------------------------------------------------------
   Where to find us

   The reference's location block: centred label and heading, three facts with
   hairlines between them, then the map.

   Dark, and last before the footer, so the two read as one closing block
   rather than as another dark surface loose in the page.
   -------------------------------------------------------------------------- */

.tg .tg-find {
  background: var(--tg-board);
  color: var(--tg-chalk);
}

.tg .tg-find__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.tg.tg .tg-find h2 { margin: var(--tg-5) 0 0; color: var(--tg-chalk); }

.tg .tg-find .tg-badge { background: rgba(255, 255, 255, .1); }
.tg .tg-find .tg-badge__text { color: var(--tg-chalk); }

.tg .tg-find__facts {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-5);
  margin-top: var(--tg-7);
  text-align: center;
}

@media (min-width: 768px) {
  .tg .tg-find__facts { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; }

  /* Hairlines between, never around: a rule on each side of every column is
     the spec-table look. */
  .tg .tg-find__fact + .tg-find__fact {
    border-left: 1px solid rgba(255, 255, 255, .15);
  }
}

.tg.tg .tg-find__key {
  margin: 0;
  font-size: 12px;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: var(--tg-chalk-dim);
}

.tg.tg .tg-find__val {
  margin: var(--tg-2) 0 0;
  font-family: var(--tg-display);
  font-size: 21px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: var(--tg-track-tight);
  color: var(--tg-chalk);
}

@media (min-width: 768px) { .tg.tg .tg-find__val { font-size: 24px; } }

/* Lazily loaded, so it costs nothing until somebody scrolls this far, and
   darkened with a filter rather than with an API key and a styled map. */
.tg .tg-find__map {
  height: 300px;
  margin-top: var(--tg-7);
  border-radius: var(--tg-r-md);
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, .08);
}

@media (min-width: 768px) { .tg .tg-find__map { height: 440px; } }

.tg .tg-find__map iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  filter: grayscale(100%) invert(92%) contrast(90%);
}

.tg.tg .tg-find__link {
  margin: var(--tg-5) 0 0;
  text-align: center;
  font-size: var(--tg-small);
}

/* The only thing in its paragraph, so it is a standalone target rather than a
   link inside a sentence, and 19px tall was under the 24px minimum. PRD 31. */
.tg.tg .tg-find__link a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--tg-chalk);
  text-decoration: underline;
  text-underline-offset: .18em;
}

/* --------------------------------------------------------------------------
   Reviews — the reference's testimonial block

   Three columns: label and score left, a cross-fading photograph in the middle,
   the quote and its author right, stepped with two round buttons.

   Two of its four animations are here, the typewriter on the quote body and the
   cross-fade on the photograph. The rating counting up to 4.8 is not: we have no
   rating, and it is the load-bearing element of that left column, so the slot is
   a labelled placeholder. Its five stars are per-review and need the same number,
   so they are left out rather than shown at a flattering default.
   -------------------------------------------------------------------------- */

/* Stretch, not centre.
   Centring three columns of different heights lines up their middles and
   nothing else, so the heading floated somewhere above the photograph's top
   edge and the buttons somewhere below its bottom. Stretched, all three share
   the row's height and the two text columns push their contents to the top and
   the bottom of it, which is what makes the block read as one composition. */
.tg .tg-rev__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-6);
  align-items: stretch;
}

@media (min-width: 1024px) {
  .tg .tg-rev__grid {
    grid-template-columns: minmax(0, 3fr) minmax(0, 4fr) minmax(0, 5fr);
    gap: var(--tg-7);
  }
}

.tg .tg-rev__side {
  display: flex;
  flex-direction: column;
  gap: var(--tg-5);
}

@media (min-width: 1024px) {
  .tg .tg-rev__side { justify-content: space-between; }
}

.tg .tg-rev__score {
  max-width: 220px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 6px;
}

.tg .tg-rev__num {
  font-family: var(--tg-display);
  font-size: clamp(3rem, 5vw, 4.5rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--tg-ink);
}

.tg .tg-rev__of {
  font-family: var(--tg-display);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--tg-ink-3);
}

.tg .tg-rev__from {
  flex-basis: 100%;
  margin-top: var(--tg-3);
  font-size: var(--tg-small);
  line-height: 1.4;
  color: var(--tg-ink-2);
}

/* The photograph.

   All three are stacked, so the box never blanks between them: the outgoing one
   is still painted underneath while the incoming one fades over it. The
   reference is explicit that removing that layer causes a flash.

   The incoming frame starts at 1.06 and settles at 1. A scaled layer is a
   softened layer, which is why the hero does not do this, but here it ends at
   exactly 1 and only the two thirds of a second in between is scaled. */
.tg .tg-rev__media {
  position: relative;
  aspect-ratio: 4 / 5;
  max-width: 380px;
  width: 100%;
  margin-inline: auto;
  border-radius: 32px;
  overflow: hidden;
  background: var(--tg-surface-2);
  box-shadow: var(--tg-card-shadow);
}

@media (min-width: 1024px) { .tg .tg-rev__media { max-width: none; align-self: center; } }

.tg .tg-rev__photo {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.06);
  transition: opacity .65s var(--tg-ease), transform .65s var(--tg-ease);
}

.tg .tg-rev__photo.is-on { opacity: 1; transform: none; }

.tg .tg-rev__photo img { width: 100%; height: 100%; object-fit: cover; }

.tg .tg-rev__body {
  display: flex;
  flex-direction: column;
  gap: var(--tg-5);
}

@media (min-width: 1024px) {
  .tg .tg-rev__body { justify-content: space-between; gap: var(--tg-5); }
}

.tg.tg .tg-rev__body h2 { margin: 0; max-width: 14ch; }

/* Every quote is in the document. Only one is visible, but a crawler and a
   screen reader get all three, and with no JavaScript the first is simply
   readable. The stack is absolute so the height does not jump between quotes of
   different lengths, which is also what stops the typewriter shifting the page
   as it types. */
/* Tall enough for the longest of the five, measured rather than guessed:
   285px at 1440, 274 at 430, 201 at 768 where the column is widest relative to
   the type. The box is fixed because the quotes stack absolutely; sized to the
   shortest, the longest one spilled over the note and the buttons underneath
   it. A fixed box is also what keeps the typewriter from shifting the page as
   it types, which is the reference's own warning. */
/* Sized to the longest of the five after they were cut back, measured rather
   than guessed. Fixed because the quotes stack absolutely, which is also what
   keeps the typewriter from shifting the page as it types. */
.tg .tg-rev__quotes {
  position: relative;
  min-height: 210px;
}

@media (min-width: 640px) { .tg .tg-rev__quotes { min-height: 165px; } }
@media (min-width: 1024px) { .tg .tg-rev__quotes { min-height: 215px; } }

.tg .tg-rev__quote {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
  transition: opacity .45s var(--tg-ease), transform .45s var(--tg-ease);
}

.tg .tg-rev__quote.is-on {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

/* Leaving upwards, arriving from below, as the reference does. */
.tg .tg-rev__quote.is-out { transform: translateY(-10px); }

.tg.tg .tg-rev__quote blockquote {
  margin: 0 0 var(--tg-4);
  font-size: 17px;
  line-height: 1.55;
  color: var(--tg-ink-2);
}

@media (min-width: 640px) { .tg.tg .tg-rev__quote blockquote { font-size: 18px; } }

.tg .tg-rev__quote figcaption {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: var(--tg-4);
  border-top: 1px solid var(--tg-line);
}

.tg .tg-rev__name {
  font-family: var(--tg-display);
  font-size: 19px;
  font-weight: 700;
  color: var(--tg-ink);
}

.tg .tg-rev__src { font-size: var(--tg-small); color: var(--tg-ink-3); }

.tg .tg-rev__foot {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
}

.tg .tg-rev__nav { display: flex; flex: none; gap: var(--tg-2); }

.tg .tg-rev__btn {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: var(--tg-r-pill);
  border: 1px solid var(--tg-line);
  background: var(--tg-surface);
  cursor: pointer;
  transition: background-color var(--tg-dur-fast) var(--tg-ease),
              border-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-rev__btn svg { width: 19px; height: 19px; stroke: var(--tg-ink); }

.tg .tg-rev__btn--next {
  background: var(--tg-ink);
  border-color: var(--tg-ink);
}

.tg .tg-rev__btn--next svg { stroke: #fff; }

@media (hover: hover) {
  .tg .tg-rev__btn:hover { border-color: var(--tg-ink); }
  .tg .tg-rev__btn--next:hover { background: var(--tg-red-ink); border-color: var(--tg-red-ink); }
}

.tg .tg-rev__btn:active { transform: scale(.95); }

/* A quote slot waiting for the client's words. Marked, not boxed: the text has
   to keep reading as a quote so the layout can be judged. The class carries the
   tg-ph substring, which is what `dump --prod` and the live-page warning look
   for. */

/* A placeholder standing in for a line of copy rather than for a photograph.

   .tg.tg, not .tg. This modifier and the .tg-ph it modifies have the same
   specificity, and .tg-ph is written 500 lines further down, so on source
   order alone the base rule won and every copy placeholder was rendered with
   the photograph placeholder's padding. Found by scripts/audit-css.mjs. */
.tg.tg .tg-ph--line {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
  width: auto;
  min-height: 0;
  padding: var(--tg-3) var(--tg-4);
  text-align: left;
}

/* --------------------------------------------------------------------------
   Good to know — the practical grid

   After the reference's "why choose" block: a statement on the left, a
   supporting line on the right, then tinted cards two across, each an icon, a
   heading and a couple of lines.

   White ground with tinted cards, because the section above it is the tinted
   one and two grey bands in a row read as one long band.

   The reference loops its icon animations for ever. Ours draw once on arrival.
   The page has already spent its one infinite loop on the activity strip, and
   four icons moving permanently in the corner of the eye is noise rather than
   life.
   -------------------------------------------------------------------------- */

.tg .tg-why__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-4);
  margin-top: var(--tg-7);
}

@media (min-width: 768px) {
  .tg .tg-why__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--tg-5); }
}

.tg .tg-why__card {
  padding: var(--tg-6) var(--tg-5) var(--tg-7);
  border-radius: var(--tg-r-md);
  background: var(--tg-surface-2);
}

@media (min-width: 768px) {
  .tg .tg-why__card { padding: var(--tg-7) var(--tg-6) var(--tg-8); }
}

.tg .tg-why__icon {
  display: block;
  width: 34px;
  height: 34px;
  margin-bottom: var(--tg-6);
}

.tg .tg-why__icon svg {
  width: 100%;
  height: 100%;
  stroke: var(--tg-red-ink);
  stroke-width: 1.6;
}

.tg.tg .tg-why__card h3 {
  margin: 0;
  font-size: var(--tg-h3);
  letter-spacing: var(--tg-track-tight);
}

.tg.tg .tg-why__card p {
  margin: var(--tg-3) 0 0;
  max-width: 42ch;
  color: var(--tg-ink-2);
}

/* The icons draw themselves as the cards arrive, one after another.

   Driven by the reveal system's .is-in, not by a scroll-scrubbed timeline.
   Scrubbing tied the drawing to scroll position, and there was no setting that
   worked: finish late and two of the four sat half drawn while the section was
   being read; finish early enough to avoid that and the whole thing was over
   before the cards cleared the fold, so nobody ever saw it move. A fixed
   nine-tenths of a second that starts when the grid arrives is seen every time.

   The hidden state lives in the keyframes rather than on the element, so an
   icon is only ever invisible while an animation is actually running on it.
   Without JavaScript, or under reduced motion, .is-in never lands and the
   icons are simply drawn. The schedule rows taught this one: a hiding rule
   that outlives its animation is content nobody can see.

   The dash length is a flat 120 rather than each path's measured length: these
   are 24px icons whose longest path is well under that, so the stroke is fully
   hidden at the start and fully drawn at the end, which is all the effect
   needs. */
@media (prefers-reduced-motion: no-preference) {
  .tg-js .tg-why__grid.is-in .tg-why__icon svg path {
    stroke-dasharray: 120;
    animation: tg-draw .9s var(--tg-ease) both;
    animation-delay: calc(var(--n) * .12s);
  }
}

@keyframes tg-draw {
  from { stroke-dashoffset: 120; }
  to   { stroke-dashoffset: 0; }
}

/* --------------------------------------------------------------------------
   Activities — centred head and a self-scrolling photo strip

   After the reference's tour-activities block: everything centred, one large
   statement that fades in word by word as it arrives, and a strip of rounded
   photographs that scrolls by itself.

   Its filter tab bar is not reproduced. That control sets a state variable
   which only styles the buttons; the strip renders every image regardless.

   Its hover:scale-110 on the photographs is not reproduced either: hover zoom
   on a card image is on our own do-not-ship list, and the strip pausing under
   the cursor is a better affordance than the picture growing.
   -------------------------------------------------------------------------- */

.tg .tg-acts { position: relative; }

.tg .tg-acts__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.tg.tg .tg-acts__title {
  max-width: 900px;
  margin: var(--tg-5) 0 0;
  text-wrap: balance;
}

.tg.tg .tg-acts__head .tg__lead {
  max-width: 46ch;
  margin: var(--tg-4) 0 0;
  text-wrap: balance;
}

/* The heading arrives one word at a time.

   The reference maps scrollYProgress onto a per-word colour with framer-motion.
   Native scroll-driven animation does it with no library and no scroll
   listener; --n carries the word index, so each word's range starts a little
   later than the one before it.

   It starts from --tg-ink-3 and not from the reference's near-white. Every
   failure mode of this animation ends with the words frozen at their starting
   colour, and a heading nobody can read is not a trade worth making for a
   slightly wider fade. #767676 on white is 4.5:1, legible on its own. */
.tg .tg-acts__title span { display: inline-block; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .tg .tg-acts__title span {
      color: var(--tg-ink-3);
      animation: tg-word linear both;
      animation-timeline: view();
      /* Both ends move, five per cent per word, so the colour travels along
         the line as a wave. Only shifting the end point makes every word start
         together and finish apart, which reads as a gradient and not as
         writing. */
      animation-range: cover calc(12% + var(--n) * 5%) cover calc(30% + var(--n) * 5%);
    }
  }
}

@keyframes tg-word { to { color: var(--tg-ink); } }


/* The strip.

   Two identical tracks side by side, translated by exactly one track's width,
   so the frame the animation lands on is the frame it started from and the
   loop has no seam. The container gap has to match the card gap or the join
   shows.

   --set must stay in step with the number of cards a track holds, so the
   builder writes --n onto the element rather than this file guessing. */
.tg .tg-marq {
  --card: 190px;
  --gap: 12px;
  --lift: 26px;
  --set: calc((var(--card) + var(--gap)) * var(--n, 6));
  display: flex;
  gap: var(--gap);
  height: 300px;
  margin-top: var(--tg-7);
  overflow-x: hidden;
  overflow-y: hidden;
}

@media (min-width: 768px) {
  .tg .tg-marq { --card: 260px; --gap: 16px; --lift: 40px; height: 400px; }
}

.tg .tg-marq__track {
  display: flex;
  flex: none;
  gap: var(--gap);
  height: 100%;
  align-items: center;
}

@media (prefers-reduced-motion: no-preference) {
  .tg .tg-marq__track { animation: tg-marq 52s linear infinite; }

  /* Pause under the cursor, and while anything inside has keyboard focus:
     a link that walks away from the focus ring is unusable. */
  .tg .tg-marq:hover .tg-marq__track,
  .tg .tg-marq:focus-within .tg-marq__track { animation-play-state: paused; }
}

/* Without the animation the strip would show only its first three cards and
   there would be no way to reach the rest, so it becomes an ordinary scroller
   and the duplicate set is dropped. */
@media (prefers-reduced-motion: reduce) {
  .tg .tg-marq {
    overflow-x: auto;
    scrollbar-width: none;
    padding-inline: var(--tg-gutter);
  }

  .tg .tg-marq > [aria-hidden="true"] { display: none; }
}

@keyframes tg-marq {
  to { transform: translateX(calc(var(--set) * -1)); }
}

.tg .tg-marq__card {
  position: relative;
  flex: none;
  width: var(--card);
  border-radius: 28px;
  overflow: hidden;
  background: var(--tg-surface-2);
  text-decoration: none;
  box-shadow: var(--tg-card-shadow);
}

/* Three heights in rotation, the middle one tallest, so the strip reads as a
   shallow wave rather than a row of equal blocks. The pattern repeats every
   three cards and the track holds six, so it lines up across the loop seam. */
.tg .tg-marq__card:nth-child(3n + 1) { height: 62%; align-self: flex-start; margin-top: var(--lift); }
.tg .tg-marq__card:nth-child(3n + 2) { height: 84%; align-self: center; }
.tg .tg-marq__card:nth-child(3n)     { height: 72%; align-self: flex-end; margin-bottom: var(--lift); }

.tg .tg-marq__card img,
.tg .tg-marq__card .tg-ph {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  object-fit: cover;
}

.tg .tg-marq__name {
  position: absolute;
  inset: auto 0 0;
  padding: 44px var(--tg-4) var(--tg-4);
  background: linear-gradient(to top, rgba(0, 0, 0, .78), rgba(0, 0, 0, .32) 45%, rgba(0, 0, 0, 0));
  color: #fff;
  font-family: var(--tg-display);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: var(--tg-track-tight);
}

@media (hover: hover) {
  .tg .tg-marq__card { transition: box-shadow var(--tg-dur) var(--tg-ease); }
  .tg .tg-marq__card:hover { box-shadow: 0 12px 40px rgba(20, 20, 20, .22); }
}

.tg .tg-btn-row--center { justify-content: center; }

/* --------------------------------------------------------------------------
   FAQ — native <details>, so it works with JS off and is machine-readable.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   FAQ — the reference's "your question answered" block

   Heading and a standing offer of help on the left, the questions as cards on
   the right.

   Native details/summary, not a scripted accordion. It opens with no
   JavaScript, it is keyboard operable and correctly announced for free, and the
   browser manages the open state. The reference's is a React component; the
   only part worth copying is the look.
   -------------------------------------------------------------------------- */

.tg .tg-qa__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--tg-6);
}

@media (min-width: 1024px) {
  .tg .tg-qa__grid {
    grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
    gap: var(--tg-8);
    align-items: start;
  }
}

.tg .tg-qa__side {
  display: flex;
  flex-direction: column;
  gap: var(--tg-6);
}

@media (min-width: 1024px) {
  .tg .tg-qa__side { justify-content: space-between; min-height: 420px; }
}

.tg.tg .tg-qa__side h2 { margin: var(--tg-5) 0 0; }
.tg.tg .tg-qa__side .tg__lead { margin: var(--tg-4) 0 0; }

.tg .tg-qa__ask {
  padding: var(--tg-6);
  border-radius: var(--tg-r-md);
  background: var(--tg-surface-2);
}

.tg.tg .tg-qa__ask h3 { margin: 0; font-size: 21px; }

.tg.tg .tg-qa__ask p {
  margin: var(--tg-2) 0 var(--tg-5);
  font-size: var(--tg-small);
  color: var(--tg-ink-2);
}

/* Each question is a card rather than a row on a rule, which is what stops a
   six-item list reading as small print. */
.tg .tg-faq {
  display: flex;
  flex-direction: column;
  gap: var(--tg-3);
  max-width: none;
}

.tg .tg-faq__item {
  border-radius: var(--tg-r-md);
  background: var(--tg-surface-2);
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-faq__item[open] { background: var(--tg-surface-2); }

.tg .tg-faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--tg-4);
  padding: var(--tg-5) var(--tg-5);
  font-family: var(--tg-display);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
}

@media (min-width: 640px) { .tg .tg-faq summary { font-size: 19px; padding: var(--tg-5) var(--tg-6); } }

.tg .tg-faq summary::-webkit-details-marker { display: none; }

.tg .tg-faq__mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface);
  transition: background-color var(--tg-dur) var(--tg-ease);
}

.tg .tg-faq__mark svg {
  width: 18px;
  height: 18px;
  stroke: var(--tg-ink);
  transition: transform var(--tg-dur) var(--tg-ease), stroke var(--tg-dur) var(--tg-ease);
}

.tg .tg-faq__item[open] .tg-faq__mark { background: var(--tg-ink); }

.tg .tg-faq__item[open] .tg-faq__mark svg {
  stroke: #fff;
  transform: rotate(180deg);
}

.tg.tg .tg-faq__item > p {
  margin: 0;
  padding: 0 var(--tg-5) var(--tg-5);
  color: var(--tg-ink-2);
}

@media (min-width: 640px) { .tg.tg .tg-faq__item > p { padding: 0 var(--tg-6) var(--tg-6); } }

/* --------------------------------------------------------------------------
   Photo placeholder — DESIGN.md §7.1

   Stands in for photography the client has not supplied. Sized to the final
   image so the layout is final now and installing the real photo is a file
   copy. Carries its slot ID and brief so it is readable without opening
   PHOTO-PLACEHOLDERS.md.

   Never ships. functions.php logs if one is ever public.
   -------------------------------------------------------------------------- */

.tg .tg-ph {
  display: grid;
  place-content: center;
  align-content: center;
  gap: var(--tg-2);
  padding: var(--tg-5);
  background: var(--tg-surface-2);
  border: 1px dashed #C9C9C9;
  border-radius: var(--tg-r-md);
  text-align: center;
  color: var(--tg-ink-3);
  font-size: var(--tg-small);
  line-height: 1.45;
}

.tg .tg-ph__id {
  font-family: var(--tg-display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: var(--tg-red-ink);
}

.tg .tg-ph__spec {
  font-variant-numeric: tabular-nums;
  color: #9A9A9A;
}

/* Placeholders fill the slot they stand in for, so a card with no photograph
   yet is exactly the size of the card that will replace it. */


/* --------------------------------------------------------------------------
   Hero

   Ported value for value from the reference template rather than approximated:

     card      mx-4 mb-4  min-h-[90vh] lg:h-[110vh]  rounded-[32px]
               shadow-[0_32px_64px_-12px_rgba(0,0,0,0.15)]
     overlay   px-6 sm:px-10 md:px-[44px]  pb-10 sm:pb-[44px]  justify-end
     row       flex-col lg:flex-row  items-start lg:items-end  justify-between
     h1        36 / 48 / 54 / 72px  leading-[1.05]  weight 500  tracking-[-0.03em]
     stat      12 / 13px  white/80  uppercase  tracking-wider
     glass     bg-white/10  backdrop-blur  border border-white/10  rounded-3xl

   svh rather than vh: on iOS the address bar makes vh taller than the visible
   viewport, which would push the call to action out of sight on exactly the
   devices most of this site's traffic uses.
   -------------------------------------------------------------------------- */

.tg .tg-hero { padding: 0 var(--tg-4) var(--tg-4); background: var(--tg-surface); }

/* Height is reckoned backwards from the fold, not chosen. The booking widget
   hangs off the bottom edge, and the nav sits above the card in normal flow
   rather than floating over it as in the reference, so a card sized in plain
   svh pushes the one thing the page exists to offer below the fold. The
   subtraction is nav + widget padding + pill height, less the overlap. */
.tg .tg-hero__card {
  position: relative;
  min-height: min(calc(100svh - 120px), 700px);
  border-radius: var(--tg-r-lg);
  overflow: hidden;
  background: var(--tg-board);
  box-shadow: var(--tg-card-shadow);
  isolation: isolate;
}

@media (min-width: 1024px) {
  .tg .tg-hero__card { min-height: min(calc(100svh - 130px), 860px); }
}

.tg .tg-hero__bg { position: absolute; inset: 0; }

/* Slightly taller than the card so the drift has somewhere to move without
   exposing an edge. The extra height is absorbed by object-fit, which resamples
   from the source file at full quality; a transform: scale() would stretch the
   already-rasterised bitmap instead and visibly soften the photograph. */
.tg .tg-hero__bg img {
  position: absolute;
  left: 0;
  top: -3%;
  width: 100%;
  height: 106%;
  object-fit: cover;
  object-position: 50% 40%;
}

/* The reference relies on its footage being dark. A photograph is not
   dependable, so a scrim carries the contrast, kept light enough that the
   picture still reads as a picture. */
.tg .tg-hero__card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(to top,
    rgba(0, 0, 0, .70) 0%, rgba(0, 0, 0, .28) 42%, rgba(0, 0, 0, 0) 72%);
}

.tg .tg-hero__overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--tg-5) var(--tg-5) 40px;
}

@media (min-width: 640px)  { .tg .tg-hero__overlay { padding: var(--tg-6) 40px 44px; } }
@media (min-width: 768px)  { .tg .tg-hero__overlay { padding: var(--tg-8) 44px 44px; } }

/* Clear the booking widget that overlaps the bottom edge, so it never lands on
   top of the hero's own pills. Matches the negative margins on .tg-hero__book. */
.tg .tg-hero--book .tg-hero__overlay { padding-bottom: 72px; }

@media (min-width: 640px)  { .tg .tg-hero--book .tg-hero__overlay { padding-bottom: 84px; } }
@media (min-width: 1024px) { .tg .tg-hero--book .tg-hero__overlay { padding-bottom: 96px; } }

/* Stacked below lg, so this gap is what separates the headline from its
   supporting line. At 40px they read as two unrelated blocks. */
/* The name and the place, inside the H1 under the tagline. Visible and
   quiet: the H1 has to carry the location (PRD 5, Addendum A6) and the
   tagline has to be what a person reads first. */
.tg .tg-hero__where {
  display: block;
  margin-top: var(--tg-3);
  font-family: var(--tg-body);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: var(--tg-track-wide);
  text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}

@media (min-width: 640px) { .tg .tg-hero__where { font-size: 15px; } }

.tg .tg-hero__row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--tg-5);
}

@media (min-width: 1024px) {
  .tg .tg-hero__row {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--tg-8);
  }
}

.tg .tg-hero__left { max-width: 56rem; }

.tg.tg .tg-hero h1 {
  margin: 0;
  color: #fff;
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.03em;
  max-width: 800px;
  text-shadow: 0 1px 30px rgba(0, 0, 0, .25);
}

.tg .tg-hero__right {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--tg-5);
}

@media (min-width: 1024px) {
  .tg .tg-hero__right { align-items: flex-end; gap: 44px; max-width: 42rem; }
}

/* Quieter than the headline on purpose. At weight 500 it read as a second
   headline, and three lines of that over a photograph is a wall rather than a
   caption. */
.tg.tg .tg-hero__sub {
  margin: 0;
  max-width: 450px;
  font-size: 15px;
  font-weight: 400;
  line-height: 1.45;
  color: rgba(255, 255, 255, .88);
}

@media (min-width: 640px)  { .tg.tg .tg-hero__sub { font-size: 16px; } }
@media (min-width: 1024px) {
  .tg.tg .tg-hero__sub { font-weight: 500; text-align: right; }
}

/* From lg only. Below that the hero is one column and the pills are a fourth
   block of text over the photograph, on a screen where the booking widget's
   chips are already doing that job 200px lower. */
.tg .tg-hero__pills { display: none; }

@media (min-width: 1024px) {
  .tg .tg-hero__pills {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--tg-3);
  }
}

.tg .tg-hero__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--tg-2);
  padding: 8px var(--tg-4);
  border-radius: 24px;
  background: rgba(255, 255, 255, .1);
  border: 1px solid rgba(255, 255, 255, .1);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .01em;
  color: #fff;
  white-space: nowrap;
}

@media (min-width: 640px) {
  .tg .tg-hero__pill { padding: 10px 22px; font-size: 14px; }
}

.tg .tg-hero__pill svg { width: 18px; height: 18px; stroke: #fff; opacity: .9; flex: none; }


/* --------------------------------------------------------------------------
   Hero booking widget

   The reference template hangs a white card off the bottom edge of its hero,
   holding a large pill and a row of quick chips. There the pill is an AI
   prompt field; here it is a link to the booking engine, because a box shaped
   like a search field that cannot search is a worse experience than no box.

   It is a sibling of .tg-hero__card, never a child: the card clips its own
   overflow to keep the photograph's rounded corners, and would cut this in
   half. The overlap is a negative top margin rather than absolute positioning,
   so the widget's own height is never something the layout has to guess at.

   The hero overlay is given matching bottom padding below, otherwise the card
   lands on top of the hero's own pills.
   -------------------------------------------------------------------------- */

.tg .tg-hero--book { padding-bottom: var(--tg-6); }

.tg .tg-hero__book {
  position: relative;
  z-index: 3;
  margin: -44px auto 0;
  max-width: 1140px;
  padding: 12px;
  background: var(--tg-surface);
  border: 1px solid var(--tg-line);
  border-radius: 28px;
  box-shadow: 0 4px 60px rgba(20, 20, 20, .10);
}

@media (min-width: 640px) {
  .tg .tg-hero__book { margin-top: -52px; padding: 14px; border-radius: 32px; }
}

@media (min-width: 1024px) {
  .tg .tg-hero__book { margin-top: -60px; padding: 16px; }
}

/* The pill. One link, so the whole strip is the target: on a phone the tap
   area is the full width rather than the 48px circle at the end. */
.tg .tg-book {
  display: flex;
  align-items: center;
  gap: var(--tg-3);
  min-height: 64px;
  padding: 8px 8px 8px 12px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface-2);
  text-decoration: none;
  color: var(--tg-ink);
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

@media (min-width: 640px) {
  .tg .tg-book { min-height: 76px; gap: var(--tg-4); padding: 10px 10px 10px 16px; }
}

@media (hover: hover) {
  .tg .tg-book:hover { background: #F1F1F1; }
}

.tg .tg-book__well {
  display: none;
  flex: none;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface);
  border: 1px solid var(--tg-line);
}

/* Below 400px the icon well costs more width than it earns. */
@media (min-width: 400px) { .tg .tg-book__well { display: grid; } }

.tg .tg-book__well svg { width: 22px; height: 22px; stroke: var(--tg-ink); }

.tg .tg-book__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.tg.tg .tg-book__title {
  font-family: var(--tg-display);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--tg-ink);
}

@media (min-width: 640px) { .tg.tg .tg-book__title { font-size: 19px; } }

.tg.tg .tg-book__sub {
  font-size: 12.5px;
  line-height: 1.35;
  color: var(--tg-ink-3);
}

@media (min-width: 640px) { .tg.tg .tg-book__sub { font-size: 14px; } }

.tg .tg-book__go {
  display: grid;
  place-items: center;
  flex: none;
  width: 48px;
  height: 48px;
  margin-left: auto;
  border-radius: var(--tg-r-pill);
  background: var(--tg-ink);
  transition: background-color var(--tg-dur-fast) var(--tg-ease);
}

@media (min-width: 640px) { .tg .tg-book__go { width: 56px; height: 56px; } }

.tg .tg-book__go svg {
  width: 20px;
  height: 20px;
  stroke: #fff;
  transition: transform var(--tg-dur) var(--tg-ease);
}

/* Red only on the arrow, and only on hover. It is the one chroma on the page,
   so it marks the single most important action rather than decorating. */
@media (hover: hover) {
  .tg .tg-book:hover .tg-book__go { background: var(--tg-red-ink); }
  .tg .tg-book:hover .tg-book__go svg { transform: translateX(3px); }
}

.tg .tg-book:active .tg-book__go { transform: scale(.94); }

/* Chips.

   A grid, not a horizontal scroller. The reference scrolls its chips, which
   hides half of them on a phone and brings the overflow-x tail-padding bug
   with it; four short labels fit two by two instead. minmax(0, 1fr) rather
   than 1fr so a long label ellipsises inside its cell instead of widening the
   track and pushing the card off screen. */
.tg .tg-book__chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--tg-2);
  margin-top: var(--tg-2);
}

@media (min-width: 768px) {
  .tg .tg-book__chips { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--tg-3); }
}

.tg .tg-book__chip {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--tg-2);
  min-width: 0;
  min-height: 44px;
  padding: 0 var(--tg-3);
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface-2);
  font-size: 13px;
  color: var(--tg-ink);
  text-decoration: none;
  transition: background-color var(--tg-dur-fast) var(--tg-ease),
              color var(--tg-dur-fast) var(--tg-ease);
}

@media (min-width: 640px) { .tg .tg-book__chip { font-size: 14px; padding: 0 var(--tg-4); } }

.tg .tg-book__chip svg {
  flex: none;
  width: 17px;
  height: 17px;
  stroke: var(--tg-ink-3);
  transition: stroke var(--tg-dur-fast) var(--tg-ease);
}

.tg .tg-book__chip span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (hover: hover) {
  .tg .tg-book__chip:hover { background: var(--tg-ink); color: #fff; }
  .tg .tg-book__chip:hover svg { stroke: #fff; }
}


/* --------------------------------------------------------------------------
   Section badge

   The pill label above each section heading. One per section, which is what
   gives the reference template its rhythm.
   -------------------------------------------------------------------------- */

.tg .tg-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--tg-2);
  height: 40px;
  padding: 0 var(--tg-4) 0 6px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-surface-2);
  margin-bottom: var(--tg-5);
}

.tg .tg-badge__dot {
  width: 28px;
  height: 28px;
  border-radius: var(--tg-r-pill);
  background: var(--tg-red-ink);
  flex: none;
}

.tg .tg-badge__text {
  font-family: var(--tg-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--tg-ink);
}


/* --------------------------------------------------------------------------
   Nearby list

   No hairline under every row. Six items with a rule each is the spec-table
   look; spacing and a bold name do the work instead.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Reviews and the closing call to action
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Reference-template overrides

   The section shapes below follow the travel template the client bought:
   split section headers, fixed-height rounded media, larger cards.

   Note on one deliberate conflict: the general anti-slop rules this build
   started from ban the split header (big headline left, small explainer right)
   and ration the pill label to one per three sections. The reference template
   uses both on every section, the client chose it, and on a photo-led travel
   page it is what gives the page its rhythm. Following the reference.
   -------------------------------------------------------------------------- */

.tg .tg__head-row {
  display: flex;
  flex-direction: column;
  gap: var(--tg-4);
}

@media (min-width: 768px) {
  .tg .tg__head-row {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--tg-8);
  }

  .tg .tg__head-row h2 { flex: 1 1 auto; max-width: 16ch; margin: 0; }

  .tg.tg .tg__head-row .tg__lead {
    flex: 0 1 380px;
    margin: 0;
    padding-bottom: var(--tg-2);
  }
}

.tg .tg__head { max-width: none; margin-bottom: var(--tg-7); }

@media (min-width: 1024px) {
  .tg .tg__head { margin-bottom: var(--tg-8); }
}

/* --- Quote cards -------------------------------------------------------- */

/* --- Closing call to action -------------------------------------------- */

/* --- Buttons pick up the pill shape ------------------------------------- */

.tg-btn { border-radius: var(--tg-r-pill); padding: 0 var(--tg-6); }


/* ==========================================================================
   Motion system

   Ported from the reference templates rather than approximated. Their entire
   vocabulary is five patterns on one curve, cubic-bezier(0.16, 1, 0.3, 1),
   used 48 times across the file with nothing else. The values below are theirs;
   only the implementation changes, because we have no build step and cannot
   ship framer-motion.

   No animation library. Reveals run on IntersectionObserver plus a CSS
   transition; the scroll-linked pieces use native scroll-driven animations,
   which cost zero JavaScript. Firefox does not support scroll-driven
   animations yet and simply shows the final state, which is correct.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /* --- 1. Reveal on enter -----------------------------------------------
     Their most-used pattern: opacity 0 -> 1 with y 30 -> 0 over 0.6 to 0.8s.
     Applied to every section, not just one, which is what makes the page feel
     built rather than pasted. */
  .tg-js .tg-reveal {
    opacity: 0;
    transform: translateY(30px);
  }

  .tg-js .tg-reveal.is-in {
    opacity: 1;
    transform: none;
    transition:
      opacity var(--tg-dur-slow) var(--tg-ease) calc(var(--i, 0) * var(--tg-stagger)),
      transform var(--tg-dur-slow) var(--tg-ease) calc(var(--i, 0) * var(--tg-stagger));
  }

  /* --- 2. Stagger inside a group ----------------------------------------
     Children reveal in sequence at 0.15 / 0.2 / 0.25 in the reference. Set on
     the parent so the markup only carries an index. */
  .tg-js .tg-stagger > * {
    opacity: 0;
    transform: translateY(20px);
  }

  .tg-js .tg-stagger.is-in > * {
    opacity: 1;
    transform: none;
    transition:
      opacity var(--tg-dur) var(--tg-ease),
      transform var(--tg-dur) var(--tg-ease);
  }

  .tg-js .tg-stagger.is-in > *:nth-child(1) { transition-delay: .05s; }
  .tg-js .tg-stagger.is-in > *:nth-child(2) { transition-delay: .12s; }
  .tg-js .tg-stagger.is-in > *:nth-child(3) { transition-delay: .19s; }
  .tg-js .tg-stagger.is-in > *:nth-child(4) { transition-delay: .26s; }
  .tg-js .tg-stagger.is-in > *:nth-child(5) { transition-delay: .33s; }
  .tg-js .tg-stagger.is-in > *:nth-child(6) { transition-delay: .40s; }

  /* --- 3. Scroll-linked media ------------------------------------------
     The reference maps scrollYProgress to symmetric keyframes such as
     [-80, 0, 0, -80], so a photograph drifts in as the section arrives and
     back out as it leaves. Native scroll-driven animation does the same with
     no library and no scroll listener.

     Chrome, Edge and Safari 26 run it. Firefox ignores it and shows the final
     frame, which is the correct fallback. */
  @supports (animation-timeline: view()) {
    .tg .tg-hero__bg img,
    .tg .tg-week__figure img {
      animation: tg-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      /* No will-change. It promotes every hero photograph to its own composited
         layer for a two per cent drift, which is a lot of layer memory on a
         phone for very little, and on a very tall viewport the compositor drops
         the layer entirely and the image renders as a black box. */
    }
  }

  /* --- 4. Ambient float -------------------------------------------------
     duration 3s, repeat infinite, easeInOut. Used in the reference only on
     small decorative marks, never on content. Here: the badge dot. */
  .tg .tg-badge__dot {
    animation: tg-float 3s ease-in-out infinite;
  }
}

/* Translate only. The first version scaled to 1.08 so there was room to drift
   inside, which resampled every hero photograph up by eight per cent and made
   the whole thing look soft. The parallax is not worth a blurred hero, so the
   image now sits at its natural size and the drift is a few pixels of movement
   the eye reads as depth. */
@keyframes tg-drift {
  from { transform: translateY(-1.5%); }
  to   { transform: translateY(1.5%); }
}

@keyframes tg-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

/* Hover physics. The reference uses whileHover scale 1.02 and whileTap 0.98
   on everything interactive; the tap state is what makes a button feel
   physical on a phone. */
/* --------------------------------------------------------------------------
   Card and rail corrections
   -------------------------------------------------------------------------- */

/* 1fr is shorthand for minmax(auto, 1fr), and auto floors at min-content. The
   placeholder cards carry a line of brief text whose min-content is wide, so
   those tracks pushed past their share and squeezed the photograph columns
   into slivers. minmax(0, 1fr) is what "four equal columns" actually means. */

/* Placeholders must not drive track width either. */
.tg .tg-ph { min-width: 0; }
.tg .tg-ph span { max-width: 100%; }

/* The grid item is the figure, not the placeholder inside it, so min-width:0
   has to sit on the figure or the long brief line pushes the track wider than
   its share and the last card overflows the container. */
/* place-content: center sizes the grid tracks to max-content and centres them,
   so a long brief line pushed the placeholder wider than its own box and the
   text spilled out of the card. Flex centres without resizing the box. */
.tg .tg-ph {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-width: 0;
  overflow: hidden;
}

.tg .tg-ph > * { max-width: 100%; }

/* ==========================================================================
   Track sizing guard

   This class of bug has bitten three times now: the rooms grid collapsed, the
   review cards overflowed, and mobile ran off to the right. Every time the
   cause was the same. `1fr` is shorthand for `minmax(auto, 1fr)`, and `auto`
   floors at the item's min-content width, so any child with a long line, a
   fixed aspect ratio or an intrinsic size can push its track past its share
   and take the whole layout with it.

   Rather than fix it one grid at a time, every grid and flex child in our
   markup is told it may shrink. Nothing here changes an intended layout: it
   only removes the implicit minimum.
   ========================================================================== */

.tg .tg__split > *,
.tg .tg-rev__grid > *,
.tg .tg-week__card > *,
.tg .tg-hero__row > *,
.tg .tg-who__grid > * {
  min-width: 0;
}

.tg .tg__split { grid-template-columns: minmax(0, 1fr); }

@media (min-width: 1024px) {
  .tg .tg__split { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
}

/* A list we have given a component class to is a layout, not body copy.
   base.css caps every ul, ol and dl at --tg-measure with `.tg.tg ul`, which is
   (0,3,1) and beats any two-class selector. That cap is right for a list inside
   a paragraph and wrong for every component built on one, and it has now
   silently broken three of them: the fact strip wrapped to two rows inside a
   container with room for one, and the location rail rendered six 83px cards
   inside a 1200px section because it was held to 578.

   Matching the specificity once, here, rather than per component. A plain list
   in prose keeps the measure. */
.tg.tg ul[class*="tg-"],
.tg.tg ol[class*="tg-"],
.tg.tg dl[class*="tg-"] {
  max-width: none;
}

/* Long unbroken strings, a URL or a place name, must wrap rather than widen. */
.tg p,
.tg li,
.tg dd,
.tg .tg-ph span { overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   Bleed is a no-op on the full template

   .tg--bleed exists to break a section out of Avada's 1200px row with a
   negative margin and width:100vw. page-tg.php has no such row, so .tg is
   already the full width of the document and the trick is not only redundant
   but harmful: 100vw counts the scrollbar, so every bleed section sat about
   15px wider than the viewport and put a horizontal scroll on the page.
   -------------------------------------------------------------------------- */

body.tg-body .tg .tg--bleed {
  margin-inline: 0;
  width: auto;
  max-width: none;
}

/* Defensive: nothing in our markup may exceed the container it sits in. The
   measurement said the page had no overflow while every screenshot showed the
   copy running off the right edge, which is the signature of a flex or grid
   child sized before the webfont loads and re-measured after. Capping width
   settles it either way and costs nothing. */
.tg .tg__head,
.tg .tg__head-row,
.tg .tg__head-row > *,
.tg .tg__split > *,
.tg .tg__container > * { max-width: 100%; }

