/* ──────────────────────────────────────────────────────────────────────────
   Typography tokens — LA-205 / LA-213.

   Loaded from app/layout.tsx on every page, before any page stylesheet, so
   every surface can reach these. Values here and in lib/type.ts MUST match:
   the content scripts and stylesheets read the CSS variables, while React
   writes inline `fontSize:` numbers that can't read a variable at all. That is
   why the scale exists twice. Change one, change the other.

   ── The size scale ────────────────────────────────────────────────────────
   Derived from what the app actually uses rather than imposed on it. Measured
   across every live surface (1850 declarations, 39 distinct sizes):

       12px  31%      18px   3%
       13px  18%      20px   2%
       14px  11%      22px   3%
       15px   5%      24px   1%
       16px   5%      28px   1%

   So 12–16px carries ~71% of the app and the rest is a thin tail. A tighter
   five-step scale would have read better on paper and moved roughly a quarter
   of the app's text by a pixel, which is a redesign wearing a refactor's
   clothes. These steps change nothing visually and still give one place to
   adjust, a floor that can't be undercut, and a name to reach for instead of
   a new arbitrary number.

   --fs-small (13px, 338 uses) and --fs-body-lg (15px, 98 uses) are the two
   worth retiring in a later pass if the scale should be tighter. They are here
   because dropping them today would be a visual change nobody asked for.

   Nothing below --fs-caption. 9/10/11px accounted for 230 declarations and is
   the readability problem this ticket was opened for: unreadable in direct
   sun, which is where this app is used.

   ── The floor, and the places it does not apply ───────────────────────────
   LA-295 fixed those 230 and `scripts/check-fontsize.mjs` now keeps them
   fixed: it fails the build, and the pre-push hook, on any font size under
   12px that does not carry an `fs-ok: <reason>` marker at the site. That gate
   exists because the sweep had no way to stop a new one landing, and one
   landed immediately -- LA-263's relevance chip was written at 11px, copied
   from its neighbour, into a file that had just been migrated.

   28 places are marked, and they fall into four kinds. All four are cases
   where 12px would be wrong, not cases nobody got to:

     * Map attribution (here and its copy in ConditionsClient). Leaflet's own
       credit line, which CARTO requires visible on every map. It is a credit,
       not app type, and it has to read as one thin line over the map.
     * Icon glyphs. A character sized to the circle it sits in -- the 16px
       remove-tag button in ImageTextEditor. 12px overflows the circle.
     * The bottom-nav labels, 10px. Documented in bottom-nav.css: 12px clipped
       PROFILE off the screen, the icons carry the recognition, and ~10px is
       what native iOS tab bars use.
     * Camera diagnostics behind the showDiag toggle, and /admin. Neither is
       read on a beach, which is the whole basis of the floor. The 22 in
       /admin are tracked on LA-376 and will be revisited with the rest of the
       admin UI; they are marked rather than silently skipped so that the
       count is honest.

   The marker is checked, not trusted: a reason under 10 characters fails, and
   so does a marker left behind on a line whose small size has since been
   fixed -- otherwise the next small value written there slips through.

   Above --fs-display the sizes are one-off display numbers — hero counts, the
   96px 🤙 burst. Those are illustration, not type, and are left alone.
   ────────────────────────────────────────────────────────────────────────── */

/* ──────────────────────────────────────────────────────────────────────────
   The reset — LA-289.

   This lived in five page stylesheets and, invisibly, in an inline <style>
   inside app/feed/page.tsx. Two of the five — alerts-styles.css and
   feed-styles.css — were loaded by nothing, so CONDITIONS ran on the browser
   default, content-box, while every other page ran on border-box. (LA-288 has
   since deleted both files; they are named here as history, not as places to
   go looking.)

   The same React component therefore rendered at two sizes: the LA-285 bell
   measured 34x34 on Discover and 36x36 on Conditions, its badge 16x16 against
   28x20. 34 + 1px border + 1px border = 36. Invisible until something shared
   crossed between pages, which is exactly what LA-285 did.

   MEASURED BEFORE MOVING IT, on live Conditions (2,460 elements):

     * { margin: 0; padding: 0 }      0 elements changed
     * { box-sizing: border-box }   511 elements changed  (20.8%)

   The margin/padding half is a no-op: `*` has zero specificity, so every
   author rule already wins, and nothing on that page leaned on a user-agent
   default. All of the movement is box-sizing, and all of it is benign —
   elements shrink to the width they already declared. The forecast day cards
   lose 21px each, which fits one more day on screen, and content below shifts
   down about 13px. Nothing clips, overflows or misaligns; checked against
   before/after screenshots at two scroll depths.

   Loaded from app/layout.tsx on every page, ahead of the page stylesheets.
   `*` has zero specificity so this cannot override anything any page says.
   ────────────────────────────────────────────────────────────────────────── */
* { margin: 0; padding: 0; box-sizing: border-box; }

/* ── The body font — LA-378 ───────────────────────────────────────────────────
   Same argument as the reset above, and the other half of the same oversight.

   LA-289 lifted margin/padding/box-sizing out of the five page stylesheets and
   into this file, because a shared component must not inherit its basics from
   whichever page it lands on. It left `body { background, color, font-family }`
   behind in those stylesheets -- and Feed and Alerts load no page stylesheet at
   all, so on those two pages `body` kept the browser default: Times.

   Invisible almost everywhere, because each page's root <div> sets the family
   on itself. It shows on anything rendered OUTSIDE that div, which is every
   fixed-position overlay: the Feed search sheet reported by Chepi (the typed
   query in DM Sans because the <input> sets its own, and the results, the
   @handle and "Cancel" all in Times around it), and the inbox overlay.

   `button, input, select, textarea { font-family: inherit }` below makes this
   worse rather than better on those pages -- it hands controls a serif instead
   of the UA's Arial. That rule is right; what it inherits FROM was wrong.

   MEASURED BEFORE AND AFTER, 6,561 elements across all five pages: 8 were
   rendering in Times, all on /feed and /alerts, and nothing else moved. The
   three page stylesheets that already declare this declare the same value and
   still win on source order, so those pages are untouched by construction.

   background and color sit in exactly the same position and are deliberately
   NOT moved here. Nothing looks broken today because the page root divs set
   them, and moving them is a wider change than the bug being fixed. Noted on
   LA-378 rather than folded in quietly.
   ────────────────────────────────────────────────────────────────────────── */
body { font-family: var(--font-body); }

:root {
  /* Families by ROLE, not by name. LA-213 moves these to next/font, which
     generates a hashed family name — pointing the variable at the hash then
     costs one line here instead of ~417 string edits across 46 files. */
  /* LA-213: these now point at the self-hosted faces next/font generates in
     app/layout.tsx. The --font-bebas/dm-sans/dm-mono/playfair names are chosen
     there rather than hashed, which is what lets a static stylesheet reach
     them. The literal names stay as the fallback: if the build ever stops
     emitting one, text renders in the right typeface from wherever it can
     rather than collapsing to Times New Roman. */
  --font-display: var(--font-bebas), 'Bebas Neue', sans-serif;
  --font-body: var(--font-dm-sans), 'DM Sans', sans-serif;
  --font-mono: var(--font-dm-mono), 'DM Mono', ui-monospace, monospace;
  /* LA-294: --font-serif is GONE, and so is the italic Playfair behind it.
     LA-252 documented two Playfair faces sharing one CSS family name; LA-288
     deleted the italic one's only consumer (`.post-caption em` in
     feed-styles.css, styling a class nothing renders). A preloaded webfont for
     a token with no users is not a style decision worth preserving.

     --font-playfair-700 is UNRELATED and still live: the upright 700 face
     behind ImageTextEditor's "Serif" option. It is declared by next/font in
     app/layout.tsx and used directly there, not through a token here. If app
     CSS ever wants a serif again, add the italic import back deliberately
     rather than reaching for --font-playfair-700 — that one belongs to the
     editor, and nothing here should depend on an option a user can change. */

  /* Text */
  --fs-caption: 12px;   /* floor — timestamps, meta, pill labels */
  --fs-small: 13px;     /* secondary body */
  --fs-body: 14px;      /* default */
  --fs-body-lg: 15px;   /* emphasised body */
  --fs-lg: 16px;        /* prominent body */

  /* Never set a focusable text field below this. iOS zooms the whole page when
     you tap an input under 16px, and the viewport meta does not save us —
     LA-79 was exactly that bug, observed on device with the current
     maximum-scale=1 in place. Same value as --fs-lg, named separately so the
     reason survives. */
  --fs-input: 16px;

  /* Headings */
  --fs-subhead: 18px;
  --fs-title: 20px;
  --fs-headline: 24px;
  --fs-display: 28px;
}

/* ── Form controls inherit the app's font ─────────────────────────────────────
   <button>, <input>, <select> and <textarea> do NOT inherit font-family: the
   user agent gives them its own default, which is Arial in Chrome. Measured on
   the feed, 67 elements were falling back that way — the like and comment
   counts, and the carousel chevron — set in Arial while everything around them
   was DM Sans.

   font-family only, NOT the `font` shorthand. The shorthand would also reset
   size and weight to the inherited values, silently resizing every button that
   relies on the UA default. This changes the family and nothing else. */
button, input, select, textarea { font-family: inherit; }

/* ── The unread count badge ───────────────────────────────────────────────────
   LA-290. One definition for the little red count that sits on an icon button.
   There were EIGHT and they disagreed — feed's bell was a fixed 16x16 square
   (a true circle); the other seven used min-width plus horizontal padding,
   which grows the box sideways, and border-radius:50% on a wider-than-tall box
   is an ellipse. Profile and market also sat at -4px against feed's -2px.

   These are the ORIGINAL feed-bell numbers, restored. A first attempt moved to
   18px/font-size 11 so that "9+" could not clip; Chepi preferred the original
   proportions and they are what ships.

   KNOWN AND ACCEPTED: a 16px circle with a 2px border has a 12px content box,
   and "9+" measures 14.56px in DM Sans 700 at font-size 12. So a count above
   nine overflows the circle slightly. That was already true of the bell before
   this ticket; it is a cosmetic edge case on a badge that only appears past
   nine unread, and it is not worth losing the proportions over. Do not "fix"
   it by adding min-width and padding — that is what made the other seven
   ellipses.

   display is set here, so a content script that hides one with
   style.display='none' still wins: inline beats a stylesheet rule. */
.maruyo-badge {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #ef476f;
  color: #fff;
  border: 2px solid #000;
  box-sizing: border-box;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  pointer-events: none;
}

/* LA-307: @keyframes spin, global.
 *
 * It was defined inside five separate pages (feed, discover, alerts, market,
 * and market-content.js) and nowhere else, so any component using it animated
 * only when it happened to be mounted on one of those pages. Extracting
 * StoryViewer out of the feed proved it: its loading skeleton asks for
 * `animation: spin 0.9s linear infinite`, and on its own route that resolved to
 * nothing and sat frozen.
 *
 * Same reasoning as LA-289 moving the box-sizing reset here: a shared component
 * must not inherit its animations from whichever page it lands on.
 *
 * The five in-page copies are now redundant. Left in place for now — a
 * duplicate @keyframes is harmless, and removing them is its own sweep.
 */
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* LA-297: the Leaflet attribution credit, global.
 *
 * CARTO requires it visible on every map, so now that all five maps are on
 * CARTO the styling has to be global too. Leaflet's default is a white box
 * with blue links, which on a dark app is a bright rectangle, and on
 * Market -- where the map runs to the screen edge -- the line sat flush
 * against that edge with "CARTO" grazing it, which is the one word that must
 * stay legible.
 *
 * First attempt capped it at 50% and let it wrap. That fixed the edge and
 * broke the look: three stacked lines in a grey slab over the map, much
 * louder than the credit it replaced. Chepi was right to call it. The credit
 * should read as one thin line; it just needs to sit in from the edge, which
 * is the .leaflet-bottom.leaflet-right rule below.
 *
 * LA-208 wrote this rule inline in ConditionsClient for the Conditions map
 * only. That copy is now redundant; left in place like the five @keyframes
 * spin copies above, since a duplicate rule is harmless and removing it is
 * its own sweep.
 *
 * The selector is deliberately long; the note above the rule says why.
 */
/* The extra .leaflet-control is not redundant. leaflet.css ships
   `.leaflet-container .leaflet-control-attribution { background: rgba(255,255,255,0.8) }`
   -- the SAME specificity as the selector this rule used to have (0,2,0), and
   Leaflet injects its stylesheet at runtime, after tokens.css. So the tie went
   to Leaflet on source order and the box stayed white while the colour from
   here applied: pale grey text on a pale box, which is how Chepi ended up
   unable to read the credit. The element carries both classes, so adding
   .leaflet-control makes this 0,3,0 and order stops mattering. */
.leaflet-container .leaflet-control-attribution.leaflet-control {
  background: rgba(0, 0, 0, 0.72);
  color: #d4e4ea;
  font-size: 10px; /* fs-ok: CARTO map attribution -- Leaflet's own credit line, not app type, and it has to stay one thin line over the map */
  line-height: 1.5;
  /* One line, always. Leaflet's own default, kept deliberately -- see above. */
  white-space: nowrap;
  padding: 2px 6px;
  border-radius: 4px;
  box-shadow: none;
}
.leaflet-container .leaflet-control-attribution.leaflet-control a { color: #eaf4f8; text-decoration: none; }

/* LA-297: kill the white flash.
 *
 * leaflet.css sets `.leaflet-container { background: #ddd }`, which is what
 * shows wherever a tile has not arrived yet -- on first paint, and in the
 * moment after a zoom-out before the wider tiles land. On a light basemap
 * nobody notices; against a dark basemap it is a grey-white flash across the
 * whole map. Setting it to a colour the app already uses means the gap reads
 * as the map still loading rather than as a glitch.
 *
 * #121212, not #061529. The first attempt used the conditions navy and it
 * read as a blue panel next to CARTO dark_all's near-black land -- Chepi
 * spotted it immediately. #121212 is the dark grey the content scripts
 * already use for surfaces, it sits between the app's #000 body and the tile
 * colour, and the seam stops being visible.
 *
 * Doubled class for the same reason as above: Leaflet's own rule is 0,1,0 and
 * lands after this file, so a single class would tie and lose. */
.leaflet-container.leaflet-container { background: #121212; }

/* The actual fix: inset the whole bottom-right control stack from the map's
   edge instead of constraining the text. The zoom buttons live in the same
   stack and were flush too, so they move with it, which is what you want
   anyway -- nothing should touch the edge on a phone. The safe-area term
   matters in landscape on a notched device, where the physical edge is inboard
   of the layout edge. */
.leaflet-container .leaflet-bottom.leaflet-right {
  right: calc(10px + env(safe-area-inset-right, 0px));
  bottom: 6px;
}
