/*
 * Styles for the Office Map code component.
 *
 * Hand-written rather than compiled from Tailwind, matching the Star Field and
 * Floating Icons components: shipped directly, with no build step that can
 * silently drop a class from the bundle.
 *
 * Everything is scoped under .wf-officemap so nothing leaks onto the page, and
 * every colour and duration comes from a custom property the component sets
 * from its Webflow props.
 *
 * The whole interaction is driven by attributes: `data-active` on the root
 * carries the active office id, `data-zoomed` says the small-screen view is in
 * effect, and `data-state` on each pin says whether it is idle, active, or
 * dimmed because a sibling is active. No state reaches the DOM any other way.
 */

.wf-officemap {
  position: relative;
  width: 100%;
  /* Fills whatever the container gives it. `height: 100%` collapses to auto
     when the container's own height is auto, and only then does the aspect
     ratio below take over — so a container with a height wins, and one without
     still gets a sensibly shaped map instead of a zero-height one. */
  height: 100%;
  aspect-ratio: var(--wf-officemap-aspect);
  border-radius: var(--wf-officemap-radius);
  /* Cards are allowed past the edges; only the artwork is clipped, by the
     layer below. */
  overflow: visible;
  isolation: isolate;
}

/* An open card reaches past the map's edges, so while one is up the map has to
   outrank whatever follows it in the page — otherwise the card is painted
   underneath the next section. */
.wf-officemap[data-active] {
  z-index: 4;
}

/* Crops the artwork — and only the artwork — at the map's own edge, so a zoomed
   map still ends cleanly while a card can hang outside it. Carries the water,
   since that is the ground the land composites over. */
.wf-officemap-clip {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--wf-officemap-water);
  overflow: hidden;
}

.wf-officemap *,
.wf-officemap *::before,
.wf-officemap *::after {
  box-sizing: border-box;
}

/* The artwork and the pins are two layers over the same rectangle, given
   identical geometry and transform from the component. Sizing that rectangle in
   script rather than in CSS is what lets the container be any shape while
   percentage-positioned pins still land on their countries. */
.wf-officemap-art,
.wf-officemap-pins {
  position: absolute;
  transform-origin: 0 0;
  transition: transform var(--wf-officemap-duration) ease;
}

.wf-officemap-art {
  will-change: transform;
}

.wf-officemap-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* ------------------------------------------------------------- colour ---- */

.wf-officemap-region {
  transition:
    fill var(--wf-officemap-duration) ease,
    fill-opacity var(--wf-officemap-duration) ease;
}

/* A country shape lists every office it answers to, so France lights up for
   either Paris or Lyon. `~=` matches one word in that list. The artwork carries
   these two shapes; an office beyond them still gets a pin, just no country. */
.wf-officemap[data-active='lyon'] .wf-officemap-region[data-region~='lyon'],
.wf-officemap[data-active='paris'] .wf-officemap-region[data-region~='paris'],
.wf-officemap[data-active='singapore']
  .wf-officemap-region[data-region~='singapore'] {
  fill: var(--wf-officemap-accent);
  fill-opacity: 1;
}

/* ---------------------------------------------------------------- pins ---- */

/* The layer is inert; only the dots and an open card take the cursor, so the
   map never swallows a click meant for the page. */
.wf-officemap-pins {
  pointer-events: none;
  /* Cards reach outside the map. */
  overflow: visible;
  z-index: 1;
}

/* A zero-size anchor sitting exactly on the office. Everything else is
   positioned outward from this point. The counter-scale cancels the map's zoom
   so labels and cards stay their real size while their anchors ride along. */
.wf-officemap-pin {
  position: absolute;
  width: 0;
  height: 0;
  --wf-officemap-nx: 0px;
  --wf-officemap-ny: 0px;
  transform: scale(var(--wf-officemap-unzoom));
  transform-origin: 0 0;
  transition:
    opacity var(--wf-officemap-duration) ease,
    transform var(--wf-officemap-duration) ease;
}

.wf-officemap-pin[data-state='dimmed'] {
  opacity: var(--wf-officemap-dim);
}

/* The open pin has to clear its neighbours — Paris and Lyon sit fourteen pixels
   apart in the artwork. */
.wf-officemap-pin[data-state='active'] {
  z-index: 3;
}

/* Once zoomed in on one office the others are context, not competition. */
.wf-officemap[data-zoomed] .wf-officemap-pin[data-state='dimmed'] {
  opacity: calc(var(--wf-officemap-dim) * 0.7);
}

.wf-officemap-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--wf-officemap-dot);
  transform: translate(-50%, -50%);
  pointer-events: auto;
  cursor: pointer;
  transition:
    background var(--wf-officemap-duration) ease,
    box-shadow var(--wf-officemap-duration) ease,
    transform var(--wf-officemap-duration) ease;
}

.wf-officemap-pin[data-state='active'] .wf-officemap-dot {
  background: var(--wf-officemap-dot-active);
  transform: translate(-50%, -50%) scale(1.55);
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--wf-officemap-ping) 26%, transparent),
    0 0 20px 2px color-mix(in srgb, var(--wf-officemap-ping) 65%, transparent);
}

.wf-officemap-ping {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--wf-officemap-ping);
  opacity: 0;
  pointer-events: none;
}

.wf-officemap-pin[data-state='active'] .wf-officemap-ping {
  animation: wf-officemap-ping 1.6s cubic-bezier(0, 0, 0.2, 1) infinite;
}

@keyframes wf-officemap-ping {
  0% {
    transform: scale(1);
    opacity: 0.7;
  }
  100% {
    transform: scale(3.4);
    opacity: 0;
  }
}

/* -------------------------------------------------------------- labels ---- */

.wf-officemap-label {
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 7px 11px 8px;
  border-radius: 10px;
  background: var(--wf-officemap-label-bg);
  border: 1px solid var(--wf-officemap-label-border);
  box-shadow: 0 6px 16px rgb(0 0 0 / 0.25);
  white-space: nowrap;
  transition:
    opacity var(--wf-officemap-duration) ease,
    transform var(--wf-officemap-duration) ease;
}

.wf-officemap-label-city {
  font-size: 12px;
  font-weight: 700;
  line-height: 1.25;
  color: var(--wf-officemap-label-text);
}

.wf-officemap-label-role {
  font-size: 11px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--wf-officemap-label-role);
}

/* The small label steps aside for the detailed card, but only when there is a
   card to replace it — otherwise hovering would just blank the pin. */
.wf-officemap-pin[data-state='active'] .wf-officemap-label[data-yields='true'] {
  opacity: 0;
  visibility: hidden;
}

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

.wf-officemap-card {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--wf-officemap-duration) ease,
    transform var(--wf-officemap-duration) ease,
    visibility var(--wf-officemap-duration);
}

.wf-officemap-pin[data-state='active'] .wf-officemap-card {
  opacity: 1;
  visibility: visible;
  /* Live once open, so the card's own links and buttons work. */
  pointer-events: auto;
}

/* A card with nowhere to go — typically on a phone, where this map is barely a
   hundred pixels tall — stays shut, and its pin keeps the small label instead.
   Hidden with visibility rather than display so the box is still laid out: the
   fit is measured from its height, and a collapsed card would measure as
   fitting and flip straight back to visible. */
.wf-officemap-pin[data-state='active'] .wf-officemap-card[data-fits='false'] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* ----------------------------------------------------------- placement ---- */

/* One transform per side, shared by labels and cards. The nudge variables let a
   single pin be moved off the ideal position where the artwork demands it. */
.wf-officemap-label[data-placement='above'],
.wf-officemap-card[data-placement='above'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-ny))
  );
}

.wf-officemap-label[data-placement='below'],
.wf-officemap-card[data-placement='below'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(var(--wf-officemap-gap) + var(--wf-officemap-ny))
  );
}

.wf-officemap-label[data-placement='right'],
.wf-officemap-card[data-placement='right'] {
  transform: translate(
    calc(var(--wf-officemap-gap) + var(--wf-officemap-nx)),
    calc(-50% + var(--wf-officemap-ny))
  );
}

.wf-officemap-label[data-placement='left'],
.wf-officemap-card[data-placement='left'] {
  transform: translate(
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-nx)),
    calc(-50% + var(--wf-officemap-ny))
  );
}

/* Closed cards start a few pixels further out and settle into place. */
.wf-officemap-card[data-placement='above'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-ny) + 8px)
  );
}

.wf-officemap-pin[data-state='active'] .wf-officemap-card[data-placement='above'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-ny))
  );
}

.wf-officemap-card[data-placement='below'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(var(--wf-officemap-gap) + var(--wf-officemap-ny) - 8px)
  );
}

.wf-officemap-pin[data-state='active'] .wf-officemap-card[data-placement='below'] {
  transform: translate(
    calc(-50% + var(--wf-officemap-nx)),
    calc(var(--wf-officemap-gap) + var(--wf-officemap-ny))
  );
}

.wf-officemap-card[data-placement='right'] {
  transform: translate(
    calc(var(--wf-officemap-gap) + var(--wf-officemap-nx) - 8px),
    calc(-50% + var(--wf-officemap-ny))
  );
}

.wf-officemap-pin[data-state='active'] .wf-officemap-card[data-placement='right'] {
  transform: translate(
    calc(var(--wf-officemap-gap) + var(--wf-officemap-nx)),
    calc(-50% + var(--wf-officemap-ny))
  );
}

.wf-officemap-card[data-placement='left'] {
  transform: translate(
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-nx) + 8px),
    calc(-50% + var(--wf-officemap-ny))
  );
}

.wf-officemap-pin[data-state='active'] .wf-officemap-card[data-placement='left'] {
  transform: translate(
    calc(-100% - var(--wf-officemap-gap) + var(--wf-officemap-nx)),
    calc(-50% + var(--wf-officemap-ny))
  );
}

/* --------------------------------------------------------- small print ---- */

/* The fallback shape gets taller at the smaller breakpoints, giving the zoomed
   view somewhere to put a card. It only applies when the container has no
   height of its own. These values have to agree with SMALL_SCREEN_MAX in
   officeMap.tsx, since a media query cannot read a prop. */
@media (max-width: 991px) {
  .wf-officemap {
    aspect-ratio: var(--wf-officemap-aspect-tablet);
  }
}

@media (max-width: 767px) {
  .wf-officemap {
    aspect-ratio: var(--wf-officemap-aspect-mobile);
  }

  .wf-officemap-label {
    padding: 5px 8px 6px;
    border-radius: 8px;
  }

  .wf-officemap-label-city {
    font-size: 10px;
  }

  .wf-officemap-label-role {
    font-size: 9px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wf-officemap *,
  .wf-officemap *::before,
  .wf-officemap *::after {
    transition-duration: 1ms !important;
    animation: none !important;
  }
}

