/* House style of justbfreefamily.com: the site's own three faces, its pills,
   and the magenta-to-orange gradient for the primary action.

   The canvas is light, as the website's is. It was dark until 10 September
   2026, and the reason written here was that a photorealistic globe loses
   all its depth on a white page. That reason was right and it is still
   right -- so the globe kept its dark window (see --stage below) and
   everything around it went light. Patrick: "die kleuren wil ik terug in de
   app zodat we van dat hele donkere af zijn."

   Every colour in this file comes from the site's own sheet. Nothing is
   mixed by eye: white, snow and mist are the three grounds, navy in three
   depths is the ink, magenta and orange are the brand, and lime and blue
   are the two accents that are neither. */
:root{
  --brand-orange:#ff8c42;
  /* Hetzelfde oranje, verdiept, voor tekst en randen op een lichte
     ondergrond. De sunset van de merkgids is gekozen tegen navy en haalt op
     wit 2,3:1 -- te weinig voor een label van 10,5px en zelfs te weinig voor
     een icoon, waar 3:1 de grens is. Deze haalt 4,9:1 en houdt de tint.

     Twee tokens en niet één, omdat de app twee ondergronden heeft: op het
     donkere bol-venster is de verdiepte versie juist te donker, en daar
     wordt dit token dan ook teruggezet op de felle (zie .globe-stage). Een
     vlak, een verloop of een gloed blijft altijd de felle: die staat nergens
     als tekst en verliest dus niets. */
  --brand-orange-ink:#b85408;
  --brand-orange-light:#ffa86b;
  --brand-magenta:#d622d0;
  --brand-magenta-light:#ef7aeb;
  /* The two accents that are not the brand gradient, kept for the places
     that need a colour meaning something other than "this is the action":
     lime for a good outcome, blue for a link or a fact. Both come off the
     site's sheet rather than being picked to sit next to the gradient. */
  --accent-lime:#b5d622;
  --accent-blue:#1862f3;
  --brand-gradient:linear-gradient(135deg,#d622d0 0%,#ff8c42 100%);
  /* The same gradient a shade lighter, which is what every gradient pill in
     this app does under a finger. It was written out four times, byte for
     byte, next to a base gradient that was already a token. */
  --brand-gradient-lit:linear-gradient(135deg,var(--brand-magenta-light) 0%,var(--brand-orange-light) 100%);
  /* The three faces the website is set in, as whole stacks rather than bare
     names, so every rule that reaches for one falls back the same way
     instead of inventing its own tail.

     Three faces, three jobs, and the job is the name of the token. The
     wordmark is Lobster and comes off the logo; it is the app's name and
     nothing else ever. Every title is Fraunces: the line a screen opens
     with, and the name of a country laid across a photo. Everything a
     person actually reads is DM Sans -- the sentences, the labels, the
     amounts, the words on the buttons.

     Playfair Display and Anton stood here for a day and are gone. Patrick
     asked for the whole app in Playfair, and set as running text at 13.5px
     on a dark ground its hairlines closed up; his own brand sheet had
     already answered it better, with DM Sans "voor de leesbare content".

     The tails follow the wordmark's old rule: a face that never arrives
     should leave a readable screen and not a broken one. Fraunces falls to
     Georgia, a serif of roughly the same colour that is on every machine
     this app is opened on, so a title stays a title. DM Sans falls to
     whatever the system calls its own sans. Lobster falls to Fraunces, so
     the app's name stays a name in the app's own serif rather than
     dropping into the face its sentences are set in. */
  --face-script:'Lobster','Fraunces',Georgia,cursive;
  --face-title:'Fraunces',Georgia,'Times New Roman',serif;
  --face-body:'DM Sans',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  /* The three grounds, in the order they stack. Snow is the page, white is
     a card lifted off it, mist is a surface pressed into it -- an input, a
     quiet row, a well. Never cream and never a warm off-white: the site's
     sheet is explicit about that, and a beige page next to this magenta
     goes muddy immediately. */
  --bg:#fafafb;
  --panel:#ffffff;
  --panel-soft:#f1f1f4;
  /* Navy in three depths, as ink rather than as ground. The deepest is the
     text, the middle one is for something that needs to sit a shade back
     without going grey, and the soft one is the hairline's parent. */
  --navy:#262a3f;
  --navy-deep:#17202b;
  --navy-soft:#303c42;
  --text:#17202b;
  /* The site's own muted, which is drawn for a light page. It was #a8adc4
     here for as long as the app was navy, because that muted is unreadable
     on navy -- and this one was unreadable on it too, which is exactly why
     the comment that used to sit here said so. On snow it is the right one
     again, and it is the site's. */
  --muted:#5a5f78;
  /* Hairlines and fills, as navy at low alpha rather than white at low
     alpha. Every one of these was a white veil over a dark panel until the
     canvas turned; on a light ground the same veil is invisible, so the
     whole family is inverted here in one place instead of at each of the
     hundred-odd rules that reach for it. */
  --border:rgba(38,42,63,.13);
  --border-soft:rgba(38,42,63,.07);
  --fill:rgba(38,42,63,.04);
  --fill-soft:rgba(38,42,63,.025);
  --fill-strong:rgba(38,42,63,.07);
  /* Shadows are navy and shallow. A black shadow on a white page is a
     smudge; the page's own darkest ink, at a low alpha and a real offset,
     reads as a card lying on paper. */
  --shadow-rest:0 1px 2px rgba(23,32,43,.05),0 10px 28px -18px rgba(23,32,43,.30);
  --shadow-lift:0 2px 4px rgba(23,32,43,.06),0 18px 40px -20px rgba(23,32,43,.38);
  /* The one dark thing left, and the reason the old comment at the top of
     this file was right: the window the globe turns in. A photorealistic
     Earth on white loses every bit of its depth, so it keeps the navy it
     always had and the app goes light around it -- a photograph in a light
     room. The place sheets and labels drawn over that window take their
     colours from the --on-stage pair below rather than from the page. */
  /* De foto achter "Jouw leven in Bitcoin". Een token en geen pad in de
     regel zelf, zodat het beeld op één plek te wisselen is.

     Aangeleverd door Patrick op 10 september 2026, en het is precies het
     beeld waar hij de hele restyle mee begon: een regenwouddal bij zonsopkomst,
     mist over de heuvels, een rivier eronderdoor. Waar het vandaan komt weet
     hij; het stond niet op Unsplash zoals de drie op de reiskaarten, dus als
     er ooit een vraag over de rechten komt is dat de plek om te kijken.

     De bestandsnaam is nieuw en niet 'vallei.webp' hergebruikt, met opzet.
     Beelden worden een jaar gecachet op hun naam (zie cacheControlFor in
     src/server.mjs), dus een beeld vervangen onder dezelfde naam betekent dat
     iedereen die de app al geopend had het oude beeld nog een jaar houdt. Een
     nieuw beeld hoort een nieuwe naam te krijgen; dat is hier de hele
     versiebeheer-machinerie voor plaatjes.

     Uitgesneden op 2,2:1 en verankerd aan de onderkant, want daar staat de
     titel: de onderste 40% van deze uitsnede meet 42 van de 255, en de hoek
     rechtsboven waar de twee knoppen zitten 118. Op een telefoon is de hero
     2:1 en op een laptop ruim 5:1, dus `cover` snijdt op een breed scherm
     boven en onder weg; de positie staat op 58% en niet op het midden. */
  --hero-photo:url(../assets/heroes/hero-jungle.webp);
  --stage:#17202b;
  --on-stage:#ffffff;
  --on-stage-muted:#a8adc4;
  /* Functional status colours, outside the brand palette. Green says
     "recently verified" and is not the lime accent: lime is a brand colour
     and this is a fact about a place. Both are darkened for a light ground,
     where the old values were pale enough to read as decoration. */
  --green:#0f9d76;
  --radius-pill:999px;
  --radius-soft:14px;
  /* The footer's own box, spelled out in three independently-named tokens
     rather than one hardcoded total, so the height below is calc()'d from
     the same numbers the footer itself is built from — not a copy of them
     that can quietly drift out of sync. --footer-line-height replaces the
     browser's own font-metric-dependent line-height for Mulish at 10.5px,
     so the total is not at the mercy of that either. */
  --footer-pad-y:9px;
  --footer-border:1px;
  --footer-line-height:20px;
  --footer-height:calc(2 * var(--footer-pad-y) + var(--footer-border) + var(--footer-line-height));
  /* The tab bar's own box, in the same three independently-named tokens the
     footer is built from and for the same reason: the sheet has to sit above
     the bar, and the number it is kept clear by must come from the bar
     itself rather than from a copy of it. The home-indicator inset is part
     of the bar's height, so it is part of the token too. main.js measures
     the real thing and overwrites this; these are the pre-JS starting point.

     The header is not built from tokens — its height is whatever the brand,
     the counter and the languages wrap to, and that differs per language and
     per width — so there is only the measured value and a sensible start. */
  --tab-pad-y:7px;
  --tab-border:1px;
  --tab-icon-line:20px;
  --tab-gap:2px;
  --tab-label-line:13px;
  --tab-height:calc(2 * var(--tab-pad-y) + var(--tab-border) + var(--tab-icon-line)
    + var(--tab-gap) + var(--tab-label-line) + env(safe-area-inset-bottom, 0px));
  --header-height:64px;
  /* The Activiteit sheet's grab handle, in one place so its own height
     (act-sheet-handle) and the extra room the wrap's cap gives it
     (act-sheet-wrap) can never drift apart — see the comment there. */
  --act-sheet-handle-height:22px;
  /* The bar the day book folds open behind, so the book's own height can be
     worked out from what is left rather than guessed at with a round vh. */
  --daily-bar-height:56px;
}
*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--bg);color:var(--text);
  font-family:var(--face-body);
  font-weight:400;display:flex;flex-direction:column;overflow:hidden;
}
/* A grid, not a row of boxes. The counter is the thing Patrick asked to
   have centred, and centring it means the space to its left and the space
   to its right being the same — which "space-between" over three items of
   different widths never gives. Two equal 1fr columns around an auto one
   do. */
.header{
  display:grid;grid-template-columns:1fr auto 1fr;
  grid-template-areas:"brand counter me";
  align-items:center;
  padding:14px 20px;border-bottom:1px solid var(--border);flex:0 0 auto;gap:16px;
}
.brand{grid-area:brand}
.counter{grid-area:counter}
.me-switch{grid-area:me;justify-self:end;position:relative}
/* The badge and the name side by side, the tagline under the name. */
.brand{display:flex;align-items:center;gap:9px;min-width:0}
/* The site's own round badge, on a white disc.
   The disc is not decoration and not a shortcut: the file is a transparent
   PNG whose artwork is dark ink — the ring reading "Just B Free Family" and
   "Freedom to Infinity" is nearly black — drawn for the light pages of
   justbfreefamily.com. Dropped straight onto this navy header it loses that
   ring completely and what is left is a smudge of palm and sunset. On its
   own white disc it is the badge the site shows, legible, at the size a
   header can give it. The padding keeps the artwork clear of the disc's
   edge, and the hairline is the app's own. */
.brand-logo{
  flex:0 0 auto;width:36px;height:36px;display:block;border-radius:50%;
  background:#fff;padding:2px;border:1px solid var(--border);
}
/* The badge's own box, so the flex gap counts from the badge and not from an
   inline anchor's line box. A link without an href is not a link, and gets
   no pointer. */
.brand-link{flex:0 0 auto;display:block;line-height:0;border-radius:50%}
.brand-link:not([href]){cursor:default}
.brand-link[href]:hover .brand-logo{border-color:rgba(255,140,66,.55)}
.brand-words{display:flex;flex-direction:column;min-width:0}
/* Lobster, the display face justbfreefamily.com sets its wordmark in, and
   the only place in this app that is not Mulish.

   26px, the site's own size, at every width — measured rather than
   assumed. "Just B Free Globe" in Lobster at 26px is 176px wide, and what
   decides how much room the brand column may take on a 375px phone is not
   the name at all but the tagline under it, which is 218px in German.
   There is therefore nothing to gain by shrinking a display face that
   loses its thin strokes the moment it is shrunk: the header is exactly as
   wide either way. Below 360px the sums do get tight, and only there does
   the name step down a little — see the media query at the end.

   If the font never arrives the stack falls back to Mulish — the name is
   then simply the app's name in the app's own face, which is a plain
   header rather than a broken one. Weight stays at 400: Lobster ships one
   weight, and asking for a bolder one makes the browser smear a synthetic
   bold over a script face. */
.wordmark{
  font-family:var(--face-script);
  font-weight:400;font-size:26px;line-height:1.15;letter-spacing:.005em;
  white-space:nowrap;
}
@supports (background-clip:text) or (-webkit-background-clip:text){
  /* The signature gradient, exactly as the site draws it. */
  .wordmark span{
    background:var(--brand-gradient);
    -webkit-background-clip:text;background-clip:text;color:transparent;
  }
}
/* The fallback for a browser without background-clip, and the colour the
   gradient is painted over: written after it so it cannot overrule it. */
@supports not ((background-clip:text) or (-webkit-background-clip:text)){
  .wordmark span{color:var(--brand-orange-ink)}
}
.tagline{
  display:block;font-weight:400;
  font-size:11.5px;color:var(--muted);margin-top:1px;
}
/* The one control in the corner of the header. For a visitor it is the
   globe with the flag of the language that is on beside it, which is the
   whole answer to "which language am I in" without opening anything. For a
   member it is their own photo, and then it is just the circle: no border,
   no padding, nothing around a face. */
.me-toggle{
  display:inline-flex;align-items:center;gap:5px;
  background:none;border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:3px 9px;font-size:15px;
  font-family:inherit;cursor:pointer;line-height:1.25;color:var(--text);
}
.me-toggle:hover{border-color:rgba(255,140,66,.55)}
.me-toggle.has-photo{padding:0;border-color:transparent}
.me-toggle.has-photo:hover{border-color:rgba(255,140,66,.55)}
.me-toggle .me-avatar{width:38px;height:38px;font-size:16px}
.lang-globe{font-size:12px;opacity:.75}
/* The four choices, under the globe. A panel in the app's own panel colour
   rather than a browser menu, and it is really gone when it is shut — a
   menu that is merely transparent still takes the taps meant for the globe
   behind it. z-index 800: over the bottom sheet (700) and over everything
   on the stage (600), because a menu that opens underneath the page is not
   a menu. */
.me-menu{
  position:absolute;top:calc(100% + 8px);right:0;z-index:800;
  display:flex;flex-direction:column;gap:2px;min-width:190px;max-width:min(78vw,280px);
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--radius-soft);box-shadow:var(--shadow-rest);
  padding:6px;
}
.me-menu[hidden]{display:none}
/* Who you are, at the head of your own menu: the greeting and the handle
   that used to stand in a card at the top of the Ik tab. Text, not a
   button — there is nothing to tap here, it only says whose menu this is. */
.me-menu-who{padding:8px 12px 10px;border-bottom:1px solid var(--border);margin-bottom:4px}
.me-menu-name{margin:0;font-size:14px;font-weight:800;overflow-wrap:anywhere}
.me-menu-handle{margin:2px 0 0;font-size:12px;color:var(--muted);overflow-wrap:anywhere}
/* And the two ways out, under the languages, behind a line of their own so
   a thumb reaching for a flag does not land on "sign out". */
.me-menu-doors{
  display:flex;flex-direction:column;gap:2px;
  border-top:1px solid var(--border);margin-top:4px;padding-top:4px;
}
.me-menu-do{
  display:block;width:100%;text-align:left;background:none;border:0;border-radius:var(--radius-pill);
  padding:10px 12px;color:var(--text);font-family:inherit;font-size:13px;font-weight:600;
  line-height:1.25;cursor:pointer;text-decoration:none;
}
.me-menu-do:hover{background:var(--fill)}
/* Deleting the account is the quietest thing in here, and it only opens a
   sheet: the weight is on that sheet, where the member has to type their own
   name before the red button comes alive. */
.me-menu-quiet{color:var(--muted);font-weight:500;font-size:12px}
.me-menu-quiet:hover{color:var(--text)}
/* A flag cannot be recoloured, so the language that is on is shown by giving
   it its colours back: the other three are dimmed and desaturated, and the
   current one is the only one in full colour, inside the same orange ring
   the buttons have always used. There is room for the language's own name
   beside the flag now, which is what the button was always about. */
.lang-button{
  display:flex;align-items:center;gap:9px;width:100%;text-align:left;
  background:none;border:1px solid transparent;color:var(--text);
  border-radius:var(--radius-pill);padding:9px 12px;font-size:15px;
  font-family:inherit;cursor:pointer;line-height:1.25;
  filter:grayscale(.6) opacity(.72);
}
.lang-button:hover{background:var(--fill)}
.lang-name{font-size:13px;font-weight:600}
.lang-button.on{border-color:rgba(255,140,66,.55);filter:none}
.counter{font-size:12.5px;color:var(--muted);text-align:center}
/* position:relative so the second tab's screen can be laid over this one
   without being laid over the header — see .me below. */
.screen{flex:1 1 auto;display:flex;min-height:0;position:relative}
/* The one dark window in a light app, and everything that floats on it.

   Rather than give the search box, the hint, the intro card and the back
   button each their own light-on-dark colours, the tokens that mean "ink",
   "ground" and "hairline" are redefined here for the length of this
   element. Everything inside then reads the dark set without knowing it is
   inside anything -- the same rules, the same tokens, a different room.
   That is also why a control added to this stage tomorrow is correct
   without anybody remembering this comment.

   The Activiteit drawer is the exception and sets them back (see
   .act-sheet-wrap): it is a panel of things to read that happens to be
   anchored to the globe, not part of the window. */
.globe-stage{
  flex:1 1 auto;position:relative;min-width:0;--search-top:14px;
  background:var(--stage);color:var(--on-stage);
  --text:var(--on-stage);
  --muted:var(--on-stage-muted);
  --panel:#20283a;
  --panel-soft:#1a2230;
  --border:rgba(255,255,255,.14);
  --border-soft:rgba(255,255,255,.08);
  --fill:rgba(255,255,255,.06);
  --fill-soft:rgba(255,255,255,.035);
  --fill-strong:rgba(255,255,255,.10);
  --shadow-rest:0 10px 30px rgba(0,0,0,.45);
  --shadow-lift:0 16px 40px rgba(0,0,0,.55);
  --brand-orange-ink:var(--brand-orange);
}
/* globe.gl owns everything inside its mount element, so it gets its own
   full-bleed layer underneath the loading text, hint and back button.
   overflow:hidden is not decoration: globe.gl sizes its canvas from its own
   render loop, so between a layout change and the next frame the canvas can
   be larger than this box — measured at 375x812 it stood 48px taller and
   reached down over the footer, on top of the credits that have to stay
   readable. Clipping to the stage makes that impossible to see at all. */
.globe-mount{
  position:absolute;inset:0;overflow:hidden;
  /* A finger on the globe belongs to the globe. Without this the browser is
     free to read a two-finger gesture that starts here as a page-level
     pinch instead of handing it to OrbitControls: the globe barely responds,
     and the page itself ends up zoomed and panned — the brand cut off on the
     left, the search box running past both edges, the hint clipped. That is
     one bug, not two, and this is the rule the pill has carried all along
     and the globe underneath it never did.

     Scoped to the mount on purpose, and not to .globe-stage around it:
     pinch-to-zoom is how people read a screen, so the bottom sheet, the
     search results, the "Ik" tab and every other panel keep it. Only here,
     where a pinch already means something else, does the app take it.

     touch-action intersects down the tree, so this also covers the wrapper
     divs globe.gl builds inside this element — which is what a finger lands
     on before the first frame has sized the canvas, and again in the window
     between a layout change and the next frame that the note above is about.
     The canvas gets its own rule below for the same reason it needs one at
     all. */
  touch-action:none;
}
/* The canvas already carries an inline touch-action:none — but it is set by
   OrbitControls inside web/vendor/globe.gl.min.js, so it belongs to the
   vendored bundle rather than to this app, and updating that bundle (see
   web/vendor/README.md) could take it away without a single line of this
   project changing. Saying it here makes it ours. */
.globe-mount canvas{touch-action:none}
.loading{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  color:var(--muted);font-size:13px;text-align:center;padding:0 24px;
}
.loading[hidden]{display:none}
/* Place-detail name and category: rendered straight into .sheet-body (see
   detail.js), not into a `.detail` wrapper — that belonged to an earlier
   two-pane layout and no longer exists anywhere in the markup, along with
   the `.panel`, `.close` and `.map-stage` rules that used to sit here. */
/* De naam van de plek, sinds de restyle van september 2026 in Anton en in
   kapitalen: 26px, de maat uit de goedgekeurde mockup. Dit is de tweede en
   laatste plek waar die letter staat, naast de landnaam op een reiskaart, en
   het is hetzelfde soort woord — een naam, geen zin.

   De regel mag zonder eigen klasse: `.sheet-body h2` is uitsluitend deze
   naam. Elke andere kop in dit vel is een h3 (zie `.sheet-body h3`), en de
   drie h2's die de app verder nog heeft dragen alle drie `.act-title`.
   Gewicht 400 omdat Anton één snit levert — zie `--display-shout`. */
.sheet-body h2{
  font-family:var(--face-title);font-weight:900;
  font-size:26px;line-height:1;letter-spacing:.01em;text-transform:uppercase;
  margin:0 0 4px;
  /* Een plaatsnaam komt van BTC Map en is van niemand hier. De langste in de
     kaart is 121 tekens, en er staat er een van 32 tekens tussen zonder een
     enkele spatie: "Computerservice.arminfischer.com". Op de 18px Mulish die
     hier stond paste dat nog binnen het vel; op 26px Anton in kapitalen is
     het 383px breed en ging het vel er horizontaal van schuiven. Dus breken
     waar het moet, net als de landnaam op een reiskaart. */
  overflow-wrap:anywhere;
}
.sheet-body .cat{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--brand-orange-ink);font-weight:600}
.row{display:flex;gap:9px;margin-top:12px;font-size:13.5px;line-height:1.5}
.row .icon{flex:0 0 18px;opacity:.75}
.row a{color:var(--text)}
.badges{display:flex;gap:7px;flex-wrap:wrap;margin-top:14px}
.badge{
  font-size:11.5px;padding:4px 9px;border-radius:var(--radius-pill);
  border:1px solid var(--border);color:var(--muted);
}
.badge.on{border-color:rgba(255,140,66,.55);color:var(--brand-orange-ink)}
.badge.fresh{border-color:rgba(16,185,129,.5);color:var(--green)}
/* Vier knoppen naast elkaar passen niet. Route, Verbeteren en Delen vullen
   de rij al op een telefoon, en "Ik heb hier betaald" is een hele zin. Dus
   mag de rij afbreken en neemt die vierde knop de regel eronder helemaal
   (`.place-paid` hieronder). De drie erboven veranderen er niets van: zolang
   ze samen passen breekt er niets af. */
.buttons{display:flex;gap:8px;margin-top:16px;flex-wrap:wrap}
.button{
  flex:1;text-align:center;padding:10px 14px;border-radius:var(--radius-pill);
  font-family:inherit;font-size:13px;font-weight:600;text-decoration:none;
  border:1px solid var(--border);color:var(--text);
  /* Two of these three are links and the third, since 4 September 2026, is
     a real button, which brings its own arrow cursor. */
  cursor:pointer;
}
/* The share button came out white, because that is what a `<button>` with
   no background of its own looks like, and Patrick kept it: "nee laat dat
   wit, dat is tof." So it is written down rather than left to a browser's
   own idea of a button — `buttonface` is white on the browsers this was
   seen in and is not promised to be white anywhere. The magenta text and
   border come from `.secondary` beside it, and read on white exactly as
   they do on the navy. */
.button.share{background:#fff}
/* The site's primary call to action: gradient pill, white text, weight 600. */
.button.primary{background:var(--brand-gradient);border-color:transparent;color:#fff}
.button.primary:hover{
  background:var(--brand-gradient-lit);
}
/* The lift justbfreefamily.com gives its own primary buttons: a soft magenta
   glow the gradient casts under itself, and a small rise when a finger or
   cursor is on it. These three classes are every full gradient call-to-action
   this app draws — Route on a place, "add a trip"/"add"/"save" on the trips
   screen, and signing in — and only those. The gradient elsewhere is a fill,
   not a floating button: the wordmark's own text, an avatar, a pressed toggle,
   a chip and a progress bar all wear it flat and must keep doing so, which is
   why this is spelled out per class rather than hung on the gradient itself. */
.button.primary,.trips-primary,.me-signin{
  box-shadow:0 6px 18px rgba(214,34,208,.4);
  transition:transform .18s ease,box-shadow .18s ease,background .18s ease;
}
.button.primary:hover,.trips-primary:hover,.me-signin:hover{
  transform:translateY(-1px);box-shadow:0 10px 26px rgba(214,34,208,.52);
}
@media (prefers-reduced-motion:reduce){
  .button.primary,.trips-primary,.me-signin{transition:box-shadow .18s ease}
  .button.primary:hover,.trips-primary:hover,.me-signin:hover{transform:none}
}
/* Secondary accent: magenta. The light tint, because #d622d0 as text does not
   carry enough contrast on this navy. */
.button.secondary{border-color:rgba(239,122,235,.55);color:var(--brand-magenta-light)}
/* "Ik heb hier betaald": een eigen regel onder de andere drie, en aan zodra
   het aangevinkt is. Aan draagt hetzelfde oranje als `.badge.on` erboven,
   want het gaat over hetzelfde: dit is een plek waar met bitcoin betaald
   wordt. Uitgezet terwijl het verzoek onderweg is, zodat een tweede tik geen
   tweede verzoek start. */
.button.place-paid{flex-basis:100%}
.button.place-paid.on{border-color:rgba(255,140,66,.55);color:var(--brand-orange-ink)}
.button.place-paid:disabled{opacity:.55;cursor:default}
.empty{
  color:var(--muted);font-size:13px;line-height:1.6;padding:12px 14px;
  border:1px solid var(--border);border-radius:var(--radius-soft);
}
/* One line on a 375px phone was the point of shrinking this (see the
   measurements on .copyright::before below): the credit, the copyright and
   — when it is shown — the satellite line sit one after another and wrap
   as whole units when they must. 10.5px is the size the owner approved; at
   11.5px "BTC Map · © OpenStreetMap-bijdragers · © 2026 Just ₿ Free"
   measures 331px against the 335px this padding leaves — technically one
   line, but with nothing to spare before the next language or the next
   satellite credit tips it over.

   `justify-content:center` rather than the left-aligned flow this used to
   be: Patrick, on his phone, wanted the row centred rather than hugging the
   left edge with empty space stranded on the right. The group is centred
   as one unit, not each span centred on its own, so the · that already
   joins the copyright to the credit (.copyright::before below) keeps doing
   that job — the separator this pairing needed was already there, and
   centring a block that already reads as one credited line does not need
   a second one invented for it. flex-wrap keeps each wrapped line — the
   ordinary case, and the one with the satellite credit added — centred on
   its own, so the Esri credit stays in the middle of the footer on its own
   line exactly as it did pinned left-to-right, just centred instead. */
.footer{
  flex:0 0 auto;display:flex;flex-wrap:wrap;row-gap:2px;
  justify-content:center;align-items:center;
  padding:var(--footer-pad-y) 20px;border-top:var(--footer-border) solid var(--border);
  font-size:10.5px;color:var(--muted);line-height:var(--footer-line-height);
}
.footer a{color:var(--muted)}
/* Esri's credit. In the footer rather than on the map, because the bottom
   sheet lies over the map's own corners at every one of its stops — see
   SATELLITE_CREDIT in web/js/map.js. The leading · is a pseudo-element
   rather than text typed into footer.satellite, so it can never show up
   attached to nothing while the credit itself is hidden — the visibility
   and the separator are the one `hidden` attribute, not two things that
   could drift apart. */
.map-credit{flex:0 1 auto}
.map-credit::before{content:" · "}
.map-credit[hidden]{display:none}
/* This space was held open for the affiliate links, which went to the bottom
   of a country's own panel instead. Since 5 September 2026 it holds the two
   pages the app has to have: the privacy statement and the terms. They are
   the opposite of an advert and the one thing that genuinely belongs in a
   footer beside the attributions. The height is still what keeps the footer
   from jumping while they are being drawn. */
.ref-space{min-height:20px;display:inline-flex;gap:12px}
.ref-legal{color:var(--muted);text-decoration:underline;text-underline-offset:3px}
.ref-legal:hover{color:var(--text)}
/* De spatie vóór de punt is een harde spatie. Beide dragers hieronder zijn
   een flexvak van de footer, en een gewone spatie aan het begin van zo n
   vak valt weg: dan staat er "bijdragers· ©" in plaats van
   "bijdragers · ©". De spatie erachter staat midden in de regel en blijft
   vanzelf staan. Breedte blijft precies die van de spatie die hier stond,
   dus de opmeting boven in dit bestand klopt nog. */
.ref-space::before{content:"\0000a0· ";color:var(--muted)}
/* Whose app this is, joined onto the credit ahead of it by the same ·
   pattern the satellite credit uses below — a pseudo-element again, so the
   separator can never appear without the name it introduces. It never
   breaks inside itself — a name split over two lines is not a name — so on
   a narrow phone it drops to a line of its own instead, and the footer
   grows by one line. That growth is why --footer-height is measured rather
   than assumed (see syncFooterHeight in web/js/main.js). */
.copyright{flex:0 0 auto;white-space:nowrap}
.copyright::before{content:"\0000a0· "}

/* The tab bar, along the bottom of every screen. A panel-coloured band with
   a hairline above it, like every other raised surface in this app, and one
   button per tab sharing the width evenly — `flex:1 1 0` rather than a width
   per tab, so two tabs and four tabs both come out right and the third and
   fourth cost nothing but a line in web/js/tabs.js.

   The tab you are on is orange, the others are the muted grey; a small
   orange bar sits on the top edge of the one you are on, so it is marked by
   more than a colour. The brand gradient is deliberately not used here: it
   belongs to the one primary action on a screen, which on this app's screens
   is the Route button. */
.tabbar{
  flex:0 0 auto;display:flex;align-items:stretch;
  background:var(--panel);border-top:var(--tab-border) solid var(--border);
  /* The home indicator on a modern phone sits over the bottom of the
     screen; without this the labels would be underneath it. */
  padding-bottom:env(safe-area-inset-bottom, 0px);
}
.tab{
  flex:1 1 0;min-width:0;position:relative;
  display:flex;flex-direction:column;align-items:center;gap:var(--tab-gap);
  background:none;border:none;padding:var(--tab-pad-y) 6px;
  font-family:inherit;font-size:11px;font-weight:600;
  line-height:var(--tab-label-line);color:var(--muted);cursor:pointer;
}
.tab:hover{color:var(--text)}
.tab.on{color:var(--brand-orange-ink)}
.tab.on::before{
  content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:26px;height:3px;border-radius:var(--radius-pill);background:var(--brand-orange);
}
/* The icon row is a fixed box exactly --tab-icon-line tall — the height
   --tab-height is worked out from — with its drawing centred inside it, so a
   line icon, the Satoshi picture and the ₿ glyph all sit on one line and the
   labels beneath them stay level whatever each icon happens to measure. The
   font-size is the emoji fallback's, for the rare case the markup is gone. */
.tab-icon{
  font-size:17px;height:var(--tab-icon-line);
  display:flex;align-items:center;justify-content:center;
}
/* The three line drawings. Stroked in currentColor, so an idle tab's icon is
   the same muted grey as its label and the chosen tab's is the same orange —
   no per-state opacity needed, the colour carries it, which the filled marks
   below cannot do and so still lean on opacity. 20px matches the icon line
   the bar is measured from. */
.tab-icon svg{
  display:block;width:20px;height:20px;
  fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
/* Somebody is waiting on you. Sits over the icon's top right corner, the way
   every phone marks a tab, and only ever on the activity tab — see tabBadge
   in web/js/tabs.js for why it is not on the others.

   `position:absolute` against `.tab`, which is already position:relative for
   the orange bar on the tab you are on. */
.tab-badge{
  position:absolute;top:2px;left:50%;margin-left:6px;
  min-width:16px;height:16px;padding:0 4px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;
  background:var(--brand-orange);color:#fff;
  font-size:10.5px;font-weight:800;line-height:1;
}
/* A picture in the place of an emoji, and exactly the height of one, so the
   bar keeps the height it has always had — --tab-height is worked out from
   that line and the label's, and a picture that measured differently would
   move the globe under it. */
.tab-image{
  /* Centred in the fixed icon box now, so it no longer compensates for a
     line box the way it did beside an emoji. 18px against the 20px line
     drawings beside it: a filled silhouette reads optically heavier than a
     stroked outline at the same height, so it is set a touch smaller to
     weigh the same on the row — the alignment Patrick asked for. */
  display:block;width:auto;height:18px;
  /* The drawing is orange, so it reads as the chosen tab whichever one is
     chosen. Dimmed until it is — the same thing the label does with
     colour, said the only way a picture can say it. */
  opacity:.55;transition:opacity .15s ease,filter .15s ease;
  /* And deepened, since the bar went white on 10 September 2026. The file
     is a flat silhouette in the brand's sunset orange, drawn to sit on
     navy; on a white bar that orange measures 2.3:1 and the figure turns
     into a pale smudge between four crisp line drawings. The filter lands
     it on the same deepened orange every other orange thing on a light
     ground now uses (--brand-orange-ink), measured rather than guessed:
     #ff8c42 through this comes out at #b8551a against the token's #b85408.

     A filter and not a second file, because the artwork is one flat colour
     — there is nothing in it that a brightness curve can spoil — and a
     second copy of Satoshi is a second thing to redraw the day the logo
     changes. If the drawing ever gains a second colour, replace this with
     a real asset. */
  filter:brightness(.72) saturate(1.6);
}
.tab.on .tab-image{opacity:1}
/* The fifth tab's icon is the character ₿ (U+20BF) rather than an emoji, so
   it needs a rule the other three plain-emoji icons have never needed: an
   emoji is already in full colour and ignores `color` outright, but a
   letterform takes whatever ink colour it is given, the same as the label
   text below it and the ₿ everywhere else this app draws one — the map's
   pins (`.btc-pin` above) and the country sheet's group marks
   (`.place-group-mark` further down) are both a plain character coloured
   by a rule, never an emoji standing in for the mark. Orange outright,
   because that colour is the mark's own identity here and not decoration a
   selected state earns — the exact reasoning `.tab-image` above already
   used for the "Ik" tab's own orange drawing, and the same fix: opacity
   carries the selected/unselected difference instead of `color`, since a
   fixed-colour mark cannot fade to the muted grey the label text does. */
/* 24px against the 17px the other four take, and the difference is the
   point: they are emoji, which draw optically bigger than a text glyph at
   the same size. Measured in the browser at 375px — an emoji renders 17px
   wide, and this glyph renders 13.1px at 20px, 14.5 at 22, 16.0 at 24 and
   17.5 at 26. 24 is where it stops looking smaller than its neighbours,
   which is what Patrick saw ("kan het B van het bitcoin tabje net iets
   groter").

   The line-height is deliberately not touched — see .tab-icon above:
   --tab-height is worked out from that line, and an icon that measured
   differently would move the globe underneath it. Confirmed: the bar stays
   50px at every size tried. */
.tab[data-tab="bitcoin"] .tab-icon{
  /* 22px now, not the old 24: the neighbours are 20px line drawings rather
     than optically-larger emoji, and a ₿ set to weigh the same as a stroked
     icon on the row lands just above their height, not half again over it. */
  color:var(--brand-orange-ink);font-weight:800;font-size:22px;
  opacity:.55;transition:opacity .15s ease;
}
.tab[data-tab="bitcoin"].on .tab-icon{opacity:1}
.tab-label{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Tab 2, "Ik". A layer over the globe's screen, not a panel beside it: the
   globe underneath keeps its size and wherever the visitor turned it, and
   the satellite map keeps the view they left it in. It stops at the top of
   this screen, so the header — and with it the language switcher — stays
   where it is, and at the bottom of it, so the credits and the tab bar do
   too. main.js makes what is underneath `inert` while this is up, since
   covered is not the same as gone to a keyboard or a screen reader. */
.me{
  position:absolute;inset:0;z-index:750;
  background:var(--bg);overflow-y:auto;padding:18px 16px 24px;
}
.me[hidden]{display:none}
/* The way to the travel statistics: one pill beside "Reis toevoegen" at the
   top of the trips list (.trips-doors, further down). It stood at the top
   right of the screen, next to an "Ik" heading, until Patrick moved it down
   on 4 September 2026 and took the heading off with it. An ordinary hairline
   pill like every other working control here — the gradient stays with the
   one primary action on the screen, which is "add a trip". It is drawn on
   the trips list only, so a signed-out visitor never meets a button whose
   only possible answer is a refusal; the way back out of the statistics is
   their own `.trips-back`. */
/* Three pills that are wide enough to hit and were not tall enough: 31px
   and 32px against the 44px a finger actually needs. The padding is left
   alone — it is what gives them their shape — and a min-height is added
   under it, so nothing moves except the part nobody could see. */
.me-stats{
  display:inline-flex;align-items:center;gap:8px;
  background:none;border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:7px 13px;font-family:inherit;font-size:12.5px;font-weight:600;
  line-height:1.2;color:var(--text);cursor:pointer;min-height:44px;
}
.me-stats:hover{border-color:rgba(255,140,66,.55)}
.me-stats-icon{font-size:13px;opacity:.85}
.me-empty{
  margin-top:16px;padding:16px;
  background:var(--fill-soft);
  border:1px solid var(--border);border-radius:var(--radius-soft);
}
.me-empty-title{font-family:var(--face-title);margin:0 0 8px;font-size:15px;font-weight:800}
.me-line{margin:0 0 10px;font-size:13.5px;line-height:1.6;color:var(--muted)}
.me-line:last-child{margin-bottom:0}

/* Signing in.

   The gradient belongs to the one primary action on a screen, and on this
   screen that is signing in — the Route button holds it on the place
   detail, and the two are never on screen at once. It is the only thing
   here that carries it: the way back out is an ordinary hairline pill, like
   every other secondary control in this app. */
/* The other door, for somebody who has never used Telegram: a quiet line
   rather than a second gradient pill. One primary action per screen is a
   rule this app keeps, and the Telegram button is that action here — this
   is the way past it, not a rival to it. */
.me-other{
  display:block;margin-top:12px;background:none;border:0;padding:4px 0;
  color:var(--muted);font-family:inherit;font-size:12.5px;line-height:1.5;
  text-align:left;text-decoration:underline;text-underline-offset:3px;cursor:pointer;
}
.me-other:hover{color:#fff}
/* And the form it opens. Boxed, so it reads as one thing rather than as
   fields loose on the screen under the button. Its own name and not
   `.me-account`: that one is the card a signed-in member's name sits in,
   and it is a flex row — which laid this form's heading out beside its
   first label. */
/* The recovery code, on the one screen it ever appears on. Boxed like the
   form it replaces, and the code itself set large and monospaced: this is
   copied onto paper by hand, and the whole point is that it is read back
   correctly months later. */
.me-code{
  display:block;margin-top:14px;padding:14px;
  background:var(--fill-soft);border:1px solid var(--brand-orange);border-radius:14px;
}
.me-code-value{
  margin:12px 0;padding:12px;border-radius:10px;
  background:rgba(0,0,0,.35);border:1px dashed var(--border);
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:19px;font-weight:700;letter-spacing:.08em;text-align:center;
  /* Selectable, and nothing else on this screen is: somebody putting it in
     a password manager should be able to take it without retyping. */
  user-select:all;-webkit-user-select:all;word-break:break-all;
}
.me-signup{
  display:block;margin-top:14px;padding:14px;
  background:var(--fill-soft);border:1px solid var(--border);border-radius:14px;
}
.me-signup-title{font-family:var(--face-title);display:block;margin:0 0 10px;font-size:15px;font-weight:700}
.me-signup .trips-label{display:block;margin-top:10px}
/* Outlined, not the brand gradient. The Telegram button above it is this
   screen's one primary action, and two gradient pills at once would be two
   primary actions — a rule this app keeps everywhere. */
.me-signup-go{
  display:block;width:100%;margin-top:14px;padding:11px 20px;
  background:none;border:1px solid var(--border);border-radius:var(--radius-pill);
  color:#fff;font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.me-signup-go:hover{border-color:var(--brand-orange-ink)}
.me-signup-go:disabled{opacity:.6;cursor:default}
/* Meereizen met een code sits its submit beside a cancel now (trips-actions,
   the same pair every other form here uses), so it can no longer claim the
   full row the way the account form's lone submit still does. */
.trips-actions .me-signup-go{display:inline-flex;align-items:center;justify-content:center;width:auto;margin-top:0}
.me-signup .me-other{margin-top:10px;text-align:center;width:100%}
.me-error{color:#ff8f8f}
.me-signin{
  display:inline-flex;align-items:center;gap:8px;margin-top:14px;
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  border-radius:var(--radius-pill);padding:11px 20px;
  font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.me-signin:hover{
  background:var(--brand-gradient-lit);
}
.me-signin-icon{font-size:14px}
/* Telegram's own window draws itself in here. Its iframe brings its own
   size; this only keeps it off the text above it. */
.me-widget{margin-top:12px;min-height:40px}
/* The last thing that went wrong, in the app's own voice. Magenta's light
   tint, because #d622d0 as small text does not carry on this navy — the
   same rule the secondary button follows.

   The trips screen's refusal is the same box and says so by sharing this
   rule rather than by repeating it: two refusals on one screen must never
   look like two different kinds of thing, and two copies of a colour are
   how that stops being true. Only the margin differs, below. */
.me-notice,
.trips-error{
  padding:10px 12px;font-size:13px;line-height:1.5;
  color:var(--brand-magenta-light);
  border:1px solid rgba(239,122,235,.45);border-radius:var(--radius-soft);
  background:rgba(214,34,208,.06);
}
.me-notice{margin:14px 0 0}
.trips-error{margin:12px 0}
/* A line that is a state rather than an explanation: "just checking",
   "signing you in". Deliberately quieter than the paragraphs above it. */
.me-quiet{margin-top:14px;font-size:13px;opacity:.85}

/* The circle holds the member's initial, and the photo lies over it. So a
   picture that is blocked, expired or slow leaves a filled circle with a
   letter in it rather than an empty square — and main.js removes an image
   that fails outright. It stood in a card at the top of the Ik tab until
   4 September 2026 and is now the button in the corner of the header; the
   size it wears there is set on .me-toggle .me-avatar above. */
.me-avatar{
  position:relative;flex:0 0 auto;width:48px;height:48px;border-radius:var(--radius-pill);
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  background:var(--brand-gradient);color:#fff;font-size:19px;font-weight:800;
}
.me-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.erase-sheet{
  position:fixed;inset:0;z-index:900;display:flex;flex-direction:column;
  background:var(--bg);padding:16px 16px 0 16px;
}
.erase-sheet-head{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding-bottom:8px}
/* En deze niet, met opzet. "Account verwijderen" is geen scherm dat zich
   voorstelt maar een vraag of je het echt zeker weet, en een serif zet daar
   een redactionele toon op die er niet hoort. Een waarschuwing hoort zo
   gewoon te staan als het maar kan. Blijft dus de huisletter op 18px. */
.erase-sheet-title{margin:0;font-size:18px;font-weight:800}
.erase-sheet-body{flex:1 1 0;min-height:0;overflow-y:auto;padding:6px 2px 28px;max-width:60ch}
.erase-sheet-body p{margin:0 0 12px;font-size:15px;line-height:1.5}
.erase-final{font-weight:700}
.erase-copy{
  display:inline-block;margin:6px 0 18px;padding:9px 16px;border:1px solid var(--border);
  border-radius:var(--radius-pill);color:var(--text);font-size:13.5px;font-weight:600;text-decoration:none;
}
.erase-copy:hover{border-color:rgba(255,140,66,.55)}
.erase-field{display:block;margin:0 0 14px}
.erase-field-name{display:block;font-size:13px;color:var(--muted);margin-bottom:6px}
/* 16px, like every field in this app: smaller and iOS Safari zooms in. */
.erase-name{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.erase-error{color:#ff6b62;font-size:14px}
.erase-confirm{
  display:block;width:100%;padding:13px 18px;border:0;border-radius:var(--radius-pill);
  background:#c0392b;color:#fff;font-family:inherit;font-size:15px;font-weight:800;cursor:pointer;
}
.erase-confirm:disabled{opacity:.4;cursor:default}

/* Het fooivel, en het vel waarin je je eigen adres zet. Dezelfde soort laag
   als het verwijdervel hierboven -- vast aan het scherm vanuit #overlay, een
   kop die blijft staan en een romp die schuift -- want het is dezelfde soort
   handeling: iets kleins doen dat het scherm eronder even helemaal met rust
   laat. Eén klassenset voor twee vellen, zodat de twee niet uit elkaar
   kunnen gaan lopen in iets waar ze nooit over verschild hebben. */
.tip-sheet{
  position:fixed;inset:0;z-index:900;display:flex;flex-direction:column;
  background:var(--bg);padding:16px 16px 0 16px;
}
.tip-sheet-head{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding-bottom:8px}
.tip-sheet-title{margin:0;font-size:18px;font-weight:800}
.tip-sheet-body{flex:1 1 0;min-height:0;overflow-y:auto;padding:6px 2px 28px;max-width:60ch}
/* Van wie de foto is. Het is de enige zin op dit vel die over een persoon
   gaat en niet over de handeling, dus hij staat groter en voorop. */
.tip-who{margin:0 0 4px;font-size:16px;font-weight:700;line-height:1.4}
.tip-saved{margin:10px 0 0;font-size:13px;color:var(--muted)}
/* Lucht tussen een uitleg en de knop die erbij hoort, aan beide kanten
   evenveel. Patrick, 7 september 2026: "iets meer ruimte tussen tekst en
   button graag, onthoudt dit zodat dit nooit meer voorkomt." De regels die
   dat al oplossen gelden voor `.trips-primary` en `.trips-quiet`; op deze
   twee vellen is de knop een `.settle-pay`-rij en een invoerrij, dus die
   vielen erbuiten. Veertien pixels boven en veertien onder, want een knop
   met veertien erboven en zes eronder hangt scheef tussen zijn alinea's. */
.tip-sheet-body .trips-hint + .settle-pay{margin-top:14px}
.tip-sheet-body .settle-pay-address + .trips-hint{margin-top:14px}
.tip-sheet-body .trips-hint + .settle-mine-row{margin-top:14px}
.tip-sheet-body .settle-mine-row + .trips-hint{margin-top:14px}

/* The day book's statistics — see web/js/daily-stats.js. The same kind of
   sheet as the erase sheet above: fixed to the viewport from #overlay, a
   head that stays, a body that scrolls. */
.stats-sheet{
  position:fixed;inset:0;z-index:900;display:flex;flex-direction:column;
  background:var(--bg);padding:16px 16px 0 16px;
}
.stats-head{flex:0 0 auto;display:flex;align-items:center;gap:12px;padding-bottom:10px}
/* De kop van het statistiekenvel en van het Toshi-vel: ook schermkoppen,
   ook Playfair. Zie .trips-open-title hierboven. */
.stats-title{margin:0;font-family:var(--face-title);font-size:24px;font-weight:800;line-height:1.2}
.stats-kinds{flex:0 0 auto;display:flex;gap:8px;margin-bottom:8px}
.stats-kind{
  flex:1 1 0;background:none;border:1px solid var(--border);color:var(--text);
  border-radius:99px;padding:7px 12px;font:inherit;font-size:14px;cursor:pointer;
}
.stats-kind[aria-pressed="true"]{background:var(--brand-gradient);border-color:transparent;font-weight:700}
.stats-chips{flex:0 0 auto;display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px}
.stats-chip{
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  border-radius:99px;padding:7px 12px;font:inherit;font-size:13.5px;font-weight:600;cursor:pointer;
}
.stats-chip.on{border-color:var(--brand-orange-ink)}
.stats-menu{
  flex:0 0 auto;display:flex;flex-direction:column;gap:2px;max-height:38vh;overflow-y:auto;
  background:var(--panel);border:1px solid var(--border);border-radius:var(--radius-soft);
  padding:6px;margin-bottom:8px;
}
.stats-option{
  text-align:left;background:none;border:0;color:var(--text);border-radius:8px;
  padding:9px 10px;font:inherit;font-size:14.5px;cursor:pointer;
}
.stats-option.on{background:rgba(255,140,66,.18);font-weight:700}
.stats-body{flex:1 1 0;min-height:0;overflow-y:auto;padding:4px 2px 28px;-webkit-overflow-scrolling:touch}
.stats-total{display:flex;align-items:center;gap:10px;padding:8px 0 12px}
.stats-total-name{flex:1 1 auto;font-size:16px;font-weight:700}
.stats-figure{display:flex;flex-direction:column;align-items:flex-end;flex:none}
.stats-money{font-size:15px;font-weight:700;font-variant-numeric:tabular-nums}
.stats-sats{font-size:11.5px;opacity:.6;font-variant-numeric:tabular-nums}
.stats-total-figure .stats-money{font-size:22px}
.stats-cats{list-style:none;margin:0;padding:0}
.stats-cat{
  display:grid;grid-template-columns:30px 1fr auto auto;align-items:center;gap:10px;
  width:100%;text-align:left;background:none;border:0;border-bottom:1px solid var(--border);
  padding:10px 0;color:var(--text);font:inherit;cursor:pointer;
}
.stats-cat-what{display:flex;flex-direction:column;gap:5px;min-width:0}
.stats-cat-name{font-size:14.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.stats-cat-share{font-size:12.5px;opacity:.7;font-variant-numeric:tabular-nums}
.stats-bar{display:block;height:5px;border-radius:3px;background:var(--fill-strong);overflow:hidden}
.stats-bar-fill{display:block;height:100%;border-radius:3px;background:var(--brand-orange)}
.stats-back{
  align-self:flex-start;background:none;border:0;padding:6px 0;color:var(--brand-orange-ink);
  font:inherit;font-size:14px;font-weight:700;cursor:pointer;
}
.stats-section{margin-top:22px}
.stats-heading{margin:0 0 8px;font-size:16px;font-weight:800}
.stats-note{margin:0 0 8px;font-size:12.5px;color:var(--muted)}
.stats-months{display:flex;align-items:flex-end;gap:4px;height:120px;padding:0 2px 4px}
.stats-month{
  flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;gap:6px;
  height:100%;background:none;border:0;padding:0;color:var(--muted);font:inherit;font-size:10.5px;cursor:pointer;
}
.stats-month-bar{display:block;width:70%;max-width:18px;border-radius:9px;background:var(--brand-orange);opacity:.55}
.stats-month.on .stats-month-bar,.stats-month:hover .stats-month-bar{opacity:1}
.stats-month.on{color:var(--text);font-weight:700}
.stats-lines{list-style:none;margin:0;padding:0}
.stats-line{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--border)}
.stats-line-name{flex:1 1 auto;font-size:14.5px;font-weight:600}
.stats-land{
  flex:1 1 auto;display:flex;flex-direction:column;gap:5px;min-width:0;text-align:left;
  background:none;border:0;padding:0;color:var(--text);font:inherit;cursor:pointer;
}
.stats-land.on .stats-line-name{color:var(--brand-orange-ink)}
/* "Mijn jaar in bitcoin" - the picture of a year, web/js/year-card.js. The
   same button the travel statistics wear, a little further from the list
   above it, with the line about what is on the picture under it. */
.stats-share{margin-top:26px}
/* The line under the button and the download link below it are centred on
   it, which is what Patrick asked for on 4 September 2026 with a red circle
   round the pair of them. This is the only download link in the app since
   that same evening: it stood on the book's own bottom line too, and three
   ways to the same file on one screen was two too many. */
.stats-share-note{margin:0 0 4px;text-align:center}
.stats-csv{display:flex;width:fit-content;margin:18px auto 0}
/* A category's days are drawn by the book's own daysHtml, which puts the
   "today" button in; that button scrolls the book, not this sheet. */
.stats-body .daily-today{display:none}
.stats-body .daily-row{cursor:default}
/* The ring with a slice's share in its middle once the slice is tapped. */
.stats-ring{position:relative;width:170px;height:170px;margin:0 auto 14px}
.stats-donut{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.stats-donut circle{cursor:pointer;transition:stroke-width .15s}
.stats-donut circle.on{stroke-width:9}
.stats-ring-label{
  position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;padding:34px;text-align:center;pointer-events:none;
}
.stats-ring-pct{font-size:26px;font-weight:800;line-height:1}
.stats-ring-name{font-size:12px;color:var(--muted);line-height:1.2}
.stats-ring-hint{font-size:11.5px;color:var(--muted)}
.stats-cat.on{background:rgba(255,140,66,.08)}

/* ------------------------------------------------------------------ *
   Trips, on the personal tab.

   Everything here lives inside .me, which already scrolls and already
   carries the screen's padding. The house rules hold: hairline borders,
   generous radii, the muted grey for anything that is not the point of a
   line, magenta only as its light tint (#ef7aeb) because #d622d0 as text or
   as a one-pixel line disappears into this navy — and the brand gradient on
   exactly one control at a time, which on this screen is whichever primary
   action is on it: "add a trip", "add a place", or "save".
   ------------------------------------------------------------------ */
.trips{margin-top:16px}
.trips-line{margin:0 0 10px;font-size:13.5px;line-height:1.6;color:var(--muted)}
.trips-line:last-child{margin-bottom:0}
.trips-hint{margin:8px 0 0;font-size:12px;line-height:1.5;color:var(--muted);opacity:.85}
/* Where the town names come from. Both licences ask to be named, and this
   is the screen the names are on, which is where a credit belongs. Quieter
   than the hint above it — it is provenance, not instruction — and its
   links are underlined so they read as links in a paragraph of grey. */
.trips-credit{opacity:.65}
.trips-credit a{color:var(--muted);text-decoration:underline}
.trips-heading{margin:20px 0 10px;font-size:12px;font-weight:800;letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted)}
/* De naam van wat je net opende: de titel van een reis, en de kop
   "Reisstatistieken" boven de tegels. Sinds de restyle van september 2026
   dezelfde Playfair op dezelfde 24px als .act-title, want het is hetzelfde
   ding -- de regel waarmee een scherm opent. Er stonden twee soorten
   schermkop in de app, en dat is slechter dan één.

   Het negatieve spatieren is eraf, om dezelfde reden als bij .act-title:
   dat hoorde bij Mulish, en het knijpt bij een serif op deze maat de
   tegenvormen dicht die hem leesbaar maken. overflow-wrap blijft: een
   reisnaam is van de gebruiker en kan één lang woord zijn. */
.trips-open-title{
  margin:14px 0 4px;font-family:var(--face-title);
  font-size:24px;font-weight:800;line-height:1.2;overflow-wrap:anywhere;
}

/* The two doors at the top of the trips list, side by side and the same
   height: "Reis toevoegen" and the way to the travel statistics. Patrick,
   4 September 2026: "reisstatistieken naar beneden en button met dezelfde
   grootte ter hoogte van reis toevoegen." Each is as wide as its own words,
   and they wrap onto two rows rather than squeeze — "Estadísticas de viaje"
   is half again as wide as "Reisstatistieken". The height is the 44px a
   finger needs, given here rather than in the padding so the pills keep the
   shape they wear everywhere else. The room underneath is Patrick's, later
   the same evening: "kan je deze tekst naar beneden brengen met genoeg
   ruimte tussen de buttons en de tekst." It is the gap to whatever follows,
   the first line of an empty book as much as the first heading of a full
   one. */
.trips-doors{display:flex;flex-wrap:wrap;align-items:stretch;gap:10px;margin:10px 0 28px}
/* The code sits between the two buttons and the list. Its top gap is the
   row's own 28 (margins collapse to the larger of the two); the bottom one
   is given here, so the first heading of a full list and the one sentence
   of an empty one both stand clear of it. */
.trips .me-other{margin-bottom:22px}
.trips-doors .trips-primary,.trips-doors .me-stats{
  min-height:44px;padding-top:0;padding-bottom:0;
}

/* The one primary action on the screen it is on. */
.trips-primary{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  border-radius:var(--radius-pill);padding:11px 20px;
  font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.trips-primary:hover{
  background:var(--brand-gradient-lit);
}
/* Everything else: an ordinary hairline pill, like the way back out of the
   account block above it. */
.trips-quiet{
  background:none;border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-pill);padding:9px 16px;
  font-family:inherit;font-size:12.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.trips-quiet:hover{border-color:rgba(255,140,66,.55)}
/* Deze reis verlaten. Een rode rand en geen rode knop: ernstig genoeg om op
   te vallen tussen de andere knoppen, maar niet onomkeerbaar — met een nieuwe
   code kun je er weer op. Het volle rood (#c0392b, de erase-knop) blijft voor
   het wissen van een account, en dat verschil hoort zichtbaar te zijn.

   Beide klassen in de kiezer, niet alleen .trips-leave: anders hangt het van
   de volgorde in dit bestand af wie er wint, en dat ging bij de eerste poging
   meteen mis. */
.trips-quiet.trips-leave{border-color:rgba(192,57,43,.55);color:#ff6b62}
.trips-quiet.trips-leave:hover{border-color:#c0392b;color:#ff8a82}
.trips-inline{padding:4px 11px;font-size:11.5px;margin-left:6px}
.trips-back{
  display:inline-flex;align-items:center;background:none;border:1px solid var(--border);
  color:var(--muted);border-radius:var(--radius-pill);padding:7px 14px;
  font-family:inherit;font-size:12.5px;font-weight:600;line-height:1.2;cursor:pointer;
  min-height:44px;
}
.trips-back:hover{border-color:rgba(255,140,66,.55);color:var(--text)}
.trips-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
/* The quiet line about the 2 km grid sits directly above the one primary
   action of an open trip; without this they touch. */
/* Lucht tussen een uitleg en de knop eronder. Gold tot 7 september 2026
   alleen voor de opvallende knop, en niet voor de stille — waardoor
   "Adres toevoegen" in Mijn bitcoin tegen zijn eigen uitleg aan plakte:
   een alinea heeft geen marge onder zich en een knop geen marge boven
   zich, dus er stond precies nul. Patrick wees het aan met een pijl.
   Beide knopsoorten nu, want welke van de twee een scherm toevallig
   gebruikt is geen reden voor een andere afstand. */
.trips-hint + .trips-primary,
.trips-hint + .trips-quiet{margin-top:14px}
/* En dezelfde lucht aan de andere kant. Een knop tussen twee alineas met
   veertien pixels erboven en zes eronder hangt scheef, en dat is precies
   wat Mijn bitcoin liet zien zodra de regel hierboven erbij kwam: de knop
   stond ineens dichter bij de tekst die erna komt dan bij de tekst waar
   hij bij hoort. Ruimte om een knop is een paar, geen enkele regel. */
.trips-primary + .trips-hint,
.trips-quiet + .trips-hint{margin-top:14px}
.trips-back + .trips-open-title{margin-top:16px}


/* A trip, as the card Patrick asked for: the photo behind it, the country
   large across the bottom, and the year, the days and the kilometres under
   that. The whole card is the button — a thumb aims at a card, not at a word
   inside one — and everything in it is a span, because that is all a button
   may hold.

   The picture is laid in absolutely and the two bands of text sit over it,
   so the card is one box whose height does not depend on whether a photo
   arrived. A trip with no photo — which is every trip that exists today —
   changes nothing but the backdrop: the same panel colour, the same words in
   the same places. No grey box, no camera icon and nothing that reads as a
   fault.

   The top right is left empty on purpose. That is where the people who came
   along go, once there is a friends tab to get them from. */
/* A year of earlier trips, folded shut: the year, how many, and whose
   flags were in it. It is a row you tap, so it is a button — and it says
   what it holds, because a list that hides things without saying so is a
   list somebody thinks has lost their trip. */
.trip-year{
  display:flex;align-items:center;gap:10px;width:100%;
  margin-top:10px;padding:11px 14px;
  background:var(--fill-soft);border:1px solid var(--border);border-radius:14px;
  font-family:inherit;font-size:14px;color:var(--muted);text-align:left;cursor:pointer;
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
/* Only while it is a closed card: open, it loses its frame and becomes a
   heading, and a heading that lifts with a shadow under it reads as broken. */
.trip-year:not(.open):hover{
  transform:translateY(-1px);border-color:var(--brand-orange-ink);
  box-shadow:var(--shadow-lift);
}
@media (prefers-reduced-motion:reduce){.trip-year:not(.open):hover{transform:none}}
.trip-year-when{font-weight:800;font-size:15px;color:var(--text)}
.trip-year-count{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* min-width:0 so a long row of flags may shrink below its own content
   width and wrap, rather than forcing the bar wider than the screen. The
   line-height gives the second row somewhere to sit. */
.trip-year-flags{font-size:14px;letter-spacing:.02em;min-width:0;line-height:1.45}
/* The mark is the whole affordance on an open year — the bar loses its
   frame there, so this triangle is the only thing left saying "this folds".
   It is ▼/▲ rather than ▾/▴: the small pair stays narrow at any font size,
   and one of them at 18px still measured seven pixels of ink. */
.trip-year-mark{color:var(--muted);font-size:11px;line-height:1}
/* Open, it is a heading with a way to shut it again rather than a box:
   the cards below are the thing now, and a framed bar over them would be
   a second frame around the same content. */
.trip-year.open{
  background:none;border:none;border-radius:0;
  margin-top:14px;padding:0 2px 6px;
}
.trip-year.open .trip-year-when{font-size:14px;color:var(--muted)}
/* And it stands out on an open year rather than fading with the heading:
   it is the way back to a quiet list, and it is the one thing on that row
   somebody is meant to reach for. */
.trip-year.open .trip-year-mark{margin-left:auto;font-size:13px;color:var(--brand-orange-ink)}
.trip-year:hover .trip-year-mark{color:var(--brand-orange-ink)}
.trip-cards{display:flex;flex-direction:column;gap:12px}
/* De ronding van de kaart staat in een eigen token omdat drie lagen hem
   moeten delen: de kaart, de foto die eronder ligt en de wash die eroverheen
   valt. Stond op var(--radius-soft) en werd met de restyle van september 2026
   18px, de maat uit de mockup — en toen liep de foto met 14px hoeken in een
   kaart met 18px hoeken, wat je op een lichte foto ziet als een randje. */
.trip-card{
  --trip-card-radius:18px;
  position:relative;isolation:isolate;
  display:flex;flex-direction:column;justify-content:space-between;gap:14px;
  width:100%;text-align:left;overflow:hidden;
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--trip-card-radius);padding:14px;
  font-family:inherit;color:var(--text);cursor:pointer;
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
/* Lifts under a finger the way the site's own cards do, a soft shadow
   dropping behind it as it rises. The photo, the wash over it and the two
   bands of text are untouched — the card is the same card, it just floats. */
.trip-card:hover{
  transform:translateY(-2px);border-color:rgba(255,140,66,.55);
  box-shadow:var(--shadow-lift);
}
@media (prefers-reduced-motion:reduce){.trip-card:hover{transform:none}}
/* A card with a photo is given a floor to stand on so the picture is a
   picture rather than a strip behind the words. A card without one takes
   the height of what is in it: six trips exist today and none has a photo,
   and a column of hollow boxes waiting for one would be the screen sulking
   about it. */
.trip-card.has-photo{min-height:212px}
/* En een kaart zonder eigen foto krijgt er een van ons, sinds 10 september
   2026. Patrick: "als mensen daar geen foto's in plaatsen kan daar een
   voorbeeld foto in".

   Drie en niet één, want één beeld op elke kaart maakt van een lijst reizen
   een lijst dezelfde kaarten. Welke van de drie een reis krijgt staat vast
   voor die reis -- zie shotFor in web/js/trips.js -- zodat je lijst niet van
   gezicht wisselt zodra er iets anders op het scherm verandert. Zodra er een
   echte foto aan de reis hangt draagt de kaart `has-photo` en wint die.

   Herkomst, alle drie Unsplash en vrij te gebruiken, ook commercieel:
     bergen  Marek Piwnicki, "Misty mountain peaks at sunrise"
     kust    Evgeni Tcherkasski, "Sunset over cliffs and the sea"
     bos     Lianhao Qu, "A forest filled with lots of tall pine trees"
   Uitgesneden op 1,7:1, de verhouding van een kaart, met het donkere deel
   onderaan waar de landnaam staat. */
.trip-card[data-shot]{
  min-height:212px;
  background-position:center;background-size:cover;background-repeat:no-repeat;
}
.trip-card[data-shot="0"]{background-image:url(../assets/heroes/reis-bergen.webp)}
.trip-card[data-shot="1"]{background-image:url(../assets/heroes/reis-kust.webp)}
.trip-card[data-shot="2"]{background-image:url(../assets/heroes/reis-bos.webp)}
.trip-cover-image{
  position:absolute;inset:0;z-index:0;width:100%;height:100%;object-fit:cover;
  border-radius:var(--trip-card-radius);
}
/* What keeps white text readable over somebody's beach. Transparent at the
   top so the picture is still a picture, and nearly the page's own
   background where the words are. On a card with no photo it lies over the
   panel colour and lifts the bottom of it a shade, which is what stops a
   photo-less card reading as flat.

   A pseudo-element rather than a span: an empty element on a screen is
   exactly what test/trips-ui.test.mjs refuses to let through, and this one
   is decoration with no content in it by definition. The three z-indexes
   are the whole trick — picture, then wash, then words — and `isolation`
   keeps them inside the card rather than competing with the sheet. */
.trip-card.has-photo::after,
.trip-card[data-shot]::after{
  content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  border-radius:var(--trip-card-radius);
  background:linear-gradient(180deg,rgba(23,32,43,.10) 0%,rgba(23,32,43,.34) 34%,rgba(23,32,43,.74) 66%,rgba(23,32,43,.95) 100%);
}
.trip-card-top,.trip-card-bottom{position:relative;z-index:2;display:flex;gap:6px}
.trip-card-top{align-items:flex-start}
.trip-card-bottom{flex-direction:column}
.trip-card-name{font-size:12.5px;font-weight:700;color:var(--muted);overflow-wrap:anywhere}
/* Over somebody's photograph the two quiet lines are lifted off the muted
   grey: that grey is a colour for a flat panel, and the wash under it here
   is a sky. The loud line is white in both cases. */
.trip-card.has-photo .trip-card-name,
.trip-card[data-shot] .trip-card-name,
.trip-card.has-photo .trip-counts,
.trip-card[data-shot] .trip-counts{color:rgba(255,255,255,.86);opacity:1}
/* En de twee luide regels erboven, die op een kaart zonder foto de inkt van
   de pagina dragen (zie .trip-card-country) en op een foto het wit dat over
   de wash leesbaar is. */
.trip-card.has-photo .trip-card-country,
.trip-card[data-shot] .trip-card-country,
.trip-card.has-photo .trip-figure-value,
.trip-card[data-shot] .trip-figure-value{color:#fff}
/* The country, large and white, which is the line Patrick pointed at in the
   screenshot. It doubles as the trip's own name on a card that has no
   country to show — a trip with no places yet — so that the loud line of a
   card is never blank and never a placeholder. */
/* De landnaam, in Fraunces, in kapitalen, en de reden dat een reiskaart een
   kaart is en geen regel. 26px Mulish 800 was de naam van een land in de
   huisletter; dit is de naam van een land.

   Gewicht 900, de zwaarste snit die Fraunces heeft. Het stond een dag in
   Anton op 400 -- die letter levert één snit -- en Anton is smal waar
   Fraunces breed is, en daar zit de maat in: op 46px paste "ZWITSERLAND" in
   Anton nog op één regel en in Fraunces niet, en dan brak hij middenin het
   woord af. Gemeten in de browser over acht landnamen is 40px de grens
   waaronder elke naam van één woord past; een naam met een spatie erin
   ("Verenigde Staten") breekt gewoon op die spatie. Vandaar de clamp: 40 op
   een gewone telefoon, en naar beneden mee op een smalle, waar de kaart zelf
   ook smaller is.

   line-height mag onder de één, want kapitalen hebben geen staarten die in
   de regel eronder hangen. `overflow-wrap` blijft het vangnet voor de naam
   die dan nog niet past. */
.trip-card-country{
  font-family:var(--face-title);font-weight:900;
  font-size:clamp(26px,9.5vw,40px);line-height:.95;letter-spacing:.01em;
  text-transform:uppercase;
  color:var(--text);overflow-wrap:anywhere;
}
/* Behalve als het de reisnaam is en niet het land — een kaart van een reis
   zonder plekken. Dan is de luide regel een zin, en een zin is niet waar
   Anton voor is (zie `tripCardHtml` in web/js/trips.js). Terug naar de
   huisletter, op de maat die deze regel had voordat het land hem overnam. */
.trip-card-country-title{
  font-family:var(--face-body);
  font-weight:800;font-size:26px;line-height:1.15;letter-spacing:-.02em;
  text-transform:none;
}
/* The three figures: a number with a small word under it, side by side, in
   the order Patrick described — when, how long, how far. They are text and
   nothing but text, so a screen reader reads "5 dagen" straight off. */
.trip-figures{display:flex;flex-wrap:wrap;gap:18px;margin-top:8px}
.trip-figure{display:flex;flex-direction:column;gap:1px}
/* De drie cijfers in Anton, sinds de restyle van september 2026: dezelfde
   letter als de landnaam erboven, een stuk kleiner. De mockup zette ze zo,
   en het werkt om dezelfde reden als daar -- een smal, zwaar cijfer naast
   een smalle, zware naam leest als één blok, waar Mulish 800 ernaast een
   tweede stem was. De maat blijft de 19px die dit scherm al had: Anton heeft
   een hoge x-hoogte, dus op hetzelfde getal staat hij van zichzelf al iets
   groter dan de Mulish die er stond. Gewicht 400 -- zie --display-shout. */
.trip-figure-value{
  font-family:var(--face-title);font-weight:800;
  font-size:19px;line-height:1.1;letter-spacing:.01em;color:var(--text);
}
.trip-figure-label{font-size:11px;font-weight:600;color:var(--muted);white-space:nowrap}
/* The flag of the country a trip happened in, right behind its name — where
   Patrick asked for it. Several flags for a trip through several countries,
   in the order it went through them, and nothing at all for a trip with no
   places in it yet: an element that is not drawn cannot look broken. Marked
   role="img" with the country names as its label, so nothing reads it out
   as pairs of letters — except on a card, where the big line under it
   carries that label and the flags are hidden from anything reading aloud.
   Never wrapped: a flag is two code points and a line break between them is
   half a flag. */
.trip-flags{margin-left:7px;font-size:13px;white-space:nowrap}
.trip-flags-more{margin-left:3px;font-size:11px;font-weight:700;color:var(--muted)}
.trips-open-title .trip-flags{font-size:16px}
.trip-status{
  flex:0 0 auto;font-size:11px;font-weight:700;color:var(--muted);
  border:1px solid var(--border);border-radius:var(--radius-pill);padding:2px 9px;
}
/* Planned is the orange one: it is the only one of the three that is about
   something still to come, which is the thing a member opens this screen
   to look at. On the road borrows magenta's light tint; a trip that is over
   stays the quiet grey above. */
.trip-status[data-status="planned"]{color:var(--brand-orange-ink);border-color:rgba(255,140,66,.45)}
.trip-status[data-status="current"]{color:var(--brand-magenta-light);border-color:rgba(239,122,235,.45)}
/* Over a photograph the pill needs something to stand on: a sky is lighter
   than this app's own background, and a hairline in the muted grey
   disappears into one. The three status colours keep their meaning; what
   changes is the ground under them. */
.trip-card.has-photo .trip-status,
.trip-card[data-shot] .trip-status{
  background:rgba(23,32,43,.55);border-color:rgba(255,255,255,.30);color:rgba(255,255,255,.92);
}
.trip-card.has-photo .trip-status[data-status="planned"],
.trip-card[data-shot] .trip-status[data-status="planned"]{
  color:var(--brand-orange-light);border-color:rgba(255,168,107,.6);
}
.trip-card.has-photo .trip-status[data-status="current"],
.trip-card[data-shot] .trip-status[data-status="current"]{
  color:var(--brand-magenta-light);border-color:rgba(239,122,235,.6);
}
.trip-counts{margin-top:8px;font-size:12px;color:var(--muted);opacity:.9}

/* The travel statistics: four tiles in a two-by-two grid, each a mark, a
   number and the word for what it counts. Two columns at every width this
   app is used at — the grid is what makes them read as four of one thing
   rather than a list of four different things.

   The number is the loud part and the caption the quiet one, which is the
   same shape the figures on a trip card have; the two screens are about the
   same numbers and ought to look like it. */
.stat-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:16px 0 4px}
.stat-tile{
  display:flex;flex-direction:column;gap:2px;
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--radius-soft);padding:14px;
}
.stat-mark{font-size:20px;line-height:1.2;margin-bottom:5px}
/* En de tegels volgen de reiskaart naar Anton, want de regel hierboven zegt
   dat die twee schermen over dezelfde getallen gaan en op elkaar moeten
   lijken -- dan mag de een niet van letter wisselen zonder de ander. Het
   negatieve spatieren is eraf: dat hoorde bij Mulish, en Anton is van
   zichzelf al smal. */
.stat-value{
  font-family:var(--face-title);font-weight:800;
  font-size:26px;line-height:1.1;letter-spacing:.01em;color:var(--text);
}
/* The kilometres tile when there was nothing to measure. Words, and set as
   words: the loud white numerals are the type of a figure somebody counted,
   and a sentence in them would be a sentence claiming to be one. */
.stat-value-word{
  /* Terug naar de huisletter, en dat is nu de helft van waar deze klasse
     voor is. Anton roept namen en getallen; dit zijn woorden, en woorden in
     die letter zijn een zin die zich voordoet als een cijfer -- precies wat
     de regel hierboven al zei toen het nog om de maat ging. */
  font-family:var(--face-body);
  font-weight:600;font-size:15px;line-height:1.25;letter-spacing:0;color:var(--muted);
}
.stat-label{font-size:11.5px;font-weight:600;color:var(--muted)}
/* The bitcoin sign is a letter, not an emoji, so it needs the brand's own
   orange to carry the same weight the three emoji beside it do. It is a
   bitcoin mark and never a pin: Patrick asked for that outright, and it is
   the one tile that says what this app is. */
.stat-btc{font-weight:800;color:var(--brand-orange-ink)}
/* Under the four tiles: the flags of every country, the six continents
   with the visited ones lit, and the button that turns it all into a
   picture (web/js/stats-card.js). */
.stat-heading{display:flex;align-items:baseline;gap:10px;margin:22px 0 8px;font-size:15px;font-weight:800}
.stat-heading-count{font-size:12.5px;font-weight:600;color:var(--muted)}
.stat-flags{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:6px 8px}
.stat-flag{font-size:30px;line-height:1.15}
.stat-flag-name{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.stat-continents{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:1fr 1fr;gap:6px 10px}
.stat-continent{
  display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:12px;
  background:var(--panel);border:1px solid var(--border);color:var(--muted);font-size:14px;
}
.stat-continent.on{color:var(--text);font-weight:700;border-color:rgba(255,140,66,.55)}
.stat-continent-mark{color:var(--brand-orange-ink);font-weight:800}
.stat-share{
  display:block;width:100%;margin:18px 0 6px;padding:13px 18px;border:0;border-radius:var(--radius-pill);
  background:var(--brand-gradient);color:#fff;font-family:inherit;font-size:15px;font-weight:800;cursor:pointer;
}
.stat-share:hover{background:var(--brand-gradient-lit)}

/* The forms. Native fields, deliberately — a phone's own date wheel hands
   back exactly the YYYY-MM-DD the server stores. 16px on every input,
   which is the size below which iOS Safari zooms the page on focus; the
   search box on the globe is 16px for the same reason. */
.trips-form{
  margin-top:14px;padding:14px;
  background:var(--fill-soft);
  border:1px solid var(--border);border-radius:var(--radius-soft);
}
.trips-form-title{font-family:var(--face-title);margin:0 0 10px;font-size:15px;font-weight:800}
.trips-label{display:block;margin:12px 0 6px;font-size:12px;font-weight:700;color:var(--muted)}
/* Een donker veld, dus witte tekst. Het veld is donker gebleven bij de
   restyle van 10 september 2026, maar `--text` werd toen de donkere inkt
   van de lichte pagina: wat je typte was net zo donker als het veld zelf
   (Patrick, 11 september, met een screenshot van "Groene Heuvels" dat je
   nauwelijks zag). Vaste witten en geen token, want dit veld heeft niets
   met de pagina eromheen te maken. `color-scheme:dark` zodat de datumkiezer
   en de select van iOS hun eigen tekst ook licht tekenen. */
.trips-input{
  display:block;width:100%;box-sizing:border-box;
  background:rgba(23,32,43,.85);border:1px solid var(--border);
  color:#fff;caret-color:#fff;color-scheme:dark;
  border-radius:var(--radius-soft);padding:11px 13px;
  font-family:inherit;font-size:16px;line-height:1.2;
}
.trips-input::placeholder{color:rgba(255,255,255,.55)}
.trips-input:focus{outline:none;border-color:rgba(255,140,66,.65)}

/* Which of the two kinds of place this is: a real choice, shown as one. */
.trip-kinds{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:4px}
.trip-kind{
  background:none;border:1px solid var(--border);color:var(--muted);
  border-radius:var(--radius-pill);padding:7px 14px;
  font-family:inherit;font-size:12.5px;font-weight:600;line-height:1.2;cursor:pointer;
  min-height:44px;
}
.trip-kind.on{color:var(--brand-orange-ink);border-color:rgba(255,140,66,.55)}

/* The rows under either picker. */
.trip-picks{display:flex;flex-direction:column;gap:6px;margin-top:8px}
.trip-pick{
  display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  background:none;border:1px solid var(--border);border-radius:var(--radius-soft);
  padding:9px 12px;font-family:inherit;color:var(--text);cursor:pointer;
}
.trip-pick:hover{border-color:rgba(255,140,66,.55)}
.trip-pick-name{font-size:13.5px;font-weight:700;overflow-wrap:anywhere}
.trip-pick-where{font-size:12px;color:var(--muted)}
.trip-chosen{
  margin:8px 0 0;font-size:13px;line-height:1.5;color:var(--text);
}

/* The places of a trip, in the order they were travelled. The number down
   the left is the leg, not a rank — it is what the kilometres will be
   walked along. */
.trip-places{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}
.trip-place{
  display:flex;align-items:center;gap:10px;
  background:var(--fill-soft);border:1px solid var(--border);
  border-radius:var(--radius-soft);padding:10px 12px;
}
.trip-place-index{
  flex:0 0 auto;width:22px;height:22px;border-radius:var(--radius-pill);
  display:flex;align-items:center;justify-content:center;
  background:var(--fill);color:var(--muted);font-size:11px;font-weight:700;
}
.trip-place-body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.trip-place-name{font-size:13.5px;font-weight:700;overflow-wrap:anywhere}
/* De plaats achter de naam van een stop. Rustiger dan de naam zelf: het
   is waar het is, niet wat het is. */
.trip-place-town{color:var(--muted);font-weight:400}
.trip-place-day{font-size:12px;color:var(--muted)}
/* The one mark that says this app is not a travel app: the place takes
   bitcoin, and it is counted for the statistics tile. */
.trip-btc{margin-left:6px;font-size:12px;color:var(--brand-orange-ink)}
.trip-place-controls{flex:0 0 auto;display:flex;gap:4px}
/* Three controls on one row of a 343px card. 38px is the biggest square
   that leaves the place's own name a readable column beside them; it is
   still under the 44pt a phone's guidelines ask for, which is the price of
   putting the order controls on the row they act on. */
.trip-move{
  width:38px;height:38px;display:flex;align-items:center;justify-content:center;
  background:none;border:1px solid var(--border);color:var(--muted);
  border-radius:var(--radius-pill);font-family:inherit;font-size:13px;line-height:1;cursor:pointer;
}
.trip-move:hover{border-color:rgba(255,140,66,.55);color:var(--text)}

/* Who is on the trip, in the style of .trip-place above. */
.companions{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.companion{
  display:flex;align-items:center;gap:10px;padding:9px 12px;
  background:var(--fill-soft);border:1px solid var(--border);border-radius:12px;
}
.companion-name{font-weight:700;font-size:14px}
.companion-role{flex:1 1 auto;color:var(--muted);font-size:12px}
/* The code is copied by hand or read out. Monospaced and wide-spaced for
   the same reason the recovery code is. */
.companion-code{
  margin:12px 0 8px;padding:12px;border-radius:10px;text-align:center;
  background:rgba(0,0,0,.35);border:1px dashed var(--border);
  font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:18px;font-weight:700;letter-spacing:.12em;
  user-select:all;-webkit-user-select:all;
}

/* ------------------------------------------------------------------
   The photos of a place.

   A second row inside .trip-place rather than a block of its own: the row
   above wraps, and this takes the whole of the line under it. That keeps
   the name, the date and the three order controls exactly where they were
   — a place with no photos is the same row it has always been, because an
   element that is not drawn takes up nothing.
   ------------------------------------------------------------------ */
/* The place a tap on one's own pin on the activity globe arrived at. Not a
   selection and not a warning: the brand's own accent, quietly, so the eye
   lands on the right row without the row shouting. */
.trip-place-arrived{border-color:var(--brand-orange-ink);background:var(--fill)}
.trip-place{flex-wrap:wrap}
/* Sideways, not wrapping — the same choice already made for a friend's
   activity feed (.act-place-thumbs below): a row that grows wide instead
   of tall keeps the card's height the same whether a place holds one photo
   or ten, and a horizontal drag is the platform's own gesture for a strip
   like this one. Nothing here sets touch-action on it for that reason —
   see the exhaustive touch-action:none allowlist in test/globe-layout.
   test.mjs, which is the test a well-meaning "gestures need touch-action:
   none" edit would run into, and which this element deliberately stays
   off of. */
.trip-photos{flex:1 0 100%;display:flex;gap:6px;margin-top:9px;overflow-x:auto}
/* 64px squares: four of them, their gaps, and the row's own padding come to
   282px inside a 343px card at 375px wide, with a sliver of a fifth
   hinting there is more to drag into view — room enough that a thumb aims
   at a picture rather than at a stamp before the strip starts scrolling.
   The picture is cropped into the square rather than letterboxed, so a
   strip of a portrait and a landscape photo reads as a strip and not as a
   ladder. */
/* position:relative is what holds the heart in the corner of the picture.
   Without it the badge is absolutely positioned against whatever ancestor
   happens to be positioned — which, on 1 September 2026, put it floating in
   the middle of the screen well below the strip. .act-photo has had this
   since likes existed; this square did not need it until it gained one. */
.trip-photo{
  position:relative;width:64px;height:64px;padding:0;flex:0 0 auto;overflow:hidden;
  background:var(--fill);border:1px solid var(--border);
  border-radius:10px;cursor:pointer;
}
.trip-photo:hover{border-color:rgba(255,140,66,.55)}
.trip-photo img{width:100%;height:100%;object-fit:cover;display:block;border-radius:9px}
/* The way to add one, the same square with a plus in it — dashed, because
   it is an empty slot rather than a picture: the outline says "something
   goes here" where the solid ones beside it say "something is here".
   It sits at the end of the strip like any other square rather than
   pinned somewhere always in view: reaching it is the same drag that
   reaches the tenth photo, which is the familiar shape of "add" at the
   end of a camera roll rather than a control that needs a place of its
   own. Once a place is full there is nothing left to reach, so it is not
   drawn at all — see `room` in `photoStripHtml`, web/js/trips.js. */
.trip-photo-add{
  position:relative;
  width:64px;height:64px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  border:1px dashed var(--border);border-radius:10px;
  color:var(--muted);font-size:22px;font-weight:400;line-height:1;cursor:pointer;
}
.trip-photo-add:hover{border-color:rgba(255,140,66,.55);color:var(--text)}
/* A keyboard lands on the input, which is invisible — so the ring goes on
   the label around it, which is the thing that is actually on screen. */
.trip-photo-add:focus-within{border-color:rgba(255,140,66,.8);color:var(--text)}
/* The input itself is never seen: the label around it is the control. Not
   display:none, which takes it out of the accessibility tree along with the
   name it carries — and positioned inside the label, so a keyboard that
   lands on it does not scroll the page to a corner. */
.trip-photo-file{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
.trip-photo-busy{flex:1 0 100%;margin-top:0}

/* One photo, over everything.

   Fixed to the viewport rather than placed in the panel, so it covers the
   tab bar and the header too: a photo being looked at is the whole screen,
   and half a screen of chrome around it is half a screen of distraction.
   The backdrop is a real button filling the layer, so a tap anywhere off
   the picture closes it. */
.photo-viewer{
  /* z-index 900: over the language switcher (800), the bottom sheet (700)
     and everything on the map. This used to end with "so nothing may come
     out in front of it" — measured wrong. `.photo-viewer` is not a sibling
     of `.tabbar`/`.footer`; it is built into `#tab-me`'s own innerHTML
     (tripsScreenHtml, web/js/trips.js), and `.me` — position:absolute,
     z-index 750, below in this file — is the nearest positioned ancestor
     between it and the document root. Nothing between `#tab-me` and the
     root forms a stacking context of its own (`main.screen` is
     position:relative with z-index auto, which does not), so `#tab-me` —
     carrying `.photo-viewer` inside it — competes directly in the *root*
     stacking context, at a positive stack level, against `.tabbar` and
     `.footer`, which are never positioned and never carry a z-index at
     all. A positioned, positive
     z-index box always paints after a plain in-flow one, so on paper this
     was never a close race: confirmed in-browser (375×812, a portrait
     photo, German labels, Schließen's own screen position deliberately
     put on top of the tab bar's) with `elementFromPoint` at the pixel
     where the two overlap — the close button, not the tab bar, comes
     back, every time. z-index 900 does win the paint order, and always
     did; ffc924a's own elementFromPoint check was right about that much.

     What that check could not see is `position:fixed` itself. On a real
     phone `inset:0` is sized against whichever viewport Safari happens to
     be using for a fixed box at that moment — not necessarily the one
     that is actually on screen while its own address bar and toolbar are
     showing, which is the default state here since this page never
     scrolls. A box computed a little taller than what is actually visible
     needs no stacking bug at all to explain a button pinned near its own
     bottom edge going dark: the pixels are simply drawn past the edge of
     what the glass is showing. Nothing in this repository can force that
     mismatch to reproduce — there is no real Safari or toolbar chrome
     here — so this is reasoned from the evidence (the stacking math is
     provably fine; the one thing the previous fix and this one cannot
     measure is exactly the one thing left) rather than seen directly.

     Either way the fix does not lean on `.photo-viewer`'s own outer box
     being exactly the height it thinks it is. `--tab-height` and
     `--footer-height` are already measured from the real elements, not
     assumed from the viewport (syncFooterHeight/syncTabHeight, web/js/
     main.js) — `.sheet` already trusts them for the same reason
     (bottom:calc(...) below, at .sheet). The padding here borrows that
     same measured pair so the tools row's own reserved space is never
     closer to the bottom of the *content* than a full tab bar and footer,
     regardless of what the box's own edge turns out to be on the phone
     that opened it. */
  position:fixed;inset:0;z-index:900;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;
  padding:20px 20px calc(20px + var(--tab-height) + var(--footer-height)) 20px;
  background:rgba(23,32,43,.94);
  /* This box paints itself the colour of the stage, so it has to hand its
     children the stage's own tokens too -- exactly what .globe-stage does
     above, and for the same reason. Without this, everything inside took
     the light set the rest of the app runs on: the cross, both arrows,
     "Sluiten" and the heart all came out #17202b on a #17202b ground, and
     the count pill and the likes line came out one step lighter and barely
     better. Patrick, on 10 September 2026, with a screenshot: "de teksten
     zijn niet goed zichtbaar op deze donkere achtergrond".

     It is a redefinition and not a list of colours on each class on
     purpose. The next control dropped into this viewer inherits a readable
     set by being here, rather than by somebody remembering to give it one
     -- which is the mistake this very screen just made twice, once with
     the wallet mark on the Bitcoin tab and once here. */
  color:var(--on-stage);
  --text:var(--on-stage);
  --muted:var(--on-stage-muted);
  --panel:#20283a;
  --panel-soft:#1a2230;
  --border:rgba(255,255,255,.14);
  --border-soft:rgba(255,255,255,.08);
  --fill:rgba(255,255,255,.06);
  --fill-soft:rgba(255,255,255,.035);
  --fill-strong:rgba(255,255,255,.10);
  --shadow-rest:0 10px 30px rgba(0,0,0,.45);
  --shadow-lift:0 16px 40px rgba(0,0,0,.55);
  --brand-orange-ink:var(--brand-orange);
}
.photo-viewer-backdrop{
  position:absolute;inset:0;width:100%;height:100%;
  background:none;border:none;padding:0;cursor:pointer;
}
/* Above the backdrop, and never wider or taller than what is left of the
   screen once the buttons under it have had their share.

   That share used to be a guessed constant — `max-height:calc(100% - 70px)`
   — which was really only ever a bet on how tall `.photo-viewer-tools`
   would turn out to be. It is not one row: the count pill, the likes line,
   the cover note or button and the remove/close buttons all land in there,
   wrapping onto two or three lines depending on how many of them are drawn,
   how long the likes line's names run, and which of the four languages is
   on screen. 70px covered one button on one line and nothing else — every
   photo shot in portrait (which is most of them; a phone held upright) is
   tall enough to hit that ceiling, at which point the tools row is however
   tall its real content needs and the reserved 70px is simply wrong. The
   picture then sits 70px above the bottom of the screen and the tools
   underneath it are pushed down into the band the tab bar occupies
   underneath the viewer — measured, not assumed: with a tall photo and
   this row at its usual height the tools already overlapped that band by
   tens of pixels, and a row one line taller (a longer likes line, a wider
   language) pushed the last button past the bottom of the screen
   altogether. The tab bar itself stays hidden either way — the viewer's
   own z-index sees to that — but "the buttons are pinned against the
   bottom edge with none of the margin every other edge of this dialog
   gets" is exactly what Patrick's screenshot showed.

   `flex:1 1 auto;min-height:0` asks for none of that arithmetic: the image
   simply claims whatever the flex column has left after `.photo-viewer-
   tools` (given `flex:0 0 auto` below, so it is never asked to shrink)
   takes its own real height, however many lines that turns out to be.
   `object-fit:contain` still letterboxes the picture inside whatever box
   that leaves, so a landscape photo that never needed all that room looks
   exactly as it did before — only a tall one is now actually kept off the
   tools instead of being trusted to fit under a number nobody remeasured. */
.photo-viewer-image{
  position:relative;flex:1 1 auto;min-height:0;max-width:100%;
  object-fit:contain;border-radius:var(--radius-soft);
  /* This is the element the swipe drags (see photo-swipe.js) — the whole
     picture, not a handle beside it — so the browser must not read a
     sideways finger movement across it as a gesture of its own (a page
     pan, an edge-swipe-back) before this app's own pointer handling gets a
     say. .photo-viewer never scrolls or is pinch-zoomed today, so nothing
     here is being taken away — but see the exhaustive allowlist in
     test/globe-layout.test.mjs, which this selector had to be added to on
     purpose rather than by accident. */
  touch-action:none;
}
/* Patrick, on his phone, once the "Sluiten" button under the picture had
   twice been the one thing his screenshots showed cut off: "waar kan ik de
   foto sluiten na het bekijken op groot formaat? kruisje toevoegen rechts
   bovenin de foto?" A cross at the corner is the reach everybody already
   has for this, and it does not replace the button under the picture —
   two ways off a full-screen view is right, and the one that already
   works stays exactly as it was.

   Positioned the same way the two arrows below are: absolute within
   `.photo-viewer` itself, inset from its own top-right corner rather than
   the picture's true (and, for a letterboxed landscape photo, narrower)
   rendered edge — the arrows have ridden the same approximation since the
   swipe shipped, and getting pixel-exact against `object-fit:contain`'s
   own letterboxing would need a resize observer for a few pixels nobody
   has ever asked about. In `photoViewerShell` itself rather than in
   either caller's own `tools`, so both the trips viewer and the
   activity one get it from the one place that already exists precisely
   so a promise like this is not kept twice — see the module comment at
   the top of this file's photo-viewer.js.

   Deliberately outside `.photo-viewer-image`, not a child of it: it sits
   over the same drag surface the swipe uses, and photo-swipe.js's own
   pointerdown handler only ever engages for a gesture whose target is
   *inside* `.photo-viewer-image` (`event.target.closest(selector)`,
   web/js/photo-swipe.js). Kept a sibling, a tap here never enters that
   handler at all — nothing to suppress, nothing to swallow, the same
   "never dragged" safety the arrows already have below rather than a new
   guard invented for a fourth button. */
.photo-viewer-x{
  position:absolute;top:12px;right:12px;
  width:36px;height:36px;padding:0;display:flex;align-items:center;justify-content:center;
  background:rgba(23,32,43,.72);border:1px solid var(--border);border-radius:var(--radius-pill);
  color:var(--text);font-size:16px;line-height:1;cursor:pointer;
}
.photo-viewer-x:hover{border-color:rgba(255,140,66,.55)}
/* The two arrows and the count that ride over the picture. Neither is
   touch-action:none: an arrow is tapped, never dragged, and the rule the
   drag itself needs already sits on .photo-viewer-image above. */
.photo-viewer-arrow{
  position:absolute;top:50%;transform:translateY(-50%);
  width:36px;height:36px;padding:0;display:flex;align-items:center;justify-content:center;
  background:rgba(23,32,43,.72);border:1px solid var(--border);border-radius:var(--radius-pill);
  color:var(--text);font-size:13px;cursor:pointer;
}
.photo-viewer-arrow:hover{border-color:rgba(255,140,66,.55)}
.photo-viewer-prev{left:12px}
.photo-viewer-next{right:12px}
/* Where the picture sits in the set — see the comment beside `count` in
   photoViewerHtml, web/js/trips.js, for why this exists at all. Sized and
   coloured like .photo-viewer-note beside it: a fact about the photo, not
   an action, so it wears the same quiet pill rather than a button's. */
.photo-viewer-count{
  display:inline-flex;align-items:center;border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:9px 16px;
  font-size:12.5px;font-weight:600;line-height:1.2;color:var(--muted);
}
/* flex:0 0 auto — its real content height, never shrunk to make room for
   the picture above it. See the comment on .photo-viewer-image: that is
   the element that now gives way to this one, not the other way round.

   A column of at most two things: the likes line (see .photo-viewer-likes)
   and the row of controls under it (see .photo-viewer-row), never side by
   side — a sentence of names sharing a line with a row of pills and
   buttons is exactly the layout Patrick's screenshots showed breaking, with
   "Nog geen vind-ik-leuks" left floating between a pill it did not belong
   to and a note it did not belong to either. gap:14px between the two is
   the same spacing this app already puts under a heading and the row of
   actions beneath it (.trips-actions, .act-back) — not a new number
   invented for this one row. */
.photo-viewer-tools{position:relative;flex:0 0 auto;display:flex;flex-direction:column;align-items:center;gap:14px}
/* The likes line itself carries its own margin:0 (.act-like-line) — this
   wrapper exists only so photoViewerShell has something to omit outright
   when a caller passes no `likes` markup, rather than leaving an empty,
   gap-holding box in a row that has nothing to put in it. */
.photo-viewer-likes{margin:0}
/* The pills and buttons under the likes line — count, cover, remove,
   close, or heart and close, depending on which viewer this is. See
   equalizePhotoViewerButtons, web/js/photo-viewer.js, for which of these
   get pinned to a common width and why.

   align-items:center replaces the default (stretch): equalizeButtonWidths
   only ever pins width, never height, so if two children of this row have
   genuinely different natural heights they keep them and sit centred on
   the line rather than one being stretched to match the other's box.

   The child-margin reset below is what actually made "dezelfde grootte"
   true — Patrick's request, item 1 of this task. Without it, a `<p>`'s
   own browser-default margin (`.photo-viewer-count`) or a button's
   margin-top written for an entirely different layout it also appears in
   (`.act-quiet`, ordinarily stacked under a heading with `margin-top:12px`
   of its own) inflates that one child's outer box, which either stretched
   its neighbour to match under the old default or, even with centring,
   shoved it visibly down the line. Reset here, once, at the row itself,
   rather than on every class that might land in it — the double class
   is deliberate, not decoration: `.act-quiet` alone already carries the
   same specificity as a single-class reset would, so anything landing in
   this row that also owns a margin somewhere else in the sheet needs this
   selector to actually win. The next thing dropped into this row inherits
   a clean, centred slate rather than needing its own patch. */
.photo-viewer-tools .photo-viewer-row{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;align-items:center}
.photo-viewer-tools .photo-viewer-row>*{margin:0}
/* A photo that is already the face of its trip says so, in the same box and
   at the same size as the buttons beside it — rather than being offered a
   button that would change nothing. */
.photo-viewer-note{
  display:inline-flex;align-items:center;border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:9px 16px;
  font-size:12.5px;font-weight:600;line-height:1.2;color:var(--muted);
}

/* ------------------------------------------------------------------ *
   Tab 3, "Activiteit".

   The shape of the reference screen: the globe filling the upper part and a
   sheet under it. There is no second globe — the one behind this layer is
   the app's own, carrying friends' pins — so this layer is see-through
   above the sheet and lets taps through to it. Everything drawn inside
   takes them back, which is what .activity > * does.

   House rules hold throughout: hairline borders, generous radii, the muted
   grey for anything that is not the point of a line, magenta only as its
   light tint, and the brand gradient on exactly one control — which on this
   screen is "find your friends", the one thing an empty screen is for.
   ------------------------------------------------------------------ */
.activity{
  position:absolute;inset:0;z-index:740;pointer-events:none;
  display:flex;flex-direction:column;justify-content:flex-end;
}
.activity[hidden]{display:none}
.activity > *{pointer-events:auto}
/* The sheet. It stops well short of the top so the globe has the upper part
   of the screen to itself, and it scrolls inside itself rather than pushing
   the globe off — a phone in landscape, or a long list of people, must not
   turn this into a page.

   Split in two, the same shape as .sheet/.sheet-handle/.sheet-body for the
   country sheet: .act-sheet-wrap carries the rounded top edge, the border
   and the height, and holds two children — the handle and the scrolling
   body — so the handle has its own fixed strip of the top edge that never
   scrolls away with the list underneath it. web/js/act-sheet-drag.js is
   the only thing that ever writes to .act-sheet-wrap's height; at rest,
   with nobody's finger on the handle, nothing here is touched at all. */
/* Never more than this much of the screen, however long the list of people
   gets: the globe above it is the other half of this tab and must not be
   squeezed into a strip. An empty screen makes a short sheet and shows more
   of the globe, which is exactly the right way round. This is the whole of
   the default stop — act-sheet-drag.js never writes an inline height for
   it, only clears one, so somebody who never touches the handle sees
   exactly this rule and nothing else.

   The cap is 46% *plus* the handle's own height, not 46% flat: .act-sheet
   (the scrolling body, below) is a flex child of this box and only ever
   gets what the handle leaves over, so if the cap stopped at a plain 46%
   the handle would be eating into the list's own room — a shorter default
   view than the app has today, which is exactly the one thing this feature
   must never do. Padding the cap by the handle's height instead means
   .act-sheet's own box comes out pixel-identical to what it was before the
   handle existed; the extra room comes from the globe's share of the
   screen, not the list's. */
.act-sheet-wrap{
  max-height:calc(46% + var(--act-sheet-handle-height));
  display:flex;flex-direction:column;overflow:hidden;
  background:var(--panel);border-top:1px solid var(--border);
  border-radius:20px 20px 0 0;box-shadow:var(--shadow-rest);
  transition:height .18s ease-out;
  /* Back to the light page. This drawer sits inside .globe-stage, which
     turns its whole inside dark for the globe's sake -- but this is not
     part of the window, it is the panel of things to read that happens to
     hang off it. So the tokens are set back to the page's own, and every
     rule inside reads the light set again without knowing either. */
  color:var(--text);
  --text:#17202b;
  --muted:#5a5f78;
  --panel:#ffffff;
  --panel-soft:#f1f1f4;
  --border:rgba(38,42,63,.13);
  --border-soft:rgba(38,42,63,.07);
  --fill:rgba(38,42,63,.04);
  --fill-soft:rgba(38,42,63,.025);
  --fill-strong:rgba(38,42,63,.07);
  --shadow-rest:0 1px 2px rgba(23,32,43,.05),0 10px 28px -18px rgba(23,32,43,.30);
  --shadow-lift:0 2px 4px rgba(23,32,43,.06),0 18px 40px -20px rgba(23,32,43,.38);
  --brand-orange-ink:#b85408;
}
/* While a finger or the mouse is actively dragging the handle, the height
   is set on every pointermove — with the transition still active, each of
   those updates would itself be animated, and the sheet would lag behind
   the finger instead of following it. Lifted again the moment the drag
   ends, so the snap to the resting stop still glides in — same reasoning
   as .sheet.dragging beside the country sheet's own handle. */
.act-sheet-wrap.dragging{transition:none}
/* The grab handle: a short rounded bar, centred on the sheet's top edge,
   where Patrick's screenshot pointed. touch-action:none is not optional —
   without it a finger down on the handle scrolls the sheet instead of
   dragging it, the exact bug that kept the activity pins unreachable for
   weeks before it was found (see tap.js). */
.act-sheet-handle{
  flex:0 0 auto;height:var(--act-sheet-handle-height);width:100%;
  border:0;padding:0;margin:0;background:none;cursor:grab;touch-action:none;
  display:flex;align-items:center;justify-content:center;
}
.act-sheet-handle::before{
  content:"";width:36px;height:4px;border-radius:var(--radius-pill);
  background:var(--border);
}
.act-sheet{
  flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:0 16px 20px;
}
/* Empty until a photo is opened, and then the whole screen. It is a box of
   its own rather than markup inside the sheet so the viewer is never
   clipped by the sheet's own scrolling. */
.act-viewer:empty{display:none}
.act-head{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
/* The line a screen opens with — "Activiteit", "Mensen zoeken",
   "Berichten" — and the first thing the restyle of September 2026 made
   bigger. It was 19px of the same Mulish 800 as the rest of the page,
   which is not a title so much as a slightly bold sentence: on a screen
   whose whole job is a search box and a list, nothing announced where you
   had landed.

   Playfair at 24px does announce it, and it does so without taking any
   room the old heading did not already have — 24px of serif over the same
   single line. The face is the reason it reads as a title rather than the
   size: a serif among an app set entirely in Mulish is a change of voice,
   and one change of voice is worth more than four more pixels.

   No negative tracking. Playfair is drawn tight already, and pulling a
   serif's letters together at 24px closes the counters that make it
   legible — the -.01em here was inherited from the Mulish version, where
   it belonged. `.act-search` below carries the 12px of air under this,
   which is why there is no bottom margin here: the gap belongs to the box
   that follows, so a screen without a search box does not keep it. */
.act-title{
  margin:0;font-family:var(--face-title);
  font-size:24px;font-weight:800;line-height:1.2;
}
.act-line{margin:10px 0 0;font-size:13.5px;line-height:1.6;color:var(--muted)}
/* The plain sentence the reference screen opens with. Set in the text
   colour rather than the muted grey: it is the answer to "what is on this
   screen", not a footnote to it. */
.act-nobody{color:var(--text);font-weight:600}
.act-quiet-line{opacity:.8}
.act-block{margin-top:16px}
.act-block-title{margin:16px 0 0;font-size:14px;font-weight:800}
/* The one primary action on this screen, and therefore the one thing on it
   wearing the brand gradient. */
.act-primary{
  display:inline-flex;align-items:center;margin-top:12px;
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  border-radius:var(--radius-pill);padding:10px 18px;
  font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.act-primary:hover{
  background:var(--brand-gradient-lit);
}
.act-quiet,.act-back,.act-do{
  display:inline-flex;align-items:center;background:none;border:1px solid var(--border);
  color:var(--text);border-radius:var(--radius-pill);padding:8px 15px;
  font-family:inherit;font-size:12.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.act-quiet{margin-top:12px}
/* The door to the Telegram group is a link drawn as a pill, so it gets the
   pill's look and none of a link's. On the signed-out screen it stands
   beside the sign-in button, so the two are spaced like a row. */
.act-join{text-decoration:none;margin-left:8px}
.act-join-alone{text-decoration:none;margin-left:0}
/* A back button always follows the screen's own title, which carries no
   margin of its own, so without this it sits hard against the heading —
   Patrick, on the Activiteit screen: "staat iets te dicht tegen de button".
   The 14px is the rhythm the app already uses between a heading and the row
   of controls under it (.trips-actions), not a number invented here. */
.act-back{margin:14px 0 12px}
.act-quiet:hover,.act-back:hover,.act-do:hover{border-color:rgba(255,140,66,.55)}
/* The one control that starts something — asking, or accepting. Orange
   rather than the gradient: the gradient belongs to the primary action of
   the screen, and a list of twenty people cannot have twenty of those. */
.act-do-on{color:var(--brand-orange-ink);border-color:rgba(255,140,66,.45)}
.act-do[disabled]{opacity:.5;cursor:default}
.act-do + .act-do{margin-left:6px}

/* A list of people: a face, a name, and what can be done about them. */
.act-people{list-style:none;margin:10px 0 0;padding:0}
/* One friend's own screen: who, and then everywhere they have been. */
/* ---- "Dat is zoveel in Bitcoin" -------------------------------------- *
   The slogan, as something on a screen. A button, because it is tappable:
   one tap swaps the speech bubble for "then versus now" and back. Both
   shapes wear the same skin so that swapping does not look like a different
   kind of thing appearing. */
.spend-bubble{
  display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  padding:8px 11px;font-family:inherit;cursor:pointer;
  color:var(--text);background:rgba(255,140,66,.08);
  border:1px solid rgba(255,140,66,.45);border-radius:var(--radius-soft);
}
.spend-bubble:hover{border-color:var(--brand-orange-ink)}
.spend-slogan{font-size:12px;color:var(--brand-orange-ink);font-weight:700}
.spend-line{font-size:13.5px;font-weight:600}
/* Then versus now: two blocks side by side, because the whole point is the
   comparison and a comparison stacked vertically is two facts. */
.spend-thenNow{flex-direction:row;gap:18px;align-items:flex-start}
.spend-block{display:flex;flex-direction:column;gap:1px}
.spend-when{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em}
.spend-figure{font-size:14px;font-weight:700}
.spend-sats{font-size:11.5px;color:var(--muted)}
.spend-moved{font-size:12px;font-weight:700}
.spend-moved.up{color:#4ade80}
.spend-moved.down{color:var(--brand-magenta-light)}
/* A booking whose own dollar conversion never happened (Task 6): neither
   shape of the bubble has anything to draw, so this is the third, plain
   one — always a <span>, never a button (see bubbleHtml's own comment),
   so it reads as quiet rather than as a control waiting for a tap. */
.spend-open{cursor:default;color:var(--muted);font-size:13px}

/* The form. The live price sits under the amount and is a link to that
   exchange: the member is looking at OKX's number, and the way to OKX is
   the honest thing to put behind it. */
.spend-form{display:flex;flex-direction:column;gap:8px;width:100%;margin-top:8px}
.spend-rate{margin:0;font-size:12px;color:var(--muted);line-height:1.5}
.spend-rate-none{color:var(--brand-magenta-light)}
.spend-rate-link{color:var(--brand-orange-ink);text-decoration:underline}
.spend-choices{display:flex;flex-wrap:wrap;gap:6px}

/* The word inside "Bedrag in euro" that opens the currency picker — the
   only door into it, per taak-5-schermen.md ("een streepje onder eur
   waardoor mensen weten dat ze die aan kunnen tikken"). Underlined so it
   reads as tappable the way a link does, in the app's ordinary text
   colour rather than the brand orange: this is not a link to anywhere,
   it is a control inside a sentence, and the orange is reserved for the
   one primary action on the screen (see .trips-primary, and the test in
   test/trips-ui.test.mjs that keeps it to exactly one). */
.spend-currency-word{text-decoration:underline;text-underline-offset:2px;cursor:pointer}

/* The currency picker: a full-screen sheet over the whole app, the same
   layer .photo-viewer already occupies (never both at once, so one
   z-index serves both) — see the note on `viewing` in main.js for how the
   header, footer and tab bar are made `inert` while either is open, and
   the note in main.js's keydown handling for why Escape closes this too.
   No `padding-bottom` of its own any more (see .currency-picker-body
   below) — the whole box is exactly the viewport, and only the part of it
   that scrolls needs to keep its last rows clear of the tab bar. */
.currency-picker{
  position:fixed;inset:0;z-index:900;
  display:flex;flex-direction:column;
  background:var(--bg);
  padding:16px 16px 0 16px;
}
/* Fix round 1: this box used to scroll as one piece with the list inside
   it, which meant scrolling a 170-currency list far enough carried the
   close cross and the search field off-screen with it — at that point,
   with the header/footer/tab bar all `inert` while this dialog is open,
   there was no visible way out of it at all. `flex:0 0 auto` here and
   `overflow-y:auto` on `.currency-picker-body` below is the fix: this box
   never shrinks and never scrolls, so the way out stays on screen exactly
   as long as the sheet itself does. */
.currency-picker-head{display:flex;flex-direction:column;gap:12px;flex:0 0 auto;margin-bottom:12px}
.currency-picker-head-top{display:flex;align-items:center;gap:10px}
.currency-picker-close{
  flex:0 0 auto;width:36px;height:36px;padding:0;display:flex;align-items:center;justify-content:center;
  background:var(--fill);border:1px solid var(--border);border-radius:var(--radius-pill);
  color:var(--text);font-size:16px;line-height:1;cursor:pointer;
}
.currency-picker-close:hover{border-color:rgba(255,140,66,.55)}
.currency-picker-title{font-family:var(--face-title);margin:0;font-size:16px;font-weight:800}
/* Its own full-width row under the cross and the title, not squeezed in
   beside them: crammed onto one row with both, the placeholder text this
   app's four languages actually carry ("Nach Name oder Code suchen" is 27
   characters) ran out of room and clipped mid-word on an ordinary 375px
   phone — checked by hand while building this, not assumed. A row of its
   own is right regardless of which language is showing or how narrow the
   phone is, so this is not conditional on either. */
.currency-picker-search{
  display:flex;align-items:center;gap:6px;
  background:var(--fill);border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:8px 12px;
}
.currency-picker-search-icon{font-size:13px;opacity:.7}
.currency-picker-input{
  flex:1 1 auto;min-width:0;background:none;border:none;color:var(--text);
  font-family:inherit;font-size:16px;line-height:1.2;padding:0;
}
.currency-picker-input:focus{outline:none}
.currency-picker-input::placeholder{color:var(--muted)}
/* The half of the sheet that actually scrolls — see the fix-round-1 note
   on .currency-picker-head above for the bug this split fixes.
   `min-height:0` is what lets a flex child shrink below its content's own
   height at all and actually scroll, rather than pushing the box (and the
   header pinned above it) taller than the viewport instead. The bottom
   padding is what `.currency-picker` itself carried before this split —
   moved here because it is the scrolling content's last rows that need to
   clear the tab bar and the footer, not the fixed header above them. */
.currency-picker-body{
  flex:1 1 auto;min-height:0;overflow-y:auto;
  padding-bottom:calc(16px + var(--tab-height) + var(--footer-height));
}
.currency-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
/* One row: the badge, the name, the rate underneath — Patrick's screenshot,
   minus the invented per-currency colour (see currencyBadge's own comment
   in web/js/spend.js for why .currency-badge below is one flat brand
   colour rather than a colour per currency). */
.currency-row{
  display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:none;border:none;border-radius:var(--radius-soft);padding:9px 8px;
  font-family:inherit;color:var(--text);cursor:pointer;
}
.currency-row:hover{background:var(--fill)}
/* The currency already chosen — see currencyRowHtml's own comment for why
   this is the row's only mark, the same .on every other chip on this
   screen already carries for "this one is the choice standing right now". */
.currency-row.on{background:rgba(255,140,66,.10)}
.currency-row.on .currency-row-name{color:var(--brand-orange-ink)}
.currency-badge{
  flex:0 0 auto;width:36px;height:36px;border-radius:var(--radius-pill);
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  background:var(--brand-gradient);color:#fff;font-size:12px;font-weight:800;
  padding:0 2px;
}
/* Bitcoin's own disc: the brand orange, not the gradient the other hundred
   and seventy share. Patrick asked for it by name on 1 September 2026 — "een
   oranje B" — and it is the one row on this list that is not a country's
   money. */
.currency-badge.btc{background:var(--brand-orange)}
.currency-row-info{display:flex;flex-direction:column;gap:1px;min-width:0}
.currency-row-name{font-size:13.5px;font-weight:600;overflow-wrap:anywhere}
.currency-row-rate{font-size:12px;color:var(--muted)}
.currency-picker-note{text-align:center;margin-top:12px}

/* The three screens of the book, and the rows on them. */
.spend-views{margin:2px 0 10px}
/* The privacy line above these chips is a .trips-hint, whose own bottom
   margin is 0 — so without this the two sat 2px apart, the closest thing
   to touching this app has. Same 14px `.trips-hint + .trips-primary`
   already puts under an open trip's own hint, not a new number. */
.trips-hint + .spend-views{margin-top:14px}
.spend-day{
  display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  margin:14px 0 4px;padding-bottom:4px;border-bottom:1px solid var(--border);
}
.spend-day-when{font-size:13px;font-weight:700}
.spend-day-total{font-size:13px;color:var(--muted)}
/* The book's own little map. The slot is the hole the map view leaves in
   the panel; the map itself is a long-lived element that gets moved into
   it, so its size is set on the map and the slot only reserves the room. */
.spend-map-slot{min-height:220px;margin:0 0 12px}
.spend-map{height:220px;border-radius:14px;overflow:hidden;background:#0b1622}
.spend-map[hidden]{display:none}
/* A marker is a label, not a pin: it sits over the place rather than
   hanging from it, so the amount reads at a glance on a phone. */
.spend-mark-icon{width:auto!important;height:auto!important;margin:0!important}
.spend-mark{display:flex;flex-direction:column;align-items:center;transform:translate(-50%,-50%);
  white-space:nowrap;text-shadow:0 1px 3px rgba(0,0,0,.9)}
.spend-mark-sum{font-weight:700;font-size:13px;color:#fff}
.spend-mark-payers{font-size:9px;color:rgba(255,255,255,.78)}
.spend-mark-where{font-size:10px;color:rgba(255,255,255,.82)}
.spend-list{list-style:none;margin:0;padding:0}
.spend-entry{display:flex;align-items:center;gap:10px;padding:8px 0}
.spend-entry-bubble{list-style:none;padding:0 0 6px 30px}
.spend-entry-bubble:empty{display:none}
.spend-icon{font-size:18px;line-height:1;flex:0 0 auto}
.spend-what{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1 1 auto}
.spend-note{font-size:14px;font-weight:600;overflow-wrap:anywhere}
.spend-where{font-size:11.5px;color:var(--muted)}
.spend-sum{display:flex;flex-direction:column;align-items:flex-end;gap:1px;flex:0 0 auto;min-width:0}
.spend-amount{font-size:14px;font-weight:700;white-space:nowrap}
/* The currency actually paid, under the big home-currency figure above it
   (entryDisplayAmounts, web/js/spend.js) — only ever drawn when the two
   differ, so a booking paid in the viewer's own home currency never grows
   a second line to say the same thing twice. */
.spend-amount-paid{font-size:11px;color:var(--muted);white-space:nowrap}
/* Who paid, beside a shared place's amount — only ever drawn for more than
   one payer, so a place that is entirely the reader's own carries no name. */
.spend-payers{font-size:11px;color:var(--muted);text-align:right;overflow-wrap:anywhere}

/* The figures on the Cijfers tab. The same tiles the travel statistics use,
   so two screens of numbers in one app look like one app. */
.stat-sats{font-size:12px;color:var(--brand-orange-ink);font-weight:700}

/* What each category came to, as a bar you can compare at a glance. */
.spend-cats{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:7px}
.spend-cat{display:flex;align-items:center;gap:8px}
.spend-cat-name{font-size:12.5px;flex:0 0 auto;min-width:92px}
.spend-cat-bar{flex:1 1 auto;height:7px;border-radius:4px;background:var(--fill-strong)}
.spend-cat-fill{display:block;height:100%;border-radius:4px;background:var(--brand-gradient)}
.spend-cat-amount{font-size:12.5px;font-weight:700;white-space:nowrap}
.act-person-head{display:flex;align-items:center;gap:10px;margin:10px 0 4px}
.act-places{list-style:none;margin:8px 0 0;padding:0;display:flex;flex-direction:column;gap:8px}
/* A place a friend shared, on the same frosted glass as the people above it:
   a translucent fill, a hairline all round and a small lift under a finger,
   so the whole activity feed reads as one set of cards rather than a mix of
   filled rows and hollow outlines. Its contents and order are untouched. */
.act-place-open{
  display:flex;flex-direction:column;gap:4px;width:100%;text-align:left;
  background:var(--fill);border:1px solid var(--border);border-radius:var(--radius-soft);
  padding:11px 13px;font-family:inherit;color:var(--text);cursor:pointer;
  box-shadow:inset 0 1px 0 var(--fill);
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
.act-place-open:hover{
  transform:translateY(-1px);border-color:rgba(255,140,66,.35);
  box-shadow:var(--shadow-lift);
}
@media (prefers-reduced-motion:reduce){.act-place-open:hover{transform:none}}
.act-place-where{font-size:14px;font-weight:600}
.act-place-when{color:var(--muted);font-size:12px;line-height:1.4}
/* The pictures they took there, scrolling sideways rather than wrapping:
   four thumbnails across a 375px row is what fits, and a fifth belongs off
   the edge instead of on a second line nobody asked for. */
.act-place-thumbs{display:flex;gap:6px;margin-top:4px;overflow-x:auto}
.act-place-thumb{
  width:56px;height:56px;flex:0 0 auto;object-fit:cover;
  border-radius:8px;border:1px solid var(--border);
}
/* Each person is a frosted card now rather than a row over a hairline: a
   translucent fill, a hairline all the way round, and a small lift when a
   finger or cursor is on it, the way the site's own list rows read. The
   list keeps its order and its contents exactly — this is the same column
   of the same people, set on glass instead of divided by lines. */
.act-person{
  display:flex;align-items:center;gap:11px;flex-wrap:wrap;
  padding:11px 13px;margin-bottom:8px;border-radius:14px;
  background:var(--fill);border:1px solid var(--border);
  box-shadow:inset 0 1px 0 var(--fill);
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
.act-person:hover{
  transform:translateY(-1px);border-color:rgba(255,140,66,.35);
  box-shadow:var(--shadow-lift);
}
.act-person:last-child{margin-bottom:0}
/* A friend with a place on the globe is a way onto it. The row itself is
   the button, so the whole of it is the target rather than a word inside
   it — a thumb on a phone is wider than a name. */
.act-person-open{
  display:flex;align-items:center;gap:11px;flex:1 1 180px;min-width:0;
  background:none;border:none;padding:0;font-family:inherit;text-align:left;cursor:pointer;
  color:inherit;
}
.act-person-who{flex:1 1 120px;min-width:0;display:flex;flex-direction:column;gap:2px}
.act-person-name{font-size:14px;font-weight:700;color:var(--text);overflow-wrap:anywhere}
/* The Just B Free Family badge, in front of a Family member's own name —
   never the only thing saying who this is: the name right after it does
   that, which is why the image's own alt stays empty (see familyMarkHtml,
   activity.js, for where the accessible name and the tap both went
   instead). The padding/negative-margin pair only grows the tappable area
   — a 14px logo alone is too small for a thumb — without shifting the name
   sideways or the row's own height: the 4px on the right is the one margin
   that stays visible, the same gap this always had before the name. */
.act-family-mark-tap{
  display:inline-flex;align-items:center;vertical-align:-2px;
  padding:6px;margin:-6px 4px -6px -6px;
  background:none;border:none;border-radius:50%;cursor:pointer;
}
.act-family-mark-tap:focus-visible{outline:2px solid var(--brand-orange);outline-offset:1px}
.act-family-mark{width:14px;height:14px;border-radius:50%;display:block}
/* The mark's own explanation, after a tap — see showFamilyMarkNote,
   main.js. Appended straight into the row's `<li>` rather than drawn from
   state and a re-render (the same call `flashButtonLabel` nearby already
   makes: a one-off, transient message earns a scoped DOM edit more than a
   new field threaded through two screens' state), so `flex-basis:100%` is
   what puts it on a line of its own — `.act-person` is already
   flex-wrap:wrap, the same trick the row's own children rely on. The left
   padding lines its text up under the name rather than under the avatar
   (38px + the row's own 11px gap). */
.act-family-note{flex-basis:100%;margin:0;padding:2px 0 0 49px;font-size:12px;line-height:1.4;color:var(--muted)}
.act-person-handle{font-size:12px;color:var(--muted);overflow-wrap:anywhere}
.act-person-do{flex:0 0 auto;display:flex;align-items:center}
/* The face: the picture over the first letter of the name, so a picture
   that is blocked, expired or slow leaves a filled circle with a letter in
   it rather than an empty square. The same arrangement as .me-avatar. */
.act-avatar{
  position:relative;flex:0 0 auto;width:38px;height:38px;border-radius:var(--radius-pill);
  display:flex;align-items:center;justify-content:center;overflow:hidden;
  background:var(--brand-gradient);color:#fff;font-size:15px;font-weight:800;
}
.act-avatar-big{width:48px;height:48px;font-size:19px}
.act-avatar-photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* Searching the group. Sixteen pixels of type, like every other box in this
   app, because anything smaller makes an iPhone zoom the page on focus. */
.act-search{
  display:flex;align-items:center;gap:8px;margin-top:12px;
  background:var(--fill);border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:0 14px;
  /* Frosted glass, the way the site's own inputs sit: a hairline and a
     faint inner highlight over a translucent fill, so the panel behind it
     shows through instead of a flat grey box. */
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  box-shadow:inset 0 1px 0 var(--fill-strong);
}
.act-search:focus-within{border-color:rgba(255,140,66,.5)}
/* The loupe is the same line drawing the Zoek tab wears in the bar below,
   stroked in currentColor so it reads the muted grey of the box around it. */
.act-search-icon{flex:0 0 auto;display:flex;align-items:center;color:var(--muted)}
.act-search-icon svg{
  width:17px;height:17px;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.act-search-input{
  flex:1 1 auto;min-width:0;background:none;border:none;outline:none;
  color:var(--text);font-family:inherit;font-size:16px;padding:11px 0;
}
.act-search-input::placeholder{color:var(--muted)}

/* Tab 4, "Zoek". A plain page over the globe, like the second tab and not
   like the third: finding somebody has no globe of its own to show, so an
   opaque page is simpler than a sheet with nothing under it to see. Reuses
   activity.js's own person-row and search-box classes above rather than
   drawing new ones — see web/js/find.js for why — so the only rules this
   screen truly needs are its own overlay and two small overrides for the
   `.trips-quiet` buttons it draws instead of activity.js's `.act-do`. */
.find{
  position:absolute;inset:0;z-index:750;
  background:var(--bg);overflow-y:auto;padding:18px 16px 24px;
}
.find[hidden]{display:none}

/* Tab 5, "Bitcoin". The same plain opaque page as the second and fourth
   tabs — there is no globe of its own to show — a full-bleed panel the
   same colour as the ones either side of it, so a tap that lands here
   never shows a flash of the globe underneath before bitcoinPanel's
   `hidden` catches up. `overflow-y` and the padding are `.find`'s own
   pair of rules, copied rather than shared because the two screens have no
   other reason to point at one selector: this tab holds one block of
   affiliate links today (bitcoinScreenHtml, web/js/bitcoin.js) and needs
   the same room to scroll and breathe on a phone that any of this app's
   other opaque pages does. */
.bitcoin{
  position:absolute;inset:0;z-index:750;
  background:var(--bg);overflow-y:auto;padding:18px 16px 24px;
}
.bitcoin[hidden]{display:none}
/* The connect block's `.refs` carries a hairline and 22px of top margin
   because it always follows something — a country's guide, its list of
   places. The bitcoin block is the only thing on this tab, so there is
   nothing for the hairline to separate it from and the top margin would
   just be a gap under the page's own padding above. */
.bitcoin .refs{margin-top:0;border-top:none;padding-top:0}

/* `.act-do + .act-do` gives activity.js's own buttons their 6px gap; a
   `.trips-quiet` pair (LINK_INVITED's "accept" and "decline") needs the
   same gap and does not inherit that rule, since it is a different class
   with a different selector. Scoped to `.act-person-do` rather than made
   general, so it reaches only the rows this screen draws and never widens
   the gap between two `.trips-quiet` buttons anywhere else in the app. */
.act-person-do .trips-quiet + .trips-quiet{margin-left:6px}
/* The same disabled look `.act-do[disabled]` already gives activity.js's
   own buttons while a change is in flight, scoped the same way. */
.act-person-do .trips-quiet[disabled]{opacity:.5;cursor:default}

/* One pin, opened. */
.act-pin-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.act-pin-who{flex:1 1 140px;min-width:0}
.act-pin-name{margin:0;font-size:15px;font-weight:800;overflow-wrap:anywhere}
.act-pin-place{margin:2px 0 0;font-size:13.5px;color:var(--text);overflow-wrap:anywhere}
.act-pin-day{margin:2px 0 0;font-size:12px;color:var(--muted)}
/* Orange for a bitcoin place, white for anywhere else — the same two
   colours the pins on the globe wear, so the card and the pin cannot be
   read as saying different things. */
.act-mark{
  flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;
  border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:6px 12px;font-size:14px;font-weight:800;line-height:1;
}
.act-mark-word{font-size:11.5px;font-weight:600}
.act-mark-btc{color:var(--brand-orange-ink);border-color:rgba(255,140,66,.45)}
.act-mark-plain{color:var(--text)}
.act-counts{color:var(--text)}
.act-photos{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.act-photo{
  position:relative;width:92px;height:92px;padding:0;overflow:hidden;cursor:pointer;
  background:var(--fill);border:1px solid var(--border);border-radius:12px;
}
.act-photo img{width:100%;height:100%;object-fit:cover;display:block}
/* How many liked it, over the corner of the picture. Small, because it is a
   number about the photo and not the photo. */
/* The heart on the corner of a thumbnail. One rule for both strips: the
   Activiteit tab has had it since likes existed, and the member's own trips
   since 1 September 2026 — two rules would be two chances to drift. */
.act-photo-likes,
.trip-photo-likes{
  position:absolute;right:4px;bottom:4px;
  background:rgba(23,32,43,.78);border-radius:var(--radius-pill);
  padding:2px 7px;font-size:11px;font-weight:700;color:#fff;
}
/* A trip's squares are 64px against Activiteit's 92, so the badge is drawn
   a size down or it covers the picture it is about. */
.trip-photo-likes{right:2px;bottom:2px;padding:1px 5px;font-size:10px}

/* The heart, and who else pressed it. flex-direction:column and
   align-items:center already come from .photo-viewer-tools itself now —
   only the width cap is specific to this viewer, keeping a long list of
   names from stretching the dialog wider than it needs to be.

   The cap sits on the line of names and not on the column around it, which
   is where it used to be. On the column it also capped the row of controls
   underneath: four of them at a common width need 564px, the cap allowed
   520, so "Sluiten" wrapped onto a line of its own and centred against a
   different width than the three above it. Patrick's screenshot of
   10 September 2026. Capping the names alone keeps the reason the number
   exists and stops it reaching a row it was never meant to be about. */
.act-like-tools .act-like-line{max-width:520px}
.act-like{
  display:inline-flex;align-items:center;gap:8px;
  background:none;border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-pill);padding:9px 18px;
  font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.act-like:hover{border-color:rgba(255,140,66,.55)}
/* Magenta's light tint, never #d622d0 itself: the brand magenta as a small
   mark on this navy disappears, which is the rule the notice on the Ik tab
   already follows. */
.act-like-on{color:var(--brand-magenta-light);border-color:rgba(239,122,235,.45)}
.act-like-heart{font-size:15px;line-height:1}
.act-like-line{margin:0;font-size:12.5px;line-height:1.5;color:var(--muted);text-align:center}
.act-like-who{font-weight:700;color:var(--text)}
/* De fooiknop, naast het hartje. Precies dezelfde vorm, want het is
   dezelfde soort handeling op dezelfde rij: iets teruggeven aan wie de foto
   maakte. Alleen het merkteken ervoor is oranje, want dat is wat er
   werkelijk anders is -- hier gaat bitcoin doorheen en bij het hartje niet.

   Geen eigen breedte en geen flex hier: de rij trekt zijn knoppen na het
   tekenen even breed (equalizePhotoViewerButtons, web/js/photo-viewer.js),
   en een breedte in dit stylesheet zou daar tegenin werken. */
.act-tip{
  display:inline-flex;align-items:center;gap:8px;
  background:none;border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-pill);padding:9px 18px;
  font-family:inherit;font-size:13.5px;font-weight:600;line-height:1.2;cursor:pointer;
}
.act-tip:hover{border-color:rgba(255,140,66,.55)}
.act-tip-mark{font-size:15px;line-height:1;color:var(--brand-orange)}

/* ------------------------------------------------------------------ *
   The pins themselves, over the globe's canvas.

   Mounted by globe.gl exactly where the country pills are, and centred on
   their point the same way. --pill-scale is the globe's own narrow-stage
   factor and everything here is built from it, so what the declutter pass
   measured (PIN_SIZE_PX in globe.js) is what is really on screen.

   White when the place is not bitcoin-related, orange when it is. That is
   Patrick's rule and it is the whole visual grammar of this screen, so it
   is the ring — the thickest, most visible part of the pin — that carries
   it rather than a mark tucked into a corner.
   ------------------------------------------------------------------ */
.act-pin{
  width:calc(46px * var(--pill-scale));height:calc(46px * var(--pill-scale));
  padding:0;cursor:pointer;border-radius:var(--radius-pill);
  background:var(--panel);border:calc(3px * var(--pill-scale)) solid #ffffff;
  box-shadow:0 2px 8px rgba(0,0,0,.5);position:relative;
  display:flex;align-items:center;justify-content:center;
  font-family:inherit;font-weight:800;color:#fff;
  font-size:calc(15px * var(--pill-scale));line-height:1;
  /* The layer globe.gl mounts these in does not take clicks — the same
     reason .country-pill says this. Without it a pin is a picture you
     cannot touch: every tap falls straight through to the canvas, which
     answers with whatever country lies behind the pin. That is exactly
     what Patrick recorded twice: "als ik een pin wil aantikken, tikt het
     een land aan waar de pin boven hangt." */
  pointer-events:auto;
  /* And a finger on a pin is a tap on that pin, never a page gesture. */
  touch-action:none;
}
.act-pin-btc{border-color:var(--brand-orange-ink)}
/* Whose pin this is, said outside the ring rather than in it.

   The member's own places are on this globe from the first moment — a
   screen that stays blank until somebody else accepts is a poor way of
   showing what accepting would do, and on day one they are the only thing
   there. So the question "which of these is mine" has to have an answer at
   a glance, and it cannot be the ring: the ring is white or orange and that
   is Patrick's rule about bitcoin, not about people.

   A halo just outside it, in magenta's light tint. Never #d622d0 itself —
   the brand magenta as a thin line on this navy disappears, which is the
   rule the notice on the Ik tab and the filled heart already follow. It is
   a box-shadow, so it paints outside the circle without moving anything:
   the pin the declutter pass measures is still 46px * --pill-scale, and the
   halo lies inside the room the count badge and the tail already take up.
   The legend under the sheet says what it means, in every language. */
.act-pin-mine{
  box-shadow:0 2px 8px rgba(0,0,0,.5),
             0 0 0 calc(2px * var(--pill-scale)) var(--brand-magenta-light);
}
/* The picture is rounded into the circle by its own radius rather than by
   `overflow:hidden` on the button. The button has a tail and a badge that
   sit *outside* it, and hidden overflow would clip both of them away. */
.act-pin img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  border-radius:var(--radius-pill);
}
/* The little tail that makes a circle read as a pin on a map, in the ring's
   own colour — so white and orange stay one decision rather than two that
   can disagree. */
.act-pin::after{
  content:"";position:absolute;left:50%;bottom:calc(-7px * var(--pill-scale));
  width:0;height:0;transform:translateX(-50%);
  border-left:calc(5px * var(--pill-scale)) solid transparent;
  border-right:calc(5px * var(--pill-scale)) solid transparent;
  border-top:calc(8px * var(--pill-scale)) solid #ffffff;
}
.act-pin-btc::after{border-top-color:var(--brand-orange)}
/* How many more places are behind this one pin. Never a nought and never a
   "1": a pin that stands for one place says nothing extra. */
.act-pin-count{
  position:absolute;right:calc(-3px * var(--pill-scale));top:calc(-3px * var(--pill-scale));
  min-width:calc(17px * var(--pill-scale));padding:0 calc(4px * var(--pill-scale));
  border-radius:var(--radius-pill);background:var(--bg);
  border:1px solid var(--border);color:#fff;
  font-size:calc(10px * var(--pill-scale));line-height:calc(16px * var(--pill-scale));
  font-weight:700;text-align:center;
}

/* While the activity tab is up, the country's satellite map and everything
   that belongs to it are out of the way — this screen's upper half is the
   globe, and a map of Portugal lying over it would be a different screen
   showing through. Done here, in the stylesheet, and *not* by touching the
   app's state: the country stays open, its framing stays exactly where the
   visitor left it, and going back to the Globe tab shows it again untouched.
   The pins layer is left alone: it is inside the globe's own mount. */
body[data-tab="activity"] .country-map,
body[data-tab="activity"] .country-open,
body[data-tab="activity"] .map-notice,
body[data-tab="activity"] .search,
body[data-tab="activity"] .search-results,
body[data-tab="activity"] .back,
body[data-tab="activity"] .hint,
body[data-tab="activity"] .intro,
body[data-tab="activity"] .loading{display:none}

.leaflet-container{background:var(--stage);font-family:inherit}
@media (max-width:820px){
  .screen{flex-direction:column}
}
/* At phone widths the header's single row runs out of space: the brand, the
   counter and four flags cannot share 375px without the counter breaking
   into a ladder of single words. Two rows instead — the brand and the
   languages above, the counter centred across the full width below, which
   is the arrangement Patrick asked for and the one that leaves the counter
   room to wrap normally, at most two lines. */
@media (max-width:480px){
  .header{
    grid-template-columns:1fr auto;
    grid-template-areas:"brand me" "counter counter";
    row-gap:8px;padding:12px 16px;
  }
  .counter{justify-self:center}
  /* The badge comes down with the rest of the header's padding; the name
     does not, because it is not what is using the space. Measured at
     375x812: padding 16 + badge 30 + gap 8 + brand column 218 (the German
     tagline, the widest thing in it) = 272, and the language control
     starts at 307 — 35px of air between them. */
  .brand-logo{width:32px;height:32px}
  .brand{gap:8px}
}
/* The phones that are narrower still. At 320px the row has 182px for the
   brand column once the badge, the gaps and the language control have had
   theirs, and the name at 26px is 176px of that — true, but with nothing
   to spare. One step down puts 26px of air back, and Lobster at 23px is
   still a long way above the size where it turns to mush. */
@media (max-width:359px){
  .wordmark{font-size:23px}
}

/* Polarsteps-style country label: a white pill with the flag in front of the
   name. These are real HTML elements on top of the WebGL canvas, so they
   stay crisp at every zoom level. globe.gl already centres the returned
   element on its coordinate via its own inline transform — do not add
   another translate here, that would double up and knock the pill off. */
/* The pill's size follows --pill-scale, which globe.js sets on the mount
   element from the stage width: at desktop sizes it is 1 and nothing about
   the pill changes, on a phone-width stage the type, padding and gap all
   come down together so a pill stays a sensible fraction of a globe that
   is itself only a few hundred pixels across. The same scale is handed to
   estimatedPillWidth in globe.js, and a test compares the numbers below
   against the constants there — change one and change the other. */
:root{--pill-scale:1}
.country-pill{
  display:inline-flex;align-items:center;gap:calc(6px * var(--pill-scale));
  background:#fff;color:#262a3f;
  font-family:var(--face-body);
  font-weight:700;font-size:calc(12px * var(--pill-scale));line-height:1;white-space:nowrap;
  padding:calc(6px * var(--pill-scale)) calc(11px * var(--pill-scale));border-radius:var(--radius-pill);
  box-shadow:0 2px 10px rgba(0,0,0,.35);
  cursor:pointer;user-select:none;
  /* The layer globe.gl mounts these in does not take clicks, so the pill
     switches them back on for itself. Here rather than as an inline style
     in globe.js, so the rule below can take them away again. */
  pointer-events:auto;
  /* A finger on a pill is a tap on a country, never a page gesture: without
     this the browser is free to read it as the start of a pan or a
     page-level pinch, which fights the globe's own two-finger zoom. */
  touch-action:none;
}
/* While a touch gesture is under way on the globe — one that did not begin
   on a pill — the pills stop taking pointer events, so a second finger
   landing on one still reaches the canvas and the pinch it belongs to.
   OrbitControls only ever sees pointers that land on the canvas itself; a
   finger caught by a pill is a finger the gesture never gets, and the pinch
   silently becomes a one-finger rotate. globe.js adds and removes the class
   from the mount element. */
/* While a pinch is under way, nothing drawn on top of the globe takes a
   finger: a second finger landing on a pill or a pin is a finger the pinch
   never gets, and the gesture silently degrades into a one-finger rotate.
   A gesture that *started* on one of them is left alone — that is a tap. */
.globe-mount.gesturing .country-pill,
.globe-mount.gesturing .act-pin{pointer-events:none}
.country-pill:hover{background:#f7f6fb}
/* The chosen country wears the house orange, not the system light-blue it
   carried before: the selection is the app's own now, and the ring matches
   the orange border every other picked control in the app takes. The blue
   stays where it belongs -- the atmosphere around the globe's limb, which is
   the planet's own glow and not a state anything earns. */
.country-pill.on{box-shadow:0 0 0 2px var(--brand-orange),0 4px 14px rgba(255,140,66,.4)}

/* The same pill again, on the satellite map. Leaflet writes its own
   transform onto the element it positions, so the centring cannot live
   there: the marker is a zero-sized box at the coordinate and the pill
   inside it centres itself on that box. --pill-scale is set on
   .country-map by map.js, from the same function that sets it on the
   globe's stage, so a phone shrinks the pills on both screens alike. */
.map-pill{width:0;height:0}
.map-pill .country-pill{position:absolute;left:0;top:0;transform:translate(-50%,-50%)}

/* The names of towns and cities, drawn by this app rather than baked into
   a tile. That is the whole point of them: a raster label tile is a picture
   of text, resampled onto a phone screen at device-pixel-ratio 2 or 3 and
   soft ever after, which is what Patrick rejected twice. Real text stays
   sharp at any pixel ratio, exactly as the country pills above do.

   Same zero-sized-box trick as .map-pill, and for the same Leaflet reason.

   White on a dark halo rather than dark on a light one: this writing has to
   hold up over dark green forest, bright tan desert, grey city and open sea
   in the same view, and only a halo dense enough to separate the letters
   from whatever is behind them does that. Three shadows, not one — a tight
   dark ring for the letterforms and a wider, softer one to lift the whole
   word off a busy background. The colour is the app's own #17202b rather
   than black, so the map belongs to the same page as the panels around it.

   The type follows --pill-scale like everything else, and the two sizes are
   copied into city-labels.js, where the collision check needs to know how
   wide a name comes out; a test compares the numbers. */
.city-label{width:0;height:0}
.city-label span{
  position:absolute;left:0;top:0;transform:translate(-50%,-50%);
  font-family:var(--face-body);
  font-weight:700;font-size:calc(11.5px * var(--pill-scale));line-height:1;white-space:nowrap;
  color:#fff;letter-spacing:.01em;
  text-shadow:0 0 2px rgba(23,32,43,.95),0 0 6px rgba(23,32,43,.85),0 1px 2px rgba(23,32,43,.9);
}
/* The places Natural Earth ranks 4 or better — the names you would expect
   to see the moment a country opens. A size up and a weight up, so the map
   has a hierarchy rather than a field of identical words. */
.city-label.major span{font-weight:800;font-size:calc(13px * var(--pill-scale))}
/* The town somebody searched for. Written like a major city and lit in the
   app's own orange, because it is not merely a name on a map: it is the
   answer to the question that was just asked, and it has to be findable at
   a glance among the names that were already there. */
.city-label.searched span{
  color:var(--brand-orange-ink);font-weight:800;
  text-shadow:0 0 6px rgba(0,0,0,.9), 0 1px 2px rgba(0,0,0,.95);
}
.hint{
  position:absolute;left:0;right:0;bottom:14px;text-align:center;
  color:var(--muted);font-size:12.5px;pointer-events:none;
}
.hint[hidden]{display:none}
/* The three cards for a first visitor, at the foot of the globe's stage
   where the hint otherwise sits (see web/js/intro.js). Above the pills
   (globe.gl's HTML layer) and the search results, below the back button's
   600 so nothing here can ever cover a way out. pointer-events on the box
   itself are off and back on for the card, so the sky beside the card
   still turns the globe. */
.intro{
  position:absolute;left:0;right:0;bottom:12px;z-index:500;
  display:flex;justify-content:center;padding:0 14px;pointer-events:none;
}
.intro[hidden]{display:none}
.intro-card{
  position:relative;pointer-events:auto;width:100%;max-width:400px;
  background:rgba(38,42,63,.94);border:1px solid var(--border);border-radius:var(--radius-soft);
  padding:16px 18px 14px;box-shadow:var(--shadow-rest);
  display:flex;flex-direction:column;align-items:flex-start;
}
.intro-close{
  position:absolute;top:8px;right:8px;background:none;border:0;padding:6px 8px;
  color:var(--muted);font-family:inherit;font-size:14px;line-height:1;cursor:pointer;
}
.intro-close:hover{color:var(--text)}
.intro-dots{display:flex;gap:6px;margin-bottom:10px}
.intro-dot{width:7px;height:7px;border-radius:50%;background:var(--border)}
.intro-dot.on{background:var(--brand-orange)}
.intro-title{font-family:var(--face-title);margin:0 0 6px;padding-right:28px;font-size:17px;font-weight:800;line-height:1.25}
.intro-line{margin:0 0 12px;font-size:14px;line-height:1.45;color:var(--muted)}
.intro-next{
  padding:10px 18px;border:0;border-radius:var(--radius-pill);
  background:var(--brand-gradient);color:#fff;font-family:inherit;font-size:14px;font-weight:800;cursor:pointer;
}
.intro-next:hover{background:var(--brand-gradient-lit)}
.back{
  position:absolute;top:14px;left:14px;z-index:600;
  background:rgba(38,42,63,.85);color:var(--text);border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:8px 14px;
  font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;
}
.back[hidden]{display:none}

/* "In de buurt", als knopje in de zoekrij naast het wis-kruisje.
   Stond eerst rechtsboven (viel over de zoekbalk) en daarna midden onder de
   bol (nog een ding dat over de bol zweeft — op een telefoon werd het
   daar te druk). In de rij zelf zweeft er niets: hij duwt gewoon een paar
   pixels van het invoerveld af. */
.nearby{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;margin-left:2px;
  background:var(--fill);border:1px solid var(--border);border-radius:50%;
  color:var(--text);font-family:inherit;line-height:1;padding:0;cursor:pointer;
}
.nearby:hover{background:var(--fill-strong)}
.nearby[disabled]{opacity:.55;cursor:default}
.nearby[hidden]{display:none}
.nearby-mark{font-size:14px;line-height:1}
/* De melding onder de zoekbalk, waar de knop nu ook zit. */
.nearby-note{
  position:absolute;top:calc(var(--search-top) + 44px);left:0;right:0;z-index:600;
  width:fit-content;max-width:min(80%,340px);margin:0 auto;text-align:center;
  background:rgba(38,42,63,.95);border:1px solid var(--border);
  border-radius:14px;padding:9px 13px;
  color:var(--text);font-size:12.5px;line-height:1.45;
  box-shadow:var(--shadow-rest);pointer-events:none;
}
.nearby-note[hidden]{display:none}

/* The bottom sheet: comes up from the bottom edge to part of the screen and
   is dragged taller by its handle. It never covers the map completely. */
.sheet{
  /* Sits above the footer and the tab bar, not over either: the BTC Map,
     OpenStreetMap and Esri credits down there have to stay readable while a
     country is open, and a bar you cannot reach is not a bar. Both heights
     are measured by main.js and calc()'d in :root from the boxes' own
     padding/border/line-height tokens, so none of the three can silently
     drift apart. */
  position:fixed;left:0;right:0;
  bottom:calc(var(--footer-height) + var(--tab-height));z-index:700;
  height:40%;
  /* The tall stop is 85% of the screen, and 85% no longer fits above both
     of those: at 375x812 the sheet would stand 690px tall in a gap of 689px
     and reach up over the header with it. So the tall stop is capped at the
     room there actually is between the header and the credits — the drag
     still asks for 85%, this is what it can have. */
  max-height:min(85%, calc(100% - var(--header-height) - var(--footer-height) - var(--tab-height)));
  background:var(--panel);border-top:1px solid var(--border);
  border-radius:22px 22px 0 0;box-shadow:var(--shadow-rest);
  display:flex;flex-direction:column;
  /* Niets steekt over de afgeronde bovenrand of onder de onderrand uit. De
     body erbinnen scrolt zelf al (flex:1 1 0 plus overflow-y:auto); dit is
     de rand van het paneel, niet de scroll. */
  overflow:hidden;
  transition:height .18s ease-out;
}
.sheet[hidden]{display:none}
/* While a finger or the mouse is actively dragging the handle, the height
   is set on every pointermove — with the transition still active, each of
   those updates would itself be animated over 180ms, so the panel lags
   behind and chases the pointer instead of following it. sheet.js toggles
   this class for the duration of the drag only; the transition stays in
   place for open(), close() and the snap that follows release. */
.sheet.dragging{transition:none}
.sheet-handle{
  flex:0 0 auto;height:26px;cursor:grab;touch-action:none;
  display:flex;align-items:center;justify-content:center;
}
.sheet-handle::before{
  content:"";width:40px;height:4px;border-radius:var(--radius-pill);
  background:var(--border);
}
/* The search box's second home, directly under the handle: inside a country
   the box is not on the map any more but in the country's own information,
   which is what Patrick asked for so that the map stays clear. main.js moves
   the one form and its one results panel in and out of here; the slot is
   empty in the markup and empty whenever there is no country open.

   `flex:0 1 auto` with `min-height:0` is what makes a long list of results
   fit a sheet resting at 40% of the screen: the panel below shrinks and
   scrolls inside itself rather than growing past the sheet's bottom edge
   and over the credits. */
.sheet-search{
  flex:0 1 auto;min-height:0;display:flex;flex-direction:column;gap:8px;
  padding:0 18px 10px;
}
.sheet-search[hidden]{display:none}
/* In here the box is a row on a panel rather than a pill floating over a
   map, so the translucent dark fill it wears there — the same colour as
   this panel — would make it disappear. It wears the quiet fill and the
   hairline every list row in this sheet already wears instead, and keeps
   its pill shape, its 16px text and its clear button. */
.sheet-search .search{
  position:static;transform:none;width:auto;flex:0 0 auto;
  background:var(--fill-soft);box-shadow:none;
}
/* The results, in the flow under the box rather than floating over it: a
   panel that hangs out of the bottom of the sheet would lie over the
   footer's credits, and the sheet is the one panel in this app with a
   height of its own. */
.sheet-search .search-results{
  position:static;transform:none;width:auto;flex:0 1 auto;min-height:0;
  max-height:none;box-shadow:none;
}
/* While the results are up the country's own guide, counts and list step
   aside. At the sheet's resting stop there is not room for both, and a
   results panel with three rows showing is not a results panel — this is
   the search taking the sheet for as long as it is being used, and giving
   it straight back when the box is emptied or put away. */
.sheet.searching .sheet-body{display:none}
/* `flex:1 1 0`, not `1 1 auto`: with a basis of its own content — a guide
   and up to 200 places — this box would claim more than the sheet has and
   every other child would be shrunk to make room, which is what squashed
   the search box above to a sliver. A basis of zero means it takes what is
   left over and nothing more. */
.sheet-body{flex:1 1 0;min-height:0;overflow-y:auto;padding:4px 18px 22px}
.sheet-body h3{font-size:15px;font-weight:800;margin:14px 0 6px}
.sheet-body .guide-loading{font-size:13.5px;color:var(--muted);margin:14px 0 6px}
/* The list is still being fetched. Deliberately the same quiet, muted line
   as the guide's own loading text above: while both are on their way the
   sheet says so twice, and never says one of them has failed. */
.sheet-body .list-loading{font-size:13px;color:var(--muted);margin:0;padding:12px 14px}
.sheet-body .guide{font-size:13.5px;line-height:1.6;color:var(--text);margin:0}
.sheet-body .source{font-size:11.5px;color:var(--muted);margin:6px 0 0}
.sheet-body .source a{color:var(--brand-magenta-light)}
.sheet-body .counts{font-size:12.5px;color:var(--muted);margin:0 0 10px}
/* The line that says a list is only part of the country's total. It has
   to read as a statement, not as a warning, so it uses the same muted
   family as the counts above it rather than an alarm colour. */
.sheet-body .partial{font-size:12px;color:var(--muted);line-height:1.5;margin:0 0 10px}
/* A country's places, split into the five kinds Patrick asked to be able to
   tell apart. Each one is a <details>: a heading with a count that opens and
   closes, which the browser already knows how to do with a tap, with a
   keyboard and for a screen reader. */
.place-groups{display:flex;flex-direction:column;gap:10px}
.place-group{border-top:1px solid var(--border);padding-top:8px}
.place-group:first-child{border-top:none;padding-top:0}
/* The native disclosure triangle is replaced by a chevron of this app's own,
   in the house orange and on the right where the counts are — the same
   chevron the country pill over the map wears, pointing the same way. */
.place-group-head{
  display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;
  /* 11px above and below a 18px line makes the row 40px tall: this is a
     control you tap with a thumb, not a caption. */
  padding:11px 2px;font-size:13px;font-weight:800;color:var(--text);
}
.place-group-head::-webkit-details-marker{display:none}
/* One colour per group of PLACE_GROUPS (place-format.js), so the heading
   itself tells apart the same five kinds of place the list already splits
   into — "ik wilde enkel in het overzicht (zie screenshot) een andere
   kleur B voor de plekken", after "de pins op de wereldbol blijven
   oranje" ruled the map's own pins back out. The count below follows
   PLACE_GROUPS rather than being written down anywhere — a sixth group
   there needs a sixth rule here and nothing else, and sheet.test.mjs
   checks exactly that: every key PLACE_GROUPS holds today has a rule
   below, read from the list itself rather than five names copied in.

   Each rule only sets --group-mark-color, read by .place-group-mark's own
   `color` above — the same one-variable-per-rule shape the pins used
   before this was undone, kept because a group added to PLACE_GROUPS
   without a rule here still needs a mark: a wrong colour is a much
   smaller problem than a heading with no ₿ in front of it at all.

   The first four are the exact values reviewed for the pins — including a
   simulated deuteranopia and protanopia check, not just eyeballed, since
   orange next to green is the hardest pair here — carried over rather
   than re-picked: `atm` the house orange, `restaurant` the app's own
   --green (the "fresh" data badge, not a second green nobody asked for),
   `cafe` (cafe+bar+pub) blue, `accommodation` purple, and `other` — still
   three quarters of every country's list — its own quiet grey rather than
   a muted echo of the orange, so it does not read as the row colour
   itself among four that actually mean something to tell apart. */
.place-group-mark{
  color:var(--group-mark-color, var(--brand-orange));
  font-weight:800;line-height:1;flex:0 0 auto;
}
.place-group-mark-atm{--group-mark-color:var(--brand-orange)}
.place-group-mark-restaurant{--group-mark-color:var(--green)}
.place-group-mark-cafe{--group-mark-color:#1d6fe0}
.place-group-mark-accommodation{--group-mark-color:#a3319b}
/* The fourteen groups added on 31 August 2026, spread around the rest of
   the wheel: the four above sit at roughly 24°, 160°, 217° and 305°, so
   these fill the reds, ambers, limes, teals and violets between them.
   Where two would have landed on the same hue they are separated by
   lightness instead — bakery is a dark amber against sport's bright gold,
   sightseeing a deep purple against hairdresser's lighter violet.

   These are NOT claimed to be as separable as the original four, and that
   is a deliberate limit rather than an oversight: nineteen colours cannot
   be told apart the way five can, under any palette. Every mark sits
   directly beside the group's own name, which is what actually says what a
   heading is — the colour is an accent that makes a list scannable, not
   the thing carrying the meaning. The pair worth knowing about is fuel's
   orange-red against health's pink-red; they are neighbours. */
.place-group-mark-camping{--group-mark-color:#2f7d32}
.place-group-mark-bakery{--group-mark-color:#a06a10}
.place-group-mark-groceries{--group-mark-color:#86a81f}
.place-group-mark-shop{--group-mark-color:#d13f8f}
.place-group-mark-fuel{--group-mark-color:#d93a2b}
.place-group-mark-health{--group-mark-color:#d1315c}
.place-group-mark-hairdresser{--group-mark-color:#8b47cf}
.place-group-mark-wellness{--group-mark-color:#12a1a1}
.place-group-mark-car{--group-mark-color:#55708a}
.place-group-mark-electronics{--group-mark-color:#0e8ab5}
.place-group-mark-sport{--group-mark-color:#d4a017}
.place-group-mark-exchange{--group-mark-color:#1f7a5c}
.place-group-mark-transport{--group-mark-color:#4f63c9}
.place-group-mark-sightseeing{--group-mark-color:#7d3c98}
.place-group-mark-other{--group-mark-color:#555c5e}
.place-group-count{
  margin-left:auto;color:var(--muted);font-size:12px;font-weight:600;
  font-variant-numeric:tabular-nums;
}
/* ▼ and ► rather than ▾ and ▸: the small pair is drawn narrow by design
   and stays narrow at any size — one at 18px measured seven pixels of ink.
   These were set at *nine*, which is why Patrick could hardly see them.
   Every group is folded to begin with now, so this mark is the whole of
   what says a country's list can be opened at all. */
.place-group-head::after{
  content:"▼";color:var(--brand-orange-ink);font-size:12px;line-height:1;flex:0 0 auto;
}
.place-group:not([open]) .place-group-head::after{content:"►"}
/* De filterknoppen boven de lijst. Scrollen zijwaarts op een smal scherm in
   plaats van te breken over twee regels: vijf knopjes op 375 pixels passen
   niet, en een tweede regel duwt de lijst waar het om gaat naar beneden. */
.chips{
  display:flex;gap:6px;overflow-x:auto;padding:2px 0 8px;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.chips::-webkit-scrollbar{display:none}
.chip{
  flex:0 0 auto;background:var(--fill);border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:6px 12px;
  color:var(--muted);font-family:inherit;font-size:12.5px;font-weight:600;cursor:pointer;
}
.chip:hover{background:var(--fill-strong);color:var(--text)}
.chip.on{background:var(--brand-gradient);border-color:transparent;color:#fff}
/* De openingstijd in het detail: de zin die ertoe doet vet, de rauwe regel
   eronder klein — want "tot 19:00" zegt niets over morgen en daar staat de
   hele week. */
.hours-now{display:block;font-weight:700}
.hours-raw{display:block;color:var(--muted);font-size:12px;margin-top:2px}

.place-list{display:flex;flex-direction:column;gap:6px}
.place-line{
  display:flex;align-items:center;gap:8px;width:100%;text-align:left;
  background:var(--fill-soft);border:1px solid var(--border);
  border-radius:var(--radius-soft);padding:10px 12px;
  font-family:inherit;color:var(--text);font-size:13.5px;cursor:pointer;
}
.place-line:hover{background:var(--fill-strong)}
.place-name{font-weight:600}
.place-where{color:var(--muted);font-size:12px}
/* How you can pay, at the end of the line: the ₿ for on-chain, the bolt for
   Lightning, both when the place takes both. Both in the house orange —
   they are the same kind of fact, so they are the same colour. A place BTC
   Map records neither for gets no marks and no empty box; see
   paymentMarksHtml in web/js/place-format.js. */
.place-pay{margin-left:auto;display:inline-flex;align-items:center;gap:6px;flex:0 0 auto}
.pay-mark{color:var(--brand-orange-ink);line-height:1}
/* The ₿ is a letterform and the bolt is a symbol; at one size the ₿ reads
   as the smaller of the two, so it carries the weight the bolt gets for
   free. */
.pay-mark.onchain{font-weight:800}

/* The affiliate links (web/js/partners.js). Two screens draw this markup
   since 30 August 2026 — the very bottom of a country's information panel
   for the connect block, and the whole of the fifth tab for the bitcoin
   block, see `.bitcoin .refs` below — but never anywhere else in the app.

   Everything here is deliberately quieter than what sits above it, on the
   country panel where there is something above it. They are pills like the
   rest of the app, but hollow ones — a hairline border and the same faint
   white wash the place lines wear, no fill of their own and emphatically
   not the gradient, which belongs to the one primary action. The name is
   ordinary body text rather than the orange the map's own facts are
   printed in: an advert that borrows the colour the bitcoin marks use
   would be dressing itself up as part of the data.

   The separator above the block is the same hairline the place groups use,
   with real air over it, so on the country panel the block reads as a
   footnote to the country rather than as the next section of it. */
.refs{margin:22px 0 0;border-top:1px solid var(--border);padding-top:14px}
/* Written as .sheet-body .refs-title and not as .refs-title: the panel gives
   every h3 inside it 15px at weight 800, which is the size the country's own
   headings wear, and a heading over a pair of paid links has no business
   being as loud as "Bitcoin in Spanje". The extra class is what wins that
   argument. `.bitcoin .refs-title` joined it on 30 August 2026 when the
   bitcoin block moved onto its own tab: that page has no competing h3 of
   its own to stay quieter than, but the block should read exactly the way
   it always has rather than pick up the browser's bare h3 styling just
   because the panel it used to borrow this rule from is not the one
   drawing it any more. */
.sheet-body .refs-title, .bitcoin .refs-title{font-size:12px;font-weight:700;color:var(--muted);margin:0 0 8px}
/* En op de Bitcoin-tab wél hardop, sinds de restyle van september 2026.

   Het argument hierboven — een kop over een paar betaalde links hoort niet
   zo hard te staan als "Bitcoin in Spanje" — gaat over het landenpaneel,
   waar die twee naast elkaar staan. Op deze tab staat er niets naast: hier
   zijn "Exchanges" en "Bitcoin kopen & verkopen" de enige verdeling die het
   scherm heeft, en op 12px grijs waren ze zo stil dat de tab één lange
   lijst leek. Ze verdelen die lijst nu ook echt. */
.bitcoin .refs-title{font-size:15px;font-weight:800;color:var(--text);margin:0 0 10px}
.bitcoin .refs-links + .refs-title{margin-top:22px}
/* The first title in a screen's .refs needs no top margin of its own —
   .refs' own padding-top:14px already clears the hairline above it (or, on
   the bitcoin tab, .bitcoin .refs' own reset does) — but every title after
   it opens a new block directly under the previous block's last link, and
   without this the heading sits tight against the card above it.
   `.refs-links` is the previous sibling now: a block is a title and its
   links and nothing else, since the disclosure moved out of the block and
   down to the foot of the screen on 4 September 2026 (see refsSectionHtml
   in web/js/partners.js). This rule named `.refs-note` until then, which
   was correct for exactly as long as a note sat between every two blocks,
   and became a rule matching nothing the moment there was one note per
   screen. The sibling selector reaches only a second and later title, so
   the first one's own spacing never moves. */
.refs-links + .refs-title{margin-top:18px}
/* And the one sentence at the foot of the screen, which now follows the
   last block's links rather than sitting inside a block. */
.refs-links + .refs-note{margin-top:12px}
.refs-links{display:flex;flex-direction:column;gap:8px}
/* Each partner is a frosted card, the same glass the people and places wear
   on the other tabs: a translucent fill, a hairline all round, and a small
   lift under a finger. The partner's own logo, its name and its note keep
   their places exactly. */
.ref-link{
  display:flex;flex-direction:row;align-items:center;gap:11px;
  background:var(--fill);border:1px solid var(--border);
  border-radius:var(--radius-soft);padding:11px 14px;
  color:var(--text);text-decoration:none;
  box-shadow:inset 0 1px 0 var(--fill);
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
/* De naam en de regel eronder, naast het logo. `min-width:0` want zonder dat
   duwt een lange partnernaam de kaart breder dan het scherm in plaats van af
   te breken. */
.ref-words{display:flex;flex-direction:column;gap:2px;min-width:0}
.ref-link:hover{
  transform:translateY(-1px);border-color:rgba(255,140,66,.35);
  box-shadow:var(--shadow-lift);
}
@media (prefers-reduced-motion:reduce){.ref-link:hover{transform:none}}
/* Geen flexrij meer met een logo erin, gewoon de naam: het logo staat sinds
   september 2026 ernaast (zie `.ref-words` hierboven). */
.ref-name{font-weight:600;font-size:13.5px;line-height:1.25}
/* The partner's own logo, replacing the globe/EU-flag mark used until 26
   August 2026. All six source files are square brand tiles with their
   colour baked in and no transparency at all — Bybit's black one and
   BloFin's, added 30 August 2026, included: both were assumed to be a bare
   mark on transparency until their pixels were decoded, and every one of
   the 16,384 in each file is fully opaque. `logoBg` in partners.js is
   therefore a fallback that shows through nowhere today, kept for the day
   a transparent logo arrives and would otherwise read as a hole beside
   five tiles.

   The hairline border is what keeps the darkest of those tiles — Bybit's,
   which is very nearly this panel's own colour — from disappearing into
   the background. That border is load-bearing, unlike `logoBg`. The box
   never shrinks and never wraps: a logo on its own line is a logo that has
   come loose from the name it belongs to. */
.ref-logo{
  flex:0 0 auto;width:20px;height:20px;border-radius:6px;
  border:1px solid var(--border);overflow:hidden;
  display:flex;align-items:center;justify-content:center;
}
.ref-logo-img{width:100%;height:100%;object-fit:contain;display:block}
/* Op de Bitcoin-tab een maat groter, sinds de restyle van september 2026.
   Daar zijn deze rijen niet de voetnoot onder een land maar het scherm zelf:
   de tab is de lijst. Een merktegel van 20px naast 13.5px tekst is een
   pictogram voor een alinea; op 44px is het het logo van een partner, en
   herken je Bybit van OKX zonder de naam te lezen.

   Het landenpaneel houdt de 20px hierboven: daar staan dezelfde rijen wél
   als voetnoot, onder de plekken en de openingstijden van een land, en
   groeien ze mee zou dat scherm laten kantelen naar de betaalde links. */
.bitcoin .ref-link{gap:12px;padding:12px 13px;border-radius:16px}
.bitcoin .ref-logo{width:44px;height:44px;border-radius:13px}
.bitcoin .ref-name{font-size:15px;font-weight:800}
/* De regel eronder blijft helder wit, ook al zet de mockup hem grijs. Die
   mockup had er een pil naast staan ("Wereldwijd" / "EU") die het verschil
   tussen de twee Bybits ook zonder lezen liet zien; die pil bouwen we niet
   — dat is een gegeven erbij, geen vorm — en dan is deze regel het enige
   wat "Wereldwijd, buiten de EU" van "Voor wie in de EU woont" onderscheidt.
   Grijs maken zou precies dat onderscheid wegpoetsen. Zie `.ref-note`. */
/* Two of these links are called Bybit, and now wear the same logo too —
   this line is what is left to tell them apart. Heavier and brighter than
   plain muted text, so "Wereldwijd, buiten de EU" against "Voor wie in de
   EU woont" is a difference somebody can catch scanning past rather than
   reading closely. The same weight goes on every partner's note, not just
   the two Bybits, so the row of links reads as one consistent list rather
   than singling one pair out as more important than the rest. */
.ref-note{color:var(--text);font-weight:600;font-size:12px;line-height:1.4}
/* The disclosure. Required in the European Union, and the design document
   asks for it "niet in kleine letters onderaan" — so it is the same size
   as the note inside a link above it, though it keeps the ordinary muted
   colour and weight rather than the note's: it is one sentence about the
   arrangement as a whole, not a fact belonging to one partner that needs
   to be caught at a glance the way the note above does. It is drawn by
   the same function that draws them and there is no path that renders
   one without the other. */
.refs-note{font-size:12px;color:var(--muted);line-height:1.5;margin:10px 0 0}

/* The country view: the satellite map lies over the globe once a country is
   chosen. The globe stays underneath, so going back is instant. */
.country-map{position:absolute;inset:0;z-index:500}
.country-map[hidden]{display:none}
/* Leaflet's zoom buttons. They start life in the top-left corner, which is
   the back button's and has been since there was a back button — at 375px
   the "+" sat underneath it outright. web/js/map.js puts them on the right
   instead, and this drops them clear of the back button's row: measured
   from --search-top, which is the top row of the stage, plus a control's
   own height and a little air, so the two move together for good. */
.country-map .leaflet-top.leaflet-right .leaflet-control-zoom{
  margin-top:calc(var(--search-top) + 48px);
}
/* The line saying the map is drawing only part of what is in view. It sits
   on the row under the back button — the row the search box used to take,
   before the search moved into the country's own information and left this
   map to its pins. Above the map's own layer and narrow enough
   to clear Leaflet's zoom control on a 375px phone (that control ends at
   x=44; at 72% of 375 this pill starts at x=53). A statement, not a
   warning, so it wears the same muted family as the sheet's own partial
   line rather than an alarm colour. */
.map-notice{
  position:absolute;top:calc(var(--search-top) + 44px);left:0;right:0;z-index:600;
  width:fit-content;max-width:min(72%,340px);margin:0 auto;text-align:center;
  background:rgba(38,42,63,.92);border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:7px 14px;
  color:var(--muted);font-size:12px;line-height:1.45;pointer-events:none;
}
.map-notice[hidden]{display:none}
/* The way to the country's guide and its list of places, now that neither
   comes up by itself any more. Same dark pill as the back button in the
   opposite corner — the app's existing control shape, not a new one — and
   at the bottom edge, where the sheet it lifts comes from. Above the map's
   own layer, and gone the moment the sheet is up, since it would then only
   sit underneath it. */
.country-open{
  position:absolute;left:50%;bottom:14px;transform:translateX(-50%);z-index:600;
  display:inline-flex;align-items:center;gap:8px;max-width:min(80%,340px);
  background:rgba(38,42,63,.92);color:var(--text);border:1px solid var(--border);
  border-radius:var(--radius-pill);padding:9px 16px;
  font-family:inherit;font-size:13px;font-weight:600;line-height:1.2;cursor:pointer;
  box-shadow:var(--shadow-rest);
}
.country-open:hover{background:rgba(38,42,63,.98)}
/* The chevron: the pill lifts a panel from below, and says so. */
.country-open-lift{color:var(--brand-orange-ink);font-size:9px}
/* And the magnifying glass: what comes up when it does can be searched.
   The same glyph as the real box in the sheet, a shade quieter than the
   country's own name so it reads as a mark on the pill rather than as a
   button beside it. An emoji carries its own colours, so this can only set
   its size and its weight in the row. */
.country-open-find{font-size:12px;opacity:.85;line-height:1}
.country-open[hidden]{display:none}
/* Searching. The same dark pill as the back button and the country pill —
   the app's existing control shape, not a new one — at the top of the
   stage, where nothing else on the globe screen sits.

   These rules are the box's globe home only. Inside a country the very same
   element is moved into the bottom sheet and is laid out by .sheet-search
   above instead, which is why every rule here that would fight that one —
   the absolute position, the transform, the fixed width, the shadow — is
   overridden there rather than repeated. It used to drop to a second row
   over the satellite map; it does not live there any more, so the row under
   the back button belongs to the map's own notice. */
.search{
  position:absolute;top:var(--search-top);left:50%;transform:translateX(-50%);z-index:600;
  display:flex;align-items:center;gap:8px;width:min(88%,420px);
  /* Frosted glass over the globe: translucent enough that the sphere shows
     through the blur, a hairline and a faint inner highlight the way the
     site's own inputs sit. */
  background:rgba(38,42,63,.6);border:1px solid rgba(255,255,255,.14);
  border-radius:var(--radius-pill);padding:8px 14px;
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  box-shadow:0 8px 22px -10px rgba(0,0,0,.5);
}
.search[hidden]{display:none}
.search-icon{flex:0 0 auto;font-size:13px;opacity:.8;line-height:1}
.search-input{
  flex:1 1 auto;min-width:0;background:none;border:none;outline:none;
  color:var(--text);font-family:inherit;font-size:16px;line-height:1.3;
  /* iOS Safari zooms the whole page in when a focused text input is under
     16px, and never zooms back out — 16px here is the fix, not a style
     choice. See the "no focusable input under 16px" test below. */
}
.search-input::placeholder{color:var(--muted)}
/* WebKit draws its own grey cross inside a type=search box, which would sit
   next to this app's own clear button doing the same job. */
.search-input::-webkit-search-cancel-button{display:none}
.search-clear{
  flex:0 0 auto;background:none;border:none;color:var(--muted);
  font-family:inherit;font-size:13px;line-height:1;padding:0 2px;cursor:pointer;
}
.search-clear[hidden]{display:none}
/* The results, under the box. Bounded in height and scrolled inside itself,
   so a full list can never reach down over the hint at the bottom of the
   globe or, on a short phone in landscape, off the screen entirely. */
.search-results{
  position:absolute;top:calc(var(--search-top) + 42px);left:50%;transform:translateX(-50%);z-index:600;
  width:min(88%,420px);max-height:min(56vh,420px);overflow-y:auto;
  background:var(--panel);border:1px solid var(--border);
  border-radius:var(--radius-soft);box-shadow:var(--shadow-rest);
  padding:6px;
}
.search-results[hidden]{display:none}
/* Two lines per result, not one: the name, and underneath it what kind of
   place it is, the town and the country. Two places can easily share a
   name, and the second line is how a visitor tells them apart. */
.search-row{
  display:flex;flex-direction:column;gap:3px;width:100%;text-align:left;
  background:none;border:none;border-radius:10px;padding:9px 10px;
  font-family:inherit;color:var(--text);font-size:13.5px;cursor:pointer;
}
.search-row:hover{background:var(--fill)}
.search-name{display:flex;align-items:center;gap:8px;font-weight:600}
.search-where{color:var(--muted);font-size:12px;line-height:1.4}
/* A town is not a bitcoin place and must not look like one: no payment
   marks, and a name in the ordinary text colour rather than the app's
   accent. What it offers is "here is where that is". */
.search-town .search-name{font-weight:600;color:var(--text)}
.search-town-mark{font-size:12px;opacity:.75}
/* Where the search is looking, at the very top of the panel: this country,
   or the whole world. Above the rows rather than below them, because it is
   the caption on every row underneath — a list you have to scroll past to
   learn what it is a list of is exactly the silent scoping this box exists
   not to do. The live one wears the same orange ring every "this one is on"
   in this app wears. */
.search-scope{
  display:flex;gap:6px;padding:2px 4px 8px;
  border-bottom:1px solid var(--border);margin-bottom:6px;
}
.scope-chip{
  flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  background:none;border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:5px 11px;font-family:inherit;font-size:12px;font-weight:600;
  color:var(--muted);cursor:pointer;line-height:1.3;
}
.scope-chip:hover{background:var(--fill)}
.scope-chip.on{border-color:rgba(255,140,66,.55);color:var(--brand-orange-ink)}
/* What the list is leaving out, or that it is still being fetched. The same
   quiet muted family as the sheet's own partial line — a statement, not a
   warning. */
.search-notice{
  margin:0;padding:8px 10px;color:var(--muted);font-size:12px;line-height:1.5;
}

/* An orange pin with the bitcoin sign, the house orange. Lightning gets a
   glow; on-chain only stays flat.

   18px, not the 26px this used to be. Patrick, with a screenshot of Spain:
   "is het nog mogelijk om de B-pinpoints kleiner te maken? Nu valt het
   land zo weg tegenover de pinpoints... is ook zo bij vele andere landen" —
   a country with a few hundred pins in view (BORDER_PANE's own comment in
   map.js has the real counts BTC Map's busiest countries hit: the United
   States alone holds 10,239) turns into a solid orange mass at 26px, and
   no amount of z-index juggling changes that: the pins themselves were
   simply too big for how many of them share the screen. Shrunk here
   rather than thinned out or decluttered further, because the size was
   the actual complaint, not the count — this map already caps a viewport
   request at 400 pins, and `pinBudget`/`prioritizedPins` (globe.js) do the
   equivalent for the globe's own pins; neither ceiling was the thing
   Patrick circled.

   Every other number here moves with it, proportionally, rather than only
   the box: `iconSize`/`iconAnchor` in map.js's own `icon()` (which must
   match this exactly — Leaflet positions a divIcon by that box, not by
   reading the stylesheet), the corner radius that makes the sharp tip, the
   font size of the ₿ and the border. Left unscaled, the tip would go
   blunt, the sign would crowd the pin's own edge, and the border would eat
   a visibly bigger share of a smaller box than it used to. */
.btc-pin{
  display:flex;align-items:center;justify-content:center;
  width:18px;height:18px;border-radius:50% 50% 50% 3px;
  transform:rotate(-45deg);
  background:var(--brand-orange);color:#fff;
  font-weight:800;font-size:10px;line-height:1;
  border:1.5px solid rgba(23,32,43,.85);
}
.btc-pin > *{transform:rotate(45deg)}
/* Softer as well as smaller: the glow was at least as much of the fused
   orange mass in a dense country as the pin's own box, since nearly every
   pin in a country carries it. Half the blur radius and a little over half
   the opacity of what this used to be — Patrick's own second message,
   after seeing the shrink alone: "de pins gelijk mee, kleiner en zachtere
   gloed in deze deploy, allemaal in oranje" (orange stays; see .btc-pin
   above and .btc-pin.picked below for the "no colour work yet" half of
   that sentence). */
.btc-pin.on{box-shadow:0 0 6px rgba(255,140,66,.55)}
/* The pin of the place that was just tapped in the country's list. Bigger,
   in a white ring, and lifted over its neighbours by map.js — the three
   things that make one pin findable among the hundreds around it.

   White rather than a second brand colour on purpose: the pin is already
   orange and may already carry the orange Lightning glow, and the one
   thing this ring must never be mistaken for is a different *kind* of
   place. It is the only white ring on the map, so nothing else can be
   read as it. Written after .on so the ring wins over the glow.

   The transform repeats rotate(-45deg): .btc-pin is a rotated square, and
   a transform is one declaration — scaling it means restating the rotation
   or the pin lies flat on its side. */
.btc-pin.picked{
  transform:rotate(-45deg) scale(1.28);
  border-color:#fff;
  box-shadow:0 0 0 3px rgba(255,255,255,.6),0 0 18px rgba(255,140,66,.95);
  animation:pin-picked 1.05s ease-in-out 3;
}
/* Three beats and it settles at its enlarged size: enough movement to catch
   the eye on a map of hundreds of pins, not a pin that pulses forever
   beside a paragraph somebody is trying to read. */
@keyframes pin-picked{
  0%,100%{transform:rotate(-45deg) scale(1.28)}
  50%{transform:rotate(-45deg) scale(1.5)}
}
@media (prefers-reduced-motion:reduce){
  .btc-pin.picked{animation:none}
}
.back-list{
  background:none;border:none;color:var(--muted);font-family:inherit;
  font-size:12.5px;font-weight:600;padding:0 0 8px;cursor:pointer;
}

/* ------------------------------ the day book -----------------------------
   "Jouw leven in Bitcoin", the fifth tab's own sheet. Dragged by the same
   kind of handle the Activiteit sheet has (see .act-sheet-wrap above), and
   laid out the way Patrick drew it: the period, two tiles, a search box,
   then the bookings per day. */
.daily{display:flex;flex-direction:column;gap:12px;padding:0 14px 14px}
.daily-period{
  align-self:flex-start;background:none;border:1px solid var(--border);color:var(--text);
  border-radius:99px;padding:6px 14px;font:inherit;font-size:14px;cursor:pointer;
}
/* Two tiles, side by side and equal whatever is in them: a figure that is
   longer than its neighbour must not make its own tile wider. */
/* What this member wrote in that box before, offered from the first letter
   and only from the start of the word (see pastValues in web/js/daily.js).
   Inside the label, so it sits directly under its own box and moves with
   it; the label is a column for exactly that reason. */
/* The invite button runs the width of the screen, the way the links on the
   Bitcoin tab do — Patrick, 4 September 2026: "misschien die button
   Uitnodigen langer maken (net als de exchanges balken)". It is the one
   thing to do on this half of the screen once somebody has read the line
   above it, and a short pill in a wide empty column read as an
   afterthought. */
.find-invite{
  display:block;width:100%;text-align:left;
  border-radius:var(--radius-soft);padding:13px 16px;font-size:14.5px;
}
.daily-suggest{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
.daily-suggest-one{
  display:block;width:100%;text-align:left;cursor:pointer;
  background:var(--panel);border:1px solid var(--border);border-radius:10px;
  padding:9px 12px;color:var(--text);font:inherit;font-size:14.5px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.daily-suggest-one:hover{border-color:var(--brand-orange-ink)}
.daily-tiles{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.daily-tile{
  display:flex;flex-direction:column;gap:2px;text-align:left;cursor:pointer;
  background:var(--panel);border:1px solid var(--border);border-radius:var(--radius-soft);
  padding:12px;color:var(--text);font:inherit;
}
.daily-tile-name{font-size:13px;opacity:.7}
.daily-tile-money{font-size:22px;font-weight:700}
.daily-tile-sats{font-size:12px;opacity:.6}
.daily-open{margin:0;font-size:13px;opacity:.7}
.daily-search{display:block}
/* 16px is not a taste: anything smaller and iOS Safari zooms the page in
   when the field takes focus, which no amount of viewport tinkering undoes.
   A test in test/screen.test.mjs holds every field in this app to it. */
.daily-search-box{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-day{display:flex;justify-content:space-between;align-items:baseline;gap:10px;padding:14px 0 4px}
.daily-day-when{font-size:13px;opacity:.7;flex:1;min-width:0}
.daily-day-total{font-size:13px;opacity:.9;font-weight:700}
/* What came in that day, beside what went out. `--green` is this app's own
   green — the one the "fresh" data badge already wears — and not a second
   one invented for this line. It is marked at all because it is the one
   figure on the row that means the opposite of the other. */
.daily-day-in{font-size:13px;font-weight:700;color:var(--green)}
.daily-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}
/* A day that has not happened yet, and the bookings under it.
   Patrick asked for this on 2 September 2026 so that "wat licht is is geweest"
   — what is bright has been lived. Rent spread over a month writes rows well
   ahead of today, and at the top of a newest-first list they looked exactly
   like money already gone.

   Opacity rather than a colour of its own: it dims the category's own colour,
   the amount and the green of an income together, so the whole row recedes as
   one thing instead of turning into a second palette to maintain. */
.daily-day-ahead{opacity:.45}
.daily-list-ahead{opacity:.45}
.daily-row{display:flex;align-items:center;gap:10px;padding:9px 0;border-top:1px solid #ffffff0f}
.daily-row-what{display:flex;flex-direction:column;min-width:0;flex:1}
.daily-row-note{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.daily-row-cat{font-size:12px;opacity:.6}
.daily-row-amount{display:flex;flex-direction:column;align-items:flex-end;flex:none;font-weight:700}
/* Money arriving reads as arriving. The same green the app uses elsewhere
   for a figure that went the good way. */
.daily-in{color:var(--green)}
.daily-row-paid{font-size:12px;opacity:.6;font-weight:400}
.daily-picker{display:flex;flex-direction:column;gap:6px;padding-top:8px}
.daily-grid-head{margin:10px 0 2px;font-size:14px;opacity:.8;font-weight:700}
/* Three to a row, exactly as drawn. */
.daily-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
.daily-cat{
  display:flex;flex-direction:column;align-items:center;gap:6px;cursor:pointer;
  background:var(--panel);border:1px solid var(--border);border-radius:12px;
  padding:12px 6px;color:var(--text);font:inherit;font-size:12px;
}
.daily-cat-name{text-align:center;line-height:1.2}
/* The statistics button: the plus's twin at the other end of the same
   line — sticky at the bottom, at the start of the last row of the book,
   with the plus pulled up onto it by exactly its own two numbers (see
   .daily-add) so the book still ends where its content does. It shared this
   row with the download link until 4 September 2026; the link now lives on
   the statistics sheet alone, so this makes the row itself rather than being
   pulled onto the link's. Same size as the plus, on Patrick's word
   (3 September 2026); the panel's own colour rather than the gradient, so
   the one button that writes is still the one that shouts. */
.daily-stats-open{
  position:sticky;bottom:0;align-self:flex-start;margin-left:2px;margin-top:6px;
  width:56px;height:56px;border-radius:50%;flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--panel);border:1px solid var(--border);box-shadow:0 6px 18px #0006;
  color:var(--text);cursor:pointer;
}
.daily-stats-open:hover{border-color:var(--brand-orange-ink);color:var(--brand-orange-ink)}
.daily-stats-icon{flex:none}
/* The one gradient button on this screen. It floats, so a long book never
   puts it out of reach. */
/* The plus belongs to the book, not to the screen.

   It used to be position:fixed, which meant it hung over whatever happened
   to be underneath — including the exchanges, once somebody scrolled past
   the book. Sticky inside the scrolling book keeps it in view while the
   months move and leaves it behind the moment the book does. */
.daily-add{
  position:sticky;bottom:0;align-self:flex-end;margin-right:2px;
  /* Patrick, 1 September 2026: "zorg er voor dat je niet verder naar beneden
     kunt scrollen als het plusje exact uitgelijnd is met de kasboek
     downloaden button." It used to take a row of its own at the end of the
     book — its 56px plus the column's 12px gap — so there was always that
     much empty book left to scroll past after the last thing worth reading.
     Pulled back up by exactly those two, it shares the download link's line
     instead: the link sits at the start of the row, the plus at the end, and
     the book ends where its content does. Negative margin rather than
     absolute placement because sticky is what keeps it in view while the
     months move, and that only works from inside the flow. */
  margin-top:calc(-56px - 12px);
  /* `bottom:0`, measured rather than reasoned: sticky's offset here counts
     from the scrolling box's *content* edge, which is already 14px in from
     the visible bottom because of `.daily`'s own padding. Zero is therefore
     what makes the sticky position and the flow position land on the same
     pixel — the plus floats 14px off the floor while the months move, and
     comes to rest exactly on the download link's line at the end of the
     book, instead of hopping up 14px in the one moment it should be still.
     Verified in the browser both ways. */
  width:56px;height:56px;border-radius:50%;flex:none;
  border:none;cursor:pointer;font-size:30px;line-height:1;color:#fff;
  background:var(--brand-gradient);box-shadow:0 6px 18px #0006;
}

/* A category's colour is the disc; its drawing is a white stroke on top.
   One file of path data (web/js/category-icons.js) serves all nineteen,
   which only works because the glyph takes currentColor and the colour
   arrives per category as --cat. */
.cat-icon{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:30px;height:30px;border-radius:50%;background:var(--cat,var(--panel));color:#fff;
}
.daily-cat .cat-icon{width:34px;height:34px}

/* The form for one booking. It takes the whole sheet while it is open —
   two things asking for a tap at once on a phone is how a booking lands in
   the wrong category. */
.daily-form{display:flex;flex-direction:column;gap:12px;padding-top:4px}
.daily-form-head{display:flex;align-items:center;gap:10px}
/* De categorie is een knop sinds 8 september 2026, en draagt daarom een rand:
   zonder rand blijft hij lezen als een opschrift, en dan tikt niemand erop.
   Dezelfde vorm als de muntpil bij het bedrag, want het is hetzelfde gebaar:
   tikken opent de keuze en het formulier blijft staan. */
.daily-form-cat{
  display:inline-flex;align-items:center;gap:8px;margin-right:auto;
  padding:5px 12px 5px 6px;border-radius:999px;
  background:none;border:1px solid var(--border);color:var(--text);
  font:inherit;font-weight:700;cursor:pointer;text-align:left;
}
.daily-form-cat:hover{border-color:var(--brand-orange-ink)}
.daily-form-cat-name{min-width:0}
/* Het opschrift boven de tegels zelf is geen knop: daar kies je juist. */
.daily-pick-name{font-weight:700;flex:1}
.daily-form-close{
  background:none;border:none;color:var(--text);font-size:26px;line-height:1;
  cursor:pointer;padding:0 4px;opacity:.7;
}
.daily-field{display:flex;flex-direction:column;gap:5px}
.daily-field-name{font-size:13px;color:var(--muted)}
/* 16px on each of the three, and written out one rule per class rather
   than as one grouped selector: the guard in test/search-ui.test.mjs looks
   for each field's own rule, which is what stops a field being added to a
   group and quietly inheriting nothing when the group is later split. */
.daily-amount{
  width:100%;font:inherit;font-size:22px;font-weight:700;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-note{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-day-field{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
/* The amount, and beside it what it is written in.

   Patrick, 1 September 2026: an income in dollars should be typed as
   dollars. The pill opens the trip book's own currency sheet — the same
   170-odd currencies, the same search, the same rate per row — and the line
   underneath says what one of them is worth at home, so the conversion the
   server is about to do is not a surprise. It is absent when the two
   currencies are the same: nobody needs telling that ten euro is ten euro. */
.daily-amount-row{display:flex;align-items:stretch;gap:8px}
.daily-amount-row .daily-amount{flex:1 1 auto;min-width:0}
.daily-currency-pill{
  flex:none;background:var(--panel);border:1px solid var(--border);color:var(--text);
  border-radius:12px;padding:0 16px;
  /* 16px, like every other box on this form: below that iOS Safari zooms
     the page, and a pill that is half of a field should behave like one. */
  font:inherit;font-size:16px;font-weight:700;letter-spacing:.02em;cursor:pointer;
}
.daily-currency-pill:hover{border-color:var(--brand-orange-ink);color:var(--brand-orange-ink)}
.daily-fx-line{margin:6px 0 0;font-size:13px;color:var(--muted)}

/* The date, and behind it the question almost nobody answers.

   "Verspreid over (dagen)" had a label and a box of its own, and on nearly
   every booking the answer was 1. Patrick asked for it behind the date on
   1 September 2026. The two share one box: the date reads as the field, the
   button reads as an aside, and the number only appears once it is asked
   for. The date input keeps its own 16px rule — below that iOS Safari zooms
   the page the moment it is focused — and only gives up its edges. */
.daily-day-row{
  display:flex;align-items:center;gap:8px;
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:4px;
}
.daily-day-row .daily-day-field{
  flex:1;min-width:0;background:none;border:none;padding:6px 8px;
}
.daily-spread-pill{
  flex:none;background:none;border:1px solid var(--border);color:var(--muted);
  border-radius:var(--radius-pill);padding:7px 14px;
  font:inherit;font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap;
}
/* Not the gradient, on purpose: that is spoken for by the one button this
   screen is asking you to press (.daily-save below). A pill that is merely
   switched on says so in the brand's orange and stays out of the way. */
.daily-spread-pill[aria-pressed="true"]{
  border-color:var(--brand-orange-ink);color:var(--brand-orange-ink);font-weight:700;
}
.daily-form-error{margin:0;color:var(--brand-orange-ink);font-size:14px}
/* The one gradient button on this screen while a form is open — the
   floating plus is not drawn then, so the rule of one per screen holds. */
.daily-save{
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  border-radius:var(--radius-pill);padding:11px 22px;
  font-family:inherit;font-size:14px;font-weight:700;cursor:pointer;
}
.daily-save:hover{background:var(--brand-gradient-lit)}
.daily-save[disabled]{opacity:.55;cursor:default}
.daily-form-buttons{display:flex;gap:10px;align-items:center;padding-top:4px}
.daily-delete{margin-left:auto}

/* The bar the day book folds open behind. Patrick asked for it on 31 August:
   a year of bookings is a very long page to scroll past on the way to the
   exchanges underneath. */
.daily-wrap{display:block;margin-bottom:18px}
/* Geen knop meer sinds 7 september 2026, maar een gewone rij met twee
   knoppen erin: een knop in een knop bestaat niet. Vandaar ook geen pointer
   op de balk zelf — alleen de twee knoppen rechts zijn aan te tikken.
   Minder verticale ruimte dan de 14px van vroeger, omdat de knoppen zelf nu
   40px hoog zijn: samen komt de balk daarmee op de 56px die
   --daily-bar-height noemt. */
.daily-bar{
  display:flex;flex-direction:column;align-items:stretch;width:100%;
  background:var(--panel);border:1px solid var(--border);border-radius:var(--radius-soft);
  gap:3px;padding:8px 14px;color:var(--text);font:inherit;font-size:16px;font-weight:700;
  /* A faint inner highlight along the top, the same the glass cards carry,
     so the "Jouw leven in Bitcoin" bar belongs to the same set. No lift: it
     is a bar that folds a panel open, not a card that floats. */
  box-shadow:inset 0 1px 0 var(--fill);
}
/* ——— De hero, sinds de restyle van september 2026 ———

   Dichte stand: geen balk meer maar het gezicht van de tab. Een beeld van
   186px hoog met de naam er groot in serif over de onderrand, en de twee
   knoppen als matglas in de rechterbovenhoek.

   Waarom alleen de dichte stand. Klapt het kasboek open, dan is het boek de
   inhoud en is de hero in de weg: 186px van de 812 die een telefoon heeft,
   weggehaald bij precies de lijst waar je naartoe getikt hebt. Bovendien
   rekent het boek zijn eigen hoogte uit met --daily-bar-height (56px, zie
   boven), en een hero die openstaat zou het boek 130px te lang maken en zijn
   laatste rij achter de tabbalk hangen. Vandaar `:not(.on)`: de open stand
   houdt de compacte balk die hij altijd was, en de calc() blijft kloppen.

   `.daily-invite` valt er ook buiten. Dat is dezelfde klasse voor wie niet
   ingelogd is, met drie regels uitleg en een knop erin — inhoud die een
   fotohero niet dragen kan.

   Het beeld is voorlopig sfeer en geen foto: drie gradients die samen een
   avond aan de kust zijn, precies zoals in de goedgekeurde mockup, met de
   wash er als bovenste laag in dezelfde background-stapel (eerst genoemd is
   bovenop). Geen los element en geen ::before dus. Een echte reisfoto van de
   eigenaar kan hier later achter, maar dat is data en geen vorm. */
.daily-bar:not(.on):not(.daily-invite){
  position:relative;overflow:hidden;
  min-height:186px;border-radius:20px;padding:18px;
  justify-content:flex-end;gap:0;
  border-color:rgba(255,255,255,.07);
  /* De wash boven de foto, niet als versiering maar als contrastvloer: de
     titel staat in wit tegen de onderrand en de twee glasknoppen tegen de
     bovenhoek, en een foto die daar toevallig licht is maakt allebei
     onleesbaar. Bovenin licht, onderin bijna dekkend, want daar staat de
     letter. Eerst genoemd is bovenop. */
  background:
    linear-gradient(180deg,rgba(12,16,25,.14) 0%,rgba(12,16,25,.22) 40%,rgba(12,16,25,.66) 76%,rgba(12,16,25,.90) 100%),
    var(--hero-photo) center 58%/cover no-repeat;
  box-shadow:0 18px 40px -22px rgba(0,0,0,.75);
}
/* De rij is in de hero geen rij meer: de naam zakt naar de onderrand en de
   twee knoppen worden uit de stroom gehaald naar de bovenhoek. Daarom
   `display:block` en niet flex — de naam heeft dan de volle breedte om over
   twee regels te breken, wat "A tua vida em Bitcoin" op een telefoon ook
   echt doet. `position:static` zodat de knoppen zich aan de hero ophangen en
   niet aan de rij. */
.daily-bar:not(.on):not(.daily-invite) .daily-bar-row{display:block;position:static}
.daily-bar:not(.on):not(.daily-invite) .daily-bar-tabs{
  position:absolute;top:18px;right:18px;z-index:3;gap:9px;
}
/* Matglas, en donker matglas: de zon in het beeld zit precies achter deze
   hoek, en een lichte glasplaat zou de twee tekens op een lichte vlek
   zetten. Gemeten op het echte scherm haalt het witte kasboek-icoon 6,6:1
   op deze vulling en het oranje bitcoin-teken 4,0:1 — beide boven de 3:1
   die een icoon nodig heeft, maar het oranje met een stuk minder marge,
   want oranje op licht is van zichzelf al een zwakke combinatie.

   Vandaar .62 en niet de .42 van de mockup: hier komt volgens de brief nog
   een echte reisfoto achter, en die kan een witte lucht hebben waar deze
   gradient een gedempte zon heeft. De marge is er voor dat beeld, niet voor
   dit. Wordt het oranje ooit lichter of het beeld feller, dan is dit getal
   de knop om aan te draaien. */
.daily-bar:not(.on):not(.daily-invite) .daily-bar-tab{
  width:46px;height:46px;border-radius:14px;
  background:rgba(14,17,26,.62);border:1px solid rgba(255,255,255,.22);
  backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px);
  box-shadow:0 6px 18px rgba(0,0,0,.35);
  transition:transform .16s ease,border-color .16s ease,background .16s ease;
}
.daily-bar:not(.on):not(.daily-invite) .daily-bar-tab:hover{
  transform:translateY(-1px);border-color:rgba(255,140,66,.6);background:rgba(14,17,26,.74);
}
.daily-bar:not(.on):not(.daily-invite) .daily-bar-tab.on{
  border-color:var(--brand-orange-ink);background:rgba(14,17,26,.74);
}
/* En de twee tekens in die knoppen zijn wit en oranje, niet de inkt van de
   pagina. Ze staan overal elders op een lichte kaart -- de portemonnee in de
   tekstkleur, het bitcoin-teken in het verdiepte oranje -- en dat zijn
   precies de twee kleuren die op donker glas over een foto verdwijnen. De
   portemonnee was daardoor navy op navy: Patrick zag hem, en hij had gelijk.

   Wit op vol, niet op de 75% die de lichte versie draagt: over een foto is
   er niets om tegen af te zwakken, en een lijntekening van 20px die al
   doorschijnend glas onder zich heeft, heeft die extra dunte niet te
   verliezen. Het bitcoin-teken gaat terug naar de felle sunset, waar het
   voor getekend is. Gemeten op het felste punt van dit beeld haalt het wit
   8,7:1 en het oranje 4,4:1, allebei boven de 3:1 die een icoon nodig heeft. */
.daily-bar:not(.on):not(.daily-invite) .daily-bar-purse{
  color:var(--on-stage);opacity:1;
}
.daily-bar:not(.on):not(.daily-invite) .daily-bar-mark{
  color:var(--brand-orange);
}
/* De naam, en de reden dat dit een hero is. Playfair over een beeld is een
   ander register dan 16px Mulish in een balk, en dat is precies wat de tab
   miste: je landde op een lijst met exchanges en niets zei waar je was.

   clamp() en geen vast getal: 33px is de maat uit de mockup op een 390px
   telefoon, maar op 320px zou "A tua vida em Bitcoin" dan over drie regels
   omhoog kruipen tot achter de knoppen. De schaduw is er voor de bovenste
   regel, die op een licht stuk beeld kan vallen. */
.daily-bar:not(.on):not(.daily-invite) .daily-bar-name{
  position:relative;z-index:2;flex:none;color:var(--on-stage);
  font-family:var(--face-title);font-weight:800;
  font-size:clamp(26px,8.5vw,33px);line-height:1.04;
  text-shadow:0 2px 18px rgba(0,0,0,.55);
}
/* De proefweek-regel hoort bij de naam en niet bij het beeld: hij staat er
   direct onder, in wit met wat lucht, want de muted grijs van de balk
   verdwijnt in de donkere onderrand van de wash. */
.daily-bar:not(.on):not(.daily-invite) .daily-bar-note{
  position:relative;z-index:2;margin-top:10px;color:rgba(255,255,255,.82);
  text-shadow:0 1px 10px rgba(0,0,0,.6);
}
@media (prefers-reduced-motion:reduce){
  .daily-bar:not(.on):not(.daily-invite) .daily-bar-tab:hover{transform:none}
}
/* Op een laptop is de hero geen kaart meer maar een band: 1248px breed bij
   186px hoog is bijna zeven op één, en een titel van 33px in de hoek daarvan
   staat er verloren bij. Boven de 820px -- de grens waarop dit app-brede
   stylesheet ophoudt een telefoon aan te nemen, zie de media-regel bij
   .screen -- wordt de band hoger en de titel groter, zodat de verhouding
   tussen de twee ongeveer blijft wat hij op een telefoon is.

   240px en niet meer: hier hangt de kasboek-balk onder, en alles wat de hero
   erbij neemt gaat van de exchanges eronder af. Het is een gezicht, geen
   voorpagina. */
@media (min-width:821px){
  .daily-bar:not(.on):not(.daily-invite){min-height:240px;padding:22px}
  .daily-bar:not(.on):not(.daily-invite) .daily-bar-tabs{top:22px;right:22px}
  .daily-bar:not(.on):not(.daily-invite) .daily-bar-name{font-size:44px}
}
/* De eigen regel van de balk: de naam links, de twee knoppen rechts. Tot 7
   september 2026 stonden hier vier dingen naast elkaar — teken, portemonnee,
   naam, chevron — die op 375px de breedte al vulden. Nu zijn het er drie, en
   de naam heeft de ruimte die het chevron en de losse iconen innamen. */
.daily-bar-row{display:flex;align-items:center;justify-content:space-between;gap:10px}
/* De twee knoppen, tegen elkaar aan aan de rechterkant. */
.daily-bar-tabs{display:flex;align-items:center;gap:6px;flex:none}
/* Een van de twee. 40px in het vierkant, want dit is op een telefoon het
   enige wat er nog aan te tikken valt: de balk zelf was vroeger een raakvlak
   van de volle breedte en is dat niet meer. Rustig tot hij aan staat; dan
   draagt hij de rand en de kleur van het merk, zodat je zonder chevron ziet
   welk paneel eronder hangt. */
.daily-bar-tab{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:40px;height:40px;border-radius:12px;
  background:transparent;border:1px solid transparent;
  color:var(--text);font:inherit;cursor:pointer;
}
.daily-bar-tab:hover{background:#ffffff0d}
.daily-bar-tab.on{background:#ffffff12;border-color:var(--brand-orange-ink)}
/* How much trial is left. Quiet on purpose: it is a fact about the book, not
   a demand. See barNoteHtml in web/js/daily.js for when it is drawn at all.

   Flush with the bitcoin mark above it rather than indented under the name.
   Patrick, 1 September 2026: "ter hoogte van het Bitcoin B-tje oftewel onder
   elkaar, dat ziet er net iets mooier uit" — and he is right: two lines that
   start on the same pixel read as one block, and an indent invented a second
   left edge the bar did not have. */
.daily-bar-note{font-size:12.5px;font-weight:600;color:var(--muted);text-align:left}
/* The same card for somebody who has not signed in, saying what the book is.
   Not a bar: nothing folds open behind it, so no pointer and no chevron. */
.daily-invite{cursor:default}
.daily-invite-lines{margin:8px 0 0;padding-left:18px;font-size:14px;font-weight:500;line-height:1.45;color:var(--muted)}
.daily-invite-lines li{margin:2px 0}
.daily-invite-go{align-self:flex-start;margin-top:12px}
/* The same mark the tab wears, and drawn the same way: the character
   itself, coloured by a rule. See tabs.js for why this app never makes it
   a picture. */
.daily-bar-mark{color:var(--brand-orange-ink);font-weight:800;font-size:22px;line-height:1}
/* The purse beside the mark. The mark says bitcoin; this says the book is
   about what things cost. Sized and coloured off the text around it rather
   than given numbers of its own, so it moves with the bar if the bar ever
   changes. */
.daily-bar-purse{display:inline-flex;align-items:center;color:var(--text);opacity:.75}
.daily-bar-name{flex:1;text-align:left}
/* Het pijltje van een inklapper. Stond tot 7 september 2026 ook in de balk
   zelf; daar is het weg, want de twee knoppen zeggen nu zelf of ze aan
   staan. De klasse blijft, want "Wie deelt dit boek" (householdHtml,
   web/js/daily.js) klapt met hetzelfde pijltje open en leende deze regel al
   voor de balk hem had. Weggehaald met de balk mee, en toen stond dat
   pijltje er ineens ongestyled bij. */
.daily-bar-chevron{font-size:20px;line-height:1;opacity:.7}
.daily-bar.on{border-bottom-left-radius:0;border-bottom-right-radius:0}
/* The book, when it is out, sits in the same box the bar opened from — and
   scrolls inside it.

   Patrick, 1 September 2026: "ik moet alleen in jouw leven in bitcoin kunnen
   scrollen als ik die button aantik". So the book is its own window: the
   months move inside it, and the exchanges underneath stay where they are
   until the page itself is scrolled.

   overscroll-behavior:contain is what stops the page from taking over the
   moment the list reaches its end — without it a swipe carries straight on
   and the whole tab jumps. */
.daily-bar.on + .daily{
  position:relative;
  border:1px solid var(--border);border-top:none;
  border-radius:0 0 var(--radius-soft) var(--radius-soft);padding-top:12px;
  /* Everything that is not the book: the header above it, the bar it opened
     from, and the tab bar and credits below. A round 72vh looked right on a
     desktop and put the book's bottom edge — and the plus sitting on it —
     forty pixels behind the tab bar on a phone. Measured, not guessed. */
  max-height:calc(100vh - var(--header-height) - var(--daily-bar-height)
    - var(--tab-height) - var(--footer-height) - 26px);
  /* Patrick, 1 September 2026: the plus and the save button still sat half
     behind the tab bar on his phone. The measurement above was right and the
     unit was wrong. On iOS Safari 100vh is the height the page would have if
     the address bar were hidden, which it is not while you are reading — so
     the book was built about an address bar too tall and hung its last row,
     and the plus resting on it, underneath the tabs. 100dvh is the height
     actually on screen right now. Declared second so a browser that has
     never heard of it keeps the line above. */
  max-height:calc(100dvh - var(--header-height) - var(--daily-bar-height)
    - var(--tab-height) - var(--footer-height) - 26px);
  overflow-y:auto;overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  /* Ruimte voor de scrollbalk, naast de inhoud en niet eroverheen.
     Patrick, 7 september 2026, op de laptop: de balk lag over de bedragen.
     Op een telefoon is die balk een dun streepje dat vanzelf verdwijnt, op
     een Mac of Windows met een vaste balk is het vijftien pixels die de
     rechterkant van elke rij opeten — en dat is precies de kant waar de
     bedragen staan. `scrollbar-gutter` houdt die ruimte vrij zodra de balk
     plaats inneemt, en de extra padding zorgt dat een bedrag de balk ook
     dan niet aanraakt. */
  scrollbar-gutter:stable;
  padding-right:4px;
}

/* Phase 2's three fields: the shop's name, how it was paid, and where.
   16px on every box, one rule per class — iOS Safari zooms the page on
   focus below that, and the guard in test/search-ui.test.mjs looks for
   each field's own rule. */
.daily-title-box{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-place-box{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-pays{display:flex;flex-wrap:wrap;gap:8px}
.daily-pay{
  background:none;border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-pill);padding:8px 14px;font:inherit;font-size:13px;cursor:pointer;
}
/* Chosen reads as chosen without a second colour to learn. */
.daily-pay[aria-pressed="true"]{background:var(--brand-gradient);border-color:transparent;font-weight:700}
.daily-place-hits{list-style:none;margin:6px 0 0;padding:0;display:flex;flex-direction:column;gap:4px}
.daily-place-hit{
  width:100%;text-align:left;background:var(--panel);border:1px solid var(--border);
  border-radius:12px;padding:10px 12px;color:var(--text);font:inherit;font-size:15px;cursor:pointer;
}
.daily-place-chosen{
  display:flex;align-items:center;gap:8px;background:var(--panel);
  border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-place-flag{font-size:18px;line-height:1}
.daily-place-name{flex:1}
.daily-place-clear{
  background:none;border:none;color:var(--text);font-size:22px;line-height:1;
  cursor:pointer;padding:0 2px;opacity:.7;
}

/* The days no longer scroll on their own: the whole open book does (see
   .daily-bar.on + .daily). Two scroll areas inside each
   other is one too many — a thumb never knows which one it is moving, and
   the plus belonged to neither. */
.daily-days{display:block;position:relative}
/* The way back to today, floating over the days while they move.

   Sticky and not fixed: it belongs to the book, and a fixed button would
   hang over the exchanges underneath the moment the book is folded away.
   At the top, level with the date, where Patrick marked it on 2 September
   2026 — the empty middle of the day's line, and the far corner from the
   plus, which sits bottom right and would otherwise be under the same
   thumb. */
.daily-today{
  position:sticky;top:2px;z-index:5;display:block;margin:0 auto;
  background:var(--panel);border:1px solid var(--border);border-radius:var(--radius-pill);
  padding:7px 18px;color:var(--text);font:inherit;font-size:13px;font-weight:700;
  cursor:pointer;box-shadow:0 4px 14px #0007;
}
.daily-today:hover{border-color:var(--brand-orange-ink);color:var(--brand-orange-ink)}

/* How many days a booking is spread over, and the line that warns a member
   they are about to change every one of them. */
.daily-spread-box{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-run-note{margin:0;font-size:13px;color:var(--muted)}

/* List or map. Two ways of looking at the same book; Stats joins them later. */
.daily-views{display:flex;gap:8px}
.daily-view{
  background:none;border:1px solid var(--border);color:var(--text);
  border-radius:var(--radius-pill);padding:7px 16px;font:inherit;font-size:13px;cursor:pointer;
}
.daily-view[aria-pressed="true"]{background:var(--brand-gradient);border-color:transparent;font-weight:700}
/* The hole the long-lived map element is moved into. */
.daily-map-slot{min-height:52vh;border-radius:var(--radius-soft);overflow:hidden}

/* What the book says about itself: how much trial is left, or that it is
   read-only and where a code goes. Nothing else is taken away — only the
   ways of writing disappear. */
.daily-trial{margin:0;font-size:13px;color:var(--muted)}
/* No box of its own. Patrick drew a line down this block on 1 September 2026
   — "lijn dit even uit... oogt net wat strakker" — and measuring said he was
   right: every other thing in the book starts at the same pixel, and only
   this block's contents sat fourteen further in, because it was a bordered
   card inside a book that is already a bordered card. Two nested boxes make
   two left edges, and the eye finds the second one every time.

   What held the block together was never the border: it is the sentence at
   the top and the space around it. Both stay. */
.daily-locked{
  display:flex;flex-direction:column;gap:8px;margin-bottom:6px;
}
.daily-locked-line{margin:0;font-weight:700}
.daily-locked-keep{margin:0;font-size:13px;color:var(--muted)}
.daily-code{display:flex;gap:8px;align-items:center}
.daily-code-box{
  flex:1;min-width:0;font:inherit;font-size:16px;color:var(--text);
  background:var(--bg);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}

/* Who shares this book. Up to five, with equal rights over everything in
   it — which is exactly why the warning under the search box is there. */
.daily-house{display:flex;flex-direction:column;gap:8px;padding-top:10px}
/* The heading became the handle on 1 September 2026 — the panel is folded
   away by default now, and this is what unfolds it. Built like the bar the
   whole book folds behind (.daily-bar above): the name, then the count, then
   the same chevron, so a member who has learned one has learned both. */
.daily-house-head{
  display:flex;align-items:center;gap:10px;width:100%;
  background:none;border:none;padding:0;color:inherit;font:inherit;cursor:pointer;text-align:left;
}
.daily-house-head .daily-grid-head{flex:1;margin:0}
.daily-house-count{
  flex:none;min-width:24px;text-align:center;
  background:var(--panel);border:1px solid var(--border);border-radius:99px;
  padding:1px 8px;font-size:12px;font-weight:700;color:var(--muted);
}
.daily-house-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
.daily-house-row{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-house-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.daily-house-owner{font-size:12px;color:var(--muted)}
.daily-house-out{
  background:none;border:1px solid var(--border);color:var(--text);border-radius:var(--radius-pill);
  padding:6px 12px;font:inherit;font-size:12px;cursor:pointer;
}
.daily-house-add{display:flex;flex-direction:column;gap:5px}
.daily-house-box{
  width:100%;font:inherit;font-size:16px;color:var(--text);
  background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 12px;
}
.daily-house-hits{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.daily-house-warn{margin:0;font-size:13px;color:var(--muted)}
/* Somebody who has been asked and has not answered. The same row, quieter:
   they are not in the book yet and the row should not pretend they are. */
.daily-house-waiting{opacity:.72;border-style:dashed}
.daily-house-note{display:block;font-size:11.5px;color:var(--muted)}

/* The question, at the top of the book of whoever was asked.
   Since 5 September 2026 nobody is moved into a book without answering this
   first; see householdHtml in web/js/daily.js for what it replaced. It wears
   the brand's own edge because it is the one thing on this screen that is
   waiting for you, and both answers are the same size: saying no is not the
   small print. */
.daily-invite{
  display:flex;flex-direction:column;gap:8px;
  background:var(--panel);border:1px solid rgba(255,140,66,.55);
  border-radius:var(--radius-soft);padding:14px;
}
.daily-invite-title{font-family:var(--face-title);margin:0;font-size:15px;font-weight:800;line-height:1.3}
.daily-invite-what{margin:0;font-size:13px;line-height:1.5;color:var(--muted)}
.daily-invite-do{display:flex;flex-wrap:wrap;gap:8px;margin-top:2px}
.daily-invite-yes,.daily-invite-no{
  flex:1 1 140px;min-height:44px;padding:0 18px;border-radius:var(--radius-pill);
  font-family:inherit;font-size:14px;font-weight:700;line-height:1.2;cursor:pointer;
}
.daily-invite-yes{background:var(--brand-gradient);border:1px solid transparent;color:#fff}
.daily-invite-yes:hover{background:var(--brand-gradient-lit)}
.daily-invite-no{background:none;border:1px solid var(--border);color:var(--text)}
.daily-invite-no:hover{border-color:rgba(255,140,66,.55)}

/* Sixteen or older, asked once at the first booking. A real checkbox, not a
   button that looks like one: this is a statement somebody makes about
   themselves, and a browser's own control is what everybody already knows
   how to read. See ageHtml in web/js/daily.js. */
.daily-age{
  display:flex;align-items:center;gap:10px;padding:12px;
  background:var(--panel);border:1px solid rgba(255,140,66,.55);border-radius:12px;
  font-size:14px;line-height:1.35;cursor:pointer;
}
.daily-age input{width:20px;height:20px;flex:none;accent-color:var(--brand-orange)}
.daily-age-ask{margin:0 2px 8px;font-size:13.5px;line-height:1.5;font-weight:600;color:var(--brand-orange-ink)}
.daily-age-why{margin:6px 2px 0;font-size:12.5px;line-height:1.5;color:var(--muted)}

/* Who wrote a booking down: shown, never a box to type in. There is one
   true answer and the app already knows it. */
.daily-noted-by{margin:0;padding:10px 12px;background:var(--panel);border:1px solid var(--border);border-radius:12px}

/* The two ways to open the book again by paying. Lightning, through Just B
   Free's own Strike account — nothing is held here and no card details come
   near this app. */
/* Room before the choice, and again before the way around it. Patrick drew
   an arrow at the first seam on 1 September 2026: with every line eight
   pixels from the next, what you have just been told and what you are being
   asked to decide read as one list. These two gaps are what make them three
   things — the message, the choice, and the code as an alternative. */
.daily-plans{display:flex;gap:10px;margin-top:8px}
.daily-plans + .daily-locked-keep{margin-top:8px}
.daily-plan{
  flex:1;display:flex;flex-direction:column;gap:2px;cursor:pointer;
  background:var(--bg);border:1px solid var(--border);border-radius:12px;
  padding:12px;color:var(--text);font:inherit;text-align:left;
}
.daily-plan[disabled]{opacity:.55;cursor:default}
.daily-plan-name{font-size:13px;opacity:.75}
.daily-plan-price{font-size:19px;font-weight:700}
.daily-pay-open{display:flex;flex-direction:column;gap:8px}
/* The one way to pay: Stripe's own page. The rule that drew a Lightning
   code underneath went with the Strike rail on 5 September 2026 — see
   payHtml in web/js/daily.js. */
.daily-pay-go{text-align:center;text-decoration:none}

/* ── Berichten tussen twee vrienden ────────────────────────────────────────
   Patrick, 6 september 2026. Alles hier leunt op wat er al stond: de rij
   is de act-person-rij nog eens, de knoppen zijn trips-quiet en
   trips-primary. Nieuw is precies een ding: een wolkje dat naar links of
   naar rechts leunt, al naar gelang wie het zei. */
.msg-screen{display:flex;flex-direction:column;gap:10px;padding-bottom:8px}
.msg-list{list-style:none;margin:8px 0 0;padding:0}
.msg-list li{border-bottom:1px solid var(--border)}
.msg-list li:last-child{border-bottom:none}
/* De hele rij is de knop, zodat een duim de rij raakt en niet een woord. */
.msg-row{
  width:100%;display:flex;align-items:center;gap:11px;padding:10px 0;
  background:none;border:none;font-family:inherit;text-align:left;cursor:pointer;color:inherit;
}
.msg-row-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.msg-row-top{display:flex;align-items:baseline;gap:8px}
.msg-row-name{font-size:14px;font-weight:700;color:var(--text);overflow-wrap:anywhere}
.msg-row-when{margin-left:auto;font-size:11.5px;color:var(--muted);white-space:nowrap}
/* Een regel, nooit twee: het laatste wat er gezegd is, is een voorproefje
   en geen bericht. */
.msg-row-last{
  font-size:12.5px;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.msg-row-last.unread{color:var(--text);font-weight:600}
/* Hetzelfde wolkje als op de tabbalk, hier naast een naam. */
.msg-count{
  min-width:18px;height:18px;padding:0 5px;border-radius:9px;flex:none;
  display:flex;align-items:center;justify-content:center;
  background:var(--brand-orange);color:#fff;font-size:11px;font-weight:800;line-height:1;
}
.msg-head{display:flex;align-items:center;gap:10px;margin-top:4px}
.msg-head-name{flex:1;min-width:0;font-size:15px;font-weight:700;overflow-wrap:anywhere}
.msg-title{flex:1;min-width:0}
.msg-back,.msg-drop{flex:none;font-size:12px;padding:6px 10px}
.msg-error{color:var(--brand-orange-ink)}
.msg-start{margin-top:14px}
/* Het gesprek zelf. Oudste boven, want zo leest een gesprek. */
.msg-thread{
  list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;
  max-height:52vh;overflow-y:auto;
}
.msg-bubble{
  max-width:78%;align-self:flex-start;
  display:flex;flex-direction:column;gap:3px;
  padding:9px 12px;border-radius:14px 14px 14px 4px;
  background:var(--bg);border:1px solid var(--border);
}
/* Wat jij zei leunt naar rechts en draagt de merkkleur, zodat je in een
   oogopslag ziet wie wat zei zonder dat er een naam bij hoeft. */
.msg-bubble.mine{
  align-self:flex-end;
  border-radius:14px 14px 4px 14px;
  background:linear-gradient(135deg,rgba(255,79,143,.22),rgba(255,140,66,.22));
  border-color:rgba(255,140,66,.45);
}
.msg-body{font-size:14px;line-height:1.5;color:var(--text);overflow-wrap:anywhere}
.msg-when{font-size:10.5px;color:var(--muted);align-self:flex-end}
.msg-form{display:flex;gap:8px;align-items:flex-end}
/* 16px, want alles daaronder laat een iPhone bij aanraking inzoomen. */
.msg-input{
  flex:1;min-width:0;resize:none;font:inherit;font-size:16px;line-height:1.4;
  padding:9px 11px;border-radius:12px;
  background:var(--bg);border:1px solid var(--border);color:var(--text);
}
.msg-input:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
.msg-send{flex:none}
.msg-send[disabled]{opacity:.5;cursor:default}
/* De envelop naast een vriend op de Activiteit-tab: het Koh Tao-moment
   zelf. Naast de rij en niet erin, want de rij is al een knop. */
.act-person-write{
  flex:none;background:none;border:1px solid var(--border);border-radius:50%;
  width:34px;height:34px;display:flex;align-items:center;justify-content:center;
  color:var(--muted);font-size:15px;cursor:pointer;
}
.act-person-write:hover{border-color:rgba(255,140,66,.55);color:var(--text)}
.act-person-write:focus-visible{outline:2px solid var(--brand-orange);outline-offset:1px}
/* De ingang staat bij de andere twee deuren op de reizenlijst en draagt
   dus me-stats, dezelfde knop als de statistieken. Wat hij extra heeft is
   het wolkje, en dat is msg-count hierboven. */

/* ------------------------------------------------------------------ *
 * Het afrekenen: het vierde tabje van een gedeelde reis
 * ------------------------------------------------------------------ *
 * Twee blokken onder elkaar, en ze doen expres niet hetzelfde. Boven
 * staat per persoon wat hij voorschoot en wat zijn aandeel is: rustig,
 * grijs, een tabel om te controleren. Daaronder de overboekingen, en die
 * zijn het antwoord op de vraag waarvoor je dit tabje opent, dus die
 * krijgen de rand en het formaat. Wie alleen wil weten wat hij moet
 * overmaken hoeft het bovenste niet te lezen. */
.settle-view{margin-top:14px}
.settle-people{display:flex;flex-direction:column;gap:2px;margin-top:12px}
.settle-person{
  display:grid;grid-template-columns:auto auto 1fr;gap:2px 14px;
  padding:9px 0;border-bottom:1px solid var(--border);
}
.settle-person:last-child{border-bottom:none}
.settle-name{grid-column:1/3;grid-row:1;font-size:13.5px;font-weight:700}
/* Betaald en aandeel staan onder de naam naast elkaar, elk in een eigen
   kolom: samen zijn ze de onderbouwing van het derde getal, en twee spans
   in dezelfde cel zouden per regel op een andere plek beginnen. */
.settle-paid,.settle-share{font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.settle-paid{grid-column:1;grid-row:2}
.settle-share{grid-column:2;grid-row:2}
.settle-diff{grid-column:3;grid-row:1/3;justify-self:end;align-self:center;
  font-size:13px;font-weight:700;font-variant-numeric:tabular-nums;text-align:right}
/* Krijgt terug is groen, is schuldig is de oranje van het merk en niet
   rood: schuld is hier geen fout maar een afspraak tussen vrienden. */
.settle-plus{color:var(--good, #2e9e6b)}
.settle-min{color:var(--brand-orange-ink)}
.settle-even{color:var(--muted)}
.settle-moves{display:flex;flex-direction:column;gap:8px;margin-top:16px}
.settle-move{
  display:flex;flex-direction:column;gap:3px;
  border:1px solid var(--border);border-radius:var(--radius-card, 12px);padding:11px 13px;
}
.settle-who{font-size:13.5px;font-weight:700}
/* De drie getallen naast elkaar: euro, bitcoin, en wat dat vandaag waard
   is. Ze wikkelen op een smal scherm, want geen van de drie mag afgekapt
   worden; een half bedrag is erger dan een tweede regel. */
.settle-amounts{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px;
  font-variant-numeric:tabular-nums}
.settle-cents{font-size:14px;font-weight:700}
.settle-sats{font-size:12px;color:var(--muted)}
.settle-worth{font-size:11.5px;color:var(--muted);opacity:.85}
.settle-none{margin:16px 0 0;font-size:13px;color:var(--muted)}
.settle-windup{margin:16px 0 0;font-size:12.5px;line-height:1.5;color:var(--text)}
/* De Lightning-knop onder een overboeking die jij moet doen.

   Twee knoppen naast elkaar en de tweede is de betrouwbare: de koppeling
   opent de wallet op een telefoon die er een heeft, en het adres ernaast
   kopieert altijd. Op een computer werkt alleen die tweede, en dat is
   precies waarom hij er staat. */
.settle-pay{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px}
.settle-pay-open{
  display:inline-flex;align-items:center;min-height:34px;padding:0 14px;
  border-radius:999px;background:var(--brand-orange);color:#1b1200;
  font-size:13px;font-weight:700;text-decoration:none;
}
.settle-pay-open:hover{filter:brightness(1.08)}
/* Het adres zelf, als knop die kopieert. In een schreefloze schrijfmachine-
   letter, want je leest hem teken voor teken om te controleren of het klopt. */
/* Het reisbudget. Regels van naam-links, bedrag-rechts, en verder niets:
   geen balken, geen kleuren, geen waarschuwingen. Wie boven zijn budget
   uitkomt weet dat zelf ook, en een app die daar streng over doet is er een
   die je op vakantie niet meer opent. */
.budget-lines{display:flex;flex-direction:column;gap:2px;margin:12px 0 0}
.budget-line{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:8px 0;border-bottom:1px solid var(--border);font-size:13.5px;
}
.budget-line:last-child{border-bottom:0}
.budget-label{color:var(--muted)}
.budget-value{font-variant-numeric:tabular-nums;text-align:right}
/* Wat er over is, want dat is het getal waarvoor je dit tabje opent. */
.budget-strong .budget-value{font-size:17px;font-weight:800}
.budget-set{margin-top:16px;padding-top:14px;border-top:1px solid var(--border)}
.budget-set-label{display:block;font-size:12px;color:var(--muted);font-weight:700}
.budget-set-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:8px}
/* 16px, zoals elk veld hier: kleiner en iOS Safari zoomt in bij het aantikken. */
.budget-box{
  flex:1 1 140px;min-height:38px;padding:0 12px;border-radius:10px;
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  font-family:inherit;font-size:16px;font-variant-numeric:tabular-nums;
}
.budget-box:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
.budget-currency{flex:none;font-size:13px;color:var(--muted);font-weight:700}

/* De kopieerknop, even zwaar als de betaalknop ernaast. Op een telefoon
   bepaalt het besturingssysteem zélf welke wallet een lightning-koppeling
   opent en is er geen keuzemenu; wie een andere wallet wil, kopieert het
   adres en opent zijn eigen app. Die weg mag dus niet in een tooltip
   verstopt zitten, want op een aanraakscherm bestaat zweven niet. */
.settle-pay-copy{
  display:inline-flex;align-items:center;min-height:34px;padding:0 14px;
  border-radius:999px;background:none;border:1px solid var(--border);
  color:var(--text);font-family:inherit;font-size:13px;font-weight:700;cursor:pointer;
}
.settle-pay-copy:hover{border-color:var(--brand-orange-ink)}
/* Het adres eronder, om te lezen wat je kopieert. */
.settle-pay-address{
  margin:6px 0 0;font-family:ui-monospace,Menlo,monospace;
  font-size:12.5px;color:var(--muted);word-break:break-all;
}
/* De drie knoppen om te zeggen dat het geregeld is.

   Rustig en klein: dit is geen hoofdhandeling maar een bevestiging achteraf,
   en het staat onder een bedrag dat al de aandacht heeft. Wel 32 hoog, want
   een duim moet hem raken zonder de knop ernaast te pakken. */
.settle-done-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px}
.settle-done-label{font-size:12px;color:var(--muted)}
.settle-done{
  min-height:32px;padding:0 12px;border-radius:999px;
  background:none;border:1px solid var(--border);color:var(--muted);
  font-family:inherit;font-size:12.5px;cursor:pointer;
}
.settle-done:hover{color:var(--text);border-color:var(--brand-orange-ink)}
/* Wat er al is afgerekend. Doorgestreept staat het er niet: het is geen
   afgehandelde taak maar een boeking die meetelt in de som erboven. */
/* `settle-paid` bestond al, voor het bedrag "Betaald € 114,00" in de regel
   van een persoon. Deze lijst heet daarom settle-done-*, anders herschrijft
   de opmaak hieronder dat bedrag mee. */
.settle-done-list{list-style:none;margin:14px 0 0;padding:0;display:flex;
  flex-direction:column;gap:6px}
.settle-done-line{display:flex;align-items:center;justify-content:space-between;gap:10px;
  font-size:12.5px;color:var(--muted)}
.settle-undo{
  flex:none;min-height:30px;padding:0 10px;border-radius:8px;
  background:none;border:0;color:var(--muted);
  font-family:inherit;font-size:12px;text-decoration:underline;cursor:pointer;
}
.settle-undo:hover{color:var(--text)}
/* Je eigen adres, voor wie geld terugkrijgt. */
.settle-mine{margin-top:18px;padding-top:14px;border-top:1px solid var(--border)}
.settle-mine-label{display:block;font-size:12px;color:var(--muted);font-weight:700}
.settle-mine-row{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}
/* 16px is geen smaak maar een grens: daaronder zoomt iOS Safari in zodra je
   het veld aantikt, en dan staat het scherm scheef. Een toets bewaakt dat. */
.settle-mine-box{
  flex:1 1 200px;min-height:38px;padding:0 12px;border-radius:10px;
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  font-family:inherit;font-size:16px;
}
.settle-mine-box:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
.settle-buttons{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
/* De streep is het enige hier wat niet terug te draaien is, dus hij draagt
   de merkkleur en staat als tweede: eerst "nog niet", dan pas dit. */
.settle-final{border-color:rgba(255,140,66,.55);color:var(--brand-orange-ink)}
.settle-final:hover{border-color:var(--brand-orange-ink)}

/* De regel onder een eigen uitgave waarmee je hem alsnog deelt. Onder het
   wolkje en ingesprongen zoals dat, want hij hoort bij die ene uitgave en
   niet bij de lijst. Klein en grijs: het is een schakelaar die je een keer
   omzet, geen getal om te lezen. */
.spend-entry-share{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;
  margin:2px 0 10px 44px;padding:0;list-style:none;
}
.spend-share-label{font-size:11px;color:var(--muted);opacity:.85}
.spend-entry-share .scope-chip{font-size:11.5px;padding:5px 11px}
/* En de zin op het afrekenscherm die zegt waar je dat aanzet. */
.settle-how{margin-top:6px}

/* ------------------------------------------------------------------ *
 * Mijn bitcoin
 * ------------------------------------------------------------------ *
 * Het blok onderaan het dagboek waarmee iemand zijn eigen adressen
 * bekijkt. Bewust rustig: één groot getal, wat het waard is, en daaronder
 * zinnen. Geen grafiek en geen tegels, want dit is niet het boek zelf maar
 * een blik op wat er in de portemonnee zit. */
/* Mijn bitcoin. De marge, de binnenruimte en de streep die hier tot 7
   september 2026 boven stonden waren de scheiding met het kasboek: dit blok
   hing toen onderaan diezelfde vouw. Sinds het een paneel van zichzelf is
   staat er niets meer boven om van te scheiden, en tekenden ze samen een
   streep met een gat erboven. Het paneel brengt zijn eigen binnenruimte mee
   (.daily-bar.on + .daily). */
.wallet{margin-top:0}
/* De kop van het blok, met het oogje er direct naast. De h4 brengt zijn
   eigen marges mee (.trips-heading), dus de rij zelf krijgt er geen: anders
   staat het oogje niet op dezelfde hoogte als de tekst ernaast.

   flex-start en niet space-between. Met space-between stond het oogje aan
   het eind van de regel, en op een breed scherm is dat twee meter van de
   kop waar het bij hoort: je ziet niet meer dat die knop over dit blok
   gaat. Patrick wees het aan met een streepje naast MIJN BITCOIN. */
.wallet-head{display:flex;align-items:baseline;justify-content:flex-start;gap:10px}
/* Het oogje. Rustig, want het is een schakelaar en geen mededeling, en met
   een raakvlak dat groot genoeg is voor een duim naast een kop van 12px. */
.wallet-eye{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:34px;height:34px;margin:-8px -6px -8px 0;border-radius:10px;
  background:none;border:none;color:var(--muted);cursor:pointer;
}
.wallet-eye:hover{color:var(--text)}
.wallet-eye[aria-pressed="true"]{color:var(--brand-orange-ink)}
/* De bolletjes staan op de plek van het bedrag, in dezelfde maat, zodat het
   blok niet omhoog springt bij elke tik. */
.wallet-btc{letter-spacing:0}
/* Wat je leven per maand kost, onderaan de statistieken. Dezelfde rustige
   regels als het reisbudget: naam links, bedrag rechts, en geen kleur die
   een oordeel geeft over wat er overblijft. */
.living-title{font-family:var(--face-title);margin:26px 0 4px;font-size:15px;font-weight:800}
.living-lines{display:flex;flex-direction:column;gap:2px;margin-top:10px}
.living-line{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:8px 0;border-bottom:1px solid var(--border);font-size:13.5px;
}
.living-line:last-child{border-bottom:0}
.living-label{color:var(--muted)}
.living-value{font-variant-numeric:tabular-nums;text-align:right}
.living-strong .living-value{font-size:17px;font-weight:800}
/* De vaste lasten zelf, kleiner: het bedrag erboven is het getal dat telt. */
.living-items{list-style:none;margin:10px 0 0;padding:0;display:flex;
  flex-direction:column;gap:4px}
.living-item{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  font-size:12.5px;color:var(--muted)}

/* De rondleiding. Een kaart onderaan en geen vel over het hele scherm, sinds
   9 september. Patrick: "ik wil dat als je praat over een onderdeel je ook
   daadwerkelijk dat onderdeel in het scherm ziet, anders weten mensen niet
   waar je over praat." Bij elke halte springt de app naar het juiste tabblad,
   en deze kaart laat de bovenkant vrij zodat je dat scherm ook echt ziet.

   Boven de tabbalk en de voettekst, met dezelfde twee tokens waarmee elk
   ander vel in deze app zich daarboven zet. Wat hij niet doet is knoppen
   aanwijzen; zie de kop van web/js/tour.js voor waarom niet. */
.tour-card{
  position:fixed;left:0;right:0;z-index:950;
  bottom:calc(var(--footer-height) + var(--tab-height));
  display:flex;flex-direction:column;gap:10px;
  max-height:52vh;overflow-y:auto;
  padding:12px 18px 16px;
  background:var(--panel);border-top:1px solid var(--border);
  border-radius:20px 20px 0 0;box-shadow:var(--shadow-rest);
}
.tour-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
.tour-title{
  margin:0;font-size:11.5px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);
}
.tour-x{
  flex:none;background:none;border:0;padding:2px 4px;color:var(--muted);
  font-size:18px;line-height:1;cursor:pointer;
}
.tour-x:hover{color:var(--text)}
.tour-body{display:flex;flex-direction:column;gap:6px;text-align:center}
/* Welk tabblad het over gaat, met hetzelfde teken dat in de balk onderaan
   staat. Nu je dat tabblad er ook bij ziet is dit het bijschrift eronder. */
.tour-where{
  margin:0;font-size:12px;font-weight:700;letter-spacing:.08em;
  text-transform:uppercase;color:var(--brand-orange-ink);
}
.tour-heading{font-family:var(--face-title);margin:0;font-size:19px;font-weight:800;line-height:1.25}
.tour-text{margin:0;font-size:14.5px;line-height:1.5;color:var(--muted)}
.tour-feet{display:flex;flex-direction:column;align-items:center;gap:8px}
/* Bolletjes en een telletje, want een rondleiding zonder eind is een gang
   waar je bij de derde halte uit stapt omdat je niet weet of er nog dertig
   komen. */
.tour-dots{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;max-width:320px}
.tour-dot{width:6px;height:6px;border-radius:50%;background:var(--border)}
.tour-dot.on{background:var(--brand-orange)}
.tour-count{margin:0;font-size:12px;color:var(--muted)}
.tour-buttons{display:flex;gap:10px;width:100%;max-width:360px}
.tour-next{
  flex:1 1 auto;min-height:44px;border-radius:999px;
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  font:inherit;font-size:15px;font-weight:700;cursor:pointer;
}
.tour-next:hover{background:var(--brand-gradient-lit)}
.tour-back{
  flex:0 0 auto;min-height:44px;padding:0 20px;border-radius:999px;
  background:none;border:1px solid var(--border);color:var(--text);
  font:inherit;font-size:15px;font-weight:700;cursor:pointer;
}
.tour-back:hover{border-color:var(--brand-orange-ink)}
/* Overslaan is stil: het mag kunnen, maar het is niet wat we voorstellen. */
.tour-skip{
  background:none;border:0;padding:2px 12px;color:var(--muted);
  font:inherit;font-size:13px;cursor:pointer;
}
.tour-skip:hover{color:var(--text)}

/* Toshi. De balk staat op de bitcoin-tab onder "Jouw leven in Bitcoin" en
   opent een vel over het hele scherm; zie web/js/toshi-card.js.

   Dezelfde laag als de balk erboven — dezelfde achtergrond, rand en ronding —
   want ze horen bij elkaar en staan tegen elkaar aan. Hij is wél een knop in
   zijn geheel, anders dan de balk erboven: er zit maar één ding in, dus er is
   geen knop in een knop. Vandaar ook de pointer. */
/* Sinds de restyle van september 2026 geen balk meer maar een kaart, en de
   enige kaart in de app die kleur in zijn glas heeft: een heel flauwe
   magenta-naar-oranje wash onder het matglas. Dat is met opzet de enige —
   Toshi is het ene ding op deze tab dat terugpraat, en de kaart mag dat
   laten zien zonder er een gradient-knop van te maken. De gradient blijft
   waar hij hoort: op één actie per blok, en dit is een kaart die een vel
   opent, geen actie.

   Het blijft één knop in zijn geheel, zoals hiervoor: er zit maar één ding
   in, dus er is geen knop in een knop. */
.toshi-bar{
  /* Toshi staat niet in de rij maar op de kaart: hij hangt aan de onderrand
     en steekt er met kop en oren bovenuit, zoals op Patricks schets. Daarom
     is dit de ophanging (`position:relative`) en mag er niets geknipt worden
     (`overflow` blijft zichtbaar, wat de standaard is en hier dus niet
     tegengesproken moet worden), en houdt de linkerkant ruimte vrij waar hij
     staat -- die ruimte is padding en geen marge op hem, zodat de woorden
     ernaast blijven staan waar ze staan als het plaatje nog niet geladen is.

     Hij loopt bewust een stukje over de hero erboven. Dat is wat het
     uitsteken zichtbaar maakt: een figuur die precies tot de rand komt leest
     als een figuur in een doos, en een die eroverheen valt leest als een
     figuur die ervoor staat. */
  position:relative;
  display:flex;align-items:center;gap:14px;width:100%;margin:0 0 22px;
  background:
    radial-gradient(120% 140% at 12% 20%, rgba(214,34,208,.42) 0%, transparent 58%),
    radial-gradient(100% 130% at 92% 88%, rgba(255,140,66,.30) 0%, transparent 55%),
    var(--navy-deep);
  border:1px solid rgba(214,34,208,.30);border-radius:20px;
  padding:14px 16px 14px 91px;color:#fff;font:inherit;text-align:left;cursor:pointer;
  box-shadow:0 2px 4px rgba(23,32,43,.06),0 18px 40px -22px rgba(150,20,140,.55);
  transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease;
}
.toshi-bar:hover{
  transform:translateY(-2px);border-color:rgba(255,140,66,.6);
  box-shadow:0 4px 8px rgba(23,32,43,.08),0 26px 54px -22px rgba(150,20,140,.7);
}
@media (prefers-reduced-motion:reduce){.toshi-bar:hover{transform:none}}
/* Het portret, en sinds 10 september 2026 komt hij met kop en oren uit de
   kaart. Patrick tekende er een cirkel omheen op zijn schets: "dat hij met
   zn oortjes uit de balk komt".

   Daarvoor staat hij niet meer in de rij maar los op de kaart, met zijn
   voeten op de onderrand -- dat is wat het uitsteken doet. De ruimte links
   die hij inneemt is padding op de kaart en geen marge op hem, zodat de
   woorden ernaast op hun plek blijven staan als het plaatje nog niet geladen
   is of nooit komt.

   104px, en dat getal is een afweging en geen smaak. De kaart is 75px hoog,
   dus hij steekt er 30 bovenuit en raakt de hero erboven nog net met 12 --
   genoeg om als "ervoor" te lezen, te weinig om het woord "Bitcoin" in die
   hero af te dekken. Groter kan pas als de kaart hoger wordt: op Patricks
   schets zit er nog een zoekbalk met drie knoppen in die wij niet gebouwd
   hebben, en dáárom heeft hij daar meer ruimte om in te groeien.

   De schaduw komt van hier en niet uit het bestand: hij is vrijstaand
   uitgeknipt en heeft er zelf geen, en zonder schaduw zweeft hij over de
   kaart in plaats van erop te staan. `pointer-events:none` omdat de hele
   kaart één knop is: een tik óp Toshi hoort gewoon zijn vel te openen. */
.toshi-bar-face{
  position:absolute;left:8px;bottom:0;z-index:2;
  width:auto;height:104px;object-fit:contain;object-position:bottom;
  /* Een zachte schaduw onder hem, zodat hij op de kaart staat en er niet
     overheen zweeft. Het bestand is vrijstaand en heeft er zelf geen. */
  filter:drop-shadow(0 6px 10px rgba(23,32,43,.45));
  pointer-events:none;
}
.toshi-bar-words{display:flex;flex-direction:column;gap:2px;min-width:0}
/* Dezelfde serif als de hero erboven, een maat kleiner: die twee zijn de
   twee dingen op deze tab die zich voorstellen, en de rest is een lijst. */
.toshi-bar-name{font-family:var(--face-title);font-size:23px;font-weight:800;line-height:1.15}
.toshi-bar-hint{font-size:12.5px;color:rgba(255,255,255,.72)}

/* Het vel zelf: dezelfde volle-schermlaag als de statistieken, met dezelfde
   kop erboven, zodat het kruisje staat waar het altijd staat. */
.toshi-sheet{
  position:fixed;inset:0;z-index:900;display:flex;flex-direction:column;
  background:var(--bg);padding:16px 16px 0 16px;
}
/* Wat eronder hangt scrollt; de kop niet. Gecentreerd, want er staat weinig
   op en links uitgelijnd zou het scherm leeg laten ogen. */
.toshi-body{
  flex:1 1 auto;overflow-y:auto;display:flex;flex-direction:column;
  align-items:center;text-align:center;padding-bottom:24px;
}
/* En op het vel groot. `contain` en geen `cover`: hij is uitgeknipt en niet
   vierkant, en `cover` zou zijn oren of zijn voeten afsnijden om een vierkant
   te vullen dat er niet meer is. */
.toshi-face{
  flex:none;width:auto;height:150px;object-fit:contain;
  margin-top:8px;
}
.toshi-hi{font-family:var(--face-title);margin:14px 0 0;font-size:20px;font-weight:800}
.toshi-help{margin:6px 0 0;font-size:15px;color:var(--muted)}
/* De drie keuzes onder elkaar, over de volle breedte: op een telefoon is dat
   het raakvlak dat je met een duim haalt zonder te kijken. 14px onder de
   vraag erboven, dezelfde lucht die elke knop in deze app boven zich heeft. */
.toshi-choices{
  display:flex;flex-direction:column;gap:10px;width:100%;max-width:360px;margin-top:14px;
}
/* Het tekentje links, het opschrift ernaast, allebei links uitgelijnd zoals
   Patrick het tekende. Niet gecentreerd: drie iconen die per regel opschuiven
   omdat het woord ernaast langer is, lezen als drie losse knopjes. */
.toshi-choice{
  display:flex;align-items:center;gap:12px;
  min-height:48px;padding:0 18px;border-radius:14px;text-align:left;
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  font:inherit;font-size:15px;font-weight:700;cursor:pointer;
}
.toshi-choice:hover{border-color:var(--brand-orange-ink)}
/* De kleur van het merk, want dit zijn knoppen en geen categorieën: die
   dragen hun eigen kleur op een schijfje (zie .cat-icon), en dat zou hier
   drie verschillende kleuren geven waar niets achter zit. */
.toshi-choice-icon{
  display:inline-flex;flex:none;align-items:center;justify-content:center;
  color:var(--brand-orange-ink);
}
/* Terug naar de keuzes. Onder het antwoord en gecentreerd sinds 9 september:
   hij stond erboven en links, en viel daar tussen de begroeting en het
   antwoord in. Onderaan is hij ook waar je hem zoekt, namelijk als je klaar
   bent met lezen. 22px lucht erboven, ruim genoeg om niet aan het antwoord
   te plakken. */
.toshi-back{
  align-self:center;background:none;border:0;margin-top:22px;padding:8px 14px;
  color:var(--brand-orange-ink);
  font:inherit;font-size:14px;font-weight:700;cursor:pointer;
}
.toshi-answer{width:100%;max-width:360px;margin-top:10px}
.toshi-said{margin:10px 0 0;font-size:15px;line-height:1.5;color:var(--muted)}

/* De drie soorten bericht, en het veld eronder. Eén brievenbus voor een vraag
   en voor een verbeterpunt: het gaat naar dezelfde twee mensen, en die willen
   in één oogopslag zien wat het is. */
.toshi-kinds{display:flex;flex-wrap:wrap;gap:8px;width:100%;max-width:360px;margin-top:14px}
.toshi-kind{
  flex:1 1 auto;min-height:38px;padding:0 14px;border-radius:999px;
  background:none;border:1px solid var(--border);color:var(--muted);
  font:inherit;font-size:13.5px;font-weight:700;cursor:pointer;white-space:nowrap;
}
.toshi-kind:hover{border-color:var(--brand-orange-ink)}
.toshi-kind.on{background:#ffffff12;border-color:var(--brand-orange-ink);color:var(--text)}
/* 16px, zoals elk invoerveld hier: kleiner en iOS Safari zoomt in zodra je
   het aantikt, en dan staat het scherm scheef. */
.toshi-note{
  width:100%;max-width:360px;margin-top:12px;padding:12px 14px;border-radius:var(--radius-soft);
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  font-family:inherit;font-size:16px;line-height:1.45;resize:vertical;
}
.toshi-note:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
/* De knop krijgt de merkkleur, want dit is de handeling van dit scherm. En
   14px lucht boven en onder, zoals elke knop naast een uitleg hier. */
.toshi-send{
  width:100%;max-width:360px;min-height:46px;margin-top:14px;border-radius:999px;
  background:var(--brand-gradient);border:1px solid transparent;color:#fff;
  font:inherit;font-size:15px;font-weight:700;cursor:pointer;
}
.toshi-send:hover{background:var(--brand-gradient-lit)}
.toshi-send:disabled{opacity:.6;cursor:default}
.toshi-send + .toshi-said{margin-top:14px}

/* Het veld waarin je Toshi een uitgave vertelt. Eén veld en één knop, want
   dit moet sneller zijn dan het formulier zelf openen — anders is het geen
   tijdwinst maar een omweg.

   16px op het veld, zoals overal hier: kleiner en iOS Safari zoomt in zodra
   je het aantikt, en dan staat het scherm scheef. */
.toshi-ask{display:flex;gap:8px;width:100%;max-width:360px;margin-top:14px}
.toshi-box{
  flex:1 1 auto;min-height:40px;padding:0 14px;border-radius:999px;
  background:var(--panel);border:1px solid var(--border);color:var(--text);
  font-family:inherit;font-size:16px;
}
.toshi-box:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
.toshi-go{
  flex:none;min-height:40px;padding:0 16px;border-radius:999px;
  background:none;border:1px solid var(--border);color:var(--text);
  font-family:inherit;font-size:13px;font-weight:700;cursor:pointer;
}
.toshi-go:hover{border-color:var(--brand-orange-ink)}

/* Het kaartje van de week, boven het kasboek. Dezelfde rustige laag als het
   dagkaartje in Mijn bitcoin, want het is hetzelfde soort bericht: meestal
   geen nieuws, en op de dagen dat het dat wel is doen de woorden het werk. */
.week-card{
  margin:0 0 14px;padding:4px 14px 12px;
  background:var(--fill);border:1px solid var(--border);
  border-radius:var(--radius-soft);
}
.week-card-head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;min-height:34px;padding:0;
  background:none;border:0;color:var(--muted);
  font-family:inherit;font-size:12px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;cursor:pointer;
}
.week-card-head:hover{color:var(--text)}
.week-card-chevron{font-size:20px;line-height:1;opacity:.7}
.week-card-line{margin:0;font-size:13px;line-height:1.45}
.week-card-line + .week-card-line{margin-top:6px}

/* Het kaartje van de assistent, boven de bedragen.

   Rustig en niet alarmerend: een lichte laag op het paneel, geen eigen
   kleur, geen kader dat om aandacht vraagt. Wat erop staat is meestal geen
   nieuws, en op de dagen dat het dat wel is doen de woorden het werk. Een
   gekleurde rand zou elke dag hetzelfde roepen. */
.wallet-life{
  margin:10px 0 14px;padding:4px 14px 12px;
  background:var(--fill);border:1px solid var(--border);
  border-radius:var(--radius-soft);
}
/* Dichtgeklapt is het alleen de kop, dus dan mag de ruimte eronder weg. */
.wallet-life.shut{padding-bottom:4px}
/* De kop is de knop: de hele regel is aan te tikken en niet alleen het
   pijltje, want een pijltje van twaalf pixels is geen doelwit voor een duim.
   Daarom ook de hoogte van 34, de maat die dit scherm overal aanhoudt. */
.wallet-life-head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;min-height:34px;padding:0;
  background:none;border:0;color:var(--muted);
  font-family:inherit;font-size:12px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;cursor:pointer;
}
.wallet-life-head:hover{color:var(--text)}
/* Hetzelfde pijltje als op de balk erboven (.daily-bar-chevron), zodat het
   scherm er één taal op na houdt voor "hier zit iets onder". */
.wallet-life-chevron{font-size:20px;line-height:1;opacity:.7}
.wallet-life-line{margin:0;font-size:13px;line-height:1.45}
.wallet-life-line + .wallet-life-line{margin-top:6px}
.wallet-figures{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;margin-top:4px}
.wallet-btc{font-size:22px;font-weight:800;letter-spacing:-.01em;font-variant-numeric:tabular-nums}
.wallet-worth{font-size:15px;color:var(--muted);font-variant-numeric:tabular-nums}
.wallet-change{margin:6px 0 0;font-size:13px;font-weight:700}
.wallet-pending,.wallet-fees,.wallet-note{margin:6px 0 0}
/* De adressen zelf: klein, verkort, met het kruisje van de reisregels. */
.wallet-addresses{list-style:none;margin:12px 0 0;padding:0;display:flex;
  flex-direction:column;gap:2px}
.wallet-address{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:7px 0;border-top:1px solid #ffffff0f}
.wallet-address-text{font-size:12.5px;color:var(--muted);font-family:ui-monospace,Menlo,monospace}
/* En na de adreslijst net zo veel lucht als na een alinea. De regels bij
   .trips-hint hierboven dekken de knop onder een stuk tekst; hier is de
   buurman een lijst, en die viel er dus buiten: de knop plakte tegen het
   laatste adres aan. Zelfde veertien pixels, zelfde gedachte. */
.wallet-addresses + .trips-quiet{margin-top:14px}
.wallet-form{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.wallet-label{font-size:12px;color:var(--muted)}
/* 16px, want iOS Safari zoomt de pagina in zodra een veld kleiner is en
   dan staat het adres dat je net plakte half buiten beeld. Zelfde regel als
   elk ander tekstveld in dit boek. */
.wallet-input{
  width:100%;background:var(--bg-soft, #ffffff0d);border:1px solid var(--border);
  color:var(--text);border-radius:10px;padding:11px 12px;
  font-family:ui-monospace,Menlo,monospace;font-size:16px;
}
.wallet-input:focus{outline:2px solid var(--brand-orange);outline-offset:1px}
.wallet-form-buttons{display:flex;flex-wrap:wrap;gap:8px}
