/* ==========================================================================
   tgz-flow26.bundle.css — the site-wide consistency and motion layer.
   Wave W0, 2026-09-06. Registered LAST in both asset registries, so every
   rule here wins on load order. There is no !important in this file and
   there must never be: if something here loses, that rule is reaching in
   from a bundle that should be narrowed at its source instead.

   It owns four things and nothing else:

   1. ONE PAGE WIDTH. Six different <main> shells were rendering at three
      different widths — 1409px (/, /packages/, /taghazout/, /agadir/,
      /articles/, /build/, /weather/), 1160px (/surf-camps/, /resorts/,
      /transport/, /retreats/, the spa page…) and 1120px (/where-to-stay/,
      /things-to-do/, /flights/agadir/). Clicking between them jumped the
      content column twice. `.container` alone is declared three times inside
      core-pre-base (1120, then 1100, then var(--max)) before
      tgz-release-4743b sets 1160. One token settles it for all of them.

   2. ONE SECTION RHYTHM, so vertical spacing does not change between routes.

   3. PAGE-TO-PAGE CONTINUITY. Cross-document View Transitions: the header,
      the footer and the page title persist across a navigation instead of
      the whole document blinking. This is why the width fix has to come
      first — a smooth transition between two different column widths makes
      the jump MORE visible, not less.

   4. SCROLL-AWARE SECTIONS. Sections settle into place as they enter the
      viewport, using a scroll-driven animation timeline. Everything is
      visible at rest: where `animation-timeline` is unsupported the
      keyframes simply never run and the section is already in its final
      state, so nothing is ever hidden from a reader, a crawler or a
      no-JS visitor.
   ========================================================================== */

:root {
  /* The one page width and the one gutter. Everything below reads these;
     nothing hard-codes them again. 16px matches the value the spa hero's
     full-bleed break-out already assumes, so that page keeps working. */
  --tgz-page-w: 1160px;
  /* --tgz-page is ALSO public-shell-2026's page colour (#f7efe1, and #050b0e
     in dark). In dark mode the colour won the name, so every width below
     resolved to min(..., #050b0e) - invalid - and .container fell back to
     auto: 366px instead of 358px on a phone, 1120 instead of 1160 at 1280
     (W2.1, layout_gate ldiff). Widths read --tgz-page-w. This line stays
     only because light mode has always resolved public-shell's colour uses
     to it; dropping it would repaint those grounds site-wide, a change of
     its own. */
  --tgz-page: 1160px;
  --tgz-gutter: 16px;
  /* The one gap between cards. 14px is the value the most grids already
     used, so this compacts the loose ones rather than inflating the tight
     ones — and "too big" is the recurring rejection. */
  --tgz-card-gap: 14px;
  /* Section rhythm, shared by every route family. */
  --tgz-section-y: clamp(1.5rem, 4vw, 3.8rem);
  --tgz-flow-ease: cubic-bezier(.22, 1, .36, 1);
}

/* --- 1. One page width ---------------------------------------------------
   The contract has to be written as `width`, not `max-width`: every existing
   owner expresses it as `width: min(100% - <gutter>, <cap>)`, and a smaller
   `width` beats any `max-width` we could set. Six owners were writing that
   declaration with five different gutters (32, 28, 24, 22, 18px) and two
   different caps (1160, 1120) — so both the column AND the side margin
   changed as you moved between routes.

   Shells are listed explicitly rather than by wildcard so a new one has to
   opt in deliberately: silent inheritance is how three widths happened.
   The two route bundles that outranked this on specificity
   (local.bundle `.localPlanningRoute .container`, things.bundle
   `.thingsCockpitApp`) were narrowed at their own source instead of being
   out-specified here. */
.container,
main.tgzArticleShell {
  width: min(100% - (var(--tgz-gutter) * 2), var(--tgz-page-w));
  max-width: var(--tgz-page-w);
  margin-inline: auto;
}
/* tripsV2Main carries its own 8px padding, which would eat 16px out of the
   column and leave /packages/ 16px narrower than every other page. */

/* Shells that are full-bleed by design keep their band, but the content
   inside each band sits on the same column as every other page. */

main.tgzArticleShell > .section {
  max-width: var(--tgz-page-w);
  margin-inline: auto;
}

/* --- 2. One section rhythm ----------------------------------------------- */
main .section {
  padding-block: var(--tgz-section-y);
}

/* --- 2b. ONE CARD WIDTH SCALE -------------------------------------------
   The reported defect: "in page cards largeur sometimes they are large
   others they are narrow." Measured on /surf-camps/ at 1440px, SEVEN
   three-column card grids sat at seven different card widths — 228, 230,
   233, 253, 361, 376 and 377px — because every grid had invented its own
   gap (10, 14, 16 and 17.6px). /transport/ had two three-up grids 2px
   apart; /retreats/ had 233 against 228; /best-time-to-visit/ ran 220,
   228, 233 and 360 on one page.

   Two grids with the same column count inside the same container must
   produce the same card width. That is arithmetic, and it only holds if
   they share one gap. One token, listed explicitly so a grid has to opt
   in — this does NOT change any card's colour, border or radius, only the
   space between them, so nothing is redesigned here. Card treatment is
   rebuilt per family in the later waves. */
main :is(
  .includesGrid, .related, .cards, .landingMiniGrid, .kpiRow, .highlights, .ugcGrid, .tpPartnerCatalogGrid, .tgzNomad468Grid,
  .tgzNomad468Next, .tgzWorkshopGrid, .tgzEventGrid, .relatedCompact,
  .routeRibbon, .premiumCompareGrid, .tgzSpotGrid
) {
  gap: var(--tgz-card-gap);
}

/* --- 3. Page-to-page continuity ------------------------------------------
   Chrome, Edge and Safari 18+ cross-fade the document and carry the named
   elements across. Everywhere else this at-rule is ignored and navigation
   is exactly what it is today. */
@view-transition {
  navigation: auto;
}

/* The chrome persists; only the page body changes. A view-transition-name
   must be unique per document, so these go on the single header, footer and
   first h1 — never on a repeated element. */
.tgz411Header { view-transition-name: tgz-chrome-top; }
.tgzF26 { view-transition-name: tgz-chrome-bottom; }
main > .section:first-of-type h1 { view-transition-name: tgz-page-title; }

::view-transition-group(tgz-chrome-top),
::view-transition-group(tgz-chrome-bottom) {
  animation-duration: .01s; /* held in place rather than animated */
}
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: .28s;
  animation-timing-function: var(--tgz-flow-ease);
}
::view-transition-group(tgz-page-title) {
  animation-duration: .34s;
  animation-timing-function: var(--tgz-flow-ease);
}

/* --- 4. Scroll-aware sections --------------------------------------------
   `animation-timeline: view()` drives the keyframes from the element's own
   position in the scrollport — no JavaScript, no IntersectionObserver, no
   layout thrash. `animation-range` ends the effect once the section is
   properly on screen, so nothing animates while it is being read.

   The keyframes run FROM a settled-in state TO the resting state, and the
   element's own CSS is already the resting state. A browser without
   scroll-driven animation support ignores the timeline, never starts the
   animation, and paints the section exactly as it will finally appear. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    main .section,
    main .tgzUiPanel,
    main .related > a {
      animation: tgzFlowSettle linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 42%;
    }
    /* The first screen is never animated: it is what a share preview and a
       skimming reader see, and it must be finished before it is looked at. */
    main > .section:first-of-type,
    main > .section:nth-of-type(2) {
      animation: none;
    }
  }
}
@keyframes tgzFlowSettle {
  from { opacity: .55; transform: translateY(10px); }
  to   { opacity: 1;   transform: none; }
}

/* --- Consistency details the audit turned up ----------------------------- */
/* Headlines in ten languages were dropping a single orphan word onto its own
   line on almost every localized h1. */
main h1, main h2, main h3 { text-wrap: balance; }
main p, main li { text-wrap: pretty; }

/* Tall pages — several run past 7,000px, one past 13,000px — stop paying
   layout and paint for what is far off screen. `contain-intrinsic-size`
   keeps the scrollbar honest so the scroll position does not jump. */
main > .section {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}
/* …except the first screens, and anything the browser must measure to scroll
   to, which must never be skipped. */
main > .section:first-of-type,
main > .section:nth-of-type(2),
main > .section:target {
  content-visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  main .section,
  main .tgzUiPanel,
  main .related > a { animation: none; }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none; }
}

/* --- 5. Fluidity (W4c) ------------------------------------------------------
   The homepage read as one long scroll without motion. Four moments, all
   visible at rest and all off under prefers-reduced-motion:
   the hero's text rises in on load; a tab panel cross-fades when switched
   (tgz-tabs adds is-enhanced once its script runs, so without script nothing
   animates and every panel simply shows); rail cards lift under the pointer;
   the map sheet slides up when it opens. */
@media (prefers-reduced-motion: no-preference) {
  .tgzUiHero__text > * { animation: tgzRise .55s var(--tgz-flow-ease) both; }
  .tgzUiHero__text > :nth-child(2) { animation-delay: .05s; }
  .tgzUiHero__text > :nth-child(3) { animation-delay: .1s; }
  .tgzUiHero__text > :nth-child(4) { animation-delay: .15s; }
  .tgzUiHero__text > :nth-child(n + 5) { animation-delay: .2s; }
  tgz-tabs.is-enhanced .tgzUiPanel:not([hidden]) { animation: tgzPanelIn .32s var(--tgz-flow-ease) both; }
  tgz-rail > .tgzUiShot, tgz-rail > .tgzUiOffer { transition: transform .35s var(--tgz-flow-ease); }
  tgz-rail > .tgzUiShot:hover, tgz-rail > .tgzUiOffer:hover { transform: translateY(-3px); }
}
@keyframes tgzRise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes tgzPanelIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes tgzSheetUp { from { opacity: 0; transform: translateY(28px); } to { opacity: 1; transform: none; } }
@keyframes tgzFade { from { opacity: 0; } to { opacity: 1; } }
