/* Local-only adaptations layered over the archived site. */
/* The archived site sets overflow-x:hidden on <body> but never on <html>,
 * so elements that intentionally bleed past the viewport (the hero's
 * oversized background <video>, the Instagram marquee's off-screen tiles,
 * horizontally-scrolling card rows, etc.) still let <html> itself scroll
 * horizontally, adding a blank gutter on the right on narrow/mobile
 * viewports. Locking overflow-x on <html> keeps those intentionally
 * oversized elements clipped without touching their own layout. */
html {
  overflow-x: hidden;
}
/*
 * react-reveal recalculates visibility from viewport state after hydration.
 * In the static archive it can reset already-rendered content to opacity: 0
 * on the first scroll. Keep page content visible; interactive UI outside main
 * (menus, overlays, and carousels) retains its original behavior.
 */
main .react-reveal {
  opacity: 1 !important;
  visibility: visible !important;
  animation: none !important;
  transform: none !important;
}
/* Keep each practical-service label and its action stacked as in the archive.
 *
 * The archived component nests the action (a <div class="Services__ButtonWrapper...">
 * or, on the IT snapshot, a <span class="Services__Label...">) INSIDE the
 * label <p class="IButton__Wrapper...">. On the live site this DOM shape is
 * built by React via direct DOM calls, which the HTML parser's "no <div>
 * inside <p>" restriction never applies to, so the label's own default
 * display: block is enough on its own to stack the text above the action:
 * no flex/grid needed here at all. The mirror has no hydration restoring
 * that nesting from its static HTML, so reattachServiceButtons in offline.js
 * re-nests the action back inside the label at runtime instead, undoing the
 * parser's auto-close. These two rules match the archived component's own
 * generated CSS once that nesting is back in place. */
[class*="IButton__Wrapper"] {
  display: block;
}
[class*="Services__ButtonWrapper"],
[class*="Services__Label"] {
  margin-top: 10px;
}
/* Styling for the Instagram carousel cards installInstagramFeeds builds in
 * offline.js. This fully replaces the archived InstagramFeed React
 * component's own rendering (see the isArchivedBeholdFeed fetch interceptor
 * in offline.js for why that component never renders anything itself).
 *
 * The two InstagramFeed__ImagesWrapper divs the cards are inserted into keep
 * their original generated classes (offline.js only ever adds cards inside
 * them, never touches the wrapper's own class), and those classes still carry
 * the archived component's own container styling: row-reverse on the second
 * wrapper, a 20s animation-duration, and their own transform/animation-name.
 * All three fight the replacement below if left in place: row-reverse makes
 * DOM order run right-to-left, which flips the sign of every card-width
 * measurement offline.js takes off this element (getBoundingClientRect ends
 * up decreasing left-to-right instead of increasing); the animation
 * shorthand's duration/name apply cascade-first and only .mirror-instagram-
 * left/right's own animation-duration below (a later, more specific rule)
 * wins that particular conflict, but relying on cascade order here rather
 * than removing the source of the conflict is fragile. Reset explicitly. */
[class*="InstagramFeed__ImagesWrapper"] {
  flex-direction: row !important;
  animation: none !important;
  transform: none !important;
}
.mirror-instagram-card {
  min-width: 198px;
  width: 198px;
  max-width: 198px;
  height: 198px;
  margin-right: 40px;
}
.mirror-instagram-image {
  display: block;
  width: 198px;
  height: 198px;
  object-fit: cover;
}
/*
 * --mirror-instagram-loop-distance and --mirror-instagram-start-offset are set
 * per ROW from offline.js (setInstagramRowLoopDistance / centerInstagramRowOnSeam:
 * row.style.setProperty(...)), once that row's actual card width and post
 * count are known from the DOM. Custom properties inherit down through
 * descendants, so declaring them on [class*="InstagramFeed__ImagesWrapper"]
 * (the row) rather than on .mirror-instagram-left/right (the cards) is
 * deliberate: a property declared directly on an element always wins over an
 * inherited value regardless of selector specificity, so defaults declared on
 * the cards themselves would permanently shadow whatever offline.js sets on
 * their row ancestor. That exact mistake shipped once already: every card
 * measured --mirror-instagram-start-offset as 0px no matter what the row's
 * inline style said, so the seam centering silently did nothing, and the loop
 * distance was similarly stuck at the 1700px placeholder instead of the
 * measured value.
 *
 *   - loop distance used to be a fixed 1700px regardless of how many cards
 *     were in the row. With more than a handful of posts that is less than
 *     one lap, so the scroll visibly snapped back to the start before most
 *     cards had been seen. It is now exactly one full set of cards wide, so
 *     the loop's second (duplicated) copy picks up with no seam.
 *   - start offset centers the viewport on the seam between the two most
 *     recent posts (see splitCarouselRows in offline.js) as the initial
 *     position, rather than always starting scrolled all the way to one end.
 * The fallback values below (0px offset, 1700px distance) only apply before
 * offline.js has measured the row, e.g. with JavaScript disabled; the feed
 * never renders without JavaScript at all, so in practice these are never
 * used for actual layout, only as a safe default for the custom property
 * itself so var() always resolves to something.
 */
[class*="InstagramFeed__ImagesWrapper"] {
  --mirror-instagram-start-offset: 0px;
  --mirror-instagram-loop-distance: 1700px;
}
.mirror-instagram-left {
  animation: mirrorInstagramLeft 30s linear infinite;
}
.mirror-instagram-right {
  animation: mirrorInstagramRight 30s linear infinite;
}
@keyframes mirrorInstagramLeft {
  from {
    transform: translate(var(--mirror-instagram-start-offset), 0);
  }
  to {
    transform: translate(
      calc(
        var(--mirror-instagram-start-offset) -
          var(--mirror-instagram-loop-distance)
      ),
      0
    );
  }
}
@keyframes mirrorInstagramRight {
  from {
    transform: translate(var(--mirror-instagram-start-offset), 0);
  }
  to {
    transform: translate(
      calc(
        var(--mirror-instagram-start-offset) +
          var(--mirror-instagram-loop-distance)
      ),
      0
    );
  }
}
@media (min-width: 48em) {
  .mirror-instagram-card {
    min-width: 258px;
    width: 258px;
    max-width: 258px;
    height: 258px;
    margin-right: 50px;
  }
  .mirror-instagram-image {
    width: 258px;
    height: 258px;
  }
}
/* Used both before a cookie-consent decision is made (or after an explicit
 * reject) and as a kill switch if contactApi is ever cleared back to '' in
 * offline.js. Either way the form stays visible but inert beneath the
 * overlay message, rather than silently doing nothing. */
.mirror-disabled-form {
  position: relative !important;
  min-height: 260px;
}
.mirror-disabled-form > :not(.mirror-form-overlay) {
  pointer-events: none !important;
}
.mirror-disabled-form .captcha {
  display: none !important;
}
.mirror-form-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  background: rgba(255, 255, 255, 0.58);
  text-align: center;
  pointer-events: auto;
}
.mirror-form-overlay p {
  max-width: 430px;
  margin: 0;
  padding: 20px 24px;
  border: 1px solid #05425e;
  background: #fff;
  color: #05425e;
  font:
    500 18px/1.45 BrandonGrotesqueWeb-Regular,
    Arial,
    sans-serif;
}
/* Native <select> replacing the archived react-select subject dropdown (see
 * installContactForms in offline.js). It reuses the shared eujlcU input
 * class for width/height/background, but that class's 15px padding pushes a
 * <select>'s text and native arrow off from how the other inputs look. */
#c-subject {
  padding: 0 11px;
}
/* Inline status message for the live contact/office-enquiry form submit
 * handler in offline.js. Empty and unstyled until a submit attempt sets its
 * text and is-error/is-success class. */
.mirror-form-status {
  margin: 0 0 16px;
  font:
    600 14px/1.4 Helvetica,
    system-ui,
    sans-serif;
}
.mirror-form-status:empty {
  display: none;
}
.mirror-form-status.is-error {
  color: #c0392b;
}
.mirror-form-status.is-success {
  color: #1e8e3e;
}
.mirror-language-switcher {
  position: relative;
}
.mirror-language-switcher .Dropdown-control {
  cursor: pointer;
}
.mirror-language-options {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 1001;
  display: none;
  min-width: 70px;
  padding: 6px 0;
  border: 1px solid rgba(5, 66, 94, 0.14);
  background: #fff;
  box-shadow: 0 5px 18px rgba(0, 0, 0, 0.14);
  text-align: center;
}
.mirror-language-open .mirror-language-options {
  display: block;
}
.mirror-language-option {
  display: block;
  padding: 3px 14px;
  color: #05425e !important;
  font-size: 13px;
  line-height: 18px;
  text-decoration: none !important;
}
.mirror-language-option:hover,
.mirror-language-option:focus,
.mirror-language-option.is-current {
  color: #fb5237 !important;
}
/* "À vivre en ce moment" category teaser. The archived Next.js component
 * that renders these three cards does not hydrate from the frozen Apollo
 * state, so offline.js rebuilds it client-side from the same category
 * images and copy used elsewhere on the mirrored site. Reset any earlier
 * hand-patched styling so the section keeps its original sky background and
 * curved title treatment. */
.CategorySlides__Wrapper-jf0isp-0 {
  overflow: hidden;
}
/* The archived CurvedText__Wrapper (.ecgRxZ) carries a large negative
 * margin-bottom (-80px/-100px) that assumed React's absolute-positioned
 * letters, which visually float above the text container's own box, so the
 * *next* element could sit close beneath the glyphs despite the wrapper box
 * itself being comparatively short. installCurvedText's plain-JS
 * replacement (see offline.js) instead grows the wrapper to the arc's own
 * measured height so the letters are never clipped, which makes that old
 * negative margin pull the next element up into the letters instead of
 * beneath them. Cancelling it here and spacing the row with its own
 * padding-top keeps the fix self-contained to this section rather than
 * overriding .ecgRxZ (shared by every other CurvedText instance on the
 * site) globally.
 */
.CategorySlides__Wrapper-jf0isp-0 .CurvedText__Wrapper-ao9lsf-0 {
  margin-bottom: 0;
  font-size: 32px;
}
@media (min-width: 48em) {
  .CategorySlides__Wrapper-jf0isp-0 .CurvedText__Wrapper-ao9lsf-0 {
    font-size: 48px;
  }
}
/* The site-wide .Container-s1f73g56-0 caps at 1195px, which is narrower than
 * three cards at their current size (see .mirror-category-card below) plus
 * the requested 200px gaps between them (923px of cards + 400px of gaps =
 * 1323px). Without more room flexbox wraps the third card onto its own row.
 * Widening the container only inside this section keeps the site's shared
 * 1195px max-width intact everywhere else it's used. */
@media (min-width: 48em) {
  .CategorySlides__Wrapper-jf0isp-0 .Container-s1f73g56-0 {
    max-width: 1400px;
  }
}
.mirror-category-row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  flex-wrap: wrap;
  gap: 32px;
  width: 100%;
  max-width: 780px;
  margin: 0 auto;
  padding: 6px 15px 0;
}
.mirror-category-card {
  display: block;
  flex: 0 1 252px;
  max-width: 252px;
  text-align: center;
  text-decoration: none;
}
.mirror-category-card--feature {
  flex-basis: 280px;
  max-width: 293px;
}
.mirror-category-image {
  display: block;
  width: 100%;
  height: auto;
  margin: 0 auto;
}
/* White plate beneath the tinted arch image, matching the reference: the
 * image itself already carries its own color block, so only the text
 * portion needs a background. Inset 24px from each side of the image above
 * it (rather than spanning the card's full width) per the requested sizing.
 * A negative top margin pulls it up so it overlaps the bottom of the image
 * rather than sitting flush beneath it. */
.mirror-category-content {
  width: calc(100% - 48px);
  margin: -44px auto 0;
  background: #fff;
  padding: 24px 20px 28px;
  box-sizing: border-box;
  text-align: left;
  position: relative;
}
.mirror-category-title {
  margin: 0 0 12px;
  font-family: Gustavo, system-ui, sans-serif;
  color: #05425e;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.3;
}
.mirror-category-card--feature .mirror-category-title {
  font-size: 16px;
}
.mirror-category-description {
  margin: 0 0 8px;
  font-family: Helvetica, system-ui, sans-serif;
  color: #888888;
  font-size: 9px;
  line-height: 1.35;
}
.mirror-category-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: Gustavo, system-ui, sans-serif;
  color: #05425e;
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.mirror-category-link::after {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1px solid #fd6f48;
  border-bottom: 1px solid #fd6f48;
  transform: rotate(-45deg);
}
@media (min-width: 48em) {
  .mirror-category-row {
    max-width: 1400px;
    gap: 170px;
    padding-top: 0;
    margin-top: -90px;
  }
  .mirror-category-card {
    flex-basis: 282px;
    max-width: 282px;
  }
  .mirror-category-card--feature {
    flex-basis: 313px;
    max-width: 313px;
  }
  .mirror-category-content {
    padding: 20px 20px 16px;
  }
  .mirror-category-card--feature .mirror-category-content {
    padding: 21px 31px 17px;
  }
  .mirror-category-title {
    font-size: 16px;
  }
  .mirror-category-card--feature .mirror-category-title {
    font-size: 19px;
  }
  .mirror-category-description {
    font-size: 11px;
  }
  .mirror-category-link {
    font-size: 10px;
  }
}

@media (max-width: 1500px) {
  .mirror-category-row {
    gap: 70px;
  }
}

@media (max-width: 1076px) {
  .mirror-category-row {
    flex-direction: column;
    align-items: center;
    gap: 160px;
  }
  .mirror-category-card:nth-child(1) {
    order: 1;
  }
  .mirror-category-card:nth-child(2) {
    order: -1;
  }
  .mirror-category-card:nth-child(3) {
    order: 2;
  }
}
/* Mobile burger icon / desktop "Menu" text panel toggle (see installMenuNav
 * in offline.js), needed only on pages where React no longer hydrates. The
 * archived Menu__Root component drives its white background and open/closed
 * height entirely through inline styles computed from React props (isOpen,
 * showBackground), not a toggleable class, so offline.js's plain hide/fadeIn
 * class swap on the nav list is not enough on its own: this rule supplies
 * the equivalent effect for the menu-open class it adds instead. */
/* Keep the top bar visible at every scroll position. The archived component
 * used to translate it out of view while scrolling down; the mirror keeps
 * the navigation continuously reachable in both scroll directions. */
.Menu__Root-s1sb89f4-0,
.Menu__Root-s1sb89f4-0.mirror-header-hidden {
  transform: translate3d(0, 0, 0) !important;
}
.iPiPCO.mirror-menu-open {
  pointer-events: auto !important;
}
.iPiPCO.mirror-menu-open:after {
  transform: translate3d(0px, 0px, 0px) !important;
}
/* Height is intentionally NOT forced to 100%/100vh here above mobile: the
 * archived Menu__Root itself becomes height: auto at >=48em (its own
 * @media (min-width:48em){.iPiPCO{height:auto;}} rule, already present in
 * this page's own <style>, confirmed from the bundle's g.a.tablet(Ht())
 * override), i.e. even when open it only ever grows to fit its content on
 * tablet/desktop, never the full viewport. Only :after (the white backdrop)
 * still needs an isOpen-driven height, and only below 48em does that mean
 * 100vh: at >=48em the bundle's own g.a.desktop(Ut(), ...) override instead
 * switches it from a fixed 133px to isOpen ? "100%" : "133px" -- a
 * *content-relative* 100%, since :after's positioning parent (Menu__Root)
 * is itself height: auto there, not the viewport. Forcing 100vh at every
 * width (this rule's previous version) is what made the panel always cover
 * the full screen even when its content was much shorter. */
@media (max-width: 47.99em) {
  .iPiPCO.mirror-menu-open:after {
    min-height: 100vh !important;
    max-height: 100vh !important;
  }
}
@media (min-width: 48em) {
  .iPiPCO.mirror-menu-open:after {
    min-height: 100% !important;
    max-height: 100% !important;
  }
}
/* Top-bar recoloring + burger-to-X animation while the menu is open, matching
 * the archived Header/Burger/LanguageSwitcher/Select components' own
 * isOpen-driven inline styles (see setMenuBarOpenState in offline.js, which
 * is the only place any of these classes gets toggled). Colors are
 * theme.color.brand's own values (mirror/_next/static/chunks's theme module:
 * brand.primary #05425E, brand.grey #808B91, brand.white #FFFFFF), NOT the
 * similarly-named top-level theme.color.primary (#FB5237): the header/burger/
 * language-switcher/dropdown-arrow components all read color.brand.*
 * specifically, confirmed from the same styled-components definitions this
 * file's other menu rules are drawn from. */
[class*="Header__MenuButton"].mirror-menu-open {
  color: #808b91 !important;
}
[class*="LanguageSwitcher__Root"].mirror-menu-open {
  color: #05425e !important;
}
/* Select's Dropdown-arrow (the small caret next to "FR"/"EN") takes the same
 * dark prop as its parent LanguageSwitcher__Root; its two pseudo-elements set
 * background-color rather than color, so they need their own rule even
 * though they inherit no color property from the parent. */
[class*="LanguageSwitcher__Root"].mirror-menu-open .Dropdown-arrow:before,
[class*="LanguageSwitcher__Root"].mirror-menu-open .Dropdown-arrow:after {
  background-color: #05425e !important;
}
.Burger__Root-d8ugeg-0.mirror-menu-open .Burger__Line-d8ugeg-1 {
  background: #05425e !important;
}
.Burger__Root-d8ugeg-0.mirror-menu-open .Burger__Line-d8ugeg-1:nth-of-type(1) {
  transform: rotate(45deg) !important;
  top: -4px !important;
  left: 3px !important;
}
.Burger__Root-d8ugeg-0.mirror-menu-open .Burger__Line-d8ugeg-1:nth-of-type(2) {
  width: 0% !important;
  opacity: 0 !important;
}
.Burger__Root-d8ugeg-0.mirror-menu-open .Burger__Line-d8ugeg-1:nth-of-type(3) {
  transform: rotate(-45deg) !important;
  top: 12px !important;
  left: 3px !important;
}
/* Search submit button's magnifying-glass icon: the archived <Search> passes
 * iconColor: light ? brand.white : brand.accent straight into an inline SVG
 * fill attribute (fill="#FFFFFF" baked into this page's own static markup).
 * setMenuBarOpenState toggles mirror-menu-open directly on the button
 * (.ButtonSubmit) rather than a page-level ancestor, since that is the only
 * element offline.js can reliably scope this to without also matching search
 * forms elsewhere on the page. A plain fill declaration is enough to win
 * over the inline fill attribute (SVG presentation attributes sit at
 * UA-stylesheet priority, below any author CSS), but !important is kept for
 * consistency with every other rule in this block. */
.ButtonSubmit-cvsu30-0.mirror-menu-open svg path {
  fill: #fd6f48 !important;
}
/* Search input's floating label, placeholder, and typed text all read
 * `color: currentColor` from their own generated CSS rather than setting a
 * color themselves; FormInline__InputWrapper is their one ancestor with its
 * own prop-driven color (light ? theme.color.white : theme.color.black), so
 * recoloring it here is enough to fix all three at once. theme.color.black
 * (not brand.*: this component reads the plain top-level theme.color) is
 * #413E39, confirmed from the same theme module as every other color in
 * this block. */
.FormInline__InputWrapper-zdc00u-1.mirror-menu-open {
  color: #413e39 !important;
}
/* Animated underline beneath the search field. The archived Input__Svg only
 * ever reads a translucent white stroke for this specific usage (no dark
 * variant is wired into the Search component that renders it), so unlike
 * every other rule in this block this isn't reproducing a value the bundle
 * itself would have used open -- it's picking the same brand.grey (#808B91)
 * the component's own unused `secondary` prop would switch to, since that
 * is already the color this component uses for a "this needs to read on a
 * light background" state elsewhere. */
.Input__Svg-s1etzpe8-2.mirror-menu-open {
  stroke: #808b91 !important;
}
/* GDPR cookie-consent banner. Injected by installCookieBanner in offline.js
 * and shown until the visitor accepts or rejects; both buttons share
 * .mirror-cookie-button with only a color modifier each, and neither is
 * larger or comes with pre-selected focus, so rejecting is exactly as easy
 * as accepting. */
.mirror-cookie-banner {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 2000;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 24px;
  background: #05425e;
  color: #fff;
  box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.18);
}
.mirror-cookie-banner p {
  flex: 1 1 320px;
  margin: 0;
  font:
    400 14px/1.5 Helvetica,
    system-ui,
    sans-serif;
}
.mirror-cookie-banner a {
  color: inherit;
  font-weight: 600;
  text-decoration: underline;
}
.mirror-cookie-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 10px;
}
.mirror-cookie-button {
  padding: 10px 22px;
  border: 1px solid #fff;
  border-radius: 2px;
  background: transparent;
  color: #fff;
  font:
    600 13px/1 Gustavo,
    system-ui,
    sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.mirror-cookie-button.mirror-cookie-accept {
  background: #fd6f48;
  border-color: #fd6f48;
}
.mirror-cookie-button.mirror-cookie-settings {
  border-color: #05425e;
  background: #05425e;
  color: #fff;
}
.mirror-cookie-button:hover,
.mirror-cookie-button:focus-visible {
  opacity: 0.85;
}
@media (max-width: 480px) {
  .mirror-cookie-banner {
    flex-direction: column;
    align-items: stretch;
  }
  /* flex-basis applies to height once the banner switches to a column
   * direction above, so the row layout's 320px width basis must be reset
   * here or the paragraph is forced to render 320px tall instead of wide. */
  .mirror-cookie-banner p {
    flex-basis: auto;
  }
  .mirror-cookie-actions {
    justify-content: stretch;
  }
  .mirror-cookie-button {
    flex: 1 1 0;
  }
}

/* FAQ accordion (mirror/fr/faq, mirror/en/faq). Base closed-state CSS
 * (max-height:0, chevron at rotate(0deg)) is already in each page's own
 * generated <style>; these rules only add the open state, toggled by
 * installFaqAccordion in offline.js. */
.Styles__Content-jj43qc-3.mirror-faq-open {
  max-height: none !important;
  margin-top: 10px !important;
}
.Styles__TitleIcon-jj43qc-2.mirror-faq-open {
  transform: rotate(-180deg) !important;
}

/* Chapter intro "Voir plus / Voir moins" (mirror/fr/page/audioguide and
 * sibling audioguide-family pages). Base clamped state (max-height:78px,
 * overflow:hidden) is already in each page's own generated <style>; this
 * only adds the open state, toggled by installReadMoreToggles in
 * offline.js. The button's own SVG is a plus-shaped glyph (not a chevron);
 * rotating it 45deg turns it into an X, a plain visual cue that the second
 * click now closes rather than opens. */
.Styles__Content-s1w7q36x-3.mirror-readmore-open {
  max-height: none !important;
}
.Styles__Button-s1w7q36x-1.mirror-readmore-open .IconSVG__Root-s11warqa-0 {
  transform: rotate(45deg);
}

/* Shop category filtering (enseignes/brands pages). Vanilla JavaScript
 * filtering with expand/collapse behavior. */
.styles__Root-s1yg27p1-0 {
  overflow: hidden;
  /* Override the original component's width/max-height transition (it has a
   * 100ms start delay baked in, which caused a visible flash when combined
   * with our instant show/hide). We define our own, undelayed transition
   * and drive max-height explicitly from offline.js so it animates cleanly
   * on open/close (see expandFilter/collapseFilter). The expanded width
   * itself is set below via the .shop-filter-open class + media query, NOT
   * inline from JS, so it stays responsive if the viewport is resized. */
  transition: width 300ms ease-in-out, max-height 300ms ease-in-out;
}

/* Expanded state, toggled by offline.js (expandFilter/collapseFilter).
 * 90% below the ~1328px breakpoint, then a fixed 1195px above it, so the
 * width never exceeds 1195px on wide screens. */
.styles__Root-s1yg27p1-0.shop-filter-open {
  width: 90%;
}
@media (min-width: 1328px) {
  .styles__Root-s1yg27p1-0.shop-filter-open {
    width: 1195px;
  }
}

.styles__Header-s1yg27p1-4 {
  cursor: pointer;
  user-select: none;
}
.styles__Header-s1yg27p1-4:hover {
  opacity: 0.8;
}

.styles__Content-s1yg27p1-1 {
  background-color: #ffffff;
  padding: 20px 70px 35px;
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  list-style: none;
  margin: 0;
  box-sizing: border-box;
}

/* Mobile: single column (default) */
.styles__Content-s1yg27p1-1 > li {
  flex: 1 1 100%;
  min-width: 0;
}

/* Desktop: 3 column layout */
@media (min-width: 64em) {
  .styles__Content-s1yg27p1-1 {
    justify-content: space-evenly;
  }

  .styles__Content-s1yg27p1-1 > li {
    flex: 1 1 calc(33.333% - 14px);
    min-width: 0;
  }
}

/* Audioguide player progress bar, added by offline.js's
 * installAudioGuidePlayers to occupy the styles__Wave-s1fg9t0f-2 slot the
 * archived component filled with a wavesurfer.js waveform (see offline.js's
 * own comment on why a plain bar is used instead offline). */
.mirror-audio-wave {
  display: flex;
  align-items: center;
}
.mirror-audio-progress-track {
  width: 100%;
  height: 4px;
  background-color: #d9dcde;
  border-radius: 2px;
  cursor: pointer;
  position: relative;
}
.mirror-audio-progress-fill {
  height: 100%;
  width: 0%;
  background-color: #05425e;
  border-radius: 2px;
}

/* Full-screen mobile main menu.
 *
 * Below 64em (the same DesktopOnly__Root/MobileOnly__Root split the header
 * already uses for its burger-vs-"Menu"-text and FR-switcher placement),
 * the open menu panel becomes a single-column, scrollable, accordion-style
 * list instead of the always-all-expanded desktop panel: only one category
 * open at a time (toggled by installCategoryAccordion in offline.js), search
 * and the social icons pulled into view, matching the archived design's own
 * mobile treatment (which the static crawl froze in a permanently-collapsed,
 * search/social-hidden state -- see the "hide" classes and DesktopOnly
 * wrapper this overrides below).
 */
@media (max-width: 63.99em) {
  /* The archived burger's visible box is only 23x8px. Its transparent
   * pseudo-element expands the actual hit target to roughly 45x44px without
   * moving the three lines or changing their open-to-X animation. */
  .Burger__Root-d8ugeg-0 {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
  }
  .Burger__Root-d8ugeg-0::before {
    content: "";
    position: absolute;
    inset: -18px -11px;
  }

  /* Menu__Root itself has no defined height (position:fixed, no top/bottom),
   * and its content wrapper (.lfsuVd, Menu__MenuContentWrapper) clips
   * overflow-y, with body scroll also disabled for the duration (see
   * openMenu's no-scroll class). With 8 categories, search, and social
   * icons all in view at once that easily exceeds a phone viewport, so this
   * turns Menu__Root into the scroll container directly and gives it a
   * solid background instead of relying on its :after backdrop (which is
   * sized off the panel's own content height at this breakpoint and would
   * run out mid-scroll otherwise).
   */
  .Menu__Root-s1sb89f4-0.mirror-menu-open {
    overflow-y: auto !important;
    min-height: 100vh !important;
    max-height: 100vh !important;
    background-color: #ffffff !important;
    transition:
      transform 0.4s cubic-bezier(0.3, 0.1, 0.3, 1),
      background-color 0.4s ease-in-out !important;
    -webkit-overflow-scrolling: touch;
  }
  .Menu__Root-s1sb89f4-0.mirror-menu-open
    .Menu__MenuContentWrapper-s1sb89f4-2 {
    min-height: 100vh;
    padding-bottom: 40px;
  }
  /* The archived staggered fade-in (opacity/translateY, delayed per item via
   * each category's own generated animation-delay) is a see-through-then-
   * slide-up entrance meant for every category appearing at once on desktop.
   * Transform-based animation doesn't affect flex layout, only paint
   * position, so while it's running each category's title visually overlaps
   * the row above it instead of the empty space flexbox already reserved
   * for it (mid-animation it still renders translated up toward that
   * reserved slot from -50px, on top of whatever sits above). Disabling it
   * for the mobile accordion avoids that overlap; the mobile panel doesn't
   * need the entrance flourish itself, and the accordion's own :after
   * chevron transition already off offline.css's own rules above is
   * feedback enough that a click did something.
   */
  .Nav__Root-c5ww9n-0 .Category__Root-pdc71f-0 {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
    max-width: none !important;
    width: 100%;
  }
  .Nav__CategoryContainer-c5ww9n-1 {
    flex-direction: column !important;
    align-items: stretch !important;
  }
  /* Nav__CategoryContainer's own generated ::before (an animated underline
   * that grows from 0 to full width on fadeIn, meant as a decorative rule
   * under the desktop panel's top edge) renders as a stray horizontal line
   * sitting between the search field and the first category on the mobile
   * accordion, which the target design doesn't have.
   */
  .Nav__CategoryContainer-c5ww9n-1::before {
    display: none !important;
  }

  /* Reveal the search field (Header__RightWrapper's own DesktopOnly__Root
   * wrapper -- the ">" scopes this to that one specifically, since the
   * *other* DesktopOnly__Root instance on this row wraps the "Menu" text
   * inside Header__MenuWrapper instead, one level deeper) and push it onto
   * its own full-width row below the burger/FR row via flex-wrap, rather
   * than leaving it as a third item squeezed into that row's
   * justify-content:space-between.
   */
  .Menu__Root-s1sb89f4-0.mirror-menu-open .Header__Wrapper-s182jdes-1 {
    flex-wrap: wrap;
  }
  .Menu__Root-s1sb89f4-0.mirror-menu-open
    .Header__Wrapper-s182jdes-1
    > .DesktopOnly__Root-s1qkka3u-0 {
    display: block !important;
    flex: 1 0 100%;
    order: 10;
    /* Menu__LogoWrapper is centered via position:absolute (kKGXxn: top:25px,
     * a fixed height around 61px for the logo mark + wordmark), so it takes
     * no flex space of its own here and this row, if placed right after the
     * burger/FR row above, would sit underneath it rather than below it.
     * This margin clears the logo instead of just the shorter burger row.
     */
    margin-top: 55px;
  }
  .Menu__Root-s1sb89f4-0.mirror-menu-open .Header__RightWrapper-s182jdes-4 {
    flex-direction: column;
    align-items: stretch;
    width: 100%;
  }
  /* The language switcher and social icons in that same revealed block are
   * the desktop-positioned duplicates: mobile already shows its own FR
   * switcher next to the burger (MobileOnly__Root), and its own social row
   * at the very bottom of the panel (revealed separately below), matching
   * where the target design puts them.
   */
  .Menu__Root-s1sb89f4-0.mirror-menu-open
    .Header__RightWrapper-s182jdes-4
    > .LanguageSwitcher__Root-s1lum4b2-0,
  .Menu__Root-s1sb89f4-0.mirror-menu-open
    .Header__RightWrapper-s182jdes-4
    > .SocialNetworks__Root-eh0x9i-0 {
    display: none !important;
  }

  /* Category titles and their links have no color of their own (beyond the
   * title's permanent #FD6F48, always-on regardless of open state) and
   * otherwise inherit Hero__Wrapper's white text color, which the white
   * open-menu background this file already adds leaves illegible. Nav__Root
   * gets one explicit dark color here so every descendant that doesn't set
   * its own resolves against it instead of white.
   */
  .Nav__Root-c5ww9n-0 {
    color: #05425e;
    margin-top: 30px;
    margin-bottom: 20px;
  }
  .Nav__Root-c5ww9n-0 .Category__CategoryTitleContainer-pdc71f-1 {
    color: #05425e !important;
    cursor: pointer;
    padding: 12px 0;
  }
  /* Category__Root's own generated margin-top (40px, meant for the vertical
   * gap between categories in the desktop all-expanded layout) is much
   * larger than the accordion needs between two collapsed rows; the title's
   * own padding above already provides comfortable tap-target spacing.
   */
  .Nav__Root-c5ww9n-0 .Category__Root-pdc71f-0 + .Category__Root-pdc71f-0 {
    margin-top: 0 !important;
  }
  .Nav__Root-c5ww9n-0
    .Category__Root-pdc71f-0.mirror-category-open
    > .Category__CategoryTitleContainer-pdc71f-1 {
    color: #fd6f48 !important;
  }
  /* Chevron, rotating from pointing down (closed) to pointing up (open),
   * matching the target design's collapsed/expanded indicator. Drawn the
   * same way .mirror-category-link::after already draws its arrow further
   * up this file, so it reads currentColor and needs no color rule of its
   * own beyond what the title container above sets.
   */
  .Nav__Root-c5ww9n-0 .Category__CategoryTitleContainer-pdc71f-1::after {
    content: "";
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    margin-left: 12px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.25s ease;
  }
  .Nav__Root-c5ww9n-0
    .Category__Root-pdc71f-0.mirror-category-open
    > .Category__CategoryTitleContainer-pdc71f-1::after {
    transform: rotate(-135deg);
  }
  /* Category__Root's own top-level layout (the per-category hashed classes
   * sharing margin-top/left/right and the fade-in/out entrance animation)
   * is left as-is; only its sub-list needs a collapsed/expanded state, kept
   * to a plain display toggle rather than an animated height (Category__
   * CategoryItems has no generated max-height transition to hook, unlike
   * the FAQ accordion's own Styles__Content).
   */
  .Nav__Root-c5ww9n-0 .Category__CategoryItems-pdc71f-4 {
    display: none;
  }
  .Nav__Root-c5ww9n-0
    .Category__Root-pdc71f-0.mirror-category-open
    > .Category__CategoryItems-pdc71f-4 {
    display: block;
    /* Category__CategoryItems' own generated margin-top (25px, .bbEeqG) is
     * sized for the desktop panel, where it's the only spacing between an
     * always-visible title and its links. The accordion's title row already
     * carries its own vertical padding (12px top/bottom), so stacking this
     * on top of that left a much bigger gap under an open category's title
     * than between any two closed category rows.
     */
    margin-top: 4px;
  }
  /* Category__CategoryItem's own generated CSS (.jtSwta a, baked into every
   * page's own <style>) sets every sub-link to brand.grey (#808B91) by
   * default -- a lighter-secondary treatment meant for the desktop panel,
   * where the category title above is the only strong-emphasis element and
   * every link below it reads as equally secondary. The mobile accordion's
   * design instead treats the *category name itself* as secondary once
   * collapsed (it's the whole clickable row) and wants every link inside an
   * open category, including the trailing "view all" one (Toutes les
   * boutiques, Tous les restaurants, Tout le bien-être, ...), at full
   * brand.primary (#05425E) emphasis to match. !important is needed to win
   * over that generated rule's own equal specificity (one class + one type
   * selector on both sides) since it comes later in the page's own <style>.
   */
  .Nav__Root-c5ww9n-0 .Category__CategoryItem-pdc71f-5 .Link__StyledLink-igt5n2-0 {
    color: #05425e !important;
  }

  /* Bottom-of-panel social icons (already correctly positioned in the
   * markup, right after </nav>, for exactly this mobile-open state) are
   * frozen with a permanent "hide" class in the static crawl -- the
   * archived component must have driven it the same isOpen-controlled way
   * openMenu/closeMenu already drive the category list's own hide/fadeIn/
   * fadeOut classes, so installMenuFooterSocial in offline.js hooks it into
   * that same toggle instead of duplicating it here.
   */
}

/* Interactive floor plan (see installMaps in offline.js). The archived
 * component (styles__Root-iv1os1-0 / styles__MapContainer-iv1os1-1) sizes
 * #map at 100%/100% of its own parent (styles__MapContainer-s1vmxmxz-2,
 * height:630px in every page's own generated <style>), which this reuses
 * unchanged -- only #map's own contents (previously empty) need styling.
 */
#map.mirror-map-root {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #fcfcfc;
  touch-action: none;
}
.mirror-map-svg {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
  /* Without this, dragging to pan the map (see the pointerdown/pointermove
   * handlers in installMaps' renderMap) also drags a text selection across
   * every label the pointer passes over, which is distracting and leaves a
   * stray highlighted selection behind once the drag ends. */
  -webkit-user-select: none;
  -moz-user-select: none;
  user-select: none;
}
.mirror-map-svg:active {
  cursor: grabbing;
}
.mirror-map-feature path,
.mirror-map-feature circle {
  transition: fill 0.15s ease-out;
}
.mirror-map-feature--interactive {
  cursor: pointer;
}
/* Fill color itself is set inline by mapHoverColor()/selectFeature() in
 * offline.js (it depends on the linked shop's own type: shopping blue vs.
 * restaurant orange vs. other grey), not by this stylesheet. */
.mirror-map-label {
  font-family: "Brandon Grotesque", Helvetica, sans-serif;
  font-weight: bold;
  font-size: 9px;
  fill: #5e5e5e;
  pointer-events: none;
  paint-order: stroke;
  stroke: #ffffff;
  stroke-width: 2px;
  stroke-linejoin: round;
}
/* Marker (elevator/escalator/ATM) icons: painted into the labels group so
 * they sit on top of any shop rectangle they overlap, same as text labels
 * (see buildMarkerIconLabel's own comment in offline.js). pointer-events:
 * none for the same reason .mirror-map-label has it: hover/click on the
 * marker's own Place (if it's ever made interactive) or drag-to-pan on the
 * map underneath it should not be intercepted by the icon image sitting
 * visually above.
 */
.mirror-map-marker-icon {
  pointer-events: none;
}
/* Applied directly by selectFeature/deselectFeature and the current-page
 * highlight block in offline.js, not a descendant selector: labels now
 * live in their own sibling "labels" group, drawn after every shape in the
 * same floor layer so a label never renders underneath a later, overlapping
 * shop rectangle (see buildLayerGroup's own comment in offline.js) -- which
 * also means a label is no longer a DESCENDANT of its feature's shape
 * group, so ":hover .mirror-map-label" can no longer reach it.
 */
.mirror-map-label--highlighted {
  fill: #ffffff;
  /* The base .mirror-map-label rule outlines dark text with a white
   * stroke so it stays legible over a light shop rectangle; white text
   * over this feature's dark blue/orange highlight fill needs the same
   * outline in the opposite color, not no outline at all -- dropping it
   * entirely left the highlighted label without the contrast every other
   * label has. */
  stroke: #000000;
}
/* The shop/place the CURRENT page is itself about (see the "Highlights
 * whichever shop/place..." block in offline.js): picked out on load, no
 * hover needed, and stays highlighted through mouseleave from any OTHER
 * feature (selectFeature refuses to touch it -- see its own early return).
 */
.mirror-map-feature--current {
  cursor: default;
}

/* Fallback floor + zoom controls, built by renderMap() only on pages with
 * no existing styles__Root-ddbpvr-0/vywkqp-0 siblings to reuse (currently
 * just /fr/plan and /en/map, which use the separate static BasicMap
 * component instead of this one). Every shop page's own archived buttons
 * are reused as-is instead of duplicating this, so most pages never apply
 * these rules at all -- see findExistingControls in offline.js.
 * Positioned to match the archived styles__Root-ddbpvr-0 (top:15px;
 * left:30px) / styles__Root-vywkqp-0 (top:15px;right:30px) pair anyway, so
 * both code paths still look the same.
 */
.mirror-map-controls {
  position: absolute;
  top: 15px;
  left: 30px;
  right: 30px;
  z-index: 5;
  display: flex;
  justify-content: space-between;
  pointer-events: none;
}
.mirror-map-floor-buttons,
.mirror-map-zoom-buttons {
  display: flex;
  pointer-events: auto;
}
.mirror-map-floor-button,
.mirror-map-zoom-button {
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-right: 10px;
  min-width: 60px;
  height: 40px;
  font-family: Helvetica, system-ui, sans-serif;
  font-size: 14px;
  line-height: 19.6px;
  font-weight: 700;
  background: #d9dcde;
  border: none;
  cursor: pointer;
  outline: none;
}
.mirror-map-zoom-button {
  min-width: 40px;
  width: 40px;
  border-radius: 50%;
  background: #fd6f48;
  font-size: 1.8rem;
}
.mirror-map-floor-button.is-active {
  color: #ffffff;
  background-color: #fd6f48;
}

/* Static events grid (mirror/fr/evenements). The archived Events listing
 * page fetched its cards live via GraphQL and never rendered them into the
 * static export (confirmed: every capture of this page is either a
 * permanent loading spinner or empty), so there is no original
 * styled-components card to reuse here, unlike offers/brands. This is a
 * new, plain component built to match the reference design directly: a
 * square image, the date in accent orange underneath, then the title in
 * primary blue, with the whole card as one link (no separate "Découvrir"
 * button) and 5 columns per row on desktop. */
.mirror-events-grid {
  display: flex;
  flex-wrap: wrap;
  margin-left: -10px;
  margin-right: -10px;
}
.mirror-event-card {
  display: block;
  flex: 0 0 50%;
  max-width: 50%;
  padding: 0 10px;
  margin-bottom: 30px;
  text-decoration: none;
}
@media (min-width: 40em) {
  .mirror-event-card {
    flex: 0 0 33.3333%;
    max-width: 33.3333%;
  }
}
@media (min-width: 64em) {
  .mirror-event-card {
    flex: 0 0 20%;
    max-width: 20%;
  }
}
.mirror-event-card-image {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 2px;
}
.mirror-event-card-date {
  display: block;
  margin-top: 12px;
  font-family: Helvetica, system-ui, sans-serif;
  font-size: 12px;
  line-height: 16px;
  color: #fd6f48;
  text-transform: uppercase;
}
.mirror-event-card-title {
  display: block;
  margin-top: 4px;
  font-family: Helvetica, system-ui, sans-serif;
  font-size: 15px;
  line-height: 20px;
  font-weight: 400;
  color: #05425e;
}

/* Event detail pages (mirror/fr/evenements/<slug>) reuse the generic content
 * page's two-column ImageFit layout (see styles__ImageWrapper-s1reic89-4 /
 * ImageFit__Wrapper-get83n-1's own padding-top percentage trick, copied from
 * fr/page/cite-internationale-de-la-gastronomie), but that page's own
 * padding-top values (87%/107%) were tuned for its own portrait-ish header
 * photo, not the square event thumbnails used here. Overriding to a plain
 * 1:1 box keeps the same responsive wrapper mechanics without stretching or
 * over-cropping a square image via the inherited object-fit:cover. */
.mirror-event-detail-image {
  padding-top: 100% !important;
}
