/* Photerra Nearby — embeddable widget (iframe app). Brand-matched to photerra.com.
   Themeable via CSS custom props set from URL params (theme / accent). */
:root{
  --pht-cream:#F8F6F1; --pht-cream-2:#F1EDE4;
  --pht-ink:#2C2A26; --pht-ink-2:#4A463F; --pht-ink-3:#67625A; /* ink-3 darkened from #78736A → WCAG-AA (~4.7:1) on cream */
  --pht-line:#D9D3C4;
  --pht-clay:#C97B4F; --pht-gem:#2A7F62;
  --pht-accent:var(--pht-clay);
  --pht-serif:'Fraunces','GT Super','Times New Roman',serif;
  --pht-sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --pht-bg:var(--pht-cream); --pht-panel:#fff; --pht-fg:var(--pht-ink); --pht-fg-2:var(--pht-ink-2); --pht-fg-3:var(--pht-ink-3);
  --pht-border:var(--pht-line); --pht-radius:14px; --pht-tile-radius:6px; --pht-gap:8px;
}
:root[data-theme="dark"]{
  --pht-bg:#171613; --pht-panel:#201E1A; --pht-fg:#F1EDE4; --pht-fg-2:#CFC7B6; --pht-fg-3:#9A9280;
  --pht-border:#332F27; --pht-cream-2:#242119;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:transparent}
.pht{
  font-family:var(--pht-sans); color:var(--pht-fg); background:var(--pht-bg);
  border:1px solid var(--pht-border); border-radius:var(--pht-radius);
  padding:16px 16px 12px; -webkit-font-smoothing:antialiased; overflow:hidden;
}
/* header */
.pht-head{display:flex;align-items:flex-end;justify-content:space-between;flex-wrap:wrap;gap:8px 12px;margin-bottom:12px}
.pht-head>div{min-width:0}
.pht-eyebrow{font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--pht-fg-3);margin:0 0 2px}
.pht-title{font-family:var(--pht-serif);font-weight:500;font-size:23px;line-height:1.05;letter-spacing:-.01em;margin:0;color:var(--pht-fg)}
.pht-toggle{display:inline-flex;background:var(--pht-cream-2);border:1px solid var(--pht-border);border-radius:999px;padding:2px}
.pht-toggle button{font-family:var(--pht-sans);font-size:12px;font-weight:600;color:var(--pht-fg-2);background:transparent;border:0;padding:5px 13px;border-radius:999px;cursor:pointer;line-height:1}
.pht-toggle button[aria-pressed="true"]{background:var(--pht-panel);color:var(--pht-fg);box-shadow:0 1px 2px rgba(0,0,0,.08)}
/* chips */
.pht-chips{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px}
.pht-chip{font-size:12px;font-weight:500;color:var(--pht-fg-2);background:var(--pht-cream-2);border:1px solid var(--pht-border);border-radius:999px;padding:4px 11px;cursor:pointer;transition:.12s}
.pht-chip:hover{border-color:var(--pht-accent);color:var(--pht-fg)}
.pht-chip[aria-pressed="true"]{background:var(--pht-accent-fill,var(--pht-accent));border-color:var(--pht-accent-fill,var(--pht-accent));color:var(--pht-on-accent,#fff)}
/* gallery — justified rows */
.pht-rows{display:flex;flex-direction:column;gap:var(--pht-gap)}
.pht-row{display:flex;gap:var(--pht-gap)}
.pht-tile{position:relative;display:block;overflow:hidden;border-radius:var(--pht-tile-radius);background:var(--pht-cream-2);text-decoration:none;flex:0 0 auto}
.pht-tile img{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;object-fit:cover;transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.pht-tile:hover img{transform:scale(1.05)}
/* Reaches higher and starts darker than it used to: the caption block is now
   three lines on archive/credited photos (name, meta, credit), and Photerra's
   corpus contains plenty of bright overexposed frames — white text was landing
   on near-white sky above the old gradient's 34% stop. The credit line is a
   licence condition, so it has to stay legible on ANY photo. */
.pht-scrim{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to top,rgba(0,0,0,.78) 0%,rgba(0,0,0,.52) 32%,rgba(0,0,0,.22) 56%,rgba(0,0,0,0) 80%);pointer-events:none}
/* THE DARKENING FOLLOWS THE TEXT, because the text is not a fixed height and
   the tile is. A caption runs one to four lines (a two-line name, the category,
   and a credit when the photograph is archive) while a justified row can hand
   it a 120px tile — so on the tall captions the top line climbed out of the
   scrim's gradient and landed on bare sky. `.pht-scrim` is a fixed fraction of
   the TILE; this gradient is a fraction of the CAPTION, so it covers whatever
   the caption turns out to be. Full-bleed with inner padding, so it reaches the
   tile edges instead of drawing an inset band. */
.pht-cap{position:absolute;left:0;right:0;bottom:0;padding:20px 10px 9px;color:#fff;background:linear-gradient(to top,rgba(0,0,0,.74) 0%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.28) 78%,rgba(0,0,0,0) 100%)}
/* CLAMPED TO TWO LINES. `.pht-cap` is bottom-anchored, so a long name grows
   UPWARD — and the scrim only darkens the bottom ~28% of the tile. In a
   justified row a narrow tile can be ~110px wide, where "Golden Gate Bridge
   Vista Point" and "Alcatraz Island - Guardhouse" wrapped to four lines and
   climbed out of the gradient onto bare sky: white text on white cloud, on
   somebody else's website. Two lines always sit inside the scrim. The full
   name stays available in the tile's title attribute and on hover. */
.pht-name{font-family:var(--pht-serif);font-weight:500;font-size:14px;line-height:1.15;text-shadow:0 1px 3px rgba(0,0,0,.5);display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* The verified check was a flex child; inside a -webkit-box it has to flow
   inline with the text so it cannot force a third line of its own. */
.pht-name .pht-verified{display:inline-block;vertical-align:-1px;margin-left:4px}
.pht-verified{flex:0 0 auto;width:13px;height:13px;opacity:.95}
.pht-meta{font-size:11px;font-weight:500;opacity:.9;margin-top:2px;text-shadow:0 1px 3px rgba(0,0,0,.5);letter-spacing:.01em}
.pht-hovercap{position:absolute;left:0;right:0;bottom:0;padding:20px 10px 9px;color:#fff;font-size:11.5px;line-height:1.35;opacity:0;transform:translateY(4px);transition:.2s;text-shadow:0 1px 4px rgba(0,0,0,.7);max-height:0;overflow:hidden;background:linear-gradient(to top,rgba(0,0,0,.74) 0%,rgba(0,0,0,.6) 45%,rgba(0,0,0,.28) 78%,rgba(0,0,0,0) 100%)}
.pht-tile:hover .pht-hovercap{opacity:1;transform:none;max-height:84px}
.pht-tile:hover .pht-cap{opacity:0;transition:.15s}
.pht-badge-src{position:absolute;top:8px;left:8px;font-size:9.5px;font-weight:700;letter-spacing:.04em;color:#fff;background:rgba(0,0,0,.42);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);padding:2px 7px;border-radius:999px;text-transform:uppercase}
/* load more */
.pht-more{display:flex;justify-content:center;margin-top:12px}
.pht-more button{font-family:var(--pht-sans);font-size:13px;font-weight:600;color:var(--pht-fg-2);background:var(--pht-panel);border:1px solid var(--pht-border);border-radius:999px;padding:8px 18px;cursor:pointer;transition:.12s}
.pht-more button:hover{border-color:var(--pht-accent);color:var(--pht-fg)}
/* (The static map facade — .pht-map/.pht-map-grid/.pht-river/.pht-pin/.pht-map-note/
   .pht-map-tip — was superseded by the real MapLibre map and its rules deleted.) */
/* footer
   THE WIDGET IS AS WIDE AS THE SLOT IT IS PASTED INTO — a 1200px content column,
   a 280px sidebar, or a phone. This row used to assume the first: the CTA was
   `white-space:nowrap` inside a `.pht{overflow:hidden}` box, so on a phone
   "Explore Yosemite & the High Sierra on Photerra" ran past both edges and was
   cropped mid-word, and "Powered by Photerra" was squeezed into three lines
   beside it. Nothing here may clip: the row wraps, then stacks, and the label
   itself sheds its suffix and wraps before anything is cut off. */
.pht-foot{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px 12px;margin-top:13px;padding-top:12px;border-top:1px solid var(--pht-border)}
.pht-cta{font-family:var(--pht-sans);font-size:13.5px;font-weight:600;color:var(--pht-on-accent,#fff);background:var(--pht-accent-fill,var(--pht-accent));text-decoration:none;border-radius:999px;padding:9px 17px;transition:.15s;display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:0;max-width:100%;text-align:center;line-height:1.25}
.pht-cta:hover{filter:brightness(1.06)}
.pht-cta-l{min-width:0}
.pht-cta-a{flex:0 0 auto}
.pht-credit{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;color:var(--pht-fg-3);text-decoration:none;flex:0 0 auto;white-space:nowrap}
.pht-credit b{color:var(--pht-fg-2);font-weight:600}
.pht-attrib{font-size:10.5px;color:var(--pht-fg-3);margin-top:7px;line-height:1.4}
/* Provenance. The on-tile credit is the attribution that has to sit next to the
   photograph (ToS §4.1 / CC terms); the credits block below carries the linked
   source + license deed, because a tile is itself an <a> and anchors can't nest. */
.pht-credit-t{font-size:10px;font-weight:500;opacity:.82;margin-top:2px;text-shadow:0 1px 3px rgba(0,0,0,.55);letter-spacing:.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pht-hovercap .pht-credit-t{display:block;white-space:normal}
.pht-credits{margin-top:9px;font-size:10.5px;color:var(--pht-fg-3)}
.pht-credits summary{cursor:pointer;color:var(--pht-fg-2);font-weight:600}
.pht-credits summary:focus-visible{outline:2px solid var(--pht-accent);outline-offset:2px;border-radius:4px}
.pht-credits ul{margin:6px 0 0;padding-left:16px;line-height:1.5}
.pht-credits a{color:var(--pht-fg-2)}
.pht-empty{text-align:center;padding:26px 12px;color:var(--pht-fg-3)}
.pht-empty b{display:block;color:var(--pht-fg-2);font-family:var(--pht-serif);font-size:16px;margin-bottom:4px}
/* Quiet by design: the person reading a failed widget is usually the SITE
   OWNER testing their install, but a host's page must never turn into our
   support desk, so this is a footnote and not a banner. */
.pht-empty .pht-help{display:block;margin-top:9px;font-size:11.5px;opacity:.85}
.pht-empty .pht-help a{color:var(--pht-accent);text-decoration:underline}
/* INSIDE THE FRAME, THE VIEWPORT *IS* THE WIDGET. The iframe is 100% of the
   host's slot, so these widths are container queries in everything but name —
   they fire for a 300px sidebar rail on a 27" monitor exactly as they do on a
   phone, which is the behaviour a host expects and almost no embed delivers. */
@media (max-width:520px){
  .pht-title{font-size:20px} .pht-head{align-items:center}
}
@media (max-width:430px){
  /* Side by side, these two need ~350px of the ~308px a phone actually has.
     Stacked, the CTA gets the full width and the credit centres under it. */
  .pht-foot{flex-direction:column;align-items:stretch;gap:11px}
  .pht-credit{justify-content:center}
  .pht-cta-block .pht-cta{width:100%}
  .pht-cta-x{display:none}
  .pht{padding:14px 13px 11px}
}
@media (max-width:330px){
  /* A 300px sidebar or a small phone in a padded container. */
  .pht-cta{font-size:12.5px;padding:8px 12px}
  .pht-title{font-size:18px}
  .pht-rail-thumb{width:60px;height:46px}
}
@media (prefers-reduced-motion:reduce){ .pht-tile img,.pht-hovercap,.pht-pin-real{transition:none} }

/* ===== compact layout variants (strip / rail / badge) ===== */
.pht-head-slim{margin-bottom:10px}
/* strip — one horizontally-scrolling row */
.pht-strip{display:flex;gap:var(--pht-gap);overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:4px;-webkit-overflow-scrolling:touch;scrollbar-width:thin}
.pht-strip::-webkit-scrollbar{height:7px}
.pht-strip::-webkit-scrollbar-thumb{background:var(--pht-border);border-radius:99px}
.pht-strip-tile{scroll-snap-align:start;height:152px}
/* rail — narrow vertical list for sidebars */
.pht-rail-head{margin-bottom:10px}
.pht-rail{display:flex;flex-direction:column;gap:8px}
.pht-rail-item{display:flex;gap:11px;align-items:center;text-decoration:none;color:inherit;border-radius:10px;padding:5px;transition:.12s}
.pht-rail-item:hover{background:var(--pht-cream-2)}
.pht-rail-thumb{flex:0 0 auto;width:74px;height:56px;border-radius:8px;overflow:hidden;background:var(--pht-cream-2);position:relative}
.pht-rail-thumb img{width:100%;height:100%;object-fit:cover}
.pht-rail-txt{min-width:0;display:flex;flex-direction:column;gap:1px}
/* THE NAME ELIDES; THE CHECK NEVER MOVES.
   Two ways to get this wrong, and this rule has now had both. As a flex
   container, `text-overflow:ellipsis` did nothing — it only applies to the
   inline content of a BLOCK container — so a long name was hard-cut mid-word
   ("Cook's Meadow Loop Traill"). As a block with the check flowing inline, the
   ellipsis worked but the check was laid out past the clip and disappeared
   entirely (measured 8px outside the widget at 320px). The answer is both: a
   flex row whose TEXT is the shrinkable, ellipsising block and whose check is a
   fixed sibling that always has its place. */
.pht-rail-name{font-family:var(--pht-serif);font-weight:500;font-size:14px;line-height:1.2;color:var(--pht-fg);display:flex;align-items:center;gap:5px;min-width:0}
.pht-rail-t{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pht-rail-name .pht-verified{flex:0 0 auto;width:12px;height:12px}
.pht-rail-meta{font-size:11.5px;color:var(--pht-fg-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pht-cta-block{display:flex;justify-content:center;margin-top:12px;min-width:0}
.pht-credit-line{display:flex;align-items:center;gap:5px;justify-content:center;margin-top:9px;font-size:11px;color:var(--pht-fg-3);text-decoration:none}
.pht-credit-line b{color:var(--pht-fg-2);font-weight:600}
/* badge — one hero photo + count */
.pht-badge-card{position:relative;display:block;border-radius:var(--pht-tile-radius);overflow:hidden;height:132px;text-decoration:none;background:var(--pht-cream-2)}
/* DIRECT child only. `.pht-badge-card img` (descendant) also matched the
   .pht-logo <img> nested in the CTA — stretching the 64px green brand mark to
   position:absolute; inset:0; width/height:100%; object-fit:cover across the
   whole card (it read as green "bands" warping the photo). The badge is the one
   image-card that nests a logo inside its anchor, which is why only it broke. */
.pht-badge-card>img{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;object-fit:cover;transition:transform .5s cubic-bezier(.2,.7,.2,1)}
.pht-badge-card:hover>img{transform:scale(1.05)}
.pht-badge-txt{position:absolute;left:14px;right:14px;bottom:12px;color:#fff}
.pht-badge-n{font-family:var(--pht-serif);font-weight:500;font-size:18px;line-height:1.1;text-shadow:0 1px 4px rgba(0,0,0,.6)}
.pht-badge-cta{display:inline-flex;align-items:center;gap:5px;font-family:var(--pht-sans);font-size:12px;font-weight:600;margin-top:5px;text-shadow:0 1px 4px rgba(0,0,0,.6)}

/* ===== real map (MapLibre + OpenFreeMap, lazy-loaded on Map tab) ===== */
.pht-mapreal{position:relative;width:100%;height:400px;border-radius:var(--pht-tile-radius);overflow:hidden;border:1px solid var(--pht-border);background:var(--pht-cream-2)}
.pht-map-loading{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;color:var(--pht-fg-3);font-size:13px}
.pht-pin-real{width:16px;height:16px;border-radius:50%;background:var(--pht-gem);border:2.5px solid #fff;box-shadow:0 1px 5px rgba(0,0,0,.4);cursor:pointer;transition:transform .12s}
.pht-pin-real:hover{background:var(--pht-accent);transform:scale(1.25)}
.pht-pop{display:flex;gap:9px;align-items:center;text-decoration:none;color:var(--pht-ink);width:196px}
.pht-pop img{width:54px;height:54px;object-fit:cover;border-radius:8px;flex:0 0 auto}
.pht-pop b{font-family:var(--pht-serif);font-weight:500;font-size:13.5px;line-height:1.15;display:block;color:var(--pht-ink)}
.pht-pop i{font-style:normal;font-size:11px;color:var(--pht-ink-3)}
.maplibregl-popup-content{border-radius:10px;padding:8px;box-shadow:0 6px 20px rgba(0,0,0,.18)}
.maplibregl-ctrl-attrib{font-size:10px}

/* ===== brand mark + keyboard focus (a11y) ===== */
.pht-logo{width:16px;height:16px;display:block;flex:0 0 auto;object-fit:contain}
.pht-credit-line .pht-logo{width:14px;height:14px}
/* --pht-focus, not --pht-accent: a focus indicator has its own 3:1 floor
   (WCAG 2.2 SC 1.4.11), and a host is free to pick a near-black accent that
   disappears against a dark surface. widget.js lifts the ring to clear the bar
   while the accent itself keeps the host's exact colour. */
.pht a:focus-visible,.pht button:focus-visible{outline:2px solid var(--pht-focus,var(--pht-accent));outline-offset:2px;border-radius:5px}
.pht-tile:focus-visible{outline:3px solid var(--pht-focus,var(--pht-accent));outline-offset:3px}
.pht-rail-item:focus-visible,.pht-chip:focus-visible,.pht-toggle button:focus-visible,.pht-more button:focus-visible,.pht-cta:focus-visible,.pht-badge-card:focus-visible{outline:2px solid var(--pht-focus,var(--pht-accent));outline-offset:2px}
/* honor reduced-motion for the whole widget, not just tiles */
@media (prefers-reduced-motion:reduce){.pht *{transition:none!important;animation:none!important}}
/* Rail-layout credit. Attribution is not a grid-only obligation — the strip,
   rail and badge layouts render the same photographs. */
.pht-rail-cred{display:block;font-size:10px;color:var(--pht-fg-3);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Badge credit sibling — the card is an anchor, so an archive photo's linked
   source + licence go below it rather than nested inside. */
.pht-badge-fig{margin:0}
.pht-badge-fig .pht-credit-t{position:static;text-shadow:none;color:var(--pht-fg-3);white-space:normal;padding:4px 2px 0}
.pht-badge-fig .pht-credit-t a{color:var(--pht-fg-2);text-decoration:underline}
