/* ==========================================================================
   Shared components — the widgets that appear on more than one page
   ==========================================================================

   Why this file exists
   --------------------
   base.css holds the design *tokens*. This file holds the design *components*:
   the small number of widgets that are rendered on several pages and were, up
   to now, restyled from scratch in each of them.

   That duplication was already drifting, measurably, before anything here was
   written:

     - .page-tabs was inline-styled in FIVE files (index, jummah, find,
       timetable, directory). Four of them said margin-bottom:20px and index
       said 16px, so the strip sat four pixels higher on the front page than
       everywhere else.
     - The .af-* filter panel existed twice, in find.php and nearby.php, with
       ELEVEN of the twenty-two shared rules carrying different values. Most
       were the same measurement written two ways — 0.8125rem in one file and
       13px in the other, 0.9rem against 14px — which is drift waiting to
       happen rather than drift that had happened yet. Three were real: the
       panel's margin-bottom (16 vs 20), the empty state's padding (28 vs 36)
       and the select's font size.

   None of that was anyone's mistake. It is what happens when the same widget
   is written out per page: a fix lands in the file being worked on and the
   other four are never opened. Extracting them is what stops the page-level
   batches re-drifting the moment they run in parallel.

   Rules
   -----
   1. A component only belongs here once it is used on TWO OR MORE pages. A
      widget on one page stays in that page. .af-locate, .af-nearrow and
      .af-note are deliberately still in find.php for exactly that reason.
   2. Every value comes from a base.css token. If a component needs a value
      that has no token, add the token there — not a literal here.
   3. No page-specific positioning. Components own how they look, pages own
      where they sit, which is why nothing here sets margins other than the
      one gap that was already shared.

   LOAD ORDER MATTERS. This file is linked immediately after base.css and
   BEFORE the page's own <style>, which the shared header emits last through
   $headExtra. So a page can still override any of this — but if a page has a
   rule that merely repeats what is here, that rule is drift and should be
   deleted, not left to win.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Page tabs — the segmented strip above the title on the prayer-time pages
   and, with a different set of destinations, on Directory.

   Was: a white strip with a #cbd5e1 border, an 8px outer radius, 6px inner
   radii and the active tab filled with --brand-accent. The structure is
   unchanged and deliberately so — this is an extraction, not a redesign of
   the control. What changed is that the colours are now roles and the corners
   are on the two-value radius scale, so it stops reading as a rounded box
   sitting on a page of square-cornered ones.

   The active tab keeps its filled treatment rather than taking the nav's
   underline. A nav marks one item out of many destinations; this is a
   segmented control, where the fill is what says the other segments are
   switchable. They are different jobs and should not look the same.
   -------------------------------------------------------------------------- */
.page-tabs {
    display: flex;
    justify-content: center;
    /* No gap and no padding: the segments meet each other and the outer border,
       and the 1px divider below is what separates them. With a gap the tabs
       floated as loose words inside one rounded outline and the strip read as a
       single box you could not tell three destinations apart in. */
    gap: 0;
    padding: 0;
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--r-md);
    /* Clips a filled segment to the rounded corners, so the current tab's fill
       cannot square off the end of the strip. */
    overflow: hidden;
    /* Resolves the 16-vs-20 split in favour of the scale. */
    margin-bottom: var(--s4);
}

.page-tabs a {
    flex: 1;
    text-align: center;
    padding: 9px var(--s2);
    /* --t-xs, not --t-sm, and this is deliberate. At 14px the four labels stop
       fitting across a 360px phone, and because of the nowrap below they would
       overflow their boxes rather than wrap. 13px is what these tabs have
       always been and what they fit at. */
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-mid);
    text-decoration: none;
    /* Square: the segment is a cell in a strip, not a pill floating in one. */
    border-radius: 0;
    /* Load-bearing. Without it "Find a Prayer" and "Full Timetable" wrap onto
       two lines on a phone, which is what the duplicate strip deleted from
       index.php on 2 Aug used to get wrong. */
    white-space: nowrap;
}

/* The focus ring, drawn INSIDE the segment.

   The shared ring in base.css is an outline at outline-offset: 2px, i.e. two
   pixels outside the element's own box. The strip clips to its rounded corners
   (overflow: hidden above), so on the first and last segment the ring was
   drawn straight into that clip and the outer half of it simply vanished — the
   keyboard user tabbing along the strip lost the indicator at exactly the two
   ends they are most likely to start from. The top and bottom edges were
   clipped on every segment for the same reason, the tabs having no room around
   them inside the strip.

   A negative offset pulls the ring two pixels inside the segment instead,
   where nothing can clip it, and it still reads as a ring because the segment
   is the full height of the strip. The clip stays: it is what stops the
   current tab's fill squaring off the rounded end of the strip. */
.page-tabs a:focus-visible {
    outline-offset: -2px;
}

/* The divider. This single rule is what makes the strip read as separate
   destinations rather than one box — on Directory especially, where the labels
   ("Maktabs & Madāris", "Funeral Services") are long enough to run together. */
.page-tabs a + a {
    border-left: 1px solid var(--rule);
}

.page-tabs a:hover {
    background: var(--surface);
    color: var(--ink-brand);
}

/* .active is what index/jummah/find/timetable write; directory writes
   aria-current="page". Both are matched so neither file has to change the way
   it marks the current tab.

   The text was a literal #ffffff, and in dark mode that made the current tab
   the ONE unreadable segment on the strip — the exact thing the note here used
   to warn about, arriving from the other direction. --ink-brand resolves to
   --brand-highlight on a dark page, and Musally's Highlight is #f6f8fc: a
   near-white fill with near-white writing on it. That is the white box the
   current tab turned into on a dark page.

   So the text follows --paper, which is white on a light page and the page
   ink on a dark one, and is therefore the reverse of the fill in both. The
   literal fallbacks stay for the reason they were added: the shared header
   emits `a { color: inherit }` in a <style> AFTER this file, so if the tokens
   ever failed to resolve this state still has to be legible on its own. */
.page-tabs a.active,
.page-tabs a[aria-current="page"] {
    background: var(--ink-brand, #1e3a5f);
    color: var(--paper, #ffffff);
    font-weight: 700;
}

/* --------------------------------------------------------------------------
   Leaflet chrome — the popup, its tip, the zoom buttons and the attribution.

   Used on three pages (masaajid-map.php, mosques.php, mosque.php), which is
   what earns it a place here rather than in any one of them.

   THE BUG THIS FIXES (Aug 2026): text on the map rendered grey and was hard to
   read. Leaflet ships its own stylesheet and paints the popup card,
   its tip and the controls a fixed white with near-black text. Our popup
   CONTENT is built from tokens — .mm-pop-name takes --ink, .mm-pop-meta takes
   --ink-mid. In dark mode those go near-white and land on Leaflet's white card:
   --ink-mid on white is about 2:1, so the region and the address all but
   disappeared. The giveaway is the "Listed for reference"
   tag rendering as a DARK navy pill — that is --surface, correctly dark, sitting
   on a card that never got the message.

   So the shell has to follow the palette as well as the contents. The map TILES
   stay light, deliberately: they are OpenStreetMap's raster imagery, not ours to
   recolour, and a dark card floating over a light map is a normal and legible
   arrangement rather than a mistake.

   SPECIFICITY IS LOAD-BEARING. Every one of these selectors carries an extra
   class over Leaflet's own. leaflet.css is linked by each page AFTER this file,
   so at equal specificity Leaflet would win on document order. .leaflet-popup
   and .leaflet-container are real ancestors in Leaflet's markup, so this is a
   genuine descendant selector, not padding to inflate a score.
   -------------------------------------------------------------------------- */
/* Keep the map's stacking to itself.
 *
 * Leaflet numbers its own layers from 200 (tiles) up to 1000 (the control
 * corners), and the container it sets those on does not create a stacking
 * context. So those numbers do not stay inside the map, they compete with the
 * rest of the page. The nav is sticky at z-index 100, which is above everything
 * a page normally puts on screen and far below 400, so on any page carrying a
 * map the Prayer Times and Maps dropdown menus opened UNDERNEATH it.
 *
 * Isolating the container fixes it at the source. Leaflet's internal order is
 * untouched, because every one of those layers is inside the container and they
 * still sort against each other exactly as before; the whole map simply now
 * occupies one level in the page instead of nine. The alternative, pushing the
 * nav above 1000, would have to be re-won against every future overlay.
 *
 * Applies to every map on the site rather than the one where it was noticed. */
.leaflet-container {
    isolation: isolate;
}

.leaflet-popup .leaflet-popup-content-wrapper,
.leaflet-popup .leaflet-popup-tip {
    background: var(--paper);
    color: var(--ink);
    box-shadow: var(--overlay-shadow);
}

/* Leaflet sets this to a near-black with a hover of pure black, both of which
   vanish on a dark card. */
.leaflet-popup .leaflet-popup-close-button {
    color: var(--ink-mid);
}
.leaflet-popup .leaflet-popup-close-button:hover {
    color: var(--ink);
}

/* Zoom in / zoom out. White tiles with a black glyph and a #ccc divider. */
.leaflet-container .leaflet-bar a,
.leaflet-container .leaflet-bar a:hover {
    background-color: var(--paper);
    color: var(--ink);
    border-bottom-color: var(--rule);
}
.leaflet-container .leaflet-bar a:hover {
    background-color: var(--surface);
}
/* The disabled state at max/min zoom — Leaflet greys it to #bbb on white, which
   on a dark card reads as the ENABLED state. */
.leaflet-container .leaflet-bar a.leaflet-disabled {
    background-color: var(--surface);
    color: var(--ink-mid);
}

/* "© OpenStreetMap contributors". Leaflet gives it a translucent white plate. */
.leaflet-container .leaflet-control-attribution {
    background: var(--paper);
    color: var(--ink-mid);
}
.leaflet-container .leaflet-control-attribution a {
    color: var(--ink-brand);
}

/* --------------------------------------------------------------------------
   Filter panel (.af-*) — the "advanced filters" block on Find a Prayer and
   Nearby.

   Every value below was already in both files; only the drift is resolved.
   The three real differences went to the scale: the panel's margin-bottom to
   --s4 (16px, find's value), the empty state's padding to --s6/--s5 (32/24px,
   between find's 28 and nearby's 36), and the two font sizes to the nearest
   token, which they were both already within half a pixel of.
   -------------------------------------------------------------------------- */
/* The filter block, on all four prayer tabs.

   Two of them boxed their filters and two did not, so the same dropdown at the
   same colour looked like a different control depending on which tab you were
   on — the fill and border were identical, the panel behind them was not.

   --surface, not --paper: --paper IS the page colour, so the panel was an
   invisible box drawn in a 1.28:1 hairline. Same fault the cards had. */
.af-panel,
.tt-form,
.pt-filters,
.card-filters {
    background: var(--surface);
    border: 1px solid var(--rule);
    /* A large, quiet, full-width panel — the one thing --r-lg exists for. */
    border-radius: var(--r-lg);
    padding: 14px var(--s4);
    margin-bottom: var(--s4);
}

.af-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.af-head .af-title {
    font-size: var(--t-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-mid);
}
.af-head .af-active {
    font-size: var(--t-xs);
    font-weight: 700;
    color: var(--ink-brand);
}
.af-head .af-clear {
    margin-left: auto;
    background: none;
    border: none;
    padding: 2px var(--s1);
    font: inherit;
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-brand);
    cursor: pointer;
}
.af-head .af-clear[hidden] { display: none; }

.af-search {
    width: 100%;
    padding: 9px var(--s3);
    border: 1px solid var(--field-border);
    border-radius: var(--r-sm);
    font: inherit;
    font-size: var(--t-sm);
    background: var(--field);
    color: var(--ink);
    margin-bottom: var(--s2);
}

.af-fields {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
}
.af-field {
    flex: 1 1 150px;
    min-width: 0;
}
.af-field > label {
    display: block;
    font-size: var(--t-xs);
    font-weight: 600;
    /* Was --ink-mid, the same grey as help text and captions. A field label is
       the name of a control, not an aside — and now that the controls carry a
       fill of their own, a grey label above a filled box read as disabled.
       --ink-brand follows the portal's own colour and flips in dark mode. */
    color: var(--ink-brand);
    margin-bottom: 3px;
}
.af-field select {
    width: 100%;
    padding: var(--s2) 10px;
    border-radius: var(--r-sm);
    border: 1px solid var(--field-border);
    font: inherit;
    font-size: var(--t-xs);
    background: var(--field);
    color: var(--ink);
}
/* No local :disabled rule.

   This one set background: var(--surface) — and --surface is now the fill of
   the filter panel these selects sit in, so Postcode ("Pick a city first")
   and Near me ("Location off") rendered at exactly 1.00:1 against their own
   panel and disappeared. base.css fades the field itself instead, which stays
   correct whatever the panel behind it is. */

/* Facility chips. These stay fully round: a chip that toggles is a different
   object from a tab that navigates, and the pill shape is what says so. */
.af-facilities {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
    align-items: center;
}
.af-facilities .af-fac-label {
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-mid);
    margin-right: 2px;
}
.af-fac {
    padding: 6px var(--s3);
    /* The field tokens, like every other control. --paper is the PAGE colour,
       so on the filter panel these chips were the one thing still sitting on
       the background. */
    border: 1px solid var(--field-border);
    border-radius: 999px;
    background: var(--field);
    font: inherit;
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-mid);
    cursor: pointer;
}
.af-fac:hover {
    background: var(--field-hover);
    border-color: var(--ink-brand);
    color: var(--ink-brand);
}
.af-fac.is-on {
    background: var(--ink-brand);
    border-color: var(--ink-brand);
    color: var(--paper);
}

/* Sisters' facilities is pink wherever it appears — the directory badge, the
   Jumu'ah card pill, and this chip. It is the one facility with a colour of its
   own, so a filter switched on for it should say so in that colour rather than
   in the same brand ink as Wudu Facilities or Warm Space. */
.af-fac[data-af-facility="sisters_area"].is-on {
    background: var(--sisters-ink);
    border-color: var(--sisters-ink);
    color: var(--sisters-bg);
}
.af-fac[data-af-facility="sisters_area"]:hover {
    border-color: var(--sisters-ink);
    color: var(--sisters-ink);
}

.af-count {
    text-align: right;
    font-size: var(--t-xs);
    color: var(--ink-mid);
    margin-top: var(--s2);
}

.af-empty {
    text-align: center;
    padding: var(--s6) var(--s5);
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--r-lg);
}
.af-empty h3 {
    font-size: var(--t-base);
    font-weight: 600;
    margin: 0 0 6px;
}
.af-empty p {
    font-size: var(--t-sm);
    color: var(--ink-mid);
    margin: 0;
}
.af-empty[hidden] { display: none; }

/* ---------------------------------------------------------------------------
   Limited access strip
   ---------------------------------------------------------------------------
   A venue whose entry is restricted — a hospital prayer room, a university
   musalla, a workplace room behind a security desk. The flag has been on the
   masjid record for a while but was never shown to the public, so somebody
   could read a Jumu'ah time off the site, drive to a hospital and find they
   could not get in.

   It sits across the TOP of the card, not among the chips lower down, because
   it changes whether the rest of the card is any use to the reader.

   --warn / --warn-bg both have dark-mode values in base.css, so this needs no
   theme block of its own.
   --------------------------------------------------------------------------- */
.limited-access-strip {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px var(--s3);
    background: var(--warn-bg);
    color: var(--warn);
    font-size: var(--t-xs);
    font-weight: 600;
    line-height: 1.35;
    border-bottom: 1px solid var(--rule);
    border-radius: var(--r-md) var(--r-md) 0 0;
}
.limited-access-strip i { font-size: 0.9em; }

/* Same strip, crimson — for a warning about the PRAYER rather than about
   getting in. Today that is Jumu'ah held before the usual Zuhr time, which the
   apps mark with a red dot and an explainer; the website had no equivalent at
   all (client, 3 Sep 2026). Amber would have put it on a par with "staff only"
   entry, and this changes whether someone's Jumu'ah counts, so it takes the
   stronger of the two state colours.

   --stop / --stop-bg carry their own dark-mode values in base.css, so this
   needs no theme block, and inheriting .limited-access-strip means the
   card-flush margin rules below already apply. */
/* "Listed via Dewsbury" - a masjid mirrored from a partner city's portal.

   Deliberately quiet: it is a provenance note, not a status warning, and it
   sits on a card that may already carry a restricted-entry strip. Neutral ink
   on the standard quiet fill keeps it clearly secondary to the masjid name and
   the times, which are what the card is for. Mirrors the apps' badge, which
   uses the same "Listed via <city>" wording and a link glyph. */
.upcoming-prayer-card .up-partner {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-top: 6px;
    padding: 2px 8px;
    border: 1px solid var(--rule);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink-mid);
    font-size: var(--t-xs);
    font-weight: 600;
    line-height: 1.4;
}
.upcoming-prayer-card .up-partner i { font-size: 0.85em; opacity: 0.85; }

/* Per-partner colour. The class is built from the stored source key, so a
   partner with no rule here keeps the neutral chip above rather than losing its
   styling — which is what makes adding a fifth partner a CSS-only change, and
   why the PHP does not need to know which cities have colours.

   All three properties are set together, matching how .is-sisters does it: a
   tint fill, its own ink, and a border of that ink. Setting only the text
   colour left the border reading as a fifth, greyer category. */
.upcoming-prayer-card .up-partner.pv-gloucester {
    background: var(--pv-gloucester-bg); color: var(--pv-gloucester); border-color: var(--pv-gloucester);
}
.upcoming-prayer-card .up-partner.pv-leicester {
    background: var(--pv-leicester-bg);  color: var(--pv-leicester);  border-color: var(--pv-leicester);
}
.upcoming-prayer-card .up-partner.pv-batley {
    background: var(--pv-batley-bg);     color: var(--pv-batley);     border-color: var(--pv-batley);
}
.upcoming-prayer-card .up-partner.pv-dewsbury {
    background: var(--pv-dewsbury-bg);   color: var(--pv-dewsbury);   border-color: var(--pv-dewsbury);
}

.limited-access-strip.is-stop {
    background: var(--stop-bg);
    color: var(--stop);
}

/* Cards pad their own contents, so a strip that is a child of the card has to
   break back out to the card's edges to sit flush across the top. */
.upcoming-prayer-card > .limited-access-strip,
.dir-card > .limited-access-strip {
    margin: calc(var(--s3) * -1) calc(var(--s3) * -1) var(--s3);
}

/* Standalone use — the masjid page header and the timetable page, where the
   strip is a block in the flow rather than the lid of a card.
 *
 * Shrink-wrapped and centred rather than run edge to edge. As a full-width bar
 * it read as a page-level alert, which is far more weight than "staff only"
 * needs and left a band of amber across the top of every hospital timetable.
 * A pill sized to its own words says the same thing and
 * looks like the aside it is. width:fit-content plus auto side margins so it
 * centres wherever it is dropped, without depending on the parent's
 * text-align. */
.limited-access-block {
    width: fit-content;
    max-width: 100%;
    margin: 0 auto var(--s3);
    padding: 5px 14px;
    border: 1px solid var(--rule);
    border-radius: 999px;
}

/* ---------------------------------------------------------------------------
   Filter blocks: the label above a control, and the Clear button.

   These were per-page, and had drifted: Find a Prayer and Full Timetable drew
   the same uppercase label in two different greys, the Jumu'ah ordinal label a
   third, and only the Jumu'ah tab had a Clear button at all. All four prayer
   tabs use the same two rules now, so a control on one page cannot look like a
   different kind of thing on another.
   ------------------------------------------------------------------------- */
.pt-label,
.af-field > label,
.find-form label,
.tt-form label,
.pt-ordinal-label {
    display: block;
    font-size: var(--t-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    /* The name of a control, not an aside. Grey-on-grey read as disabled once
       the controls themselves gained a fill. */
    color: var(--ink-brand);
    margin-bottom: 4px;
}
/* The Jumu'ah ordinal row sits inline beside its chips, not above a field. */
.pt-ordinal-label { display: inline-block; margin-bottom: 0; }

.pt-clear,
.filter-clear {
    /* index.php carries a bare `select, input[type=date], button { width: 100% }`,
       so a class rule that does not state a width lets a pill button stretch the
       full 1470px of the filter block. Stated here rather than fixed there: that
       element rule is load-bearing for the pickers on that page. align-self stops
       the same thing happening inside a flex parent. */
    width: auto;
    align-self: flex-start;
    background: var(--field);
    border: 1px solid var(--field-border);
    color: var(--ink-brand);
    font-family: inherit;
    font-weight: 600;
    font-size: var(--t-xs);
    padding: 5px 14px;
    border-radius: 999px;
    cursor: pointer;
    margin-top: 8px;
}
.pt-clear:hover,
.filter-clear:hover {
    background: var(--field-hover);
    border-color: var(--brand-accent);
    color: var(--brand-accent);
}
.filter-clear[hidden] { display: none; }

/* The mark beside the page title, on all four prayer tabs.

   Upcoming used to be the only one carrying the logo, and carried it as an
   84px block on its own line above the heading — so the four tabs opened
   differently from each other and the front door spent most of a phone screen
   before showing a prayer time. Inline at 34px it reads as the same page
   furniture on every tab and costs no vertical space of its own. */
.pt-pagehead {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    margin: 0 0 2px 0;
}
.pt-pagehead > img {
    height: 34px;
    width: auto;
    max-width: 34px;
    object-fit: contain;
    flex: 0 0 auto;
    /* Without this the mark is PRESENT on the other three tabs and invisible:
       a navy-on-transparent wordmark on a #0d1826 page. --logo-filter is
       `none` in light and flattens the mark to white in dark, which is why a
       site only supplies one logo. index.php carried this rule locally, which
       is exactly why Upcoming looked like the only tab with a logo. */
    filter: var(--logo-filter);
}
.pt-pagehead > h1 { margin: 0; }

/* The sentence under a page heading. Was declared three times, once per
   prayer tab, each scoped to that page's own wrapper class and each identical
   to the other two. */
.ptlede {
    text-align: center;
    color: var(--ink-mid, #5a6478);
    font-size: var(--t-sm);
    margin: 0 0 16px;
}

/* Sisters' facilities, wherever it is named.

   The directory badge, the Jumu'ah card pill, the Find a Prayer card tag and
   the facility filter chip are four different class names on four pages for
   one fact, and they had four different colours. Collected here so the next
   surface that labels it inherits rather than picks its own. */
/* .find-card .find-tag is two classes, so a single .is-sisters class loses to
   it on specificity and only the border came through. Matched at the same
   depth here rather than reaching for !important. */
.find-card .find-tag.is-sisters,
.dir-badge.is-sisters,
.upcoming-prayer-card .women-ok,
.cf-tag.is-sisters {
    background: var(--sisters-bg);
    color: var(--sisters-ink);
    border-color: var(--sisters-ink);
}
/* The icon inside it too — it is accent-coloured by default, which on a pink
   fill is the one combination that reads as a mistake. */
.find-card .find-tag.is-sisters i { color: var(--sisters-ink); }
