/*!
 * Tower Grove Pride — festival map offline control.
 *
 * Additive companion to festival-map.css, which is owned by another
 * workstream and is not edited here. Everything below is either a new
 * `tgp-offline__*` class or a scoped addition; nothing redefines a
 * festival-map.css rule.
 *
 * EVERY CONTROL A VISITOR PRESSES IS ONE OF THE SITE'S OWN BUTTONS, and its
 * look comes from the site's stylesheets, not from here. John, 2026-09-18:
 * "I don't know why you keep making these generic AI buttons."
 *
 *   Save            .button .button--primary .button--slim
 *   Install as app  .button .button--white   .button--slim
 *   card actions    .button .button--primary
 *
 * WHERE THEY COME FROM, precisely: the `.button*` classes are GenerateBlocks
 * GLOBAL STYLES (`generateblocks/style-global.css`). The colour tokens they
 * use (`--brand`, `--base-900`, `--base-800`…) and the site's base font are
 * GeneratePress's (`generatepress/style.min.css`). Nothing below restyles a
 * site button; the blocks that touch colour and type undo third-party rules
 * that outrank those styles inside the map, and are explained where they are.
 *
 * Everything else takes its colours from the --tgp-* custom properties
 * festival-map.css declares on .tgp-map. No new hexes are invented.
 */

/* Positioning only: a right-aligned column under the full-screen control, so
   "Install as app" sits UNDER the Save button rather than floating on the
   tiles. No chrome of its own. */
.tgp-map .tgp-offline {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  border: 0;
  box-shadow: none;
  background: none;
}

/* ------------------------------------------------ the site's buttons -- */

/*
 * WHY THE BUTTONS' TEXT COLOUR IS RE-STATED HERE. Inside the map, two rules
 * outrank the GenerateBlocks `.button--primary` / `.button--white` (0,1,0):
 *
 *   festival-map.css   .tgp-map button        { color: inherit }   (0,1,1)
 *   leaflet.css        .leaflet-container a   { color: <link blue> } (0,1,1)
 *
 * Measured in the harness against the LIVE stylesheets: a <button> with the
 * site's classes inside .tgp-map computed the map's dark ink instead of the
 * button's own colour, and an <a> with them inside the Leaflet control
 * computed Leaflet's link blue. So the colour the GenerateBlocks rule sets is
 * put back, with the same custom property that rule uses and nothing
 * invented. The harness asserts every control computes exactly what the SAME
 * classes compute outside the map, so if those styles change, the test says
 * so. (The white button is a <button>, so Leaflet's `a` rule cannot reach it;
 * festival-map.css's `inherit` still would.)
 */
.tgp-map .tgp-offline-theme.button--primary {
  color: var(--base-900);
}

.tgp-map .tgp-offline-theme.button--white {
  color: var(--base-800);
}

/* `hidden` is only the browser's own `display: none`, which the site's
   `.button { display: inline-block }` outranks: a hidden button would still
   be on screen. */
.tgp-map .tgp-offline-theme[hidden] {
  display: none;
}

/*
 * THE SITE'S TYPE, TOO. Same problem as the colour above, different
 * properties. Buttons get their size from the site's BASE rules, not their
 * button classes — GeneratePress `body, button { font-family: Roboto,
 * sans-serif; font-size: 1.13rem }` and GenerateBlocks `.button
 * { line-height: 1em }` — and inside the map these outrank those:
 *
 *   festival-map.css   .tgp-map button { font-family: inherit;
 *                        font-size: inherit; line-height: inherit }   (0,1,1)
 *   leaflet.css        .leaflet-container { font-size: 12px }   (inherited)
 *
 * So a site button inside the Leaflet control rendered 12px system type
 * while the identical button anywhere else on the page is 18.08px Roboto.
 * Measured on LIVE, 2026-09-18, at 390px and at 1280px alike — there is no
 * responsive button size. These are the site's own declarations, restated;
 * the harness compares every control against the SAME classes outside the
 * map at the same viewport, so a change there shows up.
 *
 * PADDING IS DELIBERATELY NOT SET HERE. `.button--slim { padding: .4rem 1rem }`
 * (6.4px 16px) has to win for Save and Install as app, and `.button`'s
 * .75rem 1.5rem for the card's full-size buttons; both are rem-based, so the
 * map's small type never distorted them in the first place.
 *
 * The 44px minimum is a tap-target floor, not a restyle: a slim button's own
 * height (about 36px) is under it, so these sit 8px taller than a slim button
 * elsewhere on the site, with the slim padding unchanged.
 */
.tgp-map .tgp-offline-theme {
  min-height: var(--tgp-tap);
  font-size: 1.13rem;
  line-height: 1em;
}

/* The typeface is a separate declaration on purpose: it comes from the same
   GeneratePress rule as the size, so matching one and not the other would
   leave a button the site's size in a face no other button on the site uses.
   Delete this rule alone if only the size is wanted. */
.tgp-map .tgp-offline-theme {
  font-family: Roboto, sans-serif;
}

/* The LAYOUT of what is inside the Save button — an icon beside a label —
   not its look. The site's `.button` is inline-block, which would stack the
   icon on top of the label. */
.tgp-map .tgp-offline__button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  white-space: nowrap;
}

.tgp-map .tgp-offline__icon {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
}

.tgp-map .tgp-offline__icon svg {
  width: 17px;
  height: 17px;
  display: block;
}

/* Every state — Saving…, Saved, Update, the failure — is the same theme
   button, so there are no state colours here any more. (The Saved tick used
   to be a hand-picked green, and "Update" a blue outline; both are gone.)
   The editor render's disabled button only needs to look inert. */
.tgp-map .tgp-offline__button[disabled] {
  opacity: 0.7;
  cursor: default;
}

.tgp-offline__spin {
  transform-origin: 50% 50%;
  animation: tgp-offline-spin 900ms linear infinite;
}

@keyframes tgp-offline-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .tgp-offline__spin {
    animation: none;
  }
}

/* The Add-to-Home-Screen step, under the button. Only rendered once there is
   something saved worth adding. */
.tgp-map .tgp-offline__hint {
  max-width: 190px;
  margin: 6px 0 0;
  padding: 6px 9px;
  font-family: var(--tgp-font);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.35;
  white-space: normal;
  color: var(--tgp-ink-soft);
  background: var(--tgp-surface);
  border: 1px solid var(--tgp-line);
  border-radius: 9px;
  box-shadow: var(--tgp-shadow);
}

/* "Install as app" under the Save button is the site's slim WHITE button
   (John, 2026-09-18) — see the block at the top. It has been underlined
   grey text floating on the map tiles (unreadable) and the purple secondary
   (whose own colours are 3.95:1). Nothing here styles it. */

/* ------------------------------------------------------- install card -- */

/* Over the map, not in a Leaflet corner: this is the one thing on the screen
   we want read, and it is dismissable. Anchored to the BOTTOM because the
   control bar, the search field and the pill all live along the top. */
.tgp-offline__card {
  position: absolute;
  z-index: 1200;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  box-sizing: border-box;
  width: calc(100% - 24px);
  max-width: 340px;
  padding: 16px;
  font-family: var(--tgp-font);
  color: var(--tgp-ink);
  background: var(--tgp-surface);
  border: 1px solid var(--tgp-line);
  border-radius: var(--tgp-radius);
  box-shadow: var(--tgp-shadow);
}

.tgp-offline__card[hidden] {
  display: none;
}

.tgp-offline__card:focus {
  outline: 2px solid var(--tgp-focus);
  outline-offset: 2px;
}

.tgp-offline__card-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--tgp-ink);
}

.tgp-offline__card-meta {
  margin: 4px 0 0;
  font-size: 12px;
  font-weight: 500;
  color: var(--tgp-ink-soft);
}

.tgp-offline__card-text {
  margin: 10px 0 0;
  font-size: 13px;
  line-height: 1.4;
  color: var(--tgp-ink-soft);
}

.tgp-offline__steps {
  margin: 12px 0 0;
  padding: 0 0 0 22px;
  font-size: 14px;
  line-height: 1.45;
}

.tgp-offline__step {
  margin: 0 0 6px;
}

.tgp-offline__step:last-child {
  margin-bottom: 0;
}

.tgp-offline__step strong {
  font-weight: 700;
}

/* The Share glyph sits in the sentence, at the size of the text around it, so
   "tap THIS button" needs no second look. Drawn inline — the card has to
   render with no network. */
.tgp-offline__glyph {
  display: inline-flex;
  vertical-align: -3px;
  align-items: center;
  justify-content: center;
  color: var(--tgp-focus);
}

.tgp-offline__glyph svg {
  width: 16px;
  height: 16px;
  display: block;
}

.tgp-offline__card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin: 14px 0 0;
}

/* The card's main action (Install app, Copy link) is the site's full-size
   PRIMARY button — styled by GenerateBlocks, see the top of this file.

   "Later" is a text link, deliberately not a second filled button: it is the
   way out, and it sits on the card's own solid surface, so it reads without a
   fill. Scoped to beat festival-map.css's `.tgp-map button { color: inherit }`
   (0,1,1) and GeneratePress's base `button { background; padding; border }`. */
.tgp-map .tgp-offline__card-btn--quiet {
  min-height: var(--tgp-tap);
  margin-left: auto;
  padding: 0 10px;
  font-family: var(--tgp-font);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
  color: var(--tgp-ink-soft);
  background: none;
  border: 0;
  text-decoration: underline;
  cursor: pointer;
}

.tgp-map .tgp-offline__card-btn--quiet:focus-visible {
  outline: 2px solid var(--tgp-focus);
  outline-offset: 2px;
}

/* Safari's "it may be under the ••• button" caveat: its own line, a size
   down, under the step it qualifies. */
.tgp-offline__step-note {
  display: block;
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.35;
  color: var(--tgp-ink-soft);
}

/* ------------------------------------------- Chrome-for-iOS share arrow -- */

/* Fixed to the viewport's top-right and pointing UP, out of the page, at
   Chrome for iOS's Share icon in the address bar above it. On <body>, not
   the map, because `position: fixed` inside a transformed ancestor is no
   longer fixed to the viewport. Pointer-events off: it must never sit on top
   of something a visitor is trying to tap. */
.tgp-offline__share-arrow {
  position: fixed;
  top: 6px;
  right: 10px;
  z-index: 100000;
  width: 48px;
  height: 48px;
  color: var(--tgp-focus);
  pointer-events: none;
  animation: tgp-offline-nudge 1100ms ease-in-out infinite;
}

.tgp-offline__share-arrow[hidden] {
  display: none;
}

.tgp-offline__share-arrow svg {
  display: block;
  width: 100%;
  height: 100%;
}

@keyframes tgp-offline-nudge {
  0%,
  100% {
    transform: translate(0, 0);
  }

  50% {
    transform: translate(4px, -6px);
  }
}

/* Motion is the point of the arrow, but not at the cost of someone who has
   asked for none: it stays, still, pointing the same way. */
@media (prefers-reduced-motion: reduce) {
  .tgp-offline__share-arrow {
    animation: none;
  }
}

/* The address itself, when nothing on the device would copy it. Readonly and
   selectable rather than plain text: a tap selects the whole thing, which is
   the only "copy this" affordance a phone offers. */
.tgp-offline__url {
  box-sizing: border-box;
  width: 100%;
  min-height: var(--tgp-tap);
  margin: 8px 0 0;
  padding: 0 10px;
  font-family: var(--tgp-font);
  font-size: 13px;
  color: var(--tgp-ink);
  background: var(--tgp-surface-alt);
  border: 1px solid var(--tgp-line);
  border-radius: 9px;
}

/* Off-screen but focusable: the execCommand fallback needs a real selection,
   and display:none cannot be selected. */
.tgp-offline__copy-field {
  position: fixed;
  top: 0;
  left: -9999px;
  opacity: 0;
}

/* Whole-map status, not a control — so it sits on .tgp-map rather than in one
   of Leaflet's four corners, all of which are already occupied. */
.tgp-offline__pill {
  position: absolute;
  /* Top-LEFT of the stage, which is the one corner of this map nothing else
     claims. Two earlier placements failed the screenshot: top-centre at 12px
     was completely hidden behind the search field (z-index 1000 beats 900), and
     top-centre at 64px landed on top of our own Save button, which stacks
     below the full-screen bar on the right. The bottom edge is spoken for too —
     legend centre, zoom and attribution right. */
  top: 64px;
  left: 12px;
  z-index: 1100;
  max-width: calc(100% - 24px);
  padding: 7px 14px;
  font-family: var(--tgp-font);
  font-size: 12px;
  font-weight: 600;
  color: var(--tgp-ink);
  background: var(--tgp-surface);
  border: 1px solid var(--tgp-line);
  border-radius: 999px;
  box-shadow: var(--tgp-shadow);
  pointer-events: none;
}

.tgp-offline__pill[hidden] {
  display: none;
}

/* The saved PNG is a plain raster with no tiles under it, so give the map a
   matching ground colour — otherwise the strip outside the image's bounds
   renders as the panel grey and reads like a rendering fault. */
.tgp-map .leaflet-container.tgp-map--offline-basemap {
  background: #eef1ec;
}

.tgp-map .tgp-offline__basemap,
.tgp-map .leaflet-pane .tgp-offline__basemap {
  image-rendering: auto;
}

/* Phones: the map is mostly thumb territory, so the button drops to icon +
   short label and the hint wraps under it rather than pushing the control bar
   across the viewport. */
@media (max-width: 480px) {
  /* Width only. It used to shrink the padding and the type as well, which on
     a phone — where John tests — restyled the site's button.
     At the site's 18px the label may not fit one line in 62vw, so it
     WRAPS rather than being cut off: an ellipsis on the one button this
     feature exists for ("Save map for offl…") is worse than two lines. */
  .tgp-map .tgp-offline__button {
    max-width: 62vw;
    white-space: normal;
    text-align: left;
  }

  .tgp-map .tgp-offline__hint {
    max-width: 62vw;
  }

  .tgp-map .tgp-offline__install-link {
    max-width: 62vw;
    white-space: normal;
  }
}
