/* Fine Ink studio locator.
 *
 * BRAND PALETTE, supplied by Michelle 2026-09-14. These four are exact. Do not
 * substitute sampled values: an earlier draft used #BC9B5D pulled off the live
 * site, which turned out to be an incidental element rather than brand.
 *
 *   sage      #a5a073   accent, pins, active states, rules
 *   sand      #cac7a7   panel ground and borders
 *   charcoal  #2b2a2a   type and dark surfaces
 *   blue      #3b6afd   primary CTA only, matching the HighLevel form button
 *
 * Neutrals below are derived tints of sand, not new brand colours.
 *
 * The earlier build guide proposed a dark monochrome basemap. That was written
 * before anyone looked at the brand and would fight it. Light ground, sage pins,
 * charcoal type, blue reserved for the one action that matters.
 *
 * Every class is prefixed fis- because this may share a document with Wix CSS,
 * which leaks.
 */

/* Questrial, Fine Ink's brand typeface. Self-hosted rather than pulled from
   Google Fonts: this page sits on the paid-ads path, so avoiding a second DNS
   lookup and TLS handshake to fonts.gstatic.com is worth 36KB on our own origin.
   It also removes a third-party request from a page that will carry click ids.

   One weight, 400, is all Questrial has. font-display:swap so text is readable
   immediately and never invisible while the font loads. */
@font-face {
  font-family: 'Questrial';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/questrial-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Questrial';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/questrial-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --fis-sage:     #a5a073;
  --fis-sage-d:   #8b8660;
  --fis-sand:     #cac7a7;
  --fis-sand-lt:  #eae6d4;   /* derived: sand at ~45% over white */
  --fis-sand-bg:  #f3f0e4;   /* derived: page ground. Warmer than the first
                                pass, which sat at #f7f6ef and read as white. */
  --fis-charcoal: #2b2a2a;
  --fis-blue:     #3b6afd;
  --fis-blue-d:   #2f59e0;
  --fis-muted:    #6b6858;
  --fis-white:    #ffffff;
  --fis-shadow:   0 1px 2px rgba(43,42,42,.06), 0 4px 14px rgba(43,42,42,.05);
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0; height: 100%;
  background: var(--fis-sand-bg);
  color: var(--fis-charcoal);
  font: 15px/1.55 Questrial, "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.fis-wrap {
  display: grid;
  grid-template-columns: minmax(384px, 36%) 1fr;
  height: 100vh;
  min-height: 520px;
}

/* ---------------------------------------------------------------- panel */

.fis-panel {
  display: flex; flex-direction: column;
  background: var(--fis-sand-bg);
  border-right: 1px solid var(--fis-sand);
  /* min-height:0 is load-bearing. Grid and flex items default to min-height:auto,
     which refuses to shrink below their content, so .fis-list never scrolls and
     the whole page grows to fit 24 cards instead. That also stretches the map
     column to thousands of pixels. */
  min-width: 0; min-height: 0;
}

.fis-search {
  padding: 22px 22px 18px;
  border-bottom: 1px solid var(--fis-sand);
  background: var(--fis-sand-lt);
  position: relative;
}

.fis-search label {
  display: block; font-size: 12px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--fis-muted); margin-bottom: 8px;
}

.fis-field { display: flex; gap: 8px; }

.fis-input {
  flex: 1 1 auto; min-width: 0;
  padding: 13px 15px;
  border: 1.5px solid var(--fis-sand); border-radius: 999px;
  background: var(--fis-white); color: var(--fis-charcoal);
  font-size: 15px; font-family: inherit;
}
.fis-input:focus {
  outline: 2px solid rgba(165,160,115,.5); outline-offset: 1px;
  border-color: var(--fis-sage);
}
.fis-input::placeholder { color: #9b9781; }

.fis-btn {
  border: 1.5px solid var(--fis-charcoal); border-radius: 999px;
  background: var(--fis-charcoal); color: var(--fis-white);
  font: 400 15px/1 inherit; font-family: inherit;
  padding: 0 22px; min-height: 48px; cursor: pointer; white-space: nowrap;
  transition: background .15s ease, border-color .15s ease;
}
.fis-btn:hover { background: #3f3e3e; border-color: #3f3e3e; }
.fis-btn:focus-visible { outline: 2px solid var(--fis-sage); outline-offset: 2px; }

/* Geolocation is the fastest path to a useful result and it was previously a
   13px underlined link, which reads as a footnote. Promoted to a full-width
   button so it competes with the search field rather than hiding under it:
   one tap beats typing a ZIP, especially on a phone. */
.fis-geo {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; margin-top: 12px; min-height: 46px; padding: 10px 16px;
  background: var(--fis-white); color: var(--fis-charcoal);
  border: 1.5px solid var(--fis-sage); border-radius: 999px;
  font: inherit; font-size: 15px; cursor: pointer;
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.fis-geo svg {
  width: 19px; height: 19px; flex: 0 0 auto;
  fill: none; stroke: var(--fis-sage); stroke-width: 1.8; stroke-linecap: round;
}
.fis-geo .fis-geo-dot { fill: var(--fis-sage); stroke: none; }
.fis-geo:hover { background: var(--fis-sand-lt); border-color: var(--fis-charcoal); }
.fis-geo:hover svg { stroke: var(--fis-charcoal); }
.fis-geo:hover .fis-geo-dot { fill: var(--fis-charcoal); }
.fis-geo:active { transform: translateY(1px); }
.fis-geo:focus-visible { outline: 2px solid var(--fis-charcoal); outline-offset: 2px; }

/* Autocomplete. Absolutely positioned so it overlays the list rather than
   pushing it, which otherwise makes the whole panel jump on every keystroke. */
.fis-suggest {
  position: absolute; left: 18px; right: 18px; top: 100%;
  margin-top: -12px; z-index: 30;
  background: var(--fis-white);
  border: 1px solid var(--fis-sand); border-radius: 3px;
  box-shadow: var(--fis-shadow);
  max-height: 260px; overflow-y: auto;
}
.fis-suggest[hidden] { display: none; }
.fis-suggest button {
  display: block; width: 100%; text-align: left;
  padding: 10px 13px; border: 0; background: none;
  font: inherit; color: var(--fis-charcoal); cursor: pointer;
  border-bottom: 1px solid var(--fis-sand-lt);
}
.fis-suggest button:last-child { border-bottom: 0; }
.fis-suggest button:hover, .fis-suggest button[aria-selected="true"] {
  background: var(--fis-sand-lt);
}
.fis-suggest .fis-sug-sub { display: block; font-size: 12.5px; color: var(--fis-muted); }

.fis-count {
  padding: 16px 22px; font-size: 12px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--fis-muted);
  border-bottom: 1px solid var(--fis-sand);
}

.fis-list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px; margin: 0; list-style: none; }

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

.fis-card {
  background: var(--fis-white);
  border: 1px solid var(--fis-sand); border-left: 3px solid var(--fis-sage);
  border-radius: 10px; padding: 22px 24px; margin-bottom: 14px;
  box-shadow: var(--fis-shadow);
  cursor: pointer;
  transition: border-color .15s ease, transform .08s ease;
}
.fis-card:hover { border-color: var(--fis-sage); transform: translateY(-1px); }
.fis-card.fis-active {
  border-color: var(--fis-sage);
  box-shadow: 0 0 0 2px rgba(165,160,115,.4), var(--fis-shadow);
}
/* Coming soon: same family, drained of the accent, so it reads as not-yet
   rather than as a second category of studio. */
.fis-card.fis-soon { border-left-color: var(--fis-sand); background: var(--fis-sand-bg); }

/* 400, not bold: Questrial ships one weight, so a bold heading would be
   browser-synthesised and smeared. Size and colour carry the hierarchy. */
.fis-card h3 {
  margin: 0 0 10px; font-size: 26px; line-height: 1.1;
  font-weight: 400; letter-spacing: -.012em;
}

.fis-dist {
  float: right; font-size: 14px; color: var(--fis-muted);
  font-variant-numeric: tabular-nums; margin-left: 10px;
}

.fis-addr { color: var(--fis-charcoal); font-size: 15px; line-height: 1.45; margin: 0; }
.fis-meta { font-size: 14px; margin: 12px 0 0; color: var(--fis-muted); }
.fis-meta a { color: var(--fis-charcoal); text-decoration: none; border-bottom: 1px solid var(--fis-sand); }
.fis-meta a:hover { border-bottom-color: var(--fis-sage); }

.fis-open { color: #4a6b3a; font-weight: 600; }
.fis-shut { color: #8a5340; font-weight: 600; }

.fis-badge {
  display: inline-block; margin-bottom: 7px;
  background: var(--fis-sand); color: var(--fis-charcoal);
  border: 1px solid var(--fis-sage);
  font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
  font-weight: 700; padding: 3px 8px; border-radius: 2px;
}

.fis-actions { display: flex; gap: 10px; margin-top: 18px; }
/* Pills, and tall enough to be a real target. The first pass used 3px
   rectangles at 9px padding, which read as links rather than actions. 44px is
   also the minimum comfortable tap target on a phone. */
.fis-actions a {
  flex: 1 1 auto; text-align: center; text-decoration: none;
  display: flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 10px 18px;
  border-radius: 999px; font-size: 15px; letter-spacing: .005em;
  border: 1.5px solid transparent;
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.fis-actions a:active { transform: translateY(1px); }
/* Scoped as `.fis-actions .fis-x`, not `.fis-x`, deliberately.
   `.fis-actions a` above is specificity (0,1,1) and a bare `.fis-secondary` is
   (0,1,0), so the shorthand `border: ... transparent` there wins and the button
   silently loses its outline. That is exactly what happened on the first design
   pass: Directions rendered as bare text inside an invisible pill. */
.fis-actions .fis-primary {
  background: var(--fis-blue); color: var(--fis-white); border-color: var(--fis-blue);
}
.fis-actions .fis-primary:hover { background: var(--fis-blue-d); border-color: var(--fis-blue-d); }

.fis-actions .fis-secondary {
  background: transparent; color: var(--fis-charcoal); border-color: var(--fis-charcoal);
}
.fis-actions .fis-secondary:hover { background: var(--fis-charcoal); color: var(--fis-white); }

.fis-empty { padding: 26px 18px; color: var(--fis-muted); }

/* ---------------------------------------------------------------- map */

.fis-map { position: relative; background: var(--fis-sand-lt); min-width: 0; min-height: 0; }

/* Sized with height/width rather than absolute inset, deliberately.
 *
 * Mapbox GL JS adds its own .mapboxgl-map class to this element, and that class
 * sets `position: relative`. Its stylesheet is injected at map init, after ours,
 * so it wins and quietly defeats `position: absolute; inset: 0`, collapsing the
 * container to zero height. The map then initialises fine, creates its canvas
 * and all 24 markers, and renders nothing at all: no error, no console warning,
 * just an empty box.
 *
 * height:100% works whether the element ends up relative or absolute, so the
 * ordering stops mattering. .fis-map above is position:relative and full height,
 * which is what this is 100% of. */
.fis-map-el { width: 100%; height: 100%; }

/* Shown when Mapbox cannot load: no token, a URL-restricted token rejecting the
   origin, or a privacy extension blocking it. The list still works, so this is a
   notice and not a failure state. Mapbox calls URL restriction a best-effort
   measure, so some real visitors WILL see this. */
.fis-map-fallback {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 30px; text-align: center; color: var(--fis-muted);
  background: repeating-linear-gradient(45deg,
    var(--fis-sand-lt), var(--fis-sand-lt) 12px, #e4e1cf 12px, #e4e1cf 24px);
}
.fis-map-fallback p { max-width: 320px; margin: 0; font-size: 14px; }

/* Map pins: the Fine Ink flame, not a generic teardrop.
 *
 * Built from the brand mark, recoloured per state and shipped as PNG in our own
 * bundle. Deliberately NOT a Mapbox sprite icon: these are DOM markers, so the
 * artwork needs no upload, no styles:write token, and no style republish to
 * change. Swapping the flame later is a file replacement and a deploy.
 *
 * The white drop-shadow ring is doing real work. A flat charcoal shape at 30px
 * disappears over dark map features like water, parks and motorways. Four
 * offset white shadows give a cheap 1px outline that follows the alpha channel,
 * which a border cannot do on a transparent PNG.
 */
.fis-pin {
  /* 24px reads clearly without swamping a dense metro. The Orlando cluster
     is eleven studios inside about 25 miles, so pin size is a legibility
     decision rather than a taste one. */
  width: 24px; height: 38px;
  background: url('img/flame-charcoal.png') no-repeat center / contain;
  cursor: pointer;
  transform-origin: 50% 100%;   /* grow from the tip, which is the actual location */
  transition: transform .12s ease;
  filter:
    drop-shadow( 1px  0    0 #fff) drop-shadow(-1px  0    0 #fff)
    drop-shadow( 0    1px  0 #fff) drop-shadow( 0   -1px  0 #fff)
    drop-shadow( 0    2px  3px rgba(43,42,42,.45));
}

/* Coming soon: the same mark in sage, so it reads as a Fine Ink studio that is
   not open yet rather than as a different kind of thing. */
.fis-pin-soon { background-image: url('img/flame-sage.png'); }

.fis-pin:hover, .fis-pin.fis-pin-active { transform: scale(1.25); z-index: 5; }

/* Keyboard users get the same affordance as mouse users. */
.fis-pin:focus-visible {
  outline: 2px solid var(--fis-charcoal); outline-offset: 3px; border-radius: 2px;
}

/* The pin popup. Sized and weighted like a card rather than a tooltip: on the
   map this IS the card, and someone who found a studio by looking at the map
   should not have to hunt in the list for the phone number. */
/* The popup is the card, at card scale.
 *
 * On the map this is the primary presentation of a studio, so it is sized to
 * match the list card rather than shrunk into a tooltip. Clicking a pin should
 * not give you less than scrolling a list does.
 *
 * The width is capped against the VIEWPORT, not just a fixed max, because on a
 * phone the map column is only 300px tall and full width; a rigid 400px popup
 * would hang off both edges. */
.fis-pop .mapboxgl-popup-content {
  font-family: inherit;
  padding: 28px 30px 26px;
  border-radius: 12px;
  border: 1px solid var(--fis-sand);
  box-shadow: 0 10px 40px rgba(43,42,42,.22);
  min-width: 340px;
  max-width: min(400px, calc(100vw - 32px));
}
.fis-pop .mapboxgl-popup-tip { border-top-color: var(--fis-white); }

.fis-pop h3 {
  margin: 0 0 12px; font-size: 30px; line-height: 1.08;
  font-weight: 400; letter-spacing: .005em; color: var(--fis-charcoal);
}
.fis-pop-addr { margin: 0; font-size: 16px; line-height: 1.5; color: var(--fis-charcoal); }
.fis-pop-meta { margin: 12px 0 0; font-size: 15px; color: var(--fis-muted); }

.fis-pop-contact {
  display: flex; justify-content: space-between; gap: 14px;
  margin: 16px 0 0; padding: 16px 0 0;
  border-top: 1px solid var(--fis-sand);
  font-size: 16px;
}
.fis-pop-contact a { color: var(--fis-charcoal); text-decoration: none; border-bottom: 1px solid var(--fis-sand); }
.fis-pop-contact a:hover { border-bottom-color: var(--fis-sage); }

.fis-pop .fis-actions { margin-top: 20px; }

/* Mapbox's default close button is a small grey glyph that is easy to miss on a
   popup this size. */
.fis-pop .mapboxgl-popup-close-button {
  top: 10px; right: 10px; width: 32px; height: 32px;
  font-size: 24px; line-height: 1; color: var(--fis-muted);
  border-radius: 3px; background: transparent;
}
.fis-pop .mapboxgl-popup-close-button:hover {
  background: var(--fis-sand-lt); color: var(--fis-charcoal);
}

/* ---------------------------------------------------------------- responsive */

@media (max-width: 860px) {
  /* List above map, deliberately. At 24 studios a distance-sorted list converts
     better than a map, and on a phone the map is mostly reassurance. */
  .fis-wrap { grid-template-columns: 1fr; grid-template-rows: auto 300px; height: auto; }
  .fis-panel { border-right: 0; border-bottom: 1px solid var(--fis-sand); }
  .fis-list  { max-height: none; overflow: visible; }
  .fis-map   { order: 2; min-height: 300px; }
}

@media (prefers-reduced-motion: reduce) {
  .fis-card, .fis-pin { transition: none; }
  .fis-card:hover { transform: none; }
}

/* ------------------------------------------------------------------ chooser
 *
 * The compact mode, mounted in a Wix Lightbox behind the global Book Now.
 * Columns are FLIPPED against the full locator: map left, list right. That is
 * what Orangetheory does, and it is right for the reason it looks arbitrary:
 * on the full page the list is the content and the map supports it, whereas in
 * the chooser the map is the orienting device and the list is the answer.
 */
body[data-mode="chooser"] .fis-wrap {
  grid-template-columns: minmax(320px, 42%) 1fr;
  height: 100vh;
  min-height: 420px;
}
body[data-mode="chooser"] .fis-panel { order: 1; border-right: 1px solid var(--fis-sand); }
body[data-mode="chooser"] .fis-map   { order: 2; }

/* The chooser keeps BOTH search and geolocation.
 *
 * An earlier version hid the search on the reasoning that the modal opens from
 * a page that already has one. That was wrong: it opens from the global Book
 * Now, which is on every page, so for anyone who declines location sharing
 * there would be no way to find a studio at all. Geolocation is the fast path,
 * not the only path.
 *
 * The label is dropped because the modal has its own heading context and the
 * placeholder already says what to type. */
body[data-mode="chooser"] .fis-search label { display: none; }
/* No reserved gutter here any more. The host's close control sits top RIGHT,
   and with the list on the left that corner is now over the map, where it does
   no harm. It collided with the Search button only while the columns were the
   other way round. */
body[data-mode="chooser"] .fis-search { padding: 16px 18px; }
body[data-mode="chooser"] .fis-geo { margin-top: 10px; }

/* Narrower panel, so the search button loses its word rather than squeezing
   the field it sits next to. */
@media (min-width: 861px) {
  body[data-mode="chooser"] .fis-btn { padding: 0 18px; }
}

body[data-mode="chooser"] .fis-card { position: relative; }
body[data-mode="chooser"] .fis-card h3 { font-size: 19px; }

.fis-more { list-style: none; text-align: center; padding: 6px 0 14px; }
.fis-more button {
  background: none; border: 0; padding: 8px 14px;
  font: inherit; font-size: 14px; color: var(--fis-charcoal);
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.fis-more button:hover { color: var(--fis-sage-d); }

@media (max-width: 860px) {
  /* List first on a phone, matching the full locator. The list is what gets
     tapped; the map below it is reassurance.
     
     The chooser is inside a MODAL, so it has a fixed height and must scroll
     INTERNALLY. The generic mobile rule above sets .fis-list to
     `overflow: visible` because the full locator page is allowed to grow and
     let the whole document scroll. Inheriting that here made the list expand
     past its grid row while the map stayed in a fixed row beneath, so the map
     was painted over the cards. That is the bug in the 2026-09-14 screenshot.
     
     minmax(0, 1fr) rather than auto: a bare 1fr still refuses to shrink below
     its content, which is the same failure in a different costume. */
  body[data-mode="chooser"] .fis-wrap {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) 190px;
    height: 100%;
    min-height: 0;
  }
  body[data-mode="chooser"] .fis-panel {
    order: 1; min-height: 0;
    border-right: 0; border-bottom: 1px solid var(--fis-sand);
  }
  body[data-mode="chooser"] .fis-list {
    overflow-y: auto; min-height: 0; max-height: none;
  }

  /* The gutter is BACK, and only here.
  
     It was removed when the columns flipped, because on a wide screen the
     host's close control then sat over the map where it harmed nothing. On a
     phone the columns do NOT flip: the panel is full width, so that corner is
     back over the search row and the close button lands on top of the Search
     button. Measured on a 375px viewport: Search ends at x=305 and the close
     occupies 273 to 311. */
  body[data-mode="chooser"] .fis-search { padding: 12px 52px 12px 18px; }
  body[data-mode="chooser"] .fis-geo { margin-top: 8px; }

  /* 150px, not 190px. In a 640px modal every pixel the map takes is a pixel the
     list does not have, and the list is what gets tapped. At 190 the first card
     was cut roughly in half. At 150 a whole card fits with the next one peeking,
     which is also what tells a visitor the list scrolls at all. The map stays
     big enough to read as "here is where these are". */
  body[data-mode="chooser"] .fis-map { order: 2; min-height: 0; height: 150px; }
  body[data-mode="chooser"] .fis-wrap { grid-template-rows: minmax(0, 1fr) 150px; }
}

@media (max-width: 860px) {
  /* The map is only 300px tall on a phone, so a full-size popup would cover it
     entirely and hide the pin it belongs to. Trim, do not restructure. */
  .fis-pop .mapboxgl-popup-content { padding: 20px 22px 18px; min-width: 0; }
  .fis-pop h3 { font-size: 24px; }
  .fis-pop-addr, .fis-pop-contact { font-size: 15px; }
}
