/* ==========================================================================
   Starfish Group — sourcing-map.css
   Two sections: the surimi story, and the sourcing map that follows it.

   They were briefly one thing. Splitting them back apart is what let the
   photograph return to the right of the copy and the map have a section to
   itself — the merged version was doing too much at once.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. WHY WE MAKE OUR OWN SURIMI
   Copy left, photograph bleeding off the right edge.
   -------------------------------------------------------------------------- */
.surimi-story {
  position: relative;
  overflow: hidden;
  background: var(--ivory);
}

/* NOT a figure in a column. The picture runs to the right edge of the viewport
   and is masked away toward the copy, so it has no visible frame — the fade IS
   the edge. width is over half so the mask has room to be gradual; anything
   tighter and it reads as a hard vignette on a card. */
.surimi-story__media {
  position: absolute;
  z-index: 0;
  top: 0;
  left: 0;
  bottom: 0;
  width: 58%;
  /* Fades toward the copy, which is now on the right. */
  -webkit-mask-image: linear-gradient(to left, transparent 0%, rgba(0,0,0,0.55) 26%, #000 58%);
          mask-image: linear-gradient(to left, transparent 0%, rgba(0,0,0,0.55) 26%, #000 58%);
}

.surimi-story__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The worker and the knife sit left of centre in the frame; this keeps them
     in the part of the picture the mask actually reveals. */
  object-position: 46% 50%;
}

.surimi-story__inner { position: relative; z-index: 1; }

/* Half the section. The stats then wrap two to a line on their own, which is
   how they sat before the map moved in. */
/* Right half now. margin-left:auto rather than a float or a grid column —
   the section is a plain block, and the copy is the only thing in it. */
.surimi-story__copy { max-width: min(560px, 48%); margin-left: auto; }

.surimi-story .tag { color: var(--ink-meta); }
.surimi-story__heading { margin-top: var(--s-20); max-width: 15ch; }

.surimi-story__body {
  margin-top: var(--s-24);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
  color: var(--ink-soft);
}

/* --------------------------------------------------------------------------
   THE PROGRESSION
   Thai fish -> Surimi -> Finished product. Typographic, not three icons: the
   brief is explicit that the RELATIONSHIP is the point, and a row of little
   pictures would say "three benefits" instead of "one thing becomes the next".
   The arrows are the content; the words are just the stops along it.
   -------------------------------------------------------------------------- */
.chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-12);
  margin-top: var(--s-40);
  padding-top: var(--s-20);
  border-top: 1px solid rgba(23, 40, 42, 0.18);
}

.chain__step {
  display: flex;
  align-items: center;
  gap: var(--s-12);
  font-family: var(--font-body);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--coral-ink);
  white-space: nowrap;
}

/* The arrow belongs to the gap, so the last step does not trail one. */
.chain__step + .chain__step::before {
  content: "\2192";
  font-weight: 500;
  color: var(--ink-meta);
  letter-spacing: 0;   /* the glyph would otherwise carry the label's tracking */
}

.surimi-story__proof { margin-top: var(--s-40); }

.surimi-story__cta { margin-top: var(--s-40); }

/* TWO PER LINE, forced. .proof wraps on its own, but "Annual surimi production
   capacity" is about 297px set at label size and two of those will not fit in a
   537px column — so all three fell into a single stack. Pinning the basis to
   half the row makes the labels wrap to two lines instead of the items wrapping
   to three rows, which is the arrangement these had before the map moved in. */
.surimi-story__proof .proof__item {
  flex: 0 1 calc(50% - var(--s-40) / 2);
  min-width: 0;
  /* VALUES ON ONE LINE. .proof__item is column-REVERSE so the number sits above
     its label, and in that direction flex-start is the BOTTOM — so the items
     bottom-aligned, and "Annual surimi production capacity" wrapping to two
     lines pushed 30,000 MT higher than Since 1996. flex-end is the top in a
     reversed column, so the numbers line up and the labels hang below them. */
  justify-content: flex-end;
}

/* "* 2025 volume" — the qualifier on the #1 claim, on its own line and smaller
   so it reads as a footnote to the label rather than part of it. */
.proof__note {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: none;
  color: var(--ink-meta);
  opacity: 0.85;
}

@media (max-width: 900px) {
  /* The mask cannot do its job once the copy is as wide as the section: the
     text would sit on the photograph. Below 900 the picture becomes a band
     under the copy instead, still edge to edge, still unframed. */
  /* A BAND DEEP ENOUGH FOR A PORTRAIT PHOTOGRAPH.

     The picture is 1200x1600 and the figure fills it top to bottom — cap at
     the head, fish on the table at the foot. Width sets the scale here, so
     the fraction of the frame that survives is height / 520 at 390px wide:
     the old 260px band showed exactly half of it, and the half it chose cut
     the worker off at the forehead. A square band shows three quarters, and
     the crop starts high enough to keep the cap. */
  .surimi-story__media {
    position: relative;
    width: 100%;
    height: min(100vw, 440px);
    min-height: 320px;
    margin-top: var(--s-56);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 26%);
            mask-image: linear-gradient(to bottom, transparent 0%, #000 26%);
  }
  /* 26%, not the 50% the wide layout uses. Centred, the band took its slice
     from the middle of the figure. */
  .surimi-story__media img { object-position: 46% 26%; }
  .surimi-story__copy { max-width: none; margin-left: 0; }
}


/* --- Copy ------------------------------------------------------------------
   Sits in the left third, over the faded part of the canvas.
   -------------------------------------------------------------------------- */
.surimi-story__inner {
  position: relative;
  z-index: 2;
  /* NO padding-block. The section already carries .section, which is
     padding-block: var(--section-gap). Adding it here too gave the section
     200px of vertical padding and made it far longer than it was before the
     map moved in — which also cropped the map harder, because the canvas is
     sliced to fill whatever height the content asks for. */
  /* THE COPY MUST NOT SWALLOW THE MAP. This is a .shell — full width at
     z-index 2 — so without this every marker hover is intercepted by an
     invisible block of nothing. Events pass through the container and the text
     takes them back, shrink-wrapped so it only blocks where the glyphs are. */
  pointer-events: none;
}

.surimi-story__copy { max-width: min(560px, 46%); pointer-events: none; }
.surimi-story__copy > * { pointer-events: auto; width: max-content; max-width: 100%; }

/* The eyebrow sits where the threadfin passes. --ink-meta is tuned against
   flat sea-glass and measures 5.29 there, but over a coral fish it fell to
   2.94 — and the fish moves, so no single measurement of that ground is the
   whole story. The full brand ink is the only value that holds wherever the
   illustration happens to be. */
.surimi-story .tag { color: var(--ink); }

.surimi-story__heading { margin-top: var(--s-20); max-width: 15ch; }

.surimi-story__body {
  margin-top: var(--s-24);
  font-size: var(--text-body-lg);
  line-height: var(--leading-body);
  color: var(--ink-soft);
  max-width: 48ch;
}

.surimi-story__proof { margin-top: var(--s-40); }

/* "2025 volume" — the qualifier on the #1 claim, on its own line and smaller
   so it reads as a footnote to the label rather than part of it. */
.proof__note {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: none;
  color: var(--ink-meta);
  opacity: 0.85;
}
/* --------------------------------------------------------------------------
   NARROW
   The copy can no longer share the frame with the map, so it stops sitting on
   it: text first, map beneath, each at its own size.
   -------------------------------------------------------------------------- */
@media (max-width: 900px) {
  .surimi-story {
    display: flex;
    flex-direction: column;
    padding-top: var(--s-80);
  }
  .surimi-story__inner { order: -1; padding-block: 0 var(--s-40); }
  .surimi-story__copy  { max-width: none; }
  /* The shrink-wrap exists to stop the copy swallowing map hovers, and there
     are none to swallow once the two are stacked. Left on, it sets each
     paragraph as wide as its whole sentence and runs it off the screen. */
  .surimi-story__copy > * { width: auto; }
  .surimi-story__heading, .surimi-story__body { max-width: none; }
}

/* --------------------------------------------------------------------------
   2. WHERE THE FISH COMES FROM

   One oversized painted map running the whole width of the section, with the
   copy floating on the cream part of it. Not a column of text beside a panel
   of map — that read as two halves with a seam down the middle.

   ---- The two illustration tones -------------------------------------------
   A DEVIATION from "only the Starfish palette", made on instruction, and the
   same kind of deviation as the section-background block in base.css. Both are
   mixed from tokens that already exist rather than picked fresh:

     --map-sand  is --dawn (#F2B45E) lifted toward the ivory ground. Ink
                 measures 9.1 on it, so a marker or a name can sit on the land.
     --map-sea   is --teal (#3D6B7D) lifted toward --seaglass. Ink measures
                 6.2 on it, which is what lets the sea names be set in the
                 brand ink rather than in a grey that would fail there.

   They live on the section, not in :root, because nothing else on the site
   paints with them.
   -------------------------------------------------------------------------- */
.source-map {
  --map-sand: #F1CE99;
  --map-sea:  #8FB2AD;

  position: relative;
  background: var(--ivory);
  /* the map runs past the shell on every edge; nothing may spill out */
  overflow: hidden;
}

.source-map__grid {
  display: grid;
  /* ONE ROW, TWO LAYERS. The illustration is the full width of the section and
     the copy floats on it. Nothing overlaps in practice because the painting's
     own left edge falls to the right of the copy: the frame in sourcing-map.js
     starts 400 units out in the Bay of Bengal, where there is no land and
     therefore no paint. */
  grid-template-columns: minmax(0, 38fr) minmax(0, 62fr);
  gap: clamp(32px, 5vw, 88px);
  align-items: center;
}
@media (min-width: 1081px) { .source-map__grid > * { grid-row: 1; } }

/* Over the painting, under the copy — which is z-index 2, so the headline is
   never covered by it.

   ON THE AMPLITUDE OF THE EDGE, which is set on the path in index.html. It is
   a sine riding the diagonal x = 300 + y, so its slope is 1 + A(2pi/L)cos.
   Wherever that goes negative the edge doubles back on itself and the wave
   comes to a point — which is what the first version did: amplitude 18 over a
   wavelength of 50 gives a minimum slope of -1.26. One full cycle down the
   height at amplitude 12 gives +0.25: as much swing as the shape will take and
   still be a curve rather than a corner. */
/* THE MAP HAS NO COVER ANY MORE.

   There was an ivory sheet lying over the whole plate, slid off on a
   scrubbed ScrollTrigger, and it hid the map three separate times: once
   because the reveal was keyed to a section the map was only part of, once
   because a will-change promotion stopped Safari painting the group at all,
   and once because iOS fires resize as the address bar moves and a scrub
   in flight can settle wherever it likes. Each fix was correct and the next
   failure was somewhere else, which is the signal that the mechanism is
   wrong rather than the numbers.

   So it is gone — element, styles and tween. What replaces it cannot fail
   the same way, because it never hides the map: the map is simply there,
   and the ports fade up on it. See .smap__pins below. */

.source-map__copy { position: relative; z-index: 2; grid-column: 1; }
.source-map .tag { color: var(--ink-meta); }
.source-map__title { margin-top: var(--s-20); max-width: 15ch; }
.source-map__lede {
  margin-top: var(--s-24);
  color: var(--ink-soft);
  max-width: 44ch;
}

/* --------------------------------------------------------------------------
   THE MARKS
   Four of them, and the differences between them are the whole legend:

     a boat, orange   a fishing port we buy from directly
     a boat, teal     a fishing port in the wider network, and smaller
     a red disc       the plant. The one red thing on this map.
     a plain disc     Bangkok Port, which lands no fish

   NO PLATE, NO LEADER, NO TICKET. Every mark used to carry a cream label on
   the end of a hairline, and five of them turned the right-hand third of the
   painting into a stack of boxes — four of which repeated, word for word, what
   the upper-gulf plate was already saying beside them.
   -------------------------------------------------------------------------- */
.smap__pins { pointer-events: auto; }
.smap__hit  { fill: transparent; cursor: pointer; }

/* TWO COPIES OF THE BOAT, one under the other. The glyph is a hull and a sail
   that touch, so paint-order: stroke on the shape itself lays a cream line
   along the join as well as round the outside. Drawn once underneath at stroke
   width and once over it filled, only the outside is haloed. */
.smap__ship-halo {
  fill: var(--cream);
  stroke: var(--cream);
  stroke-width: 2.6;
  stroke-linejoin: round;
  pointer-events: none;
}
.smap__ship-mark { pointer-events: none; }
.smap__ship { pointer-events: none; }
.smap__pin--main .smap__ship { fill: var(--coral); }
/* The wider network. The same boat in the map's own teal and a fifth smaller —
   a step down in strength, not a different meaning. */
.smap__pin--port .smap__ship { fill: var(--teal); opacity: 0.88; }

/* THE SIZE, AND ONLY THE SIZE. The mark's position is a transform attribute on
   the wrapper above this, because a CSS transform would replace it rather than
   compose with it. Scaling about the glyph's own box rather than the viewBox
   origin, for the same reason. */
.smap__ship-inner {
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(1);
}
.smap__pin--port .smap__ship-inner { transform: scale(0.82); }

.smap__dot {
  /* CREAM, AS A SEPARATOR. These sit on the coast, so a ring in the water's
     tone put a teal halo round every mark that happened to be over sand. */
  stroke: var(--cream);
  stroke-width: 1.8;
  pointer-events: none;
  /* Without these a scale on hover is taken about the origin of the viewBox,
     which on a mark 950 units out is not a 10% grow but a 95-unit jump. */
  transform-box: fill-box;
  transform-origin: center;
}
/* ASKED FOR, AND THE ONE PLACE THIS PAGE BREAKS ITS OWN RULE. Brand red
   appears once on the homepage — the full stop in the hero. It appears twice
   now: the plant is the only thing on this map that is ours, and it was a
   coral disc among coral markers. */
.smap__pin--factory .smap__dot { fill: var(--red); }
/* Not a boat, because no fish is landed here: this is where finished product
   leaves from. Ink, so it reads as infrastructure rather than as a third
   colour of port. */
.smap__pin--export  .smap__dot { fill: var(--ink); opacity: 0.82; }

/* 1.1 on the mark itself. Enough to be felt without covering ground it does
   not own — and only the five places with a card to open ever move. */
.smap__pin[tabindex]:hover .smap__dot,
.smap__pin[tabindex]:focus-visible .smap__dot,
.smap__pin[tabindex]:hover .smap__ship-inner,
.smap__pin[tabindex]:focus-visible .smap__ship-inner { transform: scale(1.1); }
.smap__pin[tabindex] .smap__dot,
.smap__pin[tabindex] .smap__ship-inner { transition: transform 220ms var(--ease-out); }

/* THE RING, ONCE. Drawn at the mark's own radius, so scale 1 is exactly its
   edge; the keyframes take it out to 2.4 and fade it as they go. Declared on
   the hover state, so it restarts on each pointer entry and does not loop. */
.smap__pulse {
  fill: none;
  stroke: var(--teal);
  stroke-width: 1.6;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  pointer-events: none;
}
/* KEYBOARD. The ticket used to carry the focus ring — it had a stroke to
   thicken. With the tickets gone the mark is a 13px boat, and a 10% scale is
   not a focus indicator, so the group takes an outline of its own. On a plate
   mark the group holds the name too, which is exactly the extent a reader
   tabbing through wants drawn. */
.smap__pin:focus { outline: none; }
.smap__pin:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 3px;
}

.smap__pin--main .smap__pulse    { stroke: var(--coral); }
.smap__pin--factory .smap__pulse { stroke: var(--red); }
.smap__pin[tabindex]:hover .smap__pulse,
.smap__pin[tabindex]:focus-visible .smap__pulse { animation: smap-pulse 620ms var(--ease-out) 1; }

@keyframes smap-pulse {
  from { opacity: 0.55; transform: scale(1); }
  to   { opacity: 0;    transform: scale(2.4); }
}

/* --------------------------------------------------------------------------
   THE NAMES
   Set straight on the painting with nothing behind them but a cream outline.
   Where each one goes is solved at draw time; see placeNames() in
   sourcing-map.js.
   -------------------------------------------------------------------------- */
.smap__name {
  font-family: var(--font-body);
  font-size: 11px;                 /* viewBox units */
  font-weight: 500;
  fill: var(--ink-soft);
  pointer-events: none;
  paint-order: stroke;
  /* A cream outline so a name that lands where sand meets water still reads.
     paint-order puts it behind the glyph rather than over it. */
  stroke: var(--cream);
  stroke-width: 2.4;
  stroke-linejoin: round;
}
/* The three we buy from directly, and the plant, read before the other nine —
   which is the same order the marks themselves are in. */
.smap__name--main,
.smap__name--export  { fill: var(--ink); font-weight: 600; }
.smap__name--factory { fill: var(--ink); font-weight: 600; }

/* The second line is the category, not the name. Lighter and a shade smaller,
   so scanning the map reads places rather than sentences. */
.smap__name-kind {
  font-size: 0.86em;
  font-weight: 400;
  opacity: 0.78;
}

/* A hairline back to the mark, for a name that had to travel to find room. It
   should join the two and not otherwise be noticed. */
.smap__name-leader {
  stroke: var(--ink-soft);
  /* 1.4 at 0.5, not 0.9 at 0.34. At the fainter setting the line was under a
     pixel of a third-opacity stroke crossing sand of a similar value — drawn,
     measurably, and invisible. */
  stroke-width: 1.4;
  stroke-opacity: 0.5;
  fill: none;
  pointer-events: none;
}

@media (max-width: 1080px) { .smap__name { font-size: 13px; } }
/* 9.6, not 12.5. Two lines each are twice the height of the one-line version,
   and the narrow window is 400 units across against the wide one's 1458 — and
   below 1150 the plate is gone, so five more names come back onto the map. */
@media (max-width: 620px)  { .smap__name { font-size: 9.6px; } }

/* --------------------------------------------------------------------------
   THE UPPER-GULF PLATE
   A cream plate on the painting. Colours are sampled off the render, not taken
   from the tokens: --map-sand is #F1CE99 and --map-sea #8FB2AD, but the paint
   filters shift what actually appears to #EED1A5 and #86A9A4.
   -------------------------------------------------------------------------- */
/* THE SAME MATERIAL, NOT A PANEL. Both fills were cream to begin with, which
   made the plate a white box laid on a painting — a UI element on an
   illustration, and the one thing on this section that did not look drawn.
   Sand and water instead, sampled off the render, so what it reads as is the
   same map four times closer.

   What separates it from the painting is then a mat rather than a fill: a
   cream band round the outside, the way a picture is mounted, with the
   hairline every card on this site is edged with inside it. */
.smap__inset-bg    { fill: #86A9A4; }   /* water, as the filters render it */
.smap__inset-land  { fill: #EED1A5; }   /* sand, likewise */
.smap__inset-mat {
  fill: none;
  stroke: var(--cream);
  stroke-width: 9;
}
.smap__inset-frame {
  fill: none;
  stroke: var(--border-dark);
  stroke-width: 1.1;
}

/* THE PATCH BEING MAGNIFIED, marked on the map itself. Dashed rather than
   solid, and a ring rather than the window's own rectangle: it is saying
   "these five are over here", not claiming to be a viewport. The tie is
   dashed to match and stops short at both ends: a solid line running corner
   to corner would read as a route between two places, which is the one thing
   this map is not about. */
.smap__inset-locator,
.smap__inset-tie {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.4;
  stroke-opacity: 0.42;
  stroke-dasharray: 4 5;
  pointer-events: none;
}
/* THE TIE CARRIES THE RING’S OWN WEIGHT, not a fainter one. At 0.34 over sand
   it was measurably drawn and invisible: fifty-eight units is six dashes, and
   six faint dashes are a smudge rather than a line. Same opacity, one notch
   thicker, and a dash long enough to be read as dashes. */
.smap__inset-tie { stroke-width: 1.5; stroke-dasharray: 5.5 4.5; }

.smap__inset-name {
  font-family: var(--font-body);
  font-size: 13px;                 /* viewBox units */
  font-weight: 500;
  fill: var(--ink);
  pointer-events: none;
  paint-order: stroke;
  stroke: var(--cream);
  stroke-width: 2.8;
  stroke-linejoin: round;
}
.smap__inset-name--factory { font-weight: 600; }

.smap__inset-title {
  font-family: var(--font-body);
  font-size: 11px;                 /* viewBox units */
  font-weight: 600;
  letter-spacing: 1.3px;
  text-transform: uppercase;
  fill: var(--ink-soft);
  paint-order: stroke;
  stroke: #EED1A5;
  stroke-width: 2.6;
  stroke-linejoin: round;
}

/* WIDE FRAME ONLY, AND NOT EVEN ALL OF IT. Below 1080 the window becomes
   '450 10 900 778' and the plate's coordinates would fall off the left edge.
   But it has to go sooner than that: the map is slice-cropped, so as the
   viewport narrows the plate rides up under the fixed header — measured, the
   clearance runs 41px at 1600, 29 at 1440, 12 at 1206, 8 at 1150 and goes
   negative by 1100. It stops at the last width where it still clears, and
   sourcing-map.js reads the same 1151 so the five places it holds go back onto
   the map at exactly the width it disappears. */
@media (max-width: 1150px) { .smap__inset { display: none; } }

/* --------------------------------------------------------------------------
   THE LEGEND
   Four marks, four rows, bottom left of the painting on the cream the copy
   sits on. It exists because the tickets do not: once a place is a drawn mark
   rather than a named box, the difference between an orange boat and a teal
   one is the only thing saying which ports we buy from directly — and a reader
   cannot be asked to infer that.
   -------------------------------------------------------------------------- */
.smap__legend {
  margin: var(--s-24) 0 0;
  display: grid;
  gap: 7px;
  justify-items: start;
}
.smap__legend-row {
  display: grid;
  grid-template-columns: 20px auto;
  align-items: center;
  gap: 10px;
}
.smap__legend dt { margin: 0; display: flex; justify-content: center; }
.smap__legend dd {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-caption);
  line-height: 1.2;
  color: var(--ink-soft);
}

.smap__legend-icon { width: 17px; height: 17px; display: block; }
.smap__legend-icon--main { fill: var(--coral); }
.smap__legend-icon--port { fill: var(--teal); opacity: 0.88; width: 15px; height: 15px; }

.smap__legend-mark {
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
}
.smap__legend-mark--factory { background: var(--red); }
.smap__legend-mark--export  { background: var(--ink); opacity: 0.82; width: 10px; height: 10px; }

.smap__hint {
  margin-top: var(--s-40);
  font-size: var(--text-micro);
  font-weight: 500;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-meta);
}

.source-map__map {
  /* The callout is absolutely positioned against this, not the section, so it
     is clamped to the map. */
  position: relative;
  z-index: 0;

  /* EVERY EDGE OF THE SECTION. The wrapper sits in the grid's only row, spans
     both tracks, and then pulls out past the shell on both sides and past the
     section's padding above and below — so the painting is cropped by the
     section boundary rather than boxed inside it. */
  grid-column: 1 / -1;
  margin-block: calc(-1 * var(--section-gap));
  margin-inline: calc(-1 * (max(0px, (100vw - var(--shell-max)) / 2) + var(--shell-pad)));
}

/* HEIGHT IS SET, NOT DERIVED, and the painting covers it. The canvas is as
   wide as the viewport now while the frame's aspect is fixed, so an intrinsic
   height would run past 1000px on a wide monitor. This holds it to a band and
   lets preserveAspectRatio="slice" crop the top and bottom instead — the same
   crop the product illustrations take. The frame carries slack above the
   topmost mark and below the bottommost one for exactly that. */
.smap {
  display: block;
  width: 100%;
  height: clamp(520px, 60vw, 980px);
}

/* --- The painting ----------------------------------------------------------
   Geography is Natural Earth 50m, projected to Mercator and reduced with
   Douglas-Peucker at sub-pixel tolerance. Nothing is redrawn and nothing is
   smoothed — an earlier version splined 64-point outlines and the result was
   not the shape of Thailand. What the filters in the markup do is warp that
   outline by a few units and lay pigment noise over the fill; the coast is
   still the coast.

   NO OUTLINE ON EITHER SHAPE. The coastline is where the two colours meet,
   which is how a painted map reads and how a drawn one does not.
   -------------------------------------------------------------------------- */
.smap__sea-shape {
  /* Black, and never seen: the filter uses this only as an alpha silhouette
     and floods --map-sea into it afterwards. It is a rectangle larger than any
     frame now rather than an offset of the coast — see the note in the markup. */
  fill: #000;
}

/* The divider. Same treatment: an alpha shape, with --map-ivory flooded in. */
.smap__cream { fill: #000; }
#smapCreamFlood { flood-color: var(--ivory); }

.smap__land { fill: var(--map-sand); stroke: none; }

/* The land borders: dotted, thin, and quiet enough to be a fact rather than a
   feature. Round caps on a near-zero dash give dots rather than dashes. */
.smap__border {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 0.1 7;
  opacity: 0.26;
  pointer-events: none;
}

/* The filter floods this rather than taking it from the shape, because by the
   time the colour is needed the shape is an alpha mask. */
#smapSeaFlood { flood-color: var(--map-sea); }

/* Two marks, three strokes each, in the open water. Abstract, and quiet
   enough that they are texture rather than a second subject. */
.smap__ripple {
  fill: none;
  stroke: var(--ink);
  stroke-width: 3;
  stroke-linecap: round;
  opacity: 0.2;
  pointer-events: none;
}
.smap__ripple[hidden] { display: none; }

/* --- Sea names ------------------------------------------------------------
   Set into the water the way they would be on a printed chart, in the body
   sans rather than an italic serif. These two are markers as well as
   lettering: a fishing ground has no address, so the name is the target and it
   opens the same card everything else does.
   -------------------------------------------------------------------------- */
.smap__sea-name {
  font-family: var(--font-body);
  font-size: 14px;                 /* viewBox units */
  font-weight: 400;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  /* FULL INK, NOT INK AT 0.62. Measured off the render rather than computed:
     the water is filter output, not a flat fill, and against its lighter
     patches a 62%-opacity ink came out at 3.01 — under AA for text this size.
     What makes these quiet is the weight, the letter-spacing and the size, not
     a transparency that fails where the pigment happens to be pale. */
  fill: var(--ink);
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
  transition: opacity 200ms var(--ease-out);
}
.smap__pin--water:hover .smap__sea-name,
.smap__pin--water:focus-visible .smap__sea-name { opacity: 0.92; }

/* --- Callout ---------------------------------------------------------------
   A real element, not a title attribute: browser tooltips are slow, unstyled
   and never appear on touch. Placed by chooseSlot() in sourcing-map.js, which
   scores each side of the mark against every other mark, name, the plate and
   the copy itself, and takes the emptiest.
   -------------------------------------------------------------------------- */
.smap__tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  width: min(336px, 100%);
  display: grid;
  grid-template-columns: 1fr;
  column-gap: var(--s-16);
  row-gap: 6px;
  align-items: start;
  padding: var(--s-16);
  background: var(--surface-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  opacity: 0;
  /* the short upward move on open */
  transform: translateY(10px);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
  pointer-events: none;
}
/* Three children in a two-column grid auto-place as photo | name / body | —
   the body drops under the picture and the panel comes out twice as tall.
   Each cell is placed. */
.smap__tip[data-photo] { grid-template-columns: 104px 1fr; grid-template-rows: auto auto; }
.smap__tip[data-photo] .smap__tip-photo { grid-area: 1 / 1 / 3 / 2; }
.smap__tip[data-photo] .smap__tip-name  { grid-area: 1 / 2 / 2 / 3; }
.smap__tip[data-photo] .smap__tip-body  { grid-area: 2 / 2 / 3 / 3; }
.smap__tip[data-open] { opacity: 1; transform: translateY(0); }

.smap__tip-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  line-height: 1.15;
  letter-spacing: var(--track-headline);
  color: var(--ink);
}
.smap__tip-body {
  font-size: 13px;
  line-height: 1.45;
  color: var(--ink-soft);
}
.smap__tip-photo {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: 6px;
  background: var(--seaglass-2);
}
.smap__tip-photo[hidden] { display: none; }

/* Everything the markers say, as text: read by a screen reader, and the
   fallback if the script never runs. */
.smap__key {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   THIS SECTION HAS NO REVEAL, AND THAT IS THE DECISION

   There was an ivory cover that swept the map open on a scrubbed
   ScrollTrigger. It hid the map on a phone three times: keyed to a section
   the map was only part of, then a will-change promotion that stopped
   Safari painting the group at all, then iOS firing resize as its address
   bar moves, which lets a scrub in flight settle anywhere. Three correct
   fixes and a fourth failure is not a numbers problem.

   It was replaced with a fade-up of the markers, which could not hide the
   map — but it still made you wait for the section to finish before it was
   whole, and waiting was the complaint. So that is gone too.

   The map is drawn. Nothing reveals it, nothing waits for it, and there is
   no state it can be caught in. The section still moves: the copy above it
   rises with the shared [data-reveal], and the markers answer to touch and
   hover. What is gone is anything that decides when the map may be seen.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   FLAT ON A PHONE

   The painted map is six feTurbulence chains, three feDisplacementMaps and
   a 50-radius blur, and what they produce is a texture: the sand grain, the
   soft coast, the watercolour edge on the divider. On a 390px screen at a
   third of the size, none of that survives being looked at — it is a cost
   with no picture to show for it.

   So here the same shapes are filled flat, in the two colours the filters
   were flooding in anyway. The geography, the coastline, every marker and
   every name are identical; only the paint is. It reads more clearly at
   this size, it costs nothing to draw, and it cannot fail the way a filter
   chain can on a browser I cannot test on.

   CSS beats a presentation attribute, so filter: none here overrides the
   filter="url(...)" written on each element in the markup. */
@media (max-width: 900px) {
  .smap__sea-shape,
  .smap__land,
  .smap__cream { filter: none; }

  /* Both of these are black in the markup because the filter only ever used
     them as an alpha silhouette and flooded the real colour in afterwards.
     Unfiltered they need the colour on the shape itself. --smap__land
     already carries --map-sand, so it is not repeated. */
  .smap__sea-shape { fill: var(--map-sea); }

  .smap__cream     { fill: var(--ivory); }
}

@media (prefers-reduced-motion: reduce) {
  /* Every state change stays; only the movement goes. A marker that responds
     the instant you point at it is feedback, not animation. */
  .smap__dot, .smap__ship-inner, .smap__sea-name, .smap__tip { transition: none; }
  .smap__pin[tabindex]:hover .smap__pulse,
  .smap__pin[tabindex]:focus-visible .smap__pulse { animation: none; }
  .smap__tip { transform: none; }
}

/* --------------------------------------------------------------------------
   THE MAP AT EVERY OTHER WIDTH

   Two things change, at two different widths, and they are not the same thing.

   AT 1080 the columns stop being columns: copy first, map beneath it, and the
   map becomes a band of its own rather than a layer under the text. It is not
   the usual 900 because at 900-1080 the map was still sharing the row and had
   shrunk to the point where the four upper-gulf discs stopped being four.

   AT 620 the map swaps to the phone window in sourcing-map.js: 400 units wide
   instead of 1309, and the names become one column running north to
   south so that no leader crosses another.

   Between them the type has to be reset over and over, because text inside the
   map is sized in viewBox units and therefore renders at canvas / viewBox
   width. The frame is fixed and the canvas is not, so the same 12 units is
   13px at 1440 and 25px full-bleed at 900. Each band below is set so every
   name lands between about 13 and 19px on screen.
   -------------------------------------------------------------------------- */
/* The canvas is narrower than the frame between 1081 and 1400, so the base
   12 units renders at 10px. Same names, more units. */
@media (max-width: 1400px) and (min-width: 1081px) {
  .smap__sea-name    { font-size: 14.5px; }
  .smap__ripple      { stroke-width: 4.2; }
}

@media (max-width: 1080px) {
  .source-map__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-40);
  }
  .source-map__copy { grid-column: 1; }
  .source-map__title, .source-map__lede { max-width: none; }
  .smap__hint { margin-top: var(--s-24); }
  /* Its own band now, edge to edge, and no longer pulled up into the section's
     padding — there is no copy beside it left to overrun. The strip at the
     foot used to be held open for the callout whether or not a card was
     showing, so that tapping a marker never moved the page. The intent was
     right and the price was wrong: 168px of empty ivory under the map on every
     visit, plus the section's own 64 and the next section's 80 — three hundred
     pixels of nothing between two sections, which reads as something having
     failed to load. Almost nobody taps a marker; everybody sees the gap.

     The card overlays the foot of the map instead, on its own opaque ground.
     Nothing is reserved, so nothing moves when one opens either. */
  .source-map__map {
    grid-column: 1;
    margin-block: 0;
  }
  .smap__tip {
    top: auto;
    bottom: var(--s-16);
    left: var(--shell-pad);
    right: var(--shell-pad);
    width: auto;
  }
  /* HEIGHT OFF THE FRAME, NOT THE CLAMP. At 768 the clamp's 470px floor made
     the band wider in ratio than the frame is, and slice answered by cropping
     61px off each SIDE — which took the easternmost marks with it. The ratio
     is the mid window's, which is what sourcing-map.js sets below 1080. */
  .smap {
    height: auto;
    aspect-ratio: 900 / 778;
  }
}

@media (max-width: 1080px) and (min-width: 861px) {
  .smap__sea-name    { font-size: 12.5px; }
  .smap__ripple      { stroke-width: 3.2; }
}

@media (max-width: 860px) and (min-width: 741px) {
  .smap__sea-name    { font-size: 14px; }
  .smap__ripple      { stroke-width: 3.6; }
}

@media (max-width: 740px) and (min-width: 621px) {
  .smap__sea-name    { font-size: 17px; }
  .smap__ripple      { stroke-width: 4.2; }
}

/* The picture stands in for the painting on phones only — see the note in
   the markup. Above 620 the frame takes a different viewBox and this bitmap
   would be the wrong window, so it is not shown there. */
.smap__raster { display: none; }

/* THE PHONE WINDOW. Height comes off the frame's own ratio here rather than
   the clamp: at 470px on a 375px screen the band was wider than the window is
   tall, and slice cropped 59 units off the top and bottom — which took
   Bangkok Port and the Andaman Sea with it. */
@media (max-width: 620px) {
  .smap {
    height: auto;
    aspect-ratio: 400 / 640;
  }
  /* The cream separator round a mark has to hold at the size the phone
     frame draws it — and below 1150 there are five more of them on the map,
     because the plate that held them is gone. */
  .smap__raster {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* fill, not cover: the bitmap is 400:640 and so is the frame here, so this
       is an exact overlay and every marker still lands on its own coastline. */
    object-fit: fill;
  }
  /* Only once the picture is actually on screen. The class is set by
     sourcing-map.js on the image load event, so a failed request leaves the
     vector painting exactly where it was. */
  .has-raster .smap__paint { display: none; }

  /* AND THE MARKERS HAVE TO BE LIFTED OVER IT. The picture is positioned and
     the svg is not, which paints the picture on top and hides every port —
     positioned elements sit above static content whatever the source order
     says. One line of z-index puts the map back over its own painting. */
  .smap { position: relative; z-index: 1; }

  .smap__dot { stroke-width: 3; }
  .smap__ship-halo { stroke-width: 4; }

  /* The card comes down a size with everything else. It overlays the foot of
     the map rather than docking below it — see the note at 900. */
  .smap__tip { padding: var(--s-12); }
  .smap__tip[data-photo] { grid-template-columns: 84px 1fr; }
  .smap__tip-name { font-size: 15px; }
  .smap__tip-body { font-size: 12px; }
}

/* Three bands inside the phone window, because the canvas nearly doubles
   across it while the frame does not: 320 to 620 is a factor of 1.9. */
@media (max-width: 620px) and (min-width: 521px) {
  .smap__sea-name    { font-size: 8px; }
  .smap__ripple      { stroke-width: 2; }
}
@media (max-width: 520px) and (min-width: 441px) {
  .smap__sea-name    { font-size: 9.5px; }
  .smap__ripple      { stroke-width: 2.4; }
}
@media (max-width: 440px) {
  .smap__sea-name    { font-size: 13.5px; }
  .smap__ripple      { stroke-width: 3; }
}
