/* ============================================================
   THE APP'S ONE STYLESHEET — for the shell and every screen on it.

   Until 21 Aug each sheet screen (home, a person's page, create, the
   sign-in door, the confirmation landing) was its own page carrying
   its own copy of these tokens, each with a note saying it was a copy
   (05_DECISIONS.md, the shared app stylesheet). The shell
   (docs/app/08_APP_SHELL_PLAN.md) is one document, so this is the one
   copy. The map page (mockups/index.html) still carries its own; the
   day it links this file for the tokens is a later, separate step.

   ------------------------------------------------------------
   HOW IT IS ORGANISED, AND THE ONE RULE.

     1. the tokens, the page, the sheet      — once, unprefixed
     2. the shared pieces                     — once, unprefixed: the
        face, the trip card and everything on it, the stamp, the note,
        the quiet link-button, the violet .go, the empty line
     3. each screen's own rules               — under its own heading,
        EVERY rule prefixed by that screen's root class (.view-home,
        .view-person, .view-create, .view-signin, .view-confirmed),
        which the shell puts on the screen's element when it mounts it

   THE RULE: a thing two screens share is written once, above the
   screen headings, and never a second time with a different size
   under a screen — a screen that needs it different writes a
   PREFIXED override (".view-person .face{width:72px}"), not a second
   ".face". Five stylesheets merged without this rule broke each other
   on the first read (the sign-in styled every <input> on the page).

   ------------------------------------------------------------
   IT WEARS THE APP'S OWN CLOTHES, none of them new — the cream ruled
   page, the torn parchment, American Typewriter for the app's own
   voice, the phone's reading face for a person's own words and data,
   and the violet annotation ink for everything touchable. All settled
   on the owner's phone Aug 4-6 (02_SPEC.md). The tokens are
   LITERAL COPIES from mockups/index.html: not re-picked, not improved.
   ============================================================ */

/* ================================================== 1. the tokens, the page, the sheet */

:root{
  --page:#E9DFC4;
  --paper:#F6E9CB;          /* the parchment's own tone, sampled from the art */
  /* THE DOINGS' OWN BLUE (owner, 24 Aug). He brought a sky swatch for
     the trip cards' buttons, saw all three candidates on the real page
     through the paint rig, and picked the swatch dipped one shade so
     the cream lettering keeps its contrast: "sky, dipped". The one
     place a touchable thing is not the violet — his call, for the
     everyday verbs that live on a card's foot. */
  --doing:#4FA8C9;
  --ink:#2B2A26;
  --ink-soft:#4A3F26;
  --ink-label:#6B5E40;
  --ink-faint:#9C8B62;
  --mark:#5E5BD6;           /* the annotation violet: you-can-touch-this */
  --red:#B5342A;            /* the road's own red */
  --dot:#6B5C38;
  --cart:#FFF8E4;
  --rule:rgba(120,100,60,.18);
  --read:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Roboto,
         system-ui,sans-serif;
  --hand:"Marker Felt","Bradley Hand","Segoe Print","Comic Sans MS",cursive;
  --press:"American Typewriter","Courier New",monospace;

  /* THE ROAD'S TWO ACCENTS (22 Aug) — the first tokens NOT copied from
     the map page, both the owner's asks on the live card: the "on the
     road" badge's green ("a more lively color like green" — on every
     live trip's card, his "same way" for a friend's) and the upload
     doors' teal ("a color, different than blue and different than
     green" — the live pill's alone). Red stays the one thing that cannot
     be undone, violet stays you-may-press-this. */
  --road:#2E8B57;           /* the "on the road" badge on the live pill */
  --door:#2A7F8E;           /* the camera and library doors on the live pill — teal, his
                               pick of four shown on the pill (amber was first and
                               did not sit right with the page) */
}

/* ============================================ the map screen's own eight
   Carried in from mockups/index.html on 4 Sep 2026, MODULE 4b of the fold
   (docs/app/12_FOLD_PLAN.md). ELEVEN more came with them and were dropped
   on the way: --page, --ink, --ink-soft, --ink-label, --ink-faint, --mark,
   --red, --dot, --cart, --rule and --read were already declared above,
   byte for byte, and a second copy of a number is the thing that drifts.

   ⚠️ THESE STAY AT :root RATHER THAN GOING ON .view-map, and the reason is
   the one MODULE 4a learnt twice: a custom property inherits DOWNWARD, so
   a token declared on a screen is invisible to everything above it — the
   BODY, whose background is the canvas a phone paints its bars from, and
   the bottom bar, which app/bar.js hangs on the body. Nothing else reads
   these eight, so they cost the other screens nothing.

   ⚠️ AND THREE OF THEM ARE ONLY THE FIRST PAINT'S ANSWER. --vh, --bar and
   --map-foot are MEASURED — the code writes them onto the screen's own
   root (trip/page/parts.js, fitBar, measureViewport) — and what is written
   here is what holds until script runs. */
/* Palette is the approved one from mockups/palette-test — see
   docs/app/02_SPEC.md. Nothing here re-decides colour.

   ⛔ AND THE TOKENS STAY AT :root. MODULE 4a moved this block onto the
   screen, reasoning that a token on :root follows the reader onto
   every other screen. That is true and it does not matter — app.css
   declares the same names at :root already — and moving them broke
   TWO things outside the map, because a custom property inherits
   DOWNWARD and nothing above the screen could see them any more:

     the CANVAS. body{background:var(--page)} became invalid, so the
     body painted nothing and the phone drew its bars from a white
     page — owner, 3 Sep: "once i open a map, the background of the
     page turns white ... at the top where the phones show clock,
     signal, and at the bottom where the url is", and after a photo's
     dark they stayed dark, because a page that does not scroll never
     gives Safari a reason to look again.

     the BOTTOM BAR. app/bar.js injects its own dress using
     var(--ink-faint), var(--mark) and var(--ink), and the bar hangs
     on the BODY, outside every screen. Its words and its unread dot
     lost their colours.

   A token is the document's by nature. What is the SCREEN's is the
   three the page MEASURES — --vh, --bar and --map-foot — and those
   are written onto the screen's own root by the code, so they leave
   with it. */
:root{
  --paper-2:#EFE3C2;
  /* The city labels are a LITERAL COPY of the retired first-pass page
     (_rejected-mockups/first-scrapbook-pass.html): its exact hex, at its
     measured rendered size (15 in a 360-wide viewBox at his screen
     ≈ 15.8px -> 16 here). The owner asked for exactly that look five
     times (Aug 5) while the assistant "improved" the colour darker
     (#362E1C, #2B2A26, #4A3F26) and resized around it — all rejected.
     #6B5E40 is LIGHTER and warmer than --ink-soft. It is not a mistake
     to be corrected back. Change it only with the owner's say-so. */
  /* The ANNOTATION ink (owner, Aug 5): the violet of Apple's ✖️ emoji,
     which the retired first-pass page used as its treasure X. It marks
     the two "someone wrote on this map" things — the treasure X (baked
     into art/x-violet-s5.png, thickest of the five strength rungs, never
     a CSS filter) and the bubble's pointer, which the owner wanted
     neither route-red nor pencil-grey. */

  /* The app writes in the traveller's own hand. What OTHER people write is
     not the app's voice, and a handwriting face is the wrong one to read a
     stranger's paragraph in (owner, Aug 5) — so the comments, and the boxes
     they are typed into, use the phone's own reading face. */

  /* Height is whatever is left after --top and --pager, so the torn bottom
     edge can never be pushed off screen. Width is the screen minus --edge
     on each side, independent of height — the SHEET fills the box rather
     than keeping its 1:2 shape.

     --edge was tuned by the owner on his own phone rather than guessed:
     14px, which on his 440x796 screen gives a 412x756 map. Full bleed (0)
     was offered and declined — the torn edge needs a little page around it
     to read as torn. It is a margin, not a width, so the map shrinks with
     the phone and the torn edges are never cut off, down to a 320px screen. */
  --edge:14px;
  --top:0px;
  /* ON A WIDE SCREEN (a desktop — owner, 22 Aug: "the map is stretched
     horizontally .. can we fix that so it also stays in the middle like
     the profile? same with comment section") the stage, the sheets that
     ride it and the comment sheets stop at this width and sit in the
     middle: the shell's paper (460px, app.css .sheet) plus this page's
     two edges, so the map and home are one column. A phone never
     reaches it — below 488px nothing here changes. */
  --stage-max:488px;
  /* THE BAND AT THE FOOT OF THE SHEET. It held the row of plaques: 40px
     for dots, 44px for plain words, 58px once the words were bordered
     plaques with air above and below (owner, 21 Aug: make them look
     like part of the map). It is a strip of the SAME parchment as the
     map — see .sheet-wrap, which draws one sheet behind the map AND
     this band so there is no torn edge between them.

     ⚠️ THE ROW EMPTIED ON 23 AUG and the band with it: every plaque
     went to a better seat, and the only thing left standing in it is
     the detour page's way back — which is why .stage.is-detour puts
     the 58 back for exactly as long as that page is up. The band's
     height is now the BOTTOM BAR's, because the bar took its place at
     the foot of the sheet (his ask: "these bottom nav section should
     always be there"). The map neither gained nor lost paper in the
     trade, which is the nicest thing about it. */
  --pager:0px;
  /* what the four places take, kept off the map's own height. It is
     measured off the real bar by fitBar() rather than typed here,
     because the word under a mark is set in the phone's own font. */
  --bar:56px;
  /* ⭐ THE DAYLIGHT BETWEEN THE TORN FOOT OF THE PAPER AND THE MENU.
     His number, 29 Aug: "let's make that space from the bottom of the
     torn paper to the top of the menu be like 50px .. and on
     different phones that space should be consistent and not change".
     It is a flat 50 and nothing about a phone changes it — the home
     indicator's inset is handled by --bar, once, above. Zero while
     there is no menu to stand off from; fitBar() sets both. */
  --map-foot:0px;

  /* The usable height, measured in JS rather than taken from a viewport
     unit. 100svh is supposed to be the height with the browser bars shown,
     but in-app browsers (the map is opened from inside apps) report it
     larger than the area they actually leave visible, which pushed the
     pager dots underneath the address bar. The unit stays as the fallback
     for the first paint, before script runs. */
  --vh:100svh;
}

*{box-sizing:border-box}
/* ⚠️ NO RUBBER BAND. Owner, 29 Aug, on the sign-up page: "you can
   accidentally move the page a little bit up and down. i mean, it snaps
   back, but it's still annoying." That bounce is the browser's own, at
   the ends of a scroll, and it is not the page actually going anywhere —
   which is why it springs back and why it reads as slop rather than as
   movement. This turns it off. ⚠️ IT DOES NOT STOP THE PAGE SCROLLING:
   the join form is taller than a phone and still scrolls normally. What
   goes is only the give past the top and the bottom. */
html,body{min-height:100%;overscroll-behavior-y:none}
body{
  margin:0;padding:0 18px 48px;color:var(--ink);background:var(--page);
  background-image:repeating-linear-gradient(0deg,transparent 0 27px,var(--rule) 27px 28px);
  font-family:var(--hand);
  -webkit-text-size-adjust:100%;
}

/* THE SHEET — the one torn paper, drawn once, and it never leaves
   (owner, 21 Aug: "since the paper is shared between almost all pages
   we shouldn't remove it and add it again .. it should always be
   there"). The screens mount INTO it. The parchment is the app's own
   art, stretched to whatever height the words need — the tear is at
   the edges, so it reads as torn at any size. ROOT-relative address,
   because this one document is served under five addresses. */
.sheet{
  position:relative;max-width:460px;margin:0 auto;padding:30px 26px 30px;
  min-height:70vh;
}
.sheet::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:url("/art/parchment.png") center/100% 100% no-repeat;
}

/* THE BRANDING — logo 1 off the rig at his 130px, top centre of the
   paper, above whatever screen is mounted (owner, 24 Aug: "always the
   same spot on all pages"). Centre and not left because the left edge
   is where the back links live. Hidden only while the welcome holds
   the paper — the tutorial wears no branding, his note.
   THE SEAT IS HIS OWN, tuned on his phone through the ?tune=logo rig
   TWICE (24 Aug — first "logo top: -38px, space below: 40px", then a
   second round settled it: "logo top: -26px, space below: 38px") and
   baked: -26 against the sheet's 30px top padding sits the logo just
   inside the paper's torn top edge, and the 38 gives the screen below
   it air. */
/* ⭐ --logo-x IS HOW FAR THE LOGO STANDS OFF THE MIDDLE, and it is ONE
   number for the whole app — owner, 31 Aug: "give me two more buttons
   to move the logo left and right .. i hope we did this right, so it's
   the same logo on all pages when I move it". Both documents carry the
   same declaration and the map page's ?tune=logox knobs move it there;
   whatever he settles on is written into BOTH, which is the only way a
   logo can be in the same place on every screen he has. */
:root{--logo-x:128px}
/* ⚠️ A DIRECT CHILD OF THE SHEET, and that is the whole of the scoping
   (3 Sep 2026). The map page has a .brand of its own — the same logo on
   its own torn paper — and the day it is a screen it mounts INSIDE this
   sheet, where a bare `.brand` would have reached it and landed a
   -26px top margin on it. `>` keeps this to the shell's own chrome:
   main.sheet > .brand, never a screen's. */
.sheet > .brand{display:flex;justify-content:center;margin:-26px 0 38px}
.sheet > .brand img{display:block;width:130px;height:auto;transform:translateX(var(--logo-x))}
body.welcome-up .sheet > .brand{display:none}

/* ============================================================
   THE JOIN STEP MUST NOT SCROLL, ON ANY PHONE.

   His rule, 29 Aug: "i wanted to not have vertical scrolling on any
   phone." The wheel above takes whatever height is left over, so the
   only way to keep it a usable size is to stop spending height on
   things that are only there to breathe.

   ⚠️ AND ON A SHORT PHONE SOMETHING HAS TO GIVE — that is arithmetic,
   not a preference. A fixed header plus a usable wheel plus a button
   does not fit an SE-sized screen. What gives is the LOGO, because it
   is the only thing on the step that is decoration: it shrinks and
   closes up its own air, and only under 720px of glass, so a normal
   phone never sees it. What must never give is the wheel below its
   floor, the Done button, or the age line.
   ============================================================ */
body.join-up{padding-bottom:10px}
/* ⚠️ THE AGE LINE MUST HOLD ONE LINE, and the paper is narrower than a
   phone: the sheet's own padding takes 52px and the body's another 36,
   so a 390px phone leaves it about 300. His ask was the one line ("make
   it shorter so it fits on one line"); shorter copy alone got there on
   his own screen and not on a narrower one, so it is shorter copy AND
   one step down in size. Two lines here is 20px off the wheel. */
body.join-up .view-signin .tiny{font-size:13px}
body.join-up .sheet{padding-bottom:18px}
/* ⚠️ --born-around IS THE MEASURED SUM OF EVERYTHING ON THE STEP THAT
   IS NOT THE WHEEL, so it changes whenever these blocks change what is
   on the step. Each band carries its own. Re-measure, never guess: the
   first guess here was 398 against a real 490 and the page still
   scrolled on every phone tried. */
@media (max-height:720px){
  body.join-up .brand{margin:-26px 0 20px}
  body.join-up .brand img{width:104px}
  body.join-up .view-signin .born-wheel{--born-around:513px}
}
@media (max-height:640px){
  body.join-up .brand{margin:-22px 0 12px}
  body.join-up .brand img{width:84px}
  body.join-up .view-signin h1{font-size:23px}
  body.join-up .sheet{padding-top:16px}
  /* ⚠️ THE SUBTITLE GOES, and it is the last thing that can. On an
     SE-sized screen a logo, a heading, a subtitle, a name box, a wheel
     worth using, a button and the age line do not fit — that is
     arithmetic. Ranked by what the step would miss: the subtitle is
     encouragement, the heading says where you are, the age line is a
     promise and a rule, the button is the way out. So the subtitle. */
  body.join-up .view-signin #join > p.under{display:none}
  /* the logo goes entirely down here, not just smaller — see above */
  body.join-up .brand{display:none}
  body.join-up .sheet{padding-top:14px;padding-bottom:12px}
  body.join-up .view-signin .born-wheel{margin-bottom:8px}
  body.join-up .view-signin .tiny{font-size:13px;margin-top:10px}
  body.join-up .view-signin .born-wheel{--born-floor:150px;--born-around:433px}
}

/* ⚠️ AND THE STEP IS TALLER ON A NARROW PHONE, WHICH THE THREE BANDS
   ABOVE CANNOT SEE — they ask about height only. Measured 2 Sep while
   seating the terms tick: at 381px wide the same step sums 588 where a
   390px one sums 547, because the paper loses about 12px of its own
   and both the subtitle and the age line spill onto a second line.
   That is 41px nobody had looked for, and before the tick it was
   spent on a step that happened to have about that much slack.

   360px (a very ordinary Android) and 375px (an iPhone SE or a 13
   mini) are both under this line; 390 and 393 — his own phone — are
   over it and are untouched by everything below.

   ⛔ WHAT THIS DOES NOT FIX, and it is worth knowing before somebody
   goes looking: 375x667 — an SE — OVERFLOWED BEFORE ANY OF THIS, by a
   measured 39px, because the wheel is already sitting on --born-floor
   and has nothing left to give. The honest fix there is the wrapping
   itself (the age line was written to hold one line and does not, at
   this width), not another number here.

   ⚠️ THAT 39 IS HISTORY, NOT TODAY'S FIGURE: it was measured BEFORE the
   terms tick joined this step (2 Sep 2026), which costs the step
   another line. The same SE overflows by 53px now. The sentence above
   is left as it was measured; the current number is carried in
   docs/app/09_BUGS.md ("The join step overflows 375x667 by 53 px"),
   which is also where it gets re-measured next. Nothing about the fix
   changes — it is still the age line's wrapping and never another
   number here. */
@media (max-width:389px){
  body.join-up .view-signin .born-wheel{--born-around:596px}
}
@media (max-width:389px) and (max-height:720px){
  body.join-up .view-signin .born-wheel{--born-around:558px}
}
@media (max-width:389px) and (max-height:640px){
  /* 400 -> 428: keeping the two names whole (see .agree a) pushes the
     tick onto a THIRD line at this width, which is 23px more. */
  body.join-up .view-signin .born-wheel{--born-floor:150px;--born-around:428px}
}

/* A mounted screen. It takes focus when it arrives (so a screen
   reader and the keyboard land on the new screen, not the old spot),
   and that focus must never draw a ring on the paper. */
.view{outline:none}
.view:focus{outline:none}

/* Shown only if the shell's own code does not arrive — the same honest
   line the trip map shows, so a dropped module is never a silent blank
   sheet (the recurring blank, 21 Aug). Tap to try again. */
/* ⚠️ THE SHELL'S OWN, and a direct child of the sheet for the same
   reason .brand is (3 Sep 2026): the map writes a .boot-failed of its
   own onto its paper when a trip will not load, and a bare rule here
   would have reached it once the map is a screen. */
.sheet > .boot-failed{
  position:absolute;left:0;right:0;top:44%;margin:0;padding:0 28px;
  text-align:center;font-family:var(--hand);font-size:19px;line-height:1.5;
  color:var(--ink-faint);transform:rotate(-1deg);cursor:pointer;
}

[hidden]{display:none !important}

/* ============================================ the bottom bar
   THE FOUR PLACES, UNDER THE THUMB (docs/app/08_HOME_PLAN.md,
   mockups/app/bar.js). It is fixed to the foot of the screen, it is as
   wide as the paper and no wider, and it never scrolls away. The page
   grows a matching amount of air at its foot so nothing can hide
   behind it, and it clears the home indicator on an iPhone.

   ⚠️ THE LOOK IS A TORN STRIP OF THE SAME PAPER, and it is HIS PICK of
   three shown on a rig on his own phone (23 Aug: "A") — the other two
   were the map page's inked plaques and a cream scrap floating clear of
   the bottom edge, and both are in git history at the commit before
   this one, with the ?bar= switch that offered them. The sheet does not
   end at the last line of the page: a band of it runs along the foot
   with the four marks inked onto it, the way the map page's button band
   is part of its own parchment.

   ⚠️ ITS RULES MOVED TO mockups/app/bar.js ON 23 AUG, and this is the
   only stylesheet in the app that has ever given anything away. The MAP
   PAGE wears the same bar now (his ask: "these bottom nav section
   should always be there") and it is its own document — it cannot pull
   80KB of this file to borrow a strip of paper, and a second copy of
   the rules is a second bar that drifts. So the file that owns the bar
   owns its look too, and hands it to whichever document asks for one.
   Nothing else in here moved, and nothing else should: this is what a
   piece of shared furniture costs, not a new way of writing CSS. */

/* THE SCREEN'S OWN TITLE. With a bar there is no wordmark at the top of
   a place any more — where you are is said by the lit mark below and by
   this line (08_HOME_PLAN.md). */
.screen-title{
  margin:0 0 14px;font-family:var(--hand);font-weight:400;
  font-size:27px;line-height:1.1;color:var(--ink);
}

/* ================================================== 2. the shared pieces */

/* The app's own name, small and quiet at the top — a mark of where
   you are rather than a banner. */
.wordmark{
  font-family:var(--press);font-weight:700;font-size:15px;
  letter-spacing:.2px;text-align:center;color:var(--ink-faint);
  margin:0 0 18px;
}

/* A FACE — a person's picture in a circle, or their initial in the
   app's pressed face when they have none. One drawing for your own
   face, a friend's, the person found by email, a remembered account's
   chip; a screen that needs it another size overrides the size under
   its own prefix. */
.face{
  position:relative;                /* the car of somebody on the road sits on its edge */
  width:58px;height:58px;border-radius:50%;flex:0 0 auto;
  display:flex;align-items:center;justify-content:center;
  background:var(--cart);border:2px solid var(--mark);
  font-family:var(--press);font-size:26px;font-weight:700;color:var(--mark);
  background-size:cover;background-position:center;
}

/* THE LITTLE CAR OF SOMEBODY ON THE ROAD — written ONCE, worn by every
   face that can say it: a friend in the friends list, and the person
   whose page you are on (owner, 23 Aug: "this on the road badge is not
   consistent.. let's use the car with animation and smoke same way we
   do in the friend's list").

   It sits ON the circle's edge, half in and half out (owner, 22 Aug:
   "half is outside and half is inside, like on the border") and it is
   ALIVE — the map page's own breathing and smoke, the shared .bob /
   .puff keyframes, a size down. Nothing shows until the face (or the
   friend around it) is marked .live; --car is the ONE thing a seat
   sets, and the smoke follows it. */
.face .onroad{
  display:none;position:absolute;right:-1px;bottom:-1px;
  font-size:var(--car,16px);line-height:1;
}
.friend.live .onroad,
.face.live .onroad{display:block}
.face .onroad .car-body{display:inline-block;font-weight:400}
.friend.live .onroad .car-body,
.face.live .onroad .car-body{animation:bob 1.6s ease-in-out infinite alternate}
.face .onroad .puff{display:none}
.friend.live .onroad .puff,
.face.live .onroad .puff{
  display:block;position:absolute;
  left:calc(100% - var(--car,16px) * .25);top:calc(var(--car,16px) * -.375);
  font-size:calc(var(--car,16px) * .5);line-height:1;opacity:0;
  animation:puff 2.4s ease-out infinite;
}
.friend.live .onroad .puff2,
.face.live .onroad .puff2{
  left:calc(100% - var(--car,16px) * .0625);top:calc(var(--car,16px) * -.6875);
  font-size:calc(var(--car,16px) * .4375);animation-delay:1.2s;
}

/* THE PASSPORT STAMP — "on the road", struck on at an angle in the
   page's own brown ink. On a trip card, and on a person who has a trip
   on the road that you may see.

   ⚠️ IT WAS RED, WITH A PULSING DOT IN IT, AND THAT WAS WRONG (owner,
   Aug 21): "looks like an error, or camera recording, breaks the asmr
   and relaxed vibe of the app". Red is kept for the one thing that is
   genuinely dangerous, the Delete button. Do not put red back here. */
.stamp{
  display:none;align-self:flex-start;align-items:center;
  padding:3px 7px;
  border:1.5px solid var(--ink-soft);border-radius:4px;
  box-shadow:0 0 0 1.5px var(--cart),0 0 0 3px rgba(74,63,38,.22);
  font-family:var(--press);font-weight:700;font-size:10px;
  letter-spacing:.6px;text-transform:uppercase;color:var(--ink-soft);
  opacity:.85;transform:rotate(-4deg);
}
.stamp.on{display:inline-flex}

/* THE WAY BACK — "‹ back", in the violet, the trip page's own.
   🅿️ IT SAID "home" UNTIL 23 AUG. With four places on a bar at the
   foot, "home" was a second way to do what the bar already does; what a
   pushed screen owes you is the step BACK (owner: "if i tap on friends
   and tap on a friend and then back .. it'll bring the friends again").
   It is a real <a> to the screen's natural parent so a cold arrival on
   a profile link still lands somewhere; the shell turns the tap into
   the browser's own back whenever there is a screen of ours behind it,
   which restores where you had scrolled to as well. */
/* ⚠️ SCOPED TO A SHEET SCREEN (MODULE 5 of the fold, 4 Sep 2026). The
   map's own way back wears this class too now — so the shell's
   capture-phase handler takes the tap and turns it into a real back,
   scroll and all — but the map's link is its own drawn thing on the
   parchment (.view-map .go-home) and must not take the sheet screens'
   violet inline dress. Every screen that wants this look is a
   <section class="view">; the map's root is not one. */
.view .go-back{
  display:inline-block;margin:0 0 14px -8px;padding:6px 11px;
  text-decoration:none;font-size:19px;line-height:1;color:var(--mark);
  -webkit-tap-highlight-color:transparent;
}
.view .go-back:active{opacity:.55}
/* PARKED: button.go-back stood here for an hour on 27 Aug, for the
   trip card's privacy question back when that question took the whole
   card. The list opens under its own chip now and needs no way back,
   so the rules went with it — git history has them if a button ever
   wants to wear this link again. */

/* A SECTION HEADING in the app's own voice — "Your trips", "Friends",
   "Poorya's trips". */
.trips-head{
  font-family:var(--press);font-weight:700;font-size:17px;
  color:var(--ink-soft);margin:26px 2px 12px;
}

/* TRIPS LIE AS MANY TO A ROW AS FIT, the way photos land on a
   scrapbook page, newest first, the older ones falling onto the rows
   below (owner, Aug 21). Home's and a person's page's alike.

   ⚠️ THE COLUMN'S FLOOR IS 190px AND IT IS ARITHMETIC, not taste
   (owner, 27 Aug, looking at a past trip whose privacy chip and Share
   had wrapped onto two lines: "i think you need to make the peel a
   little bit wider to fit both buttons in the same row"). The chip
   wants 98, Share 67, the gap 6 and the row's own inset 18 — 189. Two
   columns on a 390px phone give a card 163, which is why they wrapped;
   at 190 they cannot. So the grid asks for as many columns of at least
   that as the paper holds: ONE on every phone, TWO on the sheet at its
   full 460 where a card comes out at 198. The old fixed pair, and the
   narrow-phone rule under it that did this by hand below 360, are both
   gone — this says the same thing at every width and says WHY.

   ⚠️ AND IT COSTS AUG 21's "A LONE TRIP KEEPS A CARD'S SIZE", which
   said a single trip sits in the left half rather than stretching into
   a banner. On a phone there is no left half any more. Told to him
   rather than quietly traded. */
#trips{display:grid;grid-template-columns:repeat(auto-fill, minmax(190px, 1fr));gap:15px 12px}

/* ---------- the trip card, and everything that can happen to it ----------
   A trip is a scrap laid on the page — the app's own scrapbook hand.
   Every card is a link straight into that trip's map. Home and a
   person's page lay the same card; only home arms the two corner
   buttons (its own trips), so on a person's page they simply stay
   hidden. The rotations alternate by nth-child so a wall of them reads
   as hand-laid rather than printed. */
/* A slot stacks the card (or the question standing in for it) and,
   on your own on-the-road trips, the "End the trip" button under it;
   the two corner marks sit outside the flow. Cards in a row stretch to
   the same height, so the buttons line up along the bottom. */
/* ⚠️ THE SLOT WEARS THE PAPER, NOT THE CARD (owner, 24 Aug: "the
   buttons for the past trips shouldn't be outside the pill.. wtf is
   that?? same with 'ask to join'"). The paint sat on the inner .trip,
   so everything the slot held BESIDE the card — the doings row, the
   join scrap, the arming questions — floated on the bare parchment.
   The slot is the piece of paper now, exactly as the live pill's
   always was (one rule for both at last), the card inside it is
   transparent, and everything a slot holds is on the paper by
   construction. The paint rides ::before so a past slot can age its
   BACKGROUND without washing the violet buttons on it — the pill
   already used this exact trick for its own yellow. */
.trip-slot{
  position:relative;isolation:isolate;display:flex;flex-direction:column;
  border:1.5px solid rgba(120,100,60,.4);border-radius:11px;
  box-shadow:0 1px 0 rgba(120,100,60,.16);
  transform:rotate(-.6deg);transition:transform .12s ease;
}
.trip-slot::before{
  content:'';position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:var(--cart);
}
.trip-slot:nth-child(even){transform:rotate(.7deg)}
.trip-slot:nth-child(3n){transform:rotate(-.25deg)}
.trip-slot > .trip,.trip-slot > .sure{flex:1;min-width:0}
.trip{
  position:relative;display:flex;flex-direction:column;
  text-decoration:none;color:var(--ink);
  background:transparent;border:0;border-radius:11px 11px 0 0;
  padding:14px 13px 12px;
  -webkit-tap-highlight-color:transparent;
  transition:transform .12s ease;
}
.trip:active{transform:scale(.98)}
.trip-title{
  display:block;font-family:var(--hand);font-weight:400;font-size:20px;
  line-height:1.14;overflow-wrap:anywhere;
}
/* From where to where (owner, 21 Aug), just under the name — the two
   towns a person named, in the reading face so the arrow sits right. */
.trip-route{
  display:block;margin-top:5px;
  font-family:var(--read);font-size:12.5px;color:var(--ink-soft);
  overflow-wrap:anywhere;
}
.trip-route:empty{display:none}
/* Pushed to the floor of the card so every card in a row rests its
   date on the same line, however tall its name ran; the miles sit on
   their own line under it, in a slightly deeper ink (owner, 21 Aug —
   "3,500 / mi" had broken across two lines). */
.trip-sub{
  display:block;margin-top:auto;padding-top:7px;
  font-family:var(--read);font-size:12px;color:var(--ink-faint);
}
.trip-miles{
  display:block;margin-top:2px;
  font-family:var(--read);font-size:12px;color:var(--ink-label);
}
.trip-miles:empty{display:none}
/* A TRIP ON THE ROAD wears the stamp and parks the car in its corner —
   the same little car that rides the trip's own map. Since 0005 this
   is the TRUTH, not a guess: a trip with no end date is on the road. */
.trip .stamp{margin:0 0 6px}
.trip.live .stamp{display:inline-flex}
.trip.live .trip-title{font-size:21px}
.car{
  display:none;position:absolute;right:9px;bottom:8px;
  font-size:17px;line-height:1;opacity:.9;transform:rotate(6deg);
}
.trip.live .car{display:block}
.car .car-body{font-weight:400;font-style:normal}
.car .puff{display:none}

/* A TRIP ON THE ROAD IS ALIVE WHEREVER IT IS LAID — home's own live
   pill, a friend's live trip in the grid, a trip on a person's page
   (owner, 22 Aug, on a friend's page: "the 'on the road' badge should
   be in the corner and green same way as the live trip, and car should
   have the animation and smoke"). THE BADGE is struck over the card's
   top-right corner, half in and half out, tilted left-up right-down, in
   the road's green on a SOLID cream ground with a solid cream halo so
   the card's border never shows through it. THE CAR breathes and puffs
   the way the map page's car does — its .bob and .puff (mockups/
   index.html, "map life"), copied, timings untouched: up 3px and back
   every 1.6s; a puff fades in, drifts right and up and fades over 2.4s,
   the second 1.2s behind. The car faces left (the emoji's own way), so
   the exhaust sits behind it, to the right — the car stands in from the
   edge to leave the smoke room, and the date lines keep clear of it.
   Straight, not tilted: the map's car is not. */
.trip.live .stamp{
  position:absolute;top:-11px;right:-9px;margin:0;z-index:1;
  transform:rotate(4deg);opacity:1;
  color:var(--road);border-color:var(--road);
  background:var(--cart);box-shadow:0 0 0 3px var(--cart);
}
.trip.live .car{right:22px;opacity:1;transform:none}
.trip.live .car .car-body{display:inline-block;animation:bob 1.6s ease-in-out infinite alternate}
.trip.live .car .puff{
  display:block;position:absolute;left:calc(100% - 4px);top:-6px;
  font-size:9px;line-height:1;opacity:0;
  animation:puff 2.4s ease-out infinite;
}
.trip.live .car .puff2{left:calc(100% - 1px);top:-12px;font-size:8px;animation-delay:1.2s}
.trip.live .trip-sub,.trip.live .trip-miles{padding-right:40px}
@keyframes bob{from{transform:translateY(0)}to{transform:translateY(-3px)}}
@keyframes puff{
  0%{opacity:0;transform:translate(0,0)}
  15%{opacity:.85}
  60%{opacity:0;transform:translate(10px,-8px)}
  100%{opacity:0;transform:translate(10px,-8px)}
}
/* A PAST TRIP is an old photo taped into the book: the colour has gone
   out of it a little, and a strip of tape holds down the corner. The
   wash lands twice on purpose: on the card's CONTENT here, and on the
   slot's painted paper below — never on the doings row's violet, which
   is today's buttons on an old photo, exactly as tape on one is. */
.trip.past{filter:sepia(.2) saturate(.88);opacity:.9}
.trip-slot:has(> .trip.past)::before{filter:sepia(.2) saturate(.88);opacity:.9}
.tape{
  display:none;position:absolute;left:-14px;top:4px;width:56px;height:14px;
  background:rgba(226,205,150,.78);
  border-left:1px solid rgba(255,255,255,.4);
  border-right:1px solid rgba(255,255,255,.4);
  box-shadow:0 1px 2px rgba(120,100,60,.28);
  transform:rotate(-45deg);
}
.trip.past .tape{display:block}

/* THE TWO SMALL CORNER BUTTONS (owner, Aug 21: "i need a way to delete
   a trip .. and it should be on my profile not on the trip itself").
   So they live on the card, never on the map. ARM, THEN CONFIRM — the
   pattern the upload page uses for its ✕ and its nuke. The ✕
   (top-right) arms a deletion; the flag (top-left) arms "trip's over",
   and only shows on a trip of yours still on the road. */
.bin,.flag{
  position:absolute;top:-6px;z-index:2;
  width:30px;height:30px;padding:0;border:0;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--paper);box-shadow:0 1px 3px rgba(120,100,60,.35);
  font-family:var(--read);font-size:14px;line-height:1;color:var(--ink-faint);
  -webkit-tap-highlight-color:transparent;
  opacity:.75;transition:opacity .12s ease;
}
.bin{right:-6px;cursor:pointer}
.bin:active{opacity:1;transform:scale(.92)}
/* The flag is a MARK on a trip of yours still on the road, not a
   button (owner, 21 Aug: "no one will know it's clickable"). It takes
   no tap; ending the trip is the button under the card. */
.flag{left:-6px;font-size:13px;pointer-events:none;cursor:default}

/* END THE TRIP, AND LEAVE ONE — words at the foot of the card, in the
   violet, underlined the way this app underlines a thing you may press.

   🅿️ THE ✕ ON THE CORNER IS GONE (23 Aug). From 22 Aug this was a
   squircle ✕ hanging just past the pill's bottom-right corner — his own
   shape, picked off a rig — and it had two faults a phone-UI read
   found: a rare thing that is hard to undo sat a thumb's width from a
   door people press all day, and a bare mark never says WHAT it ends.
   Both rule books put the same two rules first. It is a labelled word
   with air above it now, at the end of the card where a thumb arrives
   last; tapping it still only ARMS the question in the card's own seat,
   so a mis-tap ends nothing. */
/* THE DOINGS ROW (owner, 24 Aug, both asks in one breath): the card's
   buttons sit SIDE BY SIDE at its foot — "why the fuck are those 2
   buttons on top of each other" — inside the painted slot, and a fifth
   smaller than the first violet cut ("these buttons could be like 20%
   smaller"). End/Leave still only ARM the question in the card's seat,
   so a mis-tap ends nothing — louder clothes, same safety. */
.trip-slot .doings{
  display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:6px;
  padding:0 9px 9px;
}
/* ⚠️ IT WRAPS SINCE 27 Aug, and that is a FIX rather than a new need.
   Measured on the real card: a grid card is 156px wide on a 360px
   phone and 171px on an iPhone, and "Leave trip" beside "Share" wants
   172px — so the two keys have been standing OUTSIDE their own card on
   every small phone since the row was made. Nothing about it looked
   broken, which is why nobody caught it. Wrapping is the person's page's
   own answer to the same crowding (.view-person .doings has worn it
   since the join button arrived) and costs a roomy card nothing. */
.end,.trip-slot .leave,.trip-slot .share{
  display:block;width:auto;min-height:36px;padding:8px 12px;
  background:var(--doing);border:2px solid var(--doing);border-radius:9px;
  font-family:var(--hand);font-size:13.5px;line-height:1;color:#FFF8E4;
  white-space:nowrap;   /* "Leave trip" must never fold into two lines on a narrow card */
  text-align:center;cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.end:active,.trip-slot .leave:active,.trip-slot .share:active{transform:scale(.97)}
.end[disabled],.trip-slot .leave[disabled]{opacity:.5}

/* The question, in the card's own seat, so nothing on the page moves
   while it is being asked. The DELETE one says WHAT GOES rather than
   "are you sure?" — every table hangs off a trip and follows it out
   (data/trips.js). The OVER one is the same shape in the page's own
   brown: ending a trip loses nothing. */
.sure{
  display:flex;flex-direction:column;gap:7px;
  background:var(--cart);border:1.5px dashed var(--red);border-radius:11px;
  padding:12px 11px;transform:rotate(-.4deg);
}
.sure.over{border-color:var(--ink-soft)}
.sure-what{
  font-family:var(--hand);font-weight:400;font-size:16px;line-height:1.15;
  color:var(--ink);overflow-wrap:anywhere;
}
.sure-cost{
  font-family:var(--read);font-size:11.5px;line-height:1.35;color:var(--ink-soft);
  margin-bottom:auto;
}
.sure-row{display:flex;gap:7px}
.sure button{
  flex:1;padding:8px 4px;border-radius:8px;cursor:pointer;
  font-family:var(--hand);font-size:15px;
  -webkit-tap-highlight-color:transparent;
}
/* The one place red belongs: the thing that cannot be undone. "Trip's
   over" is violet, the ordinary you-may-press-this ink. */
.sure-yes{background:var(--red);border:1.5px solid var(--red);color:#FFF8E4}
.sure.over .sure-yes{background:var(--mark);border-color:var(--mark)}
.sure-no{background:none;border:1.5px solid var(--ink-faint);color:var(--ink)}
.sure button:active{transform:scale(.97)}
.sure button[disabled]{opacity:.5}

/* THE THREE CHOICES — who may see a trip. Written ONCE (27 Aug) and
   worn by both the screens that ask: the create screen, where a trip
   is first given its answer, and a trip card on home, where the answer
   can be seen and changed for the rest of the trip's life. */
.seeing{display:flex;flex-direction:column;gap:8px;margin-top:6px}
.seeing button{
  padding:10px 12px;text-align:left;font-family:var(--hand);font-size:16px;
  color:var(--ink);background:var(--cart);
  border:2px solid rgba(120,100,60,.4);border-radius:11px;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.seeing button[aria-pressed="true"]{border-color:var(--mark);color:var(--mark)}
.seeing small{display:block;font-family:var(--read);font-size:12px;color:var(--ink-faint)}
.seeing button[aria-pressed="true"] small{color:var(--mark)}
.seeing button:active{transform:scale(.98)}
.seeing button[disabled]{opacity:.55}

/* WHO CAN SEE IT, on the card itself (owner, 27 Aug) — one row that
   both says the answer and opens the question. It wears the shape every
   settings row wears: the question in the small hand, that trip's own
   answer under it, a chevron at the end. Quiet ink and no fill, because
   it is a SETTING sitting under a card, not one of the trip's verbs —
   and it is deliberately not in the doings row, which already spills off
   a 156px card with two keys in it. */
/* ⚠️ THE WRAP IS WHAT THE SLOT SEATS, not the chip — the chip and the
   little list it opens travel together. ONLY THE ROOM IT NEEDS (owner,
   27 Aug: "make this public button not stretch all the way like how it
   is now"): a flex column stretches its children unless told
   otherwise, which is what made it a full-width empty rectangle across
   the live pill. The left edge is the CARD'S OWN TEXT COLUMN (13px,
   .trip's padding) and the foot margin is what keeps it off the
   buttons under it — "it's kissing the share button". */
/* ⚠️ THE CHIP RIDES IN THE DOINGS ROW, ON THE LEFT (owner, 27 Aug,
   having lived with it on a line of its own): "i think it makes more
   sense to put the privacy button in the same row as the other 2
   buttons (end trip, share) but those are on the bottom right, this
   can be in the same row but on the left". margin-right:auto is what
   sends the verbs to their end and leaves this at the other; the row's
   own padding is the inset. It KEEPS ITS OWN LOOK — his reason, and a
   good one: "i like that it's different than those two, cause it
   belongs to a different area". */
.seen-wrap{
  position:relative;                 /* the little menu hangs off this */
  margin-right:auto;max-width:100%;
  display:flex;flex-direction:column;gap:6px;
}
.seen-wrap[hidden]{display:none}
.trip-slot .seen{
  max-width:100%;
  display:flex;align-items:center;gap:8px;
  /* THE LEFT EDGE LINES UP WITH THE CARD'S OWN WORDS (13px, .trip's
     padding) — it was at 9 and read as falling off the paper: "the
     surrounding box is also kissing the left edge too" — and the foot
     margin is what keeps it off the buttons under it: "for the past
     trip, it's kissing the share button". */
  margin:0;padding:5px 9px 6px;
  /* SOMETHING IN BETWEEN, which is what he asked for: "i liked it
     better when there was no border and it was looking more subtle,
     but that one didn't look like a link or a button". A bare 2px
     outline shouted; a dashed rule and two grey lines was a caption.
     A tinted chip is the middle: --doing at a tenth, a hairline of it
     at a third, so the surface itself says press me without an outline
     competing with the two real keys below. */
  background:rgba(79,168,201,.10);
  border:1px solid rgba(79,168,201,.40);
  border-radius:9px;
  text-align:left;cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.trip-slot .seen:active{transform:scale(.98);background:rgba(79,168,201,.20)}
.seen-said{flex:1;min-width:0}
.seen-label{
  display:block;font-family:var(--read);font-size:9.5px;line-height:1.2;
  color:var(--ink-soft);letter-spacing:.02em;white-space:nowrap;
}
.seen-value{
  display:block;margin-top:1px;
  /* THE SAME SIZE THE MENU'S ROWS ARE (owner, 27 Aug: "the text is too
     small in the privacy button (it's fine in the popover).. make it
     the same size in the button too"), with the padding taken in a
     shade so the chip stays the size it was. */
  font-family:var(--hand);font-weight:400;font-size:15.5px;line-height:1.15;
  color:var(--doing);white-space:nowrap;
}
.seen-more{
  flex:none;font-family:var(--read);font-size:16px;font-weight:700;
  line-height:1;color:var(--doing);
  transition:transform .16s ease;
}
/* the chevron turns down while the list is open — the one thing that
   says which chip the list belongs to */
.trip-slot .seen[aria-expanded="true"] .seen-more{transform:rotate(90deg)}

/* THE LITTLE MENU, FLOATING OVER THE CARD (owner, 27 Aug, with a
   photograph of iOS's own photo-picker menu: "i meant more like a
   small pop up.. like this one.. opening over the pill (but keeping
   our looks and style)"). So it is his shape in this app's clothes:
   the same parchment the cards are cut from, a hairline of the same
   brown, a soft shadow to lift it off the paper, and the choices as
   ROWS the way a menu has them — not the outlined chips the create
   screen stands in a column.

   It hangs off .seen-wrap and pushes nothing: the card keeps its
   height, its words and its buttons exactly where they were.

   ⚠️ AND THE OPEN CARD IS RAISED WHILE IT STANDS. Every trip slot
   wears a transform, so every one of them is its own stacking context
   and a later card in the grid paints OVER an earlier card's menu.
   .seen-open lifts the slot for as long as the menu is up — without
   it the menu is simply invisible on any card but the last. */
.seen-menu{
  position:absolute;top:calc(100% + 7px);left:0;z-index:4;
  min-width:max(100%, 156px);
  padding:4px;
  background:var(--cart);
  border:1px solid rgba(120,100,60,.45);border-radius:14px;
  box-shadow:0 12px 28px rgba(60,45,20,.24),0 2px 6px rgba(60,45,20,.12);
}
/* and it turns UP on a card near the foot of the list, where a menu
   hanging down would slide under the bottom bar (home.js measures) */
.seen-menu.is-up{top:auto;bottom:calc(100% + 7px)}
.trip-slot.seen-open{z-index:5}
.seen-wrap .seeing{margin:0;gap:0}
.seen-wrap .seeing[hidden]{display:none}
.seen-wrap .seeing button{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;padding:11px 12px;
  background:none;border:0;border-radius:10px;
  font-size:15.5px;line-height:1.2;
}
/* a hairline between the rows, the way a menu separates them — inset
   from the corners so the ground's radius still reads */
.seen-wrap .seeing button + button{
  border-top:1px solid rgba(120,100,60,.20);border-radius:10px;
}
/* THE ONE IT IS ALREADY ON WEARS THE CHIP'S OWN INK, and a tick,
   because a menu says which row you are standing on. ⚠️ --doing, NOT
   --mark: the shared .seeing rules mark a chosen choice in the app's
   violet, which is right on the create screen where violet is the
   page's own action colour — but this menu belongs to a chip whose
   answer is written in sky (owner, 27 Aug: "right now the text is
   light blue in the pill but in the popover it's like purple"). One
   control, one colour. */
.seen-wrap .seeing button[aria-pressed="true"]{color:var(--doing)}
.seen-wrap .seeing button[aria-pressed="true"]::after{content:'✓';font-size:14px}
.seen-wrap .seeing button:active{background:rgba(120,100,60,.09);transform:none}
.seen-why{
  font-family:var(--read);font-size:11.5px;line-height:1.35;color:var(--red);
}
.seen-why:empty{display:none}

/* ---------- the camera and the library ----------
   THE TWO UPLOAD DOORS, written ONCE (owner, 4 Sep 2026: "follow
   automatically" — the tutorial shows a picture of these buttons, and a
   picture that stops matching the thing it teaches is worse than no
   picture). Two screens wear them: home's live pill, where they are real
   buttons that send a photograph, and the welcome tour's page 2, where
   they are an inert drawing of the same pair.

   STRAIGHT, and in their own TEAL (owner, 22 Aug, on seeing them: "they
   should be straight and not tilted in this pill .. also give them a
   color, different than blue and different than green"): the plaque's
   line, its drawing and its word all in --door, cream inside — ink on
   paper still, not a filled block. The drawings take it through
   currentColor (the templates' strokes).

   ⚠️ WHAT IS NOT HERE is what makes one of them a BUTTON — the browser
   chrome a <button> arrives wearing, the finger's cursor and highlight,
   and the press. Those are home's, under its own root, because the
   tutorial's pair is a <div> that answers no taps. */
.upload-door{
  flex:1 1 0;min-width:0;height:64px;padding:6px 10px;
  border:2px solid var(--door);border-radius:14px;
  background:var(--cart);color:var(--door);
  font-family:inherit;font-size:16px;
  display:flex;align-items:center;justify-content:center;gap:10px;
  box-shadow:0 2px 6px rgba(60,48,20,.14);
}
/* the camera is the wider of the two — it is the one people reach for */
.upload-door.camera{flex:1.45 1 0}
.upload-door.library{font-size:15px}
.upload-door svg{display:block;height:34px;width:auto;flex:none}
.upload-door.camera svg{height:40px}

/* ---------- the small words ---------- */
.empty{
  margin:6px 2px;font-family:var(--hand);font-size:18px;color:var(--ink-soft);
  transform:rotate(-.4deg);
}
.note{margin:14px 0 0;font-size:16px;line-height:1.45;color:var(--ink-soft)}
.note.bad{color:var(--red)}
.note.tight{margin:4px 0 0;font-size:14px}

/* ---------- the buttons ---------- */
/* The violet filled button — the one thing on a screen a person came
   to press. Violet is the you-can-touch-this ink and has been since
   Aug 6. */
.go{
  display:block;width:100%;padding:14px 16px;margin:24px 0 0;
  background:var(--mark);border:2px solid var(--mark);border-radius:13px;
  font-family:var(--hand);font-size:19px;color:#FFF8E4;text-align:center;
  text-decoration:none;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.go:active{transform:scale(.99)}
.go[disabled]{opacity:.5;cursor:default}

/* THE LICENCE LINE at the foot of You — the quietest thing on the
   quietest screen, and quieter than the way out above it on purpose.
   His pick, 2 Sep, chosen off a rig that flicked all three knobs: the
   hairline stays, it sits UNDER Sign out rather than pinned to the foot
   of the sheet, and the words stay small and faint.

   ⚠️ IT IS NOT .quiet. That style is violet and 17px — the app's
   you-can-touch-this — and spending it here would make a licence link
   as loud as signing out. This is the typewriter face at 11.5, the way
   the email under your name is. */
.view-you .you-legal{
  margin:16px 0 0;padding-top:12px;
  border-top:1px solid var(--rule);
  font-family:var(--press);font-size:11.5px;line-height:1.5;
  text-align:center;color:var(--ink-faint);
}
.view-you .you-legal a{color:var(--ink-faint);text-underline-offset:2px}
/* THE COMPANY'S LINE, on its own row under the two links. It rode the
   SAME line until it was measured: "Terms · Privacy · © 2026 Laserdive
   LLC" wants 331px and the sheet gives a 390px phone only 302 (body 18
   + main 26, each side), so it wrapped wherever it landed. A row of its
   own breaks it on purpose instead. */
.view-you .you-rights{display:block}

/* ---- AND THE WHOLE CLUSTER SITS AT THE PAPER'S FOOT ----
   His call, 5 Sep 2026, on seeing it under the content: "i think it'll
   look more professional if we move the terms, privacy and copyright to
   the bottom of the torn page, maybe bottom left corner". So You becomes
   a column tall enough to fill the paper, and margin:auto above the
   block pushes it to the bottom edge; left-aligned, per the corner he
   named. Sign out and Delete my account keep their order and close up
   behind it.
   ⚠️ THE :has() GATE IS LOAD-BEARING — without it .sheet turns into a
   column on EVERY screen. It names view-you and nothing else.
   ⚠️ AND order:1 IS WHY THE DELETE LINK STAYS PUT. margin-top:auto
   pushes an item AND everything after it; the block sits mid-DOM, above
   "Delete my account", so on the first try it dragged that button to
   the paper's foot with it — measured, at 393-418. order:1 renders the
   block last without moving a line of markup, so the auto margin has
   nothing left to push. */
body:has(.view-you) .sheet{display:flex;flex-direction:column}
body:has(.view-you) #view{flex:1 0 auto;display:flex;flex-direction:column}
.view-you{flex:1 0 auto;display:flex;flex-direction:column}
.view-you .you-legal{order:1;margin-top:auto;text-align:left}

/* ---- going for good (docs/app/03_HOW_THE_APP_WORKS.md, the
   delete-my-account flow; the plan is in git at 4ecdfbb) ----
   THE QUIETEST THING ON THE QUIETEST SCREEN, and quieter than Sign
   out on purpose: signing out is a thing people do, and this is a
   thing almost nobody should. It WAS red until 3 Sep 2026, on the law
   that red is this app's one colour for what cannot be undone; the red
   moved to the CONFIRMATION and this line took the licence line's dress
   instead (DECISION 128, and the ⛔ in the rule below, which is where
   he says why). It must be FINDABLE, though — Apple asks for deletion
   to be reachable inside the app, and a person who wants it should not
   have to hunt. Under the licence line is where the promise it keeps is
   written down. */
.view-you .gone{
  display:block;width:100%;margin:8px 0 0;padding:4px;
  background:none;border:0;
  /* ⛔ NOT RED, AND NOT THE HAND. His read off his own screen, 3 Sep:
     "i feel like delete shouldn't be the most dominant thing in this
     page, as if we're encouraging the user to delete lol" — and he was
     right. Red is the loudest ink on this paper and it was the only red
     thing on the screen, sitting UNDER the faint licence line, so the
     eye went to it first and the page read as an invitation to leave.

     ⭐ RED STILL MEANS "THIS CANNOT BE UNDONE" — it just belongs to the
     CONFIRMATION, not the invitation. The dashed red scrap and its red
     button are where the warning lives; getting there should be as
     quiet as reading the terms. So this wears the licence line's own
     dress exactly: the typewriter face at 11.5, the faintest ink.

     ⚠️ QUIET IS NOT HIDDEN. Apple asks for deletion to be reachable
     inside the app, and it is: its own line, under You, saying plainly
     what it is. */
  font-family:var(--press);font-size:11.5px;line-height:1.5;
  color:var(--ink-faint);
  text-decoration:underline;text-underline-offset:2px;text-align:center;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}

/* The two questions wear the cards' own .sure scrap — same dashed red
   box, same buttons — because it is the same kind of moment and the
   app should only have one way of asking it. They stand alone here
   rather than beside a card, so the rotation comes off: a lone scrap
   tilted in the middle of a column reads as a mistake. */
.view-you .gone-sure{margin:14px 0 0;transform:none}

/* The typed word. It is a plain field, deliberately unremarkable —
   the weight of this step is in having to type at all, not in how the
   box looks. Centred, because the word is short and a left-aligned
   caret under a centred question reads as a different form. */
.view-you .gone-typed{
  width:100%;margin:2px 0 0;padding:9px 10px;
  background:var(--paper);border:1.5px solid var(--ink-faint);border-radius:8px;
  font-family:var(--read);font-size:16px;color:var(--ink);text-align:center;
  /* 16px is the floor iOS zooms below — a field that zooms the page on
     focus would jump the whole screen at the worst possible moment. */
}
.view-you .gone-typed:focus{outline:none;border-color:var(--red)}
/* The word itself, faded, so the box says what it wants without ever
   being mistaken for something already typed — his ask, 3 Sep. The
   browser clears it on the first keystroke; the same ink and the same
   fade the Create screen's fields use, so a placeholder looks like a
   placeholder everywhere in the app. */
.view-you .gone-typed::placeholder{color:var(--ink-faint);opacity:.8}

/* A quiet word that can be pressed — underlined violet, no box. */
.quiet{
  display:block;width:100%;margin:14px 0 0;padding:6px;
  background:none;border:0;
  font-family:var(--hand);font-size:17px;color:var(--mark);
  text-decoration:underline;text-underline-offset:3px;text-align:center;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.quiet.tight{margin:6px 0 0}

/* The small violet confirm beside a box — Save, Find, Add. */
.tick{
  flex:0 0 auto;padding:9px 14px;
  background:var(--mark);border:2px solid var(--mark);border-radius:9px;
  font-family:var(--hand);font-size:16px;color:#FFF8E4;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.tick:active{transform:scale(.97)}
.tick[disabled]{opacity:.5}

/* ================================================== 3. the screens */

/* ---------------------------------------------- the trips place ----------
   Where a signed-in person lands, and the first mark on the bottom bar:
   your trip on the road, the trips that are over, and the ways on from
   here.

   🅿️ THE HEADER WENT ON 23 AUG (docs/app/08_HOME_PLAN.md) and its rules
   with it — the wordmark row, the two round top buttons, the bell and
   its count. The app's name at the top of every screen was a mark of
   where you are, and the lit mark on the bar says that now; the bell
   became the Alerts place, and Sign out became the last line of You.
   Whole in git history at the commit before this one. */

/* ---- the one section ----
   It was TWO framed scraps, the trips and the friends, each saying "this
   part is its own" (owner, 21 Aug). The friends have a place of their
   own now, so there is one thing on this screen and the frame has
   nothing left to separate it from — but the wash and the faint border
   are what hold the cards (solid cream) off the paper, so they stay,
   with the screen's own title above rather than a heading inside. */
/* THE FRAME CAME OFF (owner, 24 Aug: "remove the external frame and
   make them be on the torn paper .. frame is used to separate stuff..
   when all are on the same frame.. no need"). It was drawn when home
   held four sections and each needed its own fence (DECISION 21); with
   the tabs gone and one flow left, the cards lie straight on the torn
   paper — the friend page's way, which is the way he asked for. */
.view-home .part{margin:14px 0 0}
.view-home .part .go{margin:18px 0 0}

/* ---------------------------------------------- alerts ----------
   What happened to your things: the bell's list, as a place.

   🅿️ IT USED TO FLOAT OVER HOME. The list hung from the header's bottom
   edge as a shadowed cream scrap so that opening it pushed nothing down
   (owner, 22 Aug — DECISION 23), and the bell wore the count on its
   shoulder. A place of its own has nothing to push down: the wrapper,
   the absolute seat, the shadow, the 70vh cap and its inner scroll are
   gone and the list is simply the page. The count is a dot on the bar's
   own mark now, where it can be seen from every other place. THE ROW
   ITSELF IS UNTOUCHED. */
.view-alerts #notices{display:flex;flex-direction:column;gap:8px}
/* a row is a scrap; the LINK inside it is the flex row (face, words,
   still); under the link, when an ask to join is standing, its two
   answers (23 Aug) */
.view-alerts .notice{
  background:var(--cart);border:1.5px solid rgba(120,100,60,.4);border-radius:11px;
  padding:8px 10px;color:var(--ink);
}
.view-alerts .notice .n-link{
  display:flex;align-items:center;gap:10px;
  text-decoration:none;color:var(--ink);
  -webkit-tap-highlight-color:transparent;
}
.view-alerts .notice .n-link[href]:active{transform:scale(.985)}
.view-alerts .notice.unread{background:rgba(94,91,214,.08);border-color:rgba(94,91,214,.45)}
.view-alerts .notice .n-act{display:flex;gap:8px;margin:8px 0 0 50px}
.view-alerts .notice .n-act button{
  flex:0 0 auto;padding:7px 12px;border-radius:8px;cursor:pointer;
  font-family:var(--hand);font-size:14px;-webkit-tap-highlight-color:transparent;
}
.view-alerts .notice .n-act .ask-yes{background:var(--mark);border:1.5px solid var(--mark);color:#FFF8E4}
.view-alerts .notice .n-act .ask-no{background:none;border:1.5px solid var(--ink-faint);color:var(--ink)}
.view-alerts .notice .n-act button:active{transform:scale(.97)}
.view-alerts .notice .n-act button[disabled]{opacity:.5}
.view-alerts .notice .face{width:40px;height:40px;font-size:17px}
.view-alerts .notice .words{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
/* the sentence, whole; the layout clamps it to two lines rather than a
   count of letters cutting it (Rule #2) */
.view-alerts .notice .n-line{
  font-family:var(--read);font-weight:500;font-size:14px;line-height:1.3;color:var(--ink);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  overflow-wrap:anywhere;
}
.view-alerts .notice .n-where{font-family:var(--read);font-size:11.5px;color:var(--ink-faint)}
/* the shot it was about: a small print on the right */
.view-alerts .notice .still{
  flex:0 0 auto;width:44px;height:44px;border-radius:7px;
  background:var(--ink-faint) center/cover no-repeat;
}
.view-alerts .notice .still.none{display:none}

/* ---- your trips ---- (the grid itself is shared, above) */
.view-home .trips-head{display:flex;align-items:baseline;gap:8px}
.view-home .trips-head .count{font-family:var(--read);font-size:13px;color:var(--ink-faint)}

/* 🅿️ THE TWO TABS stood here 22–24 Aug ("first one is live trip, and
   2nd one is past trips") and retired with his word: "the trip page
   doesn't need tabs.. show all trips past or present in the page, same
   way we show trips when on a friend's page". The past grid breathes a
   little under the pill now that they share one scroll. */
.view-home #past-panel{margin-top:22px}

/* ---- your trip on the road, on its own ----
   The same trip-card slot the grid lays, but FULL WIDTH at the top of
   the section and a size up (owner, 22 Aug: "the live trip pill should
   be bigger and separate to have room for all these") — room for the
   upload doors and the one row of what the phone is sending, which
   only a live trip of yours carries (08_HOME_UPLOAD_PLAN.md). Laid
   straight, not tilted: it is the page's main verb while you are on
   the road, not one scrap among the others. The seat takes no room
   when it is empty (everybody not on the road). */
.view-home #live{margin:0 0 4px}
.view-home #live:empty{display:none}
/* THE SLOT IS THE PILL (owner, 22 Aug: "move the 'end the trip' button
   inside the trip's pill, and do the same with the upload"): one scrap
   — cream, bordered — holding the trip's words, the upload and the end
   button together. The words keep being the link into the map (an <a>
   cannot hold buttons), so the inner card is drawn borderless on the
   pill rather than as a card of its own. */
.view-home #live .trip-slot{
  display:flex;flex-direction:column;
  border:1.5px solid rgba(120,100,60,.55);border-radius:11px;
  box-shadow:0 1px 0 rgba(120,100,60,.16);
  padding:0 0 14px;
  transform:none;      /* the pill lies straight — the slots' hand-laid tilt is the grid's */
  /* the pill wears the past cards' own yellow (owner, 24 Aug: "make
     the color of the live trip background be the same yellow as the
     past trips"). Their yellow is not a different paint — it is the
     aged-photo wash .trip.past runs over the same --cart — so the
     pill's BACKGROUND runs through the identical wash on a layer of
     its own (the buttons and inks above stay unwashed), and the two
     can never drift apart however the wash is ever tuned. */
  position:relative;isolation:isolate;
}
.view-home #live .trip-slot::before{
  content:'';position:absolute;inset:0;z-index:-1;border-radius:inherit;
  background:var(--cart);filter:sepia(.2) saturate(.88);opacity:.9;
}
.view-home #live .trip{
  transform:none;padding:16px 17px 12px;
  background:transparent;border:0;border-radius:0;box-shadow:none;
}
.view-home #live .trip:active{transform:none;opacity:.7}
.view-home #live .trip.live .trip-title{font-size:24px}
.view-home #live .trip-route{font-size:14px;margin-top:6px}
.view-home #live .trip-sub,.view-home #live .trip-miles{font-size:13px}
.view-home #live .trip.live .trip-sub{padding-top:9px}
/* The pill's car and its smoke, a size up from a card's (the badge and
   the life itself are every live card's — the shared rules above; the
   owner asked for the badge on the corner, green, and the breathing car
   here first, then "same way" on a friend's live trip). */
.view-home #live .car{font-size:22px;right:30px;bottom:10px}
.view-home #live .car .puff{left:calc(100% - 5px);top:-7px;font-size:11px}
.view-home #live .car .puff2{left:calc(100% - 1px);top:-15px;font-size:10px}
/* the question, when it stands in for the words, sits inside the pill
   with the pill's own edges */
.view-home #live .trip-slot > .sure{margin:10px 12px 0;transform:none}
/* the slot's pieces in the order a live card reads: the words (or the
   question), then the doors and the row; the end ✕ sits on the pill's
   bottom-right corner, out of the flow. While a question stands it IS
   the pill — the doors and the row step aside (owner, 22 Aug: "cover
   the whole pill so user can't upload while deciding on the trip") */
.view-home #live .trip-slot.asking .live-upload{display:none}
.view-home #live .trip-slot.asking .live-crew{display:none}
.view-home #live .trip-slot.asking > .sure{margin:12px 12px 2px}

/* ---- who is on the trip, on the pill (23 Aug) ----
   Between the words and the doors: the little faces — the owner first,
   then everybody on — and, on the owner's pill, the asks waiting. A
   lone face is not shown (the row is about company). On the owner's
   pill a rider's face is a button (tap: remove?); on a member's pill a
   face is a link to their page. */
.view-home .live-crew{order:0;display:flex;flex-direction:column;gap:8px;padding:0 17px}
.view-home .live-crew:empty{display:none}
.view-home .crew-faces{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.view-home .crew-faces[hidden]{display:none}
.view-home .cface{
  display:block;text-decoration:none;border-radius:50%;
  -webkit-tap-highlight-color:transparent;
}
.view-home .cface .face{width:30px;height:30px;font-size:13px;box-shadow:0 0 0 2px var(--cart)}
.view-home .cface.owner .face{box-shadow:0 0 0 2px var(--cart),0 0 0 3.5px var(--road)}
.view-home .cface.removable{cursor:pointer}
.view-home .cface:active .face{transform:scale(.94)}
.view-home .crew-asks{display:flex;flex-direction:column;gap:8px}
.view-home .crew-asks:empty{display:none}
/* (the ask rows' clothes are the friends section's — shared down at
   .view-friends .ask, with the pill's one-size-down overrides right
   after them, where the cascade lets them win) */

/* LEAVE — a trip you ride, live or over: the quiet word beside where
   the owner's "End this trip" sits (23 Aug: "anyone can leave without
   owner approval"). The look is the shared one above; the pill only
   says that it, like the end word, is in the flow of the column. */
/* the pill's doings ride at the same station the three buttons held
   one by one — one row now, at the pill's foot */
.view-home #live .doings{order:2;margin:14px 15px 0;padding:0}

/* ---- the upload on the pill ----
   (08_HOME_UPLOAD_PLAN.md, 22 Aug.) Between the trip's words and "End
   the trip": the two doors side by side — the camera WIDER, the king
   of the two (owner, Aug 6; kept as a proportion here because the pill
   has little room), the map page's own plaques and drawings in the
   towns' brown ink, not the violet (the page buttons' reasoning) —
   then ONE row of what the phone is doing, "and N more waiting" under
   it, and the quiet toggle for the ones that did not send. The row
   rules began as a copy of the map page's old upload sheet's .job
   rules (that sheet was deleted 24 Aug — D5 — and these are the one
   copy now), under this prefix the way the tokens are a literal copy. */
.view-home .live-upload{order:1;display:flex;flex-direction:column;gap:8px;padding:4px 17px 0}
.view-home .live-upload .doors{display:flex;gap:10px}
/* the quiet word when a phone would not say where it is (23 Aug) — a
   note, not a warning: nothing has gone wrong with the upload itself */
.view-home .live-upload .where-note,
.view-signin .welcome .where-note{
  margin:0;font-family:var(--read);font-size:11.5px;line-height:1.35;
  color:var(--ink-faint);
}
.view-home .live-upload .where-note[hidden]{display:none}
/* THE DOORS' OWN LOOK IS SHARED — section 2, ".upload-door" — because
   the welcome tour draws a picture of this very pair and it has to keep
   matching them (owner, 4 Sep 2026). What is left here is only what
   makes these two REAL: a <button> arrives wearing the browser's own
   chrome, and it is pressed by a finger. The tutorial's are <div>s that
   answer nothing, so none of this is theirs. */
.view-home .live-upload .upload-door{
  appearance:none;-webkit-appearance:none;cursor:pointer;
  -webkit-tap-highlight-color:transparent;transition:transform .12s;
}
.view-home .live-upload .upload-door:active{transform:scale(.97)}
/* the list holds EVERY row the engine made (nothing capped, Rule #2);
   live-upload.js hides all but the one that matters */
.view-home .live-upload .upload-list{display:flex;flex-direction:column;gap:8px}
.view-home .live-upload .upload-list:empty{display:none}
.view-home .live-upload .job{
  border:1.5px solid var(--ink-faint);border-radius:10px;
  background:var(--cart);padding:9px 12px;
  display:flex;align-items:center;gap:10px;
}
.view-home .live-upload .job .what{
  flex:1 1 auto;min-width:0;font-family:var(--read);font-size:14px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.view-home .live-upload .job .how{
  flex:none;max-width:62%;font-family:var(--press);font-size:12.5px;color:var(--ink-label);
  text-align:right;
}
.view-home .live-upload .job .bar{
  flex:none;width:74px;height:7px;border-radius:4px;
  background:rgba(120,100,60,.18);overflow:hidden;
}
.view-home .live-upload .job .bar i{
  display:block;height:100%;width:0;background:var(--red);border-radius:4px;transition:width .25s;
}
.view-home .live-upload .job.sent{opacity:.75}
.view-home .live-upload .job.sent .how{color:var(--ink-soft)}
.view-home .live-upload .job.sent .bar{display:none}
.view-home .live-upload .job.failed{border-color:var(--red);cursor:pointer}
.view-home .live-upload .job.failed .how{color:var(--red)}
.view-home .live-upload .job.failed .bar{display:none}
.view-home .live-upload .waiting-line{margin:-2px 2px 0;font-family:var(--press);font-size:12px;color:var(--ink-faint)}
/* "2 didn't send — show": a quiet violet word, left, not a box */
.view-home .live-upload .failed-toggle{
  align-self:flex-start;margin:-2px 0 0;padding:4px 2px;background:none;border:0;
  font-family:var(--hand);font-size:14px;color:var(--mark);
  text-decoration:underline;text-underline-offset:3px;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}

/* ---- your friends ----
   PEOPLE, not their trips (owner, 21 Aug): a row of faces, four across,
   a name under each, the little car on whoever is on the road; the
   whole face is the link to their page. No friend count anywhere. */
.view-friends #asking{display:flex;flex-direction:column;gap:8px;margin:0 0 14px}
/* ⚠️ THE ASK ROW'S CLOTHES ARE SHARED with home's live pill (24 Aug).
   The pill clones the same request-row template, but these rules were
   scoped to this place alone, so on home the row stood undressed —
   "recap sharkwants to join" run together (the small was inline) and
   the two answers in the browser's own bare buttons, half thumb size.
   The owner photographed it. One row, one look, wherever it stands. */
.view-friends .ask,
.view-home .crew-asks .ask{
  display:flex;align-items:center;gap:10px;
  background:var(--cart);border:1.5px dashed var(--mark);border-radius:11px;
  padding:9px 10px;transform:rotate(-.3deg);
}
.view-friends .ask .who,
.view-home .crew-asks .ask .who{flex:1;min-width:0;font-family:var(--hand);font-size:16px;line-height:1.15;overflow-wrap:anywhere}
.view-friends .ask .who small,
.view-home .crew-asks .ask .who small{display:block;font-family:var(--read);font-size:11.5px;color:var(--ink-faint);margin-top:2px}
.view-friends .ask button,
.view-home .crew-asks .ask button{
  flex:0 0 auto;padding:7px 10px;border-radius:8px;cursor:pointer;
  font-family:var(--hand);font-size:14px;-webkit-tap-highlight-color:transparent;
}
.view-friends .ask .ask-yes,
.view-home .crew-asks .ask .ask-yes{background:var(--mark);border:1.5px solid var(--mark);color:#FFF8E4}
.view-friends .ask .ask-no,
.view-home .crew-asks .ask .ask-no{background:none;border:1.5px solid var(--ink-faint);color:var(--ink)}
.view-friends .ask button:active,
.view-home .crew-asks .ask button:active{transform:scale(.97)}
.view-friends .ask button[disabled],
.view-home .crew-asks .ask button[disabled]{opacity:.5}
/* the pill's rows sit ONE SIZE DOWN and untilted (their old overrides,
   moved below the shared clothes so the cascade lets them win) */
.view-home .crew-asks .ask{transform:none;padding:8px 9px}
.view-home .crew-asks .ask .face{width:34px;height:34px;font-size:15px}
.view-home .crew-asks .ask .who{font-size:15px}
/* and on the pill the row lives on TWO LINES: the face and the name
   above, the two answers on a full row of their own beneath — because
   a phone-width pill CANNOT hold face + name + two thumb-size buttons
   side by side, and the first try at "bigger buttons" (24 Aug) proved
   it by crushing the name into a one-letter column. The who's flex
   basis is wide enough that the buttons always wrap; each button then
   takes half the row — a real 44px thumb's size (his ask), with room
   to breathe. */
.view-home .crew-asks .ask{flex-wrap:wrap}
.view-home .crew-asks .ask .who{flex:1 1 12em}
.view-home .crew-asks .ask button{
  flex:1 1 40%;min-height:44px;padding:10px 12px;font-size:16px;
}

.view-friends #friends,
.view-person #circle{display:grid;grid-template-columns:repeat(4,1fr);gap:14px 8px}
@media (max-width:359px){
  .view-friends #friends,
  .view-person #circle{grid-template-columns:repeat(3,1fr)}
}
/* a person's page: the section only exists when there is a circle to
   show, and it sits under the standing with the trips' own air */
.view-person #circle:empty{display:none}
.view-person #circle{margin-bottom:22px}
.view-friends .friend,
.view-person .friend{
  display:flex;flex-direction:column;align-items:center;gap:5px;min-width:0;
  text-decoration:none;color:var(--ink);-webkit-tap-highlight-color:transparent;
}
.view-friends .friend:active,
.view-person .friend:active{transform:scale(.96)}
.view-friends .friend .face,
.view-person .friend .face{width:54px;height:54px;font-size:22px}
.view-friends .friend .fname,
.view-person .friend .fname{
  font-family:var(--hand);font-size:14px;line-height:1.1;text-align:center;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.view-friends .friend .fstate,
.view-person .friend .fstate{display:none;font-family:var(--read);font-size:10.5px;color:var(--ink-faint);margin-top:-3px}
/* Somebody you asked and are waiting on: there, but faded, and saying
   so. Their page has the "take it back". */
.view-friends .friend.waiting{opacity:.55}
.view-friends .friend.waiting .fstate{display:block}
/* The car: on the road, to you (owner, 22 Aug: "make this car animate
   on the friends badge if they're live"). The look is the SHARED one at
   the top of this file, worn by every face that can say it — a friend
   here, the person whose page you are on — and 16px is its own default,
   so a face this size sets nothing. No car for a friend who is not on
   the road; nobody in a person's circle wears one at all (whose road it
   is is a fact about YOUR friends, not theirs). */

/* Adding a friend: the email box and the share button, in a scrap that
   TAKES THE FACES' PLACE while it is open (owner, 22 Aug: with the
   keyboard up, a scrap under the faces left the share and QR options
   out of sight) — the section's heading stays, everything else steps
   aside, and "‹ back" at the scrap's top brings them back. */
.view-friends.adding .screen-title,   /* the title too — "‹ back" is the top while it is open (owner, 22 Aug) */
.view-friends.adding #asking,
.view-friends.adding #friends,
.view-friends.adding #no-friends,
.view-friends.adding #friends-note,
.view-friends.adding #add-friend{display:none}
.view-friends .addf .back{width:auto;align-self:flex-start;margin:-4px 0 0;padding:2px 2px 2px 0;font-size:16px;text-decoration:none}
.view-friends.adding .addf{margin-top:0}
.view-friends .addf{
  margin:14px 0 0;padding:12px 12px 10px;
  background:var(--cart);border:1.5px solid rgba(120,100,60,.4);border-radius:11px;
  display:flex;flex-direction:column;gap:9px;transform:rotate(.3deg);
}
.view-friends .addf form{display:flex;gap:8px;margin:0}
.view-friends .addf .how{font-family:var(--read);font-size:12px;color:var(--ink-faint);margin:0}
.view-friends .found{display:flex;align-items:center;gap:10px}
.view-friends .found .face{width:40px;height:40px;font-size:17px}
.view-friends .found b{flex:1;min-width:0;font-family:var(--hand);font-weight:400;font-size:17px;overflow-wrap:anywhere}
.view-friends .found .tick{padding:8px 12px;font-size:15px}
.view-friends .addf .or{font-family:var(--read);font-size:11.5px;color:var(--ink-faint);text-align:center;margin:0}
/* The QR: the page's own ink on a light card, big enough for a camera
   across a table, with its quiet zone inside the svg. */
.view-friends .qr-box{display:flex;flex-direction:column;align-items:center;gap:6px;margin:4px 0 2px}
.view-friends #qr{width:200px;height:200px;max-width:100%;color:var(--ink);background:#FFFDF6;border-radius:8px}
.view-friends .qr-box .how{text-align:center}

/* ---------------------------------------------- you ----------
   Your face, your name, your email and the way out — the fourth place
   (docs/app/08_HOME_PLAN.md). The look is exactly what it was at the
   top of home; only the address changed. */

.view-you .whoami{display:flex;align-items:center;gap:14px;margin:0 0 6px}
/* Your own picture is yours to change — a pencil on its corner, the
   twin of the name's (owner, 21 Aug). The picture goes to the droplet,
   which shrinks it and writes it to your row; friends read it. */
.view-you .face-wrap{position:relative;flex:0 0 auto}
.view-you .face-pencil{
  position:absolute;right:-4px;bottom:-4px;
  width:26px;height:26px;padding:0;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--mark);border:2px solid var(--paper);
  font-family:var(--read);font-size:12px;line-height:1;color:#FFF8E4;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.view-you .face-pencil:active{transform:scale(.9)}
.view-you .face-pencil[disabled]{opacity:.5}
/* While the new picture is on its way. */
.view-you .face-wrap.busy .face{opacity:.55}
.view-you .face-wrap.busy .face-pencil{opacity:.5}
.view-you .me-id{min-width:0}
.view-you .name-row{display:flex;align-items:center;gap:8px}
.view-you .name-row b{font-family:var(--hand);font-weight:400;font-size:25px;color:var(--ink)}
.view-you .email{
  display:block;font-family:var(--read);font-size:13px;color:var(--ink-faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* The pencil — a touch, so it is drawn in the violet. Small and
   unobtrusive; a name rarely changes. */
.view-you .pencil{
  background:none;border:0;padding:2px 4px;cursor:pointer;
  font-size:16px;color:var(--mark);line-height:1;
  -webkit-tap-highlight-color:transparent;
}
.view-you .pencil:active{transform:scale(.9)}

/* The name, while it is being changed (home), and the email box in
   "add a friend" (the Friends place) — one look for a typed line. */
.view-you .name-edit{display:flex;gap:8px;align-items:center;margin:8px 0 0}
.view-you .name-edit input,.view-friends .addf input{
  flex:1;min-width:0;padding:9px 11px;margin:0;
  font-family:var(--read);font-size:16px;color:var(--ink);
  background:var(--cart);border:1.5px solid var(--mark);border-radius:9px;
}
.view-you .name-edit input:focus,.view-friends .addf input:focus{outline:none}

/* ---------------------------------------------- a person's page ----------
   Their face and name, where you two stand, and the trips of theirs
   you may see. The trip cards are the shared ones above. */

.view-person .whois{display:flex;align-items:center;gap:16px;margin:4px 0 6px}
/* Their face is a third bigger than a friend's little one, so the car
   on its edge is a third bigger too — the same car, in proportion. */
.view-person .whois .face{width:72px;height:72px;font-size:32px;--car:21px}
/* The name and the chip stand on ONE line beside their picture (owner,
   23 Aug: "move the friends/add friend button next to the name not
   under it"), the name at the left and THE CHIP HALFWAY BETWEEN THE END
   OF THE NAME AND THE RIGHT EDGE — 10px off the name "kissed" it, and
   hard against the edge was "too far to the right", so the free space
   is split evenly either side of it (an auto margin on BOTH sides: the
   row takes the whole width, and flexbox shares what is left). A name
   too long to share the line takes the whole of it and the chip steps
   down under it, still beside the picture. */
.view-person .who-id{flex:1 1 auto;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px}
.view-person .who-id .standing{margin:0 auto}
.view-person .who-id b{font-family:var(--hand);font-weight:400;font-size:27px;line-height:1.1;color:var(--ink);overflow-wrap:anywhere}

/* ---- where you two stand ----
   A CHIP ON THE LINE WITH THEIR NAME, beside their picture — not a
   bar. It was a
   full-width dashed scrap of its own until 23 Aug (owner: "let's remove
   this bar.. it's so weird.. i've never seen anything like it .. just a
   button next to profile picture not a whole horizontal bar that looks
   like it's a section"). The chip says where the two of you stand;
   the one with something behind it drops a small card holding it —
   Unfriend, Take it back — which IS the confirm, so a mis-tap can still
   not end a friendship. Nothing at all on your own page. */
.view-person .standing{
  position:relative;                /* the dropped card hangs off this */
  display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0;
}
.view-person .standing .chip{
  display:inline-flex;align-items:center;gap:5px;
  padding:5px 11px;border-radius:9px;cursor:pointer;
  background:none;border:1.5px solid var(--ink-faint);color:var(--ink);
  font-family:var(--hand);font-size:14.5px;line-height:1.15;
  -webkit-tap-highlight-color:transparent;
}
/* THE TWO THAT ARE ABOUT THE FRIENDSHIP ITSELF wear the violet —
   "Add friend" and "Friends ✓" are the same chip in its two states, and
   the owner asked for them to look it: "make the friends one same as
   the add friend one". The waiting ones stay outlined; they are not
   something to press so much as something to know. */
.view-person .standing .chip.yes{background:var(--mark);border-color:var(--mark);color:#FFF8E4}
.view-person .standing button:active{transform:scale(.97)}
.view-person .standing button[disabled]{opacity:.5}
/* THE DROPPED CARD floats over the page — nothing below it moves, the
   answer home's bell gave to the same question (owner, 22 Aug). It
   hangs off the chip's RIGHT edge, because the chip sits at the paper's
   right edge and a card opening the other way would run off it. */
.view-person .standing .menu{
  position:absolute;top:calc(100% + 6px);right:0;z-index:4;
  min-width:132px;padding:4px;display:flex;flex-direction:column;
  background:var(--cart);border:1.5px solid rgba(120,100,60,.45);border-radius:10px;
  box-shadow:0 6px 16px rgba(74,63,38,.18);
}
.view-person .standing .menu-item{
  background:none;border:0;padding:8px 12px;border-radius:7px;cursor:pointer;text-align:left;
  font-family:var(--hand);font-size:15px;color:var(--ink);
  -webkit-tap-highlight-color:transparent;
}
.view-person .standing .menu-item:active{background:rgba(120,100,60,.12)}

/* ---- join their trip (23 Aug, docs/app/08_JOIN_TRIP_PLAN.md) ----
   Under their ONE trip on the road, when you two are friends: the one
   right shape — ask / asked, take it back / on it, leave — in the
   standing scrap's own hand, a size down, laid under the card in its
   slot. The one-road question before an ask is the cards' own .sure
   scrap (home's and create's), so it asks the way they ask; while it
   stands, the scrap's own chrome steps aside and the question IS the
   scrap. */
/* THE JOIN LIVES IN THE DOINGS ROW NOW (owner, 24 Aug: "'ask to join'
   and share need to be in the same row .. account for the new text
   when it's clicked and changed to 'take it back' — right now that one
   looks messy AF"). The scrap's own box dissolves (display:contents)
   and its one right shape sits beside Share as ordinary row-mates: the
   ask as a row button in the doings' own blue, the asked-state as a
   quiet two-line word on the row's left with "Take it back" as a real
   outlined button, the on-state likewise with Leave. The doings row
   wraps, so only when a state genuinely cannot share the width does it
   fold — and the momentary QUESTIONS (leave-sure, one-road) take the
   full line on purpose: a question deserves the room. */
.view-person .doings{flex-wrap:wrap;align-items:center;gap:7px}
.view-person .join{display:contents}
.view-person .join .line{
  flex:1 1 100%;min-width:0;text-align:left;
  font-family:var(--hand);font-size:14px;line-height:1.15;color:var(--ink);
}
.view-person .join .line small{display:block;font-family:var(--read);font-size:11px;color:var(--ink-faint);margin-top:2px}
.view-person .join button{
  flex:1 1 0;min-width:0;min-height:36px;padding:8px 5px;border-radius:9px;cursor:pointer;
  font-family:var(--hand);font-size:12.5px;line-height:1;white-space:nowrap;
  -webkit-tap-highlight-color:transparent;
}
/* ...and Share splits the row WITH WEIGHT, not evenly: "take it back"
   is twice Share's length, so it earns the wider half — that is what
   keeps his own words whole on one line down to the smallest phones'
   cards (measured at 164px slots before shipping) */
.view-person .join .j-takeback{flex:1.7 1 0}
.view-person .trip-slot:has(.join) .share{flex:1 1 0;min-width:0;padding:8px 5px;font-size:12.5px}
.view-person .join .yes{background:var(--doing);border:2px solid var(--doing);color:#FFF8E4}
.view-person .join .no{background:none;border:1.5px solid var(--ink-faint);color:var(--ink)}
.view-person .join .quietly{
  background:none;border:1.5px solid var(--ink-faint);color:var(--ink);
  text-decoration:none;
}
.view-person .join button:active{transform:scale(.97)}
.view-person .join button[disabled]{opacity:.5}
/* the questions stand alone on their full line, in the .sure scrap's
   own clothes — a question deserves the room */
.view-person .join .sure{flex:1 1 100%;transform:none}
.view-person .join .sure button{flex:1;padding:8px 4px;border-radius:8px;font-size:15px}
.view-person .join .j-leave-sure{flex:1 1 100%}
.view-person .join .note{flex:1 1 100%;margin:0}

/* PART OF THE CARD, NOT A SCRAP BESIDE IT (owner, 23 Aug: "asked to
   join is shown as a separate thing, since it's part of that trip, it
   should be part of it visually"). The pill-look itself — the slot
   wearing the card's cream, border and hand-laid tilt while the card
   sits borderless on it — is the UNIVERSAL slot paint since 24 Aug
   (see .trip-slot above; 4.5 folded the .joinable copies of those
   very rules, which had drifted into byte-duplicates). What is left
   here is only what a joinable slot ADDS: the card's tap-fade, and
   the join row's own seating inside the slot's edges. */
.view-person .trip-slot.joinable .trip:active{transform:none;opacity:.7}
.view-person .trip-slot.joinable .join{
  margin:0;padding:4px 13px 12px;border:0;background:none;transform:none;
}
.view-person .trip-slot.joinable .join.road{padding:0 9px 9px}

/* ---------------------------------------------- the way in ----------
   One tap with Google, or an email and a password, then ONE short
   screen asking a name and a date of birth. */

/* The app's own name, in the app's own lettering. Sized against the
   screen rather than fixed, so "Sequoia goes West" stays on ONE line
   down to a 320-wide phone — a title that wraps stops reading as a
   name and starts reading as a sentence. */
.view-signin h1{
  font-family:var(--press);font-weight:700;
  font-size:clamp(24px,7.4vw,31px);line-height:1.15;
  margin:2px 0 6px;text-align:center;color:var(--ink);
  white-space:nowrap;
}
/* ⚠️ THE ORPHANED LAST LINE. Kept SHORT enough to sit on one line on a
   normal phone, and where it does have to break, `balance` shares the
   words evenly between the lines instead of orphaning the rest. The
   measure is capped as well, so a wide window does not stretch it into
   one long thin line across the paper. */
.view-signin .under{
  margin:0 auto 22px;max-width:19em;
  text-align:center;text-wrap:balance;
  font-size:17px;line-height:1.35;color:var(--ink-soft);
  transform:rotate(-.6deg);
}

/* ---- the buttons ----
   Violet is the you-can-touch-this ink and has been since Aug 6.
   Nothing here is filled with it except the one real .go — the map's
   language is ink on paper, and a solid block of colour is the one
   thing that has been rejected every time it was tried. So: violet
   line, cream inside. */
.view-signin .door{
  display:flex;align-items:center;justify-content:center;gap:11px;
  width:100%;padding:15px 16px;margin:0 0 12px;
  background:var(--cart);border:2px solid var(--mark);border-radius:13px;
  font-family:var(--hand);font-size:19px;color:var(--ink);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
  transform:rotate(-.4deg);
}
.view-signin .door:active{transform:rotate(-.4deg) scale(.985)}
.view-signin .door svg{width:21px;height:21px;flex:0 0 auto}

/* where Google draws its own button — centred, with the same air
   around it the other buttons have */
.view-signin .google-slot{display:flex;justify-content:center;margin:0 0 12px;min-height:44px}
.view-signin .google-slot:empty{margin:0}

.view-signin .go{margin:6px 0 0}

/* ---- the hand-drawn divider ---- */
.view-signin .or{
  display:flex;align-items:center;gap:12px;
  margin:20px 2px 16px;color:var(--ink-faint);font-size:16px;
}
.view-signin .or::before,.view-signin .or::after{
  content:"";flex:1;height:1px;background:var(--ink-faint);opacity:.55;
}

/* ---- what a person types ----
   The phone's own reading face, never the handwriting — a stranger's
   words are not the app's voice (owner, Aug 5), and that goes for the
   boxes they are typed into as well.
   ⚠️ 16px MINIMUM. Below that iOS zooms the page on focus and every
   measurement afterwards is garbage (docs/app/10_PITFALLS.md, LAYOUT
   AND CSS, the iOS laws). */
.view-signin label{display:block;margin:0 0 4px;font-size:16px;color:var(--ink-soft)}
.view-signin input{
  width:100%;padding:12px 13px;margin:0 0 14px;
  font-family:var(--read);font-size:16px;color:var(--ink);
  background:var(--cart);border:1.5px solid rgba(120,100,60,.45);border-radius:10px;
}
.view-signin input:focus{outline:none;border-color:var(--mark)}

/* ---- the date of birth, on a wheel (28 Aug) ----
   Owner: "maybe use something similar to apple's date picker wheel
   instead of a legacy calendar lol", and then, on a first cut that
   scrolled three lists and painted a tilt over them: "it's not
   scrollable.. my team already solved this wheel thing in another
   project (recap shark)".

   ⚠️ SO THIS IS RECAPSHARK'S SHAPE, not ours — their parked wheel at
   _old/1/RecapShark/docs/_archive/cylinder_wheel_mobile (css/dashboard
   .css, the .wheel-* block). The lesson their code carries and ours
   had to learn twice: A COLUMN THAT SCROLLS CANNOT ALSO BE A FACE
   TURNING IN THREE DIMENSIONS. There is no overflow scrolling anywhere
   below — the rows are seated absolutely on a drum and born-wheel.js
   turns it, which is why it moves on a phone AND under a mouse.

   ⚠️ touch-action:none on the column is load-bearing: without it the
   phone claims the drag for the page and the drum never hears it. */
/* ⚠️ 300px, NOT 200 — AND IT IS A REACHING PROBLEM, NOT A LOOK. Owner,
   29 Aug: "i feel like i have to put my finger on the same row as the
   current row when i'm scrolling, so i can't see what value lands on
   the current row. so if it's longer, i can use a lower part to scroll
   while i see what is at the current row." A thumb is wider than a row.
   On a 200px drum the middle line is 100px from either edge, which is
   about one thumb, so the hand covers the answer while it is choosing
   it. At 300 there is 150px of column below the line to drag from and
   the row under the band stays in sight. He also said the room is there
   — "there's not much else in that page" — and it is: the join form
   already scrolls, and this is what is being looked at while it is up.
   Measured on the real screen at 390x640: the readable rows went from
   five to seven and the drag room below the line from 100px to 150px.

   ⚠️ AND THE ROW ANGLE CAME DOWN WITH IT, in app/views/wheel-drum.js —
   see the note on TUNE.lineAngle there. The drum's radius is taken from
   THIS height, so a taller box on the old 20 degrees would have spread
   the rows 53px apart and shown no more of them: a bigger wheel, not a
   more useful one. At 15 degrees the rows keep roughly the spacing they
   had and the extra height buys ROWS. Changing one of these two numbers
   without the other is what makes it look wrong.

   ⚠️ AND THE ROWS DO NOT SIT radius*sin(angle) APART, because the column
   carries perspective:1000px and that magnifies whatever leans toward
   the eye. The flat arithmetic says 37px; what a ruler on the page says
   is 43. Measure it, do not derive it. (It was 35px before, not 32.) */
.view-signin .born-wheel{
  position:relative;display:flex;gap:6px;margin:0 0 14px;
  /* ⚠️ SIZED OFF THE GLASS, NOT OFF A NUMBER THAT SUITED ONE PHONE.
     His rule, 29 Aug, and he asked the right question before it was
     written: "would it be the same on all phones or on some phones we
     will have vertical scrolling. because i don't want that. i wanted
     to not have vertical scrolling on any phone." A fixed 300px cannot
     promise that — it fit HIS phone and would have overflowed a short
     one. So the wheel takes whatever is left after everything else on
     the step, and --born-around is the measured sum of that everything
     (⚠️ re-measure it if anything on the join step changes size).
     100dvh, not vh, because iOS counts its own toolbars in and vh does
     not. The floor keeps it usable on a small phone; the ceiling is the
     300 he approved, so a tall phone gains air rather than a giant
     wheel. */
  /* ⚠️ 490 -> 551 ON 2 SEP, and every band moved by the SAME 61px: the
     join step gained the terms tick above its button (the sign-up
     tick of docs/app/03_HOW_THE_APP_WORKS.md; the plan is in git at
     4ecdfbb), which is two lines and its margin at every phone width
     from 360 up. Adding the same number everywhere keeps whatever room
     each band already had — the wheel gives up exactly the tick's
     height and nothing else about the step moves.
     Measured with the wheel OPEN and the date field hidden, which is
     the state born-wheel.js actually puts the step in (showWheel swaps
     one for the other) and the tallest the step ever is. */
  --born-around:551px;
  --born-floor:168px;
  height:clamp(var(--born-floor), calc(100dvh - var(--born-around)), 300px);
  overflow:hidden;
  background:var(--cart);border:1.5px solid rgba(120,100,60,.45);border-radius:10px;
  /* ⚠️ THE WHOLE BOX REFUSES THE PAGE, not only the columns. There is a
     6px gap between each column and a border around them all, and a
     thumb landing on any of that used to hand the drag to the page —
     which is the other half of "you can accidentally move the page a
     little bit up and down". The columns keep their own rule too; this
     one covers the cracks between them. */
  touch-action:none;
}
/* The light falling off the top and bottom of the drum, over everything.
   ⚠️ The stops are a FRACTION of the box, so a taller box fades a taller
   band unless they are tightened — 32% of 300px is 96px of dimmed rows
   at each end. Pulled in to 26% so the clear middle grows with the
   height instead of staying put, which is the whole point of the change
   above.

   ⚠️ AND THE TWO ENDS ARE NOT THE SAME ANY MORE, because the drum is not
   either: there are two rows above the line and six below it. The top
   has a short fade because both rows up there are meant to be READ; the
   bottom has a long one because the rows down there are crowding toward
   the rim and are meant to fall away. Matching the two stops back up
   would either wash out the top rows or cut the bottom off flat. */
.view-signin .born-wheel::before{
  content:"";position:absolute;inset:0;pointer-events:none;z-index:3;
  background:linear-gradient(var(--cart) 1%, rgba(246,233,203,0) 13%,
                             rgba(246,233,203,0) 70%, var(--cart) 99%);
}
/* ⚠️ THE BAND SITS BEHIND THE WORDS, NOT OVER THEM. It had z-index 2
   and a translucent fill, which laid a wash across the very row it was
   meant to mark — "there's something like a stain on the current word".
   A selection band belongs BEHIND the thing it selects, the way Apple's
   is; z-index 0 under a drum at 1 is all that takes. */
/* ⚠️ THE BAND SITS WHERE THE DRUM PUT THE LINE, which since 29 Aug is
   NOT the middle: the wheel shows two rows above the chosen one and six
   below it, so the line rides high and the lower two thirds of the
   column are free to drag on. app/views/born-wheel.js measures the drum
   and writes --born-line; the 50% here is only what shows if that never
   ran. Hard-coding 50% back moves the ink off the words. */
.view-signin .born-band{
  position:absolute;left:6px;right:6px;top:var(--born-line,50%);height:40px;
  transform:translateY(-50%);pointer-events:none;z-index:0;
  border-radius:8px;
  background:rgba(120,100,60,.10);
}

/* ⚠️ IT IS AN INPUT BOX, NOT A BUTTON — his words: "the button that
   says chose your dob, looks like the shittiest ugliest button ..
   shouldn't be even a button more like an input box or whatever apple
   uses". He was looking at a BARE browser button: the rule that used to
   dress it was lost when this block was rewritten for the drum, so it
   had no styling at all. It is a readonly <input> now and inherits the
   very rule the name box above it wears, so the two match to the pixel
   with one line of its own; and its emptiness is a real PLACEHOLDER
   rather than grey text we drew, so it greys and clears exactly the way
   every other field on this page does. */
.view-signin .born-open{cursor:pointer}
.view-signin .born-outer{
  flex:1 1 0;min-width:0;position:relative;overflow:hidden;
  perspective:1000px;
  cursor:grab;user-select:none;-webkit-user-select:none;
  touch-action:none;                 /* the drum takes the drag, not the page */
}
.view-signin .born-outer:active{cursor:grabbing}
.view-signin .born-drum{
  position:absolute;left:0;right:0;top:50%;z-index:1;   /* over the band */
  list-style:none;margin:0;padding:0;
  transform-style:preserve-3d;
}
/* ⚠️ NOTHING HERE MAY CHANGE AS A ROW CROSSES THE LINE, and that is the
   whole of "the scroll is hiccupy when each word passes the current
   row". The chosen row used to gain font-weight:600, and a weight change
   RE-SHAPES the text — a layout, forced in the middle of a turn, exactly
   once per row, which is precisely where he felt it. So every row is
   lettered identically and is always already right; what marks the
   chosen one is the band behind it and the opacity the drum is writing
   anyway. Colour and opacity are paint; weight and size are layout.
   Never put layout on this rule.

   ⚠️ AND THERE IS NO will-change HERE, ON PURPOSE. Their wheel had it
   and could afford it: theirs recycled 25 nodes. Ours holds 121 years in
   one column and three columns on the paper, so promoting every row asks
   the phone for ~360 layers in order to turn a wheel — memory spent
   instead of frames. A transform is composited without being asked. */
.view-signin .born-item{
  position:absolute;left:0;right:0;top:0;
  height:30px;line-height:30px;margin-top:-15px;   /* half its own height, theirs */
  text-align:center;
  /* ⚠️ 16px, HIS ASK, 29 Aug: "the text itself in the date picker like
     the one for month, day and year. you can make the font size a tiny
     bit smaller. not too much, but just a little bit." A tiny bit is
     one step. It also buys room, which is the other half of why he
     asked. ⚠️ The 30px height and the -15px above it are LAYOUT and
     stay exactly as they are — see the warning on this rule. */
  font-family:var(--read);font-size:16px;font-weight:400;color:var(--ink);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}

/* ---- the eye on the password box ----
   Owner, 21 Aug: "one of those eyes in the password field that when
   you press, it reveals the password field fully". The box is wrapped
   so the eye can sit INSIDE its right edge; the box's own bottom
   margin moves onto the wrapper so the form's rhythm does not change.
   Violet, because it can be pressed. A <button>, not a decorated
   <span>: it takes a tap, so a keyboard must reach it and a screen
   reader must be able to say what it is. */
.view-signin .field{position:relative;margin:0 0 14px}
.view-signin .field input{margin:0;padding-right:50px}
.view-signin .peek{
  position:absolute;top:0;right:0;bottom:0;width:48px;
  display:flex;align-items:center;justify-content:center;
  padding:0;background:none;border:0;color:var(--mark);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.view-signin .peek svg{
  width:22px;height:22px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
}
/* two drawings, one showing: the open eye while the password is
   hidden, the struck-through eye while it is on show */
.view-signin .peek .eye-off{display:none}
.view-signin .peek[aria-pressed="true"] .eye{display:none}
.view-signin .peek[aria-pressed="true"] .eye-off{display:block}

/* ---- remember me ----
   Owner, 21 Aug: "add a remember me check box". A tick in the violet,
   on one line with its words. What the tick buys is the door's
   business (account/providers/supabase.js, the parked sessions). The
   input rule above would dress the box like a text field, so it is
   undressed here. */
.view-signin .remember,
.view-agree .remember{
  display:flex;align-items:center;gap:10px;margin:0 0 16px;
  font-size:16px;color:var(--ink-soft);cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.view-signin .remember input,
.view-agree .remember input{
  appearance:auto;-webkit-appearance:checkbox;
  width:20px;height:20px;flex:0 0 auto;
  margin:0;padding:0;border:0;border-radius:0;background:none;
  accent-color:var(--mark);
}

/* ---- the terms tick, on the join step AND on the gate ----
   ⚠️ BOTH SCREENS WEAR IT. The gate (view-agree, module 5) asks the
   same question of the people who were already here, so it must look
   like the same question — same violet tick, same underlined links. It
   is one rule with two seats rather than a copy, because the day the
   two drift is the day one of them stops reading as a licence.

   docs/app/03_HOW_THE_APP_WORKS.md, the sign-up tick (the plan is in
   git at 4ecdfbb). It wears .remember's tick — same size, same violet,
   the same undressing of a box the input rule above would otherwise
   dress as a text field — and changes only what a line of TWO lines
   needs that a line of one does not.

   ⚠️ IT WRAPS, AND THAT IS WHY THE TICK IS TOP-ALIGNED. The paper is
   about 300px wide on a 390px phone (see the age line's note up in the
   join-up block) and these words do not fit on one line there. Centred
   against two lines, the tick floats away from the first word it
   belongs to. 1px down is the optical rest against a 16px cap height.

   ⚠️ AND THE TWO LINKS MUST LOOK LIKE LINKS. This is the one place in
   the app where being able to READ the thing before agreeing to it is
   the whole point — an underline and the violet, not a colour alone. */
.view-signin .agree,
.view-agree .agree{align-items:flex-start;line-height:1.4}
.view-signin .agree input,
.view-agree .agree input{margin-top:1px}
.view-signin .agree a,
.view-agree .agree a{
  color:var(--mark);text-decoration:underline;text-underline-offset:3px;
  /* ⚠️ A NAME OF A DOCUMENT DOES NOT BREAK IN HALF. Measured 2 Sep at
     373px wide: "Terms of" sat at the end of one line and "Service" at
     the start of the next, the underline stopping and starting with
     it, which reads as two links rather than one. The line is allowed
     to wrap wherever it likes BETWEEN the names; neither name wraps
     inside itself. The longer of the two is about 160px against the
     ~250px of paper a 320px phone leaves, so this cannot push the
     words off the side. */
  white-space:nowrap;
}

/* ---- accounts this phone remembers ----
   One chip per remembered account, the app's own "Continue as" beside
   Google's: the .door button — cream, a violet line, the handwriting —
   with the profile's face circle, smaller, and two lines set to the
   left. "not you?" under it is the way to be forgotten, kept quiet
   because it is not what most taps want. */
.view-signin .known{margin:0 0 12px}
.view-signin .door.continue{justify-content:flex-start;text-align:left;padding:11px 14px;margin:0;gap:12px}
.view-signin .known .face{width:38px;height:38px;font-size:17px}
.view-signin .known .who{display:flex;flex-direction:column;min-width:0;line-height:1.2}
.view-signin .known .as-name{
  font-weight:400;font-size:19px;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.view-signin .known .as-email{
  font-family:var(--read);font-size:13px;color:var(--ink-faint);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.view-signin .forget{
  display:block;width:100%;margin:2px 0 0;padding:4px;
  background:none;border:0;
  font-family:var(--hand);font-size:14px;color:var(--mark);
  text-decoration:underline;text-underline-offset:3px;text-align:center;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.view-signin .forget[disabled]{opacity:.5}

.view-signin .tiny{margin:18px 0 0;font-size:14px;line-height:1.5;color:var(--ink-faint);text-align:center}

/* ---- the welcome — three pages before the door, for a phone that is new
   (docs/app/08_WELCOME_PLAN.md; app/views/welcome.js; the markup is the
   welcome-pages template in the view-signin template). A horizontal
   scroller that snaps page to page — the phone's own swipe, no gesture
   code — with Skip, the dots and "next ›" outside it. Page 1 is the
   create screen's little map paper (the same numbers as .view-create
   .paper) plus the map page's car, smoke and two-halved road, drawn at
   the map's own design scale (the ink's viewBox IS the paper), so the
   road's strokes and the march's one dash cycle are plain numbers here
   rather than the per-layout rewrite the stretched map page needs. */
.view-signin .welcome{position:relative}
.view-signin .welcome .skip{
  position:absolute;right:-6px;top:-8px;z-index:1;
  background:none;border:0;padding:6px 8px;
  font-family:var(--hand);font-size:17px;color:var(--mark);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
/* out to the sheet's torn edges (the sheet's own 26px), so the next page
   comes in from the tear; each page is one scroller-width, padded back in */
.view-signin .welcome .pages{
  display:flex;overflow-x:auto;overflow-y:hidden;margin:0 -26px;
  scroll-snap-type:x mandatory;overscroll-behavior-x:contain;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;
}
.view-signin .welcome .pages::-webkit-scrollbar{display:none}
/* ONE HEIGHT FOR WHAT EVERY PAGE SHOWS under its caption — the map paper
   on page 1, the scenes on pages 2 and 3 — sized from the visible height
   so a page stands without scrolling on a modern phone, capped at the
   create preview's 300px-wide paper (600 tall). */
.view-signin .welcome .page{
  --paper-h:clamp(440px, calc(100vh - 270px), 600px);
  --paper-h:clamp(440px, calc(100svh - 270px), 600px);
  flex:0 0 100%;box-sizing:border-box;padding:22px 26px 0;
  scroll-snap-align:start;scroll-snap-stop:always;
}
/* two lines of room whatever the caption's length, so the paper sits at
   the same height on every page */
.view-signin .welcome .caption{
  margin:0 auto 14px;max-width:21em;min-height:2.7em;
  text-align:center;text-wrap:balance;
  font-family:var(--hand);font-size:19px;line-height:1.35;color:var(--ink-soft);
  transform:rotate(-.4deg);
}
/* the paper: the page's one height, half as wide (the 400×800 design) */
.view-signin .welcome .paper{
  position:relative;height:var(--paper-h);width:calc(var(--paper-h) / 2);margin:0 auto;
  box-shadow:0 10px 30px rgba(80,60,20,.22);
}
.view-signin .welcome .paper svg{position:absolute;inset:0;width:100%;height:100%;display:block}
.view-signin .welcome .paper text{font-family:inherit}
/* the road in its two halves — the map page's own strokes (page 1's
   map and page 2's window share them) */
.view-signin .welcome .w-road-ahead{fill:none;stroke:var(--ink-faint);stroke-width:2;stroke-dasharray:1 7;stroke-linecap:round}
.view-signin .welcome .w-trail{fill:none;stroke:var(--red);stroke-width:4;stroke-dasharray:10 7;stroke-linecap:round}
/* one loop of the march is exactly one dash cycle (10 + 7), or the trail
   jumps when it repeats — the map page's own law, in design units here */
.view-signin .welcome .march{animation:w-march 1.1s linear infinite}
@keyframes w-march{to{stroke-dashoffset:-17px}}
/* the car breathes and puffs with the keyframes the live pill already
   shares with the map page (bob / puff above), the map page's timings */
.view-signin .welcome .bob{animation:bob 1.6s ease-in-out infinite alternate}
.view-signin .welcome .puff{opacity:0;animation:puff 2.4s ease-out infinite}
.view-signin .welcome .puff2{animation-delay:1.2s}
/* the trip's name: the map page's face, size and ink (Papyrus — the one
   face on a phone that IS an old map; 19.2, a fifth over the towns) */
.view-signin .welcome .w-title{
  font-family:Papyrus,Trattatello,Luminari,"Marker Felt",fantasy,cursive;
  font-size:19.2px;
}
.view-signin .welcome .scene{
  height:var(--paper-h);display:flex;align-items:center;justify-content:center;
}
/* ---- page 2: the doors, and the moments page under them. THE DOORS
   WEAR THE SHARED `.upload-door` in section 2 — the same lines home's
   live pill wears, read from one place since 4 Sep 2026 (owner: "follow
   automatically") rather than written out twice. This page's pair are
   <div>s and inert, so none of the pill's button dress reaches them and
   there is nothing to undo. THE SCRAP under them is a cut of the very
   paper — one SVG whose viewBox welcome.js sets to a close window
   around the car; the parchment image rides the same window. */
/* ---- PAGE 4: THE CREW ON THE LIVE PILL (owner, 23 Aug — the fourth
   page, "the ability to join a trip, so the trip could have more than
   one people"; his pick of the three shapes he was shown, with three
   people on it).

   ⚠️ THE PILL'S OWN LOOK IS NOT REPEATED HERE. The markup wears the
   .view-home #live wrapper it wears on home, so every rule above —
   the pill, its car, the faces, the green ring on the owner — is the
   one already written. What is below is only this PAGE: how the pill
   and the doors sit on it, and the beat each face arrives on. */
.view-signin .welcome .page-crew .scene{
  flex-direction:column;align-items:stretch;justify-content:center;gap:18px;
}
/* the pill is home's, at home's width, centred on the paper's column */
.view-signin .welcome .page-crew .view-home{max-width:340px;width:100%;margin:0 auto}
/* the faces sit a touch bigger than home's 30px — this is a page being
   read at arm's length, not a row being glanced at */
.view-signin .welcome .page-crew .cface .face{width:34px;height:34px;font-size:14px}
/* a face is DEALT: visible the moment its turn comes, with a pop on top
   (never the other way round — see mountCrew's note) */
.view-signin .welcome .page-crew .cface{opacity:0}
.view-signin .welcome .page-crew .cface.dealt{
  opacity:1;animation:w-deal-face .42s cubic-bezier(.2,.9,.3,1.3) both;
}
@keyframes w-deal-face{
  0%{opacity:0;transform:scale(.6)}
  60%{opacity:1;transform:scale(1.08)}
  100%{opacity:1;transform:scale(1)}
}
/* the doors under the pill, the same picture page 2 shows */
.view-signin .welcome .page-crew .doors{max-width:340px;width:100%;margin:0 auto}

.view-signin .welcome .page-share .scene{
  flex-direction:column;align-items:stretch;justify-content:flex-start;gap:12px;
}
.view-signin .welcome .doors{display:flex;gap:10px;flex:none;pointer-events:none}
.view-signin .welcome .scrap{
  position:relative;flex:1 1 auto;min-height:0;border-radius:3px;overflow:hidden;
  box-shadow:0 10px 30px rgba(80,60,20,.22);
}
.view-signin .welcome .scrap svg{position:absolute;inset:0;width:100%;height:100%;display:block}
.view-signin .welcome .scrap text{font-family:inherit}
/* the moments page's marks, landing: the pencil line (the map page's
   own leaders' ink), and the pin and the polaroid popping onto the
   paper about their own middle */
.view-signin .welcome .w-leaders{stroke:var(--ink-faint);stroke-width:1}
.view-signin .welcome .w-leader{animation:w-fade-in .3s ease-out both}
.view-signin .welcome .w-pop{
  transform-box:fill-box;transform-origin:center;
  animation:w-pop-in .42s cubic-bezier(.2,.9,.3,1.2) both;
}
@keyframes w-pop-in{from{transform:scale(.3);opacity:0}to{transform:scale(1);opacity:1}}
@keyframes w-fade-in{from{opacity:0}to{opacity:1}}
/* ---- page 3: the stack of polaroids. THE DRESS IS THE VIEWER'S (mockups/
   index.html, .shot — the cream print, 13px of frame, deeper at the foot
   where the words go, the hand-stacked tilt on --rest-*), written once
   more here because the viewer's rules live inline in the map page,
   which the shell cannot reach (named in the plan; one copy the day the
   map joins the shell). Two honest differences: the shadow is the
   paper's soft brown rather than the viewer's dark-room black, and the
   lip's type is a size down for a smaller print. The engine is NOT
   copied — viewer/cards.js builds these cards and viewer/motion.js
   flies them. The stack box is the anchor every card sits at (a card is
   absolute at 0,0 in it, leaning its own way); its shape is a 3:4 print
   at this width plus its frame and lip. */
.view-signin .welcome .stack{
  position:relative;width:min(280px, 86%);aspect-ratio:280 / 413;
  opacity:0;transition:opacity .25s ease;
}
.view-signin .welcome .stack.is-ready{opacity:1}
.view-signin .welcome .shot{
  position:absolute;left:0;top:0;width:100%;
  background:var(--cart);border-radius:2px;
  box-shadow:0 8px 22px rgba(60,48,20,.30);
  padding:13px 13px 0;display:flex;flex-direction:column;
  transform:translate(var(--rest-x,0), var(--rest-y,0)) rotate(var(--rest-r,0));
  will-change:transform;
}
/* the flight: the viewer's own curve and pace, written per flight by the
   dealer (--fly-t / --fly-ease), the lifted shadow for the whole of it */
.view-signin .welcome .shot.is-fly{
  transition:transform var(--fly-t,.55s) var(--fly-ease,cubic-bezier(.3,0,.15,1));
  box-shadow:0 14px 30px rgba(60,48,20,.34);
}
.view-signin .welcome .shot.is-teleport{transition:none}
.view-signin .welcome .shot-media{
  position:relative;background:var(--cart);overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(43,42,38,.14);
}
.view-signin .welcome .shot-pic{display:block;width:100%;background:var(--cart);pointer-events:none}
/* the kind chip in the picture's top corner — the viewer's own dress: a
   camera for a photo, a film camera for a film */
.view-signin .welcome .shot-kind{
  position:absolute;right:8px;top:8px;z-index:4;
  display:flex;align-items:center;justify-content:center;
  height:34px;min-width:34px;padding:0 8px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);pointer-events:none;
}
.view-signin .welcome .shot-kind svg{display:block}
.view-signin .welcome .shot-kind .k-video{display:none}
.view-signin .welcome .shot-kind.is-video .k-photo{display:none}
.view-signin .welcome .shot-kind.is-video .k-video{display:block}
/* the lip: the place on its own line (a long name shrinks to fit — the
   dealer measures it when the card joins), the day under it in the
   viewer's quieter second-line ink */
.view-signin .welcome .shot-cap{
  display:flex;flex-direction:column;padding:8px 3px 12px;
  color:var(--ink);font-family:var(--hand);line-height:1.2;
}
.view-signin .welcome .shot-place{font-size:18px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.view-signin .welcome .shot-day{font-size:15px;color:var(--ink-soft);white-space:nowrap;margin-top:2px}
/* the foot: the dots (tappable, a thumb-sized target around a 9px dot)
   and the one labelled way on, in the you-may-press violet */
.view-signin .welcome .foot{margin:12px 0 0;text-align:center}
.view-signin .welcome .dots{display:flex;justify-content:center;gap:4px;margin:0}
.view-signin .welcome .dot{
  position:relative;width:26px;height:26px;padding:0;border:0;background:none;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.view-signin .welcome .dot::after{
  content:"";position:absolute;left:8.5px;top:8.5px;width:9px;height:9px;border-radius:50%;
  background:var(--ink-faint);opacity:.45;
}
.view-signin .welcome .dot.on::after{background:var(--mark);opacity:1}
.view-signin .welcome .next{
  margin:2px 0 0;padding:8px 16px;background:none;border:0;
  font-family:var(--hand);font-size:20px;color:var(--mark);
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}
.view-signin .welcome .next:active{opacity:.55}

/* ---------------------------------------------- email confirmed ----------
   A two-second screen: the tick, the words, the way to the door. */
.view-confirmed{text-align:center;padding:14px 4px 10px}
.view-confirmed .tick-mark{
  font-size:44px;line-height:1;margin:0 0 10px;color:var(--mark);
  transform:rotate(-4deg);display:inline-block;
}
.view-confirmed h1{
  font-family:var(--hand);font-weight:400;font-size:30px;line-height:1.15;
  margin:0 0 10px;color:var(--ink);
}
.view-confirmed .sub{
  font-family:var(--read);font-size:14px;line-height:1.5;
  color:var(--ink-soft);margin:0 0 24px;
}
.view-confirmed .go{display:inline-block;width:auto;padding:13px 26px;margin:0}
.view-confirmed.bad h1{color:var(--red)}
.view-confirmed.bad .tick-mark{color:var(--red)}

/* ---------------------------------------------- start a trip ----------
   The form, then the little preview paper under it, on the one torn
   sheet (owner, 21 Aug: "A"). */

.view-create h1{
  font-family:var(--press);font-weight:700;
  font-size:clamp(24px,7vw,30px);line-height:1.15;
  margin:8px 0 2px;text-align:center;color:var(--ink);
}
.view-create .under{
  margin:0 auto 8px;max-width:22em;text-align:center;text-wrap:balance;
  font-size:16px;line-height:1.35;color:var(--ink-soft);transform:rotate(-.5deg);
}
.view-create .panel{margin-top:14px}

/* ⚠️ THE FORM WRITES IN THE MAP'S OWN HAND (owner, Aug 20: "i liked
   it more like the test page with the funny fonts"). That does NOT
   reopen the comments rule (a stranger's paragraph still reads in
   --read) — this is the app asking its own questions and the traveller
   naming their own trip, which is the app's voice, not a stranger's.
   Kept at 16px+ regardless: below that iOS zooms the page on focus
   (docs/app/10_PITFALLS.md, LAYOUT AND CSS, the iOS laws). */
.view-create label.f{
  display:block;font-family:var(--hand);font-size:16px;color:var(--ink-soft);
  margin:16px 0 6px;
}
/* a plain field (the name) */
.view-create input.field{
  width:100%;padding:12px 13px;
  font-family:var(--hand);font-size:18px;color:var(--ink);
  background:var(--cart);border:1.5px solid rgba(120,100,60,.45);border-radius:10px;
}
.view-create input.field:focus{outline:none;border-color:var(--mark)}
.view-create input.field::placeholder{color:var(--ink-faint);opacity:.8}

/* ---- the town picker (trip/places/picker.js dresses itself here) ---- */
.view-create .town{position:relative}
.view-create .town input{
  width:100%;padding:12px 13px;
  font-family:var(--hand);font-size:18px;color:var(--ink);
  background:var(--cart);border:1.5px solid rgba(120,100,60,.45);border-radius:10px;
}
.view-create .town input:focus{outline:none;border-color:var(--mark)}
.view-create .town input::placeholder{color:var(--ink-faint);opacity:.8}
.view-create .town-menu{
  position:absolute;left:0;right:0;top:100%;z-index:8;
  background:var(--cart);border:1.5px solid var(--ink-faint);border-top:none;
  border-radius:0 0 10px 10px;box-shadow:0 10px 22px rgba(80,60,20,.24);
  max-height:260px;overflow:auto;
}
.view-create .town-menu[hidden]{display:none}
.view-create .town-item{
  padding:11px 13px;font-family:var(--hand);font-size:18px;color:var(--ink);cursor:pointer;
}
.view-create .town-item small{font-family:var(--read);color:var(--ink-faint);font-size:13px}
.view-create .town-item[aria-selected="true"]{background:#FDF3A8}

/* said quietly under a town the list could not place */
.view-create .why{
  font-family:var(--read);font-size:12.5px;color:var(--mark);
  margin:6px 2px 0;line-height:1.4;
}
.view-create .why[hidden]{display:none}

/* Start | swap | End on one row (owner, 21 Aug). A GRID rather than a
   flex row, so the swap button lands in the SAME row as the two boxes
   and centres on them no matter what the labels above are doing. Each
   box may shrink (min-width:0) so a long town name never shoves the
   row wider than the phone. */
.view-create .ends{
  display:grid;
  grid-template-columns:1fr auto 1fr;
  grid-template-areas:
    "slab .    elab"
    "sbox swap ebox"
    "swhy .    ewhy";
  column-gap:10px;
  align-items:center;
}
.view-create .ends > label[for="start-host"]{grid-area:slab}
.view-create .ends > label[for="end-host"]  {grid-area:elab}
.view-create .ends > #start-host{grid-area:sbox;min-width:0}
.view-create .ends > #end-host  {grid-area:ebox;min-width:0}
.view-create .ends > #start-why {grid-area:swhy}
.view-create .ends > #end-why   {grid-area:ewhy}

/* The swap — violet, the touchable ink; a horizontal double arrow
   because the two ends sit side by side. */
.view-create .swap{
  grid-area:swap;align-self:center;
  width:38px;height:38px;padding:0;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--cart);border:2px solid var(--mark);color:var(--mark);
  font-size:19px;line-height:1;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.view-create .swap:active{transform:scale(.92)}

/* who can see it — three plain choices, stacked, violet for the chosen */
/* 🅿️ THE THREE CHOICES' LOOK MOVED OUT OF HERE on 27 Aug, up beside
   the trip cards' own question, because home asks this now too (the
   owner: "for all trips past or live, the user should be able to see
   the privacy and also be able to change it"). One copy, so the two
   screens cannot drift into asking one question two ways. */

.view-create .actions{display:flex;gap:10px;align-items:center;margin-top:22px}
.view-create .actions .go{flex:1;width:auto;margin:0;padding:15px 16px}
.view-create .actions .go[disabled]{opacity:.45}
.view-create .reroll{
  flex:0 0 auto;padding:13px 15px;font-size:20px;line-height:1;
  background:var(--cart);border:2px solid rgba(120,100,60,.4);border-radius:13px;
  cursor:pointer;-webkit-tap-highlight-color:transparent;
}

/* The "end it and start this one?" question, under the Create button —
   the trip cards' own question scrap, so it asks the way home asks. */
.view-create .one-road{margin:12px 0 0;transform:rotate(-.3deg)}
.view-create .one-road .sure-cost{font-size:15px;margin-bottom:2px}
/* the yes is the long one: it takes the room and never breaks its line */
.view-create .one-road .sure-yes{flex:2 1 auto;white-space:nowrap}
.view-create .one-road .sure-no{flex:1 1 auto}

/* The error slot reserves no empty line (owner, Aug 21); the hint sits
   close under the button. */
.view-create .bad{font-family:var(--read);font-size:14px;color:var(--red);margin:10px 0 0;min-height:0}
.view-create .hint{font-family:var(--read);font-size:15.6px;color:var(--ink-faint);margin:8px 0 0;line-height:1.45}

/* ---- the preview paper ---- */
.view-create .preview{width:300px;max-width:100%;margin:22px auto 0}
.view-create .paper{
  position:relative;width:100%;aspect-ratio:400/800;
  box-shadow:0 10px 30px rgba(80,60,20,.22);
}
.view-create .paper svg{position:absolute;inset:0;width:100%;height:100%;display:block}
.view-create .paper.blank #ink{opacity:0}
.view-create .paper .waiting{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  text-align:center;padding:0 30px;font-size:17px;color:var(--ink-faint);
  transform:rotate(-1deg);
}
.view-create .paper:not(.blank) .waiting{display:none}
.view-create #road{fill:none;stroke:var(--ink-faint);stroke-width:1.7;
                   stroke-dasharray:5 5;stroke-linecap:round}
.view-create .paper text{font-family:inherit}
.view-create #ink .provisional{opacity:.42}
.view-create #ink .provisional circle{fill:none;stroke:var(--dot);stroke-width:1.2}
.view-create #ink line.provisional{stroke-dasharray:3 3}

/* ---------------------------------------------- the map ----------
   THE TRIP MAP — the hand-drawn sheet, the polaroids seated on it, the
   viewer, the feed, the real map and both comment sheets.

   Carried whole out of mockups/index.html on 4 Sep 2026, MODULE 4b of
   the fold (docs/app/12_FOLD_PLAN.md), rule for rule and comment for
   comment. Nothing here was re-decided: 172 rules moved unchanged, and
   the six that did not are named in tools/check-page-styles.mjs.

   ⚠️ IT IS STILL SERVED AS ITS OWN DOCUMENT until MODULE 5, and that
   document keeps three rules of its own that are the DOCUMENT's and not
   the screen's — html,body's height and overflow, and the body's flex
   column. They are the last thing in mockups/index.html's head and they
   say why they are there.

   ⚠️ AND app.css NOW LANDS ON THE MAP, which is the other direction and
   the one that bites: a BARE rule here reaches anything the map wears.
   Two do on purpose — the universal box-sizing reset and the global
   [hidden] — and check-page-styles sweeps the rest every run. Scope a new
   one to the shell's own chrome (".sheet > x") rather than leaving it bare.
   ------------------------------------------------------------------ */

/* No double-tap zoom anywhere (owner, Aug 6: the subtle zoom bounce
   was behind a family of gesture glitches). touch-action:manipulation
   keeps taps and pans and drops only the double-tap zoom; every element
   that declares its own touch-action still wins over this default. */
.view-map, .view-map *{box-sizing:border-box;touch-action:manipulation}

/* ============ THE MAP SCREEN'S OWN ROOT (MODULE 3 of the fold) ============
   Everything this page draws sits inside #map-root now, so that the
   code can look for its parts INSIDE the screen rather than asking the
   whole document by name (docs/app/12_FOLD_PLAN.md; trip/page/parts.js
   says why that matters — ten of these ids are also the welcome
   tour's).

   ⚠️ THIS RULE EXISTS TO MAKE THE WRAPPER INVISIBLE TO THE LAYOUT, and
   it is the whole risk of putting one in. .stage carries flex:1 and is
   "centred by the body's align-items" — both of which were the BODY's
   doing, and a plain div between them would have quietly taken both
   away: the stage would have stopped filling the height and, on a wide
   screen, stopped being centred. So the root takes exactly the three
   properties the body was lending, and fills the body the way .stage
   used to.

   It is also the seat MODULE 4 prefixes every rule on this page with,
   and the element MODULE 5 clones out of the shell's template. */
/* ⭐ AND IT TAKES THE WHOLE GLASS (MODULE 5 of the fold, 4 Sep 2026).

   Until today this was `flex:1;width:100%` — the screen filling a BODY
   that was a flex column, which is what the map page's body is. The
   shell's is not: it mounts every screen into #view, inside main.sheet,
   which is a 460px column of torn paper with padding. Dropped in there
   the map came out 302x132 with a paper of height ZERO, and its detour
   page drew nothing at all, because a fence cannot be measured off a
   sheet with no height.

   ⚠️ AND THE MAP IS NOT A SHEET SCREEN. Every other screen is words on
   the app's one piece of paper; the map IS its own piece of paper, torn
   edge to torn edge, and it has filled the glass since the day it was
   drawn. So it leaves the column rather than fitting into it.

   `inset:0` says the same thing in both documents — the map page, where
   it replaces a flex child that filled the body, and the shell, where it
   lifts the screen out of the sheet — which is what lets one rule serve
   both while both exist. z-index 1 puts it over the shell's own paper
   and under the bottom bar's 20, which stands on top of the map exactly
   as it always has. */
.view-map{
  position:fixed;inset:0;z-index:1;
  display:flex;flex-direction:column;align-items:center;
}

/* The map is the whole app — nothing lives below it. The stage owns the
   gesture, so it swallows touch scrolling rather than letting iOS pan. */
.view-map .stage{
  width:100%;max-width:var(--stage-max);   /* centred by the body's align-items */
  /* ⛔ AND THE FOOT LEAVES ROOM FOR THE BAR (29 Aug).

     The bar has stood over the map since DECISION 64 and the paper
     has been drawn the full height of the glass the whole time, so
     the bottom of every road — its house, its first town, its oldest
     photographs — has been sitting UNDER the band. Owner, off his
     phone: "the bottom is cut off". Measured: paper to 640, bar from
     588, the trip's own start name ending 17px inside it.

     ⚠️ --sheet-h ALREADY SUBTRACTS THE BAR and is declared right
     below, which is what made this easy to miss: it is the height
     the overlay SHEETS are cut to (the feed, the real map, the layer
     switch), and nothing was cutting the PAPER itself. The sheets
     were leaving the bar alive beneath them and the map underneath
     them was not.

     ⛔ AND THE INSET IS COUNTED ONCE, NOT TWICE. The first cut wrote
     `var(--bar) + env(safe-area-inset-bottom)` and that is wrong on
     every phone with a home indicator: the BAR already carries that
     inset in its own padding, so --bar — which fitBar() measures off
     the real element — includes it. Adding it again left a band of
     empty paper below the map that grew and shrank with whatever
     phone was holding it. It measured zero on a desktop, which is
     exactly why it shipped. Owner, 29 Aug: "the empty space at the
     bottom is now too much .. on different phones that space should
     be consistent and not change".

     So: max() of the two, never the sum. With a bar, the bar's own
     height (inset included). Without one — a trip opened by link,
     where there is no bar by design — the inset alone, so the paper
     still clears the home indicator. --map-foot is the daylight he
     asked for and is set beside --bar, by the same function, so it
     is there only when there is a menu to keep clear of. */
  /* ⛔ AND ONLY THE POSITIVE HALF CAN RIDE IN THE PADDING. A padding
     cannot be negative — the browser clamps it at zero and says
     nothing — so the tuner below −52 was pressing into a wall while
     he watched nothing move: "i went to −75 but there's still room".
     The paper had already stopped at the foot of the glass fifty
     units earlier. The wrap's margin carries the other half, where a
     negative number is allowed to mean what it says. */
  padding:var(--top) var(--edge)
          calc(max(var(--bar), env(safe-area-inset-bottom))
               + max(0px, var(--map-foot)));
  touch-action:none;-webkit-user-select:none;user-select:none;
  flex:1;display:flex;flex-direction:column;align-items:center;
  /* THE ONE HEIGHT EVERY OVERLAY SHEET IS CUT TO (03_HOW_THE_APP_WORKS.md,
     the map page's sheets; the plan is in git at 4ecdfbb): what is
     left of the screen after the top
     band, the foot band and the BOTTOM BAR — the sheets are sized to
     leave the bar alive beneath them, never to cover it. It was
     written out four times (#feed, #gmap, #layers — whose whole job
     is to track #gmap's geometry exactly — and the upload sheet,
     since deleted) and the copies could drift; now they cannot.
     ⚠️ DECLARED HERE, NOT AT
     :root, on purpose: .stage.is-detour raises --pager to 58px, and
     a custom property computes where it is DECLARED — at :root this
     height would never hear the detour's taller band. */
  --sheet-h:calc(var(--vh) - var(--top) - var(--pager) - var(--bar) - env(safe-area-inset-bottom));
}

/* THE BRANDING (owner, 24 Aug): logo 1 off the rig, 130px, top
   centre — the same seat every shell screen wears (app/app.css's
   .brand), and ON the torn paper like theirs (his catch: the first
   cut sat it on the ruled page above the sheet — "it falls outside
   the torn paper"). It is a band at the TOP of .sheet-wrap, inside
   the parchment, the way the button band takes the sheet's foot; the
   drawn map simply gets the rest, exactly as it does below. Static
   markup, so it is there from the first paint like the paper itself. */
.view-map .brand{
  display:flex;justify-content:center;
  /* THE SEAT IS HIS OWN, tuned on the shell through the ?tune=logo
     rig — twice, 24 Aug; the second round settled it ("logo top:
     -26px, space below: 38px") — and carried here as the same LOOK:
     both papers start at the page's top, and the shell's -26 against
     its sheet's 30px padding lands the logo 4px inside the paper's
     torn edge with 38px of air below, so this page writes that
     result directly. env() adds on where the page really runs under
     a notch (a home-screen install), the way "‹ back" protects
     itself. */
  margin:calc(env(safe-area-inset-top) + 4px) 0 38px;
}
/* ⭐ --logo-x — the same one number the shell carries (app/app.css),
   so the logo stands in the same place on every screen. His ask of 31
   Aug; the ?tune=logox knobs move it here and it is written into both
   documents when he settles it. */
.view-map .brand img{display:block;width:130px;height:auto;transform:translateX(var(--logo-x))}

/* THE TITLE'S OWN LAYER — over the whole sheet, above the branding
   band and the drawing both, so the name can be seated anywhere on
   the parchment. It takes no taps and it is not in the flex column:
   it must not move the logo or the map by existing. overflow is left
   visible on purpose; this is the layer that does NOT clip. */
.view-map .title-layer{
  position:absolute;inset:0;width:100%;height:100%;
  overflow:visible;pointer-events:none;z-index:2;
}

/* ONE SHEET, MAP AND BUTTONS BOTH (owner, 21 Aug: "make them look like
   they're part of the map .. extend the paper more vertically and put
   them on the paper at the bottom"). The parchment lives HERE now, not
   in #sheet, so a single sheet of it stretches behind the map region
   AND the button band below it. Its four torn edges (baked into the
   art) fall at this box's four corners — so the only torn edge
   between the map and the buttons is none: it is one continuous piece
   of paper, and the buttons sit on its foot the way a legend sits on a
   real map. background-size 100% 100% stretches it exactly as the old
   preserveAspectRatio="none" image did, so it looks identical, just
   taller. */

.view-map .sheet-wrap{
  position:relative;width:100%;flex:1;min-height:0;
  /* the negative half of --map-foot: the paper reaching PAST the foot
     the stage's padding left it, which is the only way it can grow
     once that padding has bottomed out at zero */
  margin-bottom:min(0px, var(--map-foot));
  display:flex;flex-direction:column;
  /* ⛔ ROOT-RELATIVE, AND IT HAD TO BECOME SO ON THE WAY HERE (MODULE 4b,
     4 Sep 2026). A url() in a stylesheet resolves against the STYLESHEET,
     not the page — written `art/parchment.png` inside the map page it
     meant /art/parchment.png, and the same seven characters inside
     app.css mean /app/art/parchment.png, which is nothing. The torn
     paper simply stops arriving, and no rectangle moves to say so: the
     measuring run caught it on the background's computed address.
     .sheet::before above already says it this way, for the same reason. */
  background:url("/art/parchment.png") center/100% 100% no-repeat;
}
/* ⚠️ NOTHING HIDES THIS AND NOTHING MAY (owner, 21 Aug). It used to
   carry opacity:0 until an animation took it off, and an animation
   that did not run left a reader on blank paper. The map is drawn and
   the map is seen — see THE SWIPE LESSON in the script. */
.view-map .paper{
  position:relative;width:100%;flex:1;min-height:0;
}
/* ============ THE FIRST-PAINT CONTRACT (23 Aug) ============
   docs/app/03_HOW_THE_APP_WORKS.md, the map page's first paint (the
   plan is in git at 4ecdfbb), and the end of the
   slideshow the owner photographed: furniture that only script can
   seat — the ink's drawing (viewBox-less until layout()), the corner
   keys (position:absolute, seated by seatLayers in map units) — is
   INVISIBLE until boot's first paint says .is-ready, in the same
   synchronous pass that seats it. Until then the reader sees the
   paper, which is a true thing, instead of every unseated element
   piled in the top-left corner, which never was.

   ⚠️ THIS IS NOT THE VEIL THE 21-AUG LAW BANNED, and the difference
   is the whole design: that veil hid THE PAPER behind an animation
   that could fail to run; this hides only the unseated furniture, and
   it lifts in plain code at the end of the same pass that draws the
   map — no animation, no timer, nothing that can be skipped. A boot
   that dies before that line lands in the failure curtain
   (mapCouldNotOpen), which writes its words on this same clean paper. */
.view-map .stage:not(.is-ready) #ink, .view-map .stage:not(.is-ready) .corner-key{visibility:hidden}
.view-map .paper svg{position:absolute;inset:0;width:100%;height:100%;display:block}
/* the drawn map fades out as the real-map (or feed) sheet fades in,
   over the same parchment — see openGmap/closeGmap */
/* ⛔ overflow:visible IS LOAD-BEARING AND MUST STAY. An SVG root
   clips to its own viewport by default, and that clip is SEPARATE
   from clip-path — a negative inset cannot undo it, which is exactly
   why the first two attempts at "stop cutting the top off" measured
   perfect and still showed him a sliced house. The sides and the foot
   are clipped by the clip-path layout() writes; the top is open as
   far as the paper goes. */
.view-map #ink{overflow:visible}
.view-map #ink, .view-map .title-layer{transition:opacity .25s}

/* A CHECKPOINT NOBODY HAS BEEN TO YET IS DRAWN IN PENCIL (owner,
   25 Aug: "at the beginning where we add our own checkpoint (which
   will be updated as the user posts).. i want those at the beginning
   to show that they're temporary and might change.. before we had
   that,, like they were faded or something, but now they look
   permanent"). He had seen it — on the CREATE screen's preview,
   which has worn these three rules since the day it was built
   (app.css, .view-create #ink .provisional). The map page never had
   them, so a guess and a fact were drawn identically the moment a
   trip left the create screen. The MARKUP was always right: the
   cartographer marks the guessed dot, its leader tick and its name
   `provisional` (trip/render/map.js) and has since 21 Aug — this is
   only the ink for it, and it is HIS OWN NUMBERS, copied from the
   preview he remembers rather than picked again.
   ⚠️ THE SAME THREE RULES LIVE IN TWO STYLESHEETS. app.css cannot
   reach this document (the map page is its own, and the tokens are
   already copied the same way — 07's pile). One copy the day the map
   joins the shell; until then, changing one means changing both. */
.view-map #ink .provisional{opacity:.42}
.view-map #ink .provisional circle{fill:none;stroke:var(--dot);stroke-width:1.2}
.view-map #ink line.provisional{stroke-dasharray:3 3}
/* the crew's faces (23 Aug): a cream circle, the picture clipped round
   or the initial in the reading face; the owner's ring in the road's
   green; never on the detour page, which has its own map */
.view-map #crew .crew-face{cursor:pointer}
.view-map #crew .crew-face text{font-family:var(--read);font-weight:600}
.view-map #crew .crew-face .crew-name{font-family:inherit;font-weight:400}   /* the towns' own hand */
.view-map .stage.is-detour #crew{display:none}

/* The only thing that appears if the trip never arrives — see boot()'s
   catch at the foot of the page. The paper, the torn edges and the title
   are all painted before any script runs, so a failure still looks like
   this app rather than like a broken tab. */
/* ⭐ AND NOTHING ELSE PRETENDING TO BE A MAP (23 Aug). A boot that dies
   part way has usually drawn something, and the furniture around it —
   the pager band, the clock — reads as a working map with a broken
   middle. This takes them off, so a failure is the paper, the line and
   the reason. ⚠️ It was written by the old page's classic script into a
   <style> of its own; MODULE 5 of the fold moved it here, where the rest
   of the screen's dress is, because the screen has no classic script
   under it any more. */
.view-map .stage.boot-broke .pagerwrap,
.view-map .stage.boot-broke #here-now{display:none}
.view-map .boot-failed{
  position:absolute;left:0;right:0;top:44%;margin:0;padding:0 28px;
  text-align:center;font-size:19px;line-height:1.5;color:var(--ink-faint);
  transform:rotate(-1deg);
}
/* the reason, under it — the phone's reading face, small, so the words
   the browser or the road actually said can be read off a screenshot */
.view-map .boot-failed .boot-why{
  display:inline-block;margin-top:6px;font-family:var(--read);font-size:12px;
  line-height:1.35;color:var(--ink-faint);opacity:.85;overflow-wrap:anywhere;
}

/* ---------- the title's face ----------
   The map's handwriting face is right for the LABELS — a traveller's own
   pen — but wrong for the title, which on a real map is lettered, not
   scribbled.

   Settled over three rounds on the owner's own phone (Aug 4): eleven faces,
   then seven treatments of the winner. American Typewriter, bold, pressed
   into the paper. It ships on iOS so nothing downloads on a bad connection;
   Courier New is the fallback everywhere else. One line, one size, one
   colour — see the title's note in the markup for why. */
/* THE TRIP'S NAME. Two passes on 21 Aug and the second is the one
   that matters.

   FIRST PASS took him literally — half the size, the map's own ink —
   and landed exactly where literal gets you: "you used the exact
   font, font size and color as the locations on the map so we can't
   tell the title apart .. it should be part of the map, but it still
   should be obvious that it's the title of this road trip and not
   another location". It also sat top-LEFT, right over the house,
   "so it feels more like it's a location and not the title".

   SO IT IS NOW ITS OWN VOICE, and differs from a town's name on all
   four counts he named:
     WHERE   top centre, over the paper's own middle, which is where a
             chart's title goes and where no stop is ever placed
             (the road runs between ROAD_TOP 104 and ROAD_BOTTOM 726).
     FONT    Papyrus — the one face on a phone that IS an old map. It
             ships on iOS and on Windows, so nothing downloads on a
             roadside connection, which is the same reason the rest of
             this page uses only faces that are already there.
     SIZE    20% over the towns' 16, his number.
     INK     --ink-soft, a deeper shade of the same brown the towns are
             written in: "a different color (or same color different
             shade at least)". Same family, plainly darker.

   Not bold: Papyrus has no bold on a phone, and asking for one gets a
   smeared synthetic weight rather than a heavier face. The pressed-
   into-paper edge does the standing-out instead. */
.view-map .title{
  font-family:Papyrus,Trattatello,Luminari,"Marker Felt",fantasy,cursive;
  font-size:19.2px;font-weight:400;
}

/* THE CLOCK ON THE PAPER (28 Aug), lettered the way he drew it in
   LOCAL TIME.png: two lines, CAPITALS, the label over the hour.

   ⚠️ EVERY PART OF THIS IS HIS BRIEF, so read it before changing one:
   "update the font and color and capitalization to what makes sense
   and is different from other texts in the map but still looks like
   it's part of the map and darker color so no blue".

     DIFFERENT     a typewriter face. The map's other words are two
                   HANDS — the towns' Marker Felt and the title's
                   Papyrus — so anything handwritten would read as one
                   of them. Typed capitals read as the cartographer's
                   own stamp instead, which is a thing maps really do.
     PART OF IT    the map's own darkest ink on the map's own paper.
     DARKER        --ink #2B2A26, the deepest this page has, against
                   the towns' lighter --ink-label. And nothing blue:
                   --mark is the plate's and the annotations' colour
                   and he ruled it out here by name.
     ⚠️ NOT CAPITALS. His screenshot was in them and he ruled
                   against them once he saw them on the paper: "not
                   sure if all caps is the right way.. fix that". So
                   it is sentence case — "Local time" over "6.05am" —
                   still tracked apart the way lettering on a chart
                   is, which is what keeps a typed face from reading
                   as a user interface. */
.view-map .map-clock{
  font-family:"American Typewriter","Courier New",monospace;
  fill:var(--ink);letter-spacing:.06em;
}
.view-map .map-clock .mc-label{font-size:11.6px;opacity:.72}
.view-map .map-clock .mc-time{font-size:16.5px;opacity:.9}

/* ---------- map life ---------- */
/* The dashes march exactly one dash cycle per loop, or the trail visibly
   jumps each time the animation repeats. That cycle is a pixel distance
   now — the dash pattern is sized by the map's scale — so the keyframe is
   written by setMarchCycle() rather than fixed here. */
.view-map .march{animation:march 1.1s linear infinite}
.view-map .bob{animation:bob 1.6s ease-in-out infinite alternate}

.view-map .puff{opacity:0;animation:puff 2.4s ease-out infinite}
.view-map .puff2{animation-delay:1.2s}

/* 🅿️ THE CAR RADIO's rules sat here until 21 Aug 2026 and are now in
   parked/radio/radio.css — the flying notes, the player card and its
   dark. Parked whole rather than deleted: it worked, and Spotify's
   five-account cap is why it cannot ship to strangers, not anything
   about the feature. parked/radio/README.md says how it comes back. */

/* Animations deliberately ignore Reduce Motion — the owner runs it on and
   still wants these. Do not add a prefers-reduced-motion rule. */

/* ---------- pager ----------
   One row at the bottom of every trip page, holding the trip's pages
   by name. It costs 44px of height, which the map would otherwise
   have, and it buys the app its only navigation.

   It used to hold two things that cross-faded: four unlabelled dots,
   and a line of violet text asking the reader to SWIPE. Both went on
   21 Aug when the owner asked for buttons instead; the buttons
   themselves left one by one and the row's machinery went whole on
   24 Aug (4.4 — the 🅿️ markers below tell the story). What stands in
   the band today is the detour page's way back, and the bottom bar
   holds the everyday foot. */
/* ---------- the live feed sheet ----------
   Sized by the same variables that size the paper, so it covers exactly
   the map and leaves the pager row alive underneath — the dots are the
   way back. Fades like the views fade; the map never moves under it. */
.view-map #feed{
  /* FULL-BLEED, not inset by --edge (owner, Aug 6): the sheet used to
     leave the body's paper showing at the sides, and two ruled papers
     on different rhythms never lined up — "the lines on the front
     notebook don't match the lines on the background". One paper now;
     the old inset lives on as padding so the content never moved. */
  position:fixed;left:0;right:0;top:var(--top);
  max-width:var(--stage-max);margin:0 auto;        /* the stage's column on a wide screen */
  height:var(--sheet-h);          /* the stage's one sheet height — see .stage */
  z-index:5;display:flex;flex-direction:column;gap:10px;
  /* content rides HIGH (owner, Aug 6): the emptied bottom band is where
     a page-swipe can land without catching the scrub rail */
  padding:10px calc(8px + var(--edge)) 8px;overflow-y:auto;
  background:var(--page);
  background-image:repeating-linear-gradient(
    to bottom, transparent 0 27px, var(--rule) 27px 28px);
  /* phase-locked to the body's rows: the strip above and the pager
     strip below keep the same rhythm across the seams */
  background-position:0 calc(var(--top) * -1);
  opacity:0;visibility:hidden;transition:opacity .25s;
}
.view-map #feed.is-open{opacity:1;visibility:visible}
.view-map .feed-head{font-size:23px;text-align:center}
.view-map .feed-sub{
  font-family:"American Typewriter","Courier New",monospace;
  font-size:12.5px;color:var(--ink-label);text-align:center;
}
/* Full width, image centred inside: the frame must never take its
   width FROM the image while the image caps its width BY the frame —
   that circle collapsed to a 0x0 print on the dev machine. */
/* the moments' own polaroid proportions: 13px of cream on top and
   sides (--frame in the viewer), the caption lip below (owner, Aug 6 —
   the 8px top read as a thin picture frame, not a polaroid) */
.view-map .feed-frame{
  background:var(--cart);border-radius:2px;padding:13px 13px 4px;
  box-shadow:0 3px 10px rgba(60,48,20,.25);
  position:relative;
  /* centred when the print room narrows it to a portrait frame's shape;
     a no-op while the width is auto */
  margin:0 auto;
}
.view-map #feed-still{
  display:block;margin:0 auto;max-width:100%;width:auto;height:auto;
  border-radius:2px;
  max-height:max(180px,
    calc((var(--vh) - var(--top) - var(--pager) - var(--bar) - 210px) * .8));
}
/* both limits at once, so the film fits the sheet on ANY shape: a
   phone's narrow paper caps the width, a desktop's tall film caps at
   what is left of the fold (owner, Aug 6 — it scrolled on the PC) */
/* the print's own caption lip — the moments' fonts exactly: the
   handwriting face the page already wears, at the viewer caption size */
/* 20.8 — the caption's +30% (owner, Aug 6 eve, same ask as the
   viewer lip's). min-height rides the em, so the lip breathes with
   the size on its own. */
.view-map .feed-cap{
  font-size:20.8px;color:var(--ink-soft);text-align:center;
  padding:10px 4px 2px;min-height:1.2em;
  /* one line each, like the viewer's lip, and TRIMMED when too long —
     his 27 Aug law, which this page now follows for free because the
     two lines that used to be resized here are not resized any more.
     ⚠️ THEY WERE NOT RESIZED FOR THREE DAYS BEFORE THAT EITHER, AND
     NOBODY KNEW: the feed print's two caption writers called fitCapLine
     long after 24 Aug's split had carried that function out of this
     page under another name, so every frame threw a ReferenceError
     and the caption stopped where it stood. It went unseen because
     the road feed opens for one account. check-lint does not read
     this page's inline script — that is the hole it fell through,
     and tools/check-caption.mjs now watches this particular one. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* the second line (owner, Aug 9): WHERE, under the when — quieter,
   the viewer lip's own big-over-small rhythm upside down, because
   this page's identity is the clock */
.view-map .feed-place{font-size:16.9px;padding:0 4px 2px}
/* the scrub bar: a video's rail with no video — drag it and the frames
   swap HARD underneath, never a fade (owner, Aug 6: a video the reader
   drives). The arrows step one frame. */
.view-map .feed-scrub{display:flex;align-items:center;gap:26px;padding:0 8px}
.view-map .feed-scrub button{
  appearance:none;border:none;cursor:pointer;flex:none;
  width:40px;height:40px;border-radius:50%;
  background:var(--cart);color:var(--ink-soft);
  font-family:inherit;font-size:24px;line-height:1;
  display:grid;place-items:center;padding:0 0 2px;
  box-shadow:0 2px 8px rgba(60,48,20,.3);
}
.view-map .feed-scrub button:active{transform:scale(.92)}
.view-map .feed-track{
  flex:1 1 auto;height:30px;display:flex;align-items:center;
  cursor:pointer;touch-action:none;position:relative;
}
/* the line is the ANNOTATION violet, not the route red (owner, Aug 6):
   violet is the app's you-can-touch-this ink — the song name, the ▶
   badge — where red belongs to the road */
.view-map .feed-track span{
  display:block;width:100%;height:6px;border-radius:4px;
  background:rgba(120,100,60,.25);overflow:hidden;
}
.view-map .feed-track i{
  display:block;height:100%;width:0;background:var(--mark);
  border-radius:4px;
}
/* the knob that makes the line a SCRUBBER (owner, Aug 6): a cream
   circle in the ‹ › buttons' own dress, riding the fill's end. It
   ANSWERS touches: at the newest frame — where every reader starts —
   it sits half OUTSIDE the rail's box, and as pass-through it left a
   finger on dead paper ("tapping on the handle and trying to swipe
   didn't work", owner, Aug 6). A touch on it bubbles to the track's
   own handler, so grabbing the knob IS grabbing the rail; the halo
   below gives the finger more than the drawn 18px. */
.view-map .feed-track b{
  position:absolute;top:50%;left:0;width:18px;height:18px;
  border-radius:50%;background:var(--cart);
  box-shadow:0 2px 8px rgba(60,48,20,.3);
  transform:translate(-50%,-50%);
}
.view-map .feed-track b::after{content:'';position:absolute;inset:-9px}
/* The first film breathes in (owner, Aug 6 eve): until a frame has
   truly decoded, the print and the rail are held invisible — an
   empty print used to sit squashed, then leap to full height the
   moment the picture landed. The growing now happens unseen and the
   reveal is one soft fade. Frame-to-frame swaps stay HARD (the
   scrub law) — only the first arrival fades. */
.view-map .feed-frame, .view-map .feed-scrub{opacity:0;transition:opacity .35s}
.view-map #feed.has-film .feed-frame, .view-map #feed.has-film .feed-scrub{opacity:1}
/* 🅿️ THE UPLOAD SHEET'S DRESS stood here — #upload cut from the
   same cloth as the real map's sheet, the two doors (camera KING,
   Aug 6), and the .job rows of what the phone was sending. The sheet
   was deleted whole on 24 Aug (D5 — home's live pill carries the
   doors; its own scoped .job dress lives in app/app.css). */

.view-map #gmap{
  /* full-bleed + phase-locked lines, the feed sheet's fix exactly —
     see #feed above for the why (owner, Aug 6) */
  position:fixed;left:0;right:0;top:var(--top);
  max-width:var(--stage-max);margin:0 auto;        /* the stage's column on a wide screen */
  height:var(--sheet-h);          /* the stage's one sheet height — see .stage */
  z-index:5;display:flex;flex-direction:column;gap:8px;
  /* 46px of top air is the band the "On the map" head used to hold —
     the words went (owner, 24 Aug: "remove that text. no need") but
     the room stays, so the canvas's feathered top edge still fades in
     under the logo instead of putting live tiles behind it */
  padding:46px calc(8px + var(--edge)) 8px;
  /* THE SAME ONE SHEET, not a second one (owner, 21 Aug: "just one
     torn paper, same as the hand drawn map and moments page"). This
     has NO paper of its own — it is transparent, so the single
     parchment of .sheet-wrap shows through, exactly the paper the
     journey and moments are drawn on. The drawn map that lives on
     that paper is faded out by openGmap() while this is up, so it
     does not read through; the Google map (feathered below) is all
     that is added to the page. An earlier try gave this its OWN
     parchment and it landed a second, differently-sized torn sheet
     over the first — the exact thing to avoid. */
  background:transparent;
  opacity:0;visibility:hidden;transition:opacity .25s;
}
.view-map #gmap.is-open{opacity:1;visibility:visible}
.view-map #gmap-canvas{
  /* GROWS TO FILL THE PARCHMENT (owner, 21 Aug: "make the real map
     vertically taller to cover that empty space under it"). It used
     to borrow the feed print's short height formula (.8 of the fold,
     Aug 6, "the two sheet pages should frame alike") which left a
     band of empty paper below the miles bar. Now it takes all the
     room between the header and the miles bar; the feed keeps its
     own framed look, they no longer match on purpose. */
  flex:1 1 auto;min-height:180px;
  overflow:hidden;background:transparent;
  /* SOFT EDGES, BLENDED INTO THE PAPER (owner, 21 Aug: not "hard edges
     on all 4 sides and a perfect rect" — the edges "smoothly blend
     into the paper"). Two gradients, one per axis, intersected: each
     fades the last 30px to transparent, so all four sides AND the
     corners dissolve into the parchment instead of ending on a line.
     No border, no radius — the feather is the whole edge now.
     ⚠️ This also softens Google's corner logo/attribution; kept a
     gentle 30px so both stay legible. */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%);
  -webkit-mask-composite:source-in;
  mask-image:
    linear-gradient(to right, transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%);
  mask-composite:intersect;
}
/* Google's first paint breathes in the same way (see has-film
   above): before tiles land the canvas is transparent, so the
   parchment shows through where the map will be, and the map fades
   over it once tiles have really landed — never a tile-by-tile pop.
   (Until 21 Aug the placeholder was a bordered sage box; it went with
   the border when the map's edges were feathered into the paper.) */
.view-map #gmap-canvas>div{opacity:0;transition:opacity .4s}
.view-map #gmap.has-map #gmap-canvas>div{opacity:1}
/* the miles bar: the scrub rail's breed, but NOT a control — the road
   fills it, nobody drags it. Route red on purpose: red is the road's
   own ink; violet stays with things a finger can touch. */
/* 14 up top: at 2 the label kissed the map's bottom edge (owner, Aug 6) */
.view-map .gmap-progress{padding:14px 6px 0}
/* the caption hand, feed-cap's size — the typewriter at 12.5 was
   "barely readable... bad" (owner, Aug 6) */
.view-map .gmap-miles{
  font-size:16.5px;color:var(--ink-soft);text-align:center;
  margin-bottom:8px;
}
.view-map .gmap-bar{
  position:relative;height:6px;border-radius:4px;
  background:rgba(120,100,60,.25);
}
.view-map .gmap-bar i{
  display:block;height:100%;width:0;background:var(--red);
  border-radius:4px;
}
/* the little arrow under the rail, aimed up at where the car is */
.view-map .gmap-bar b{
  position:absolute;top:9px;left:0;transform:translateX(-50%);
  width:0;height:0;
  border-left:5px solid transparent;
  border-right:5px solid transparent;
  border-bottom:7px solid var(--red);
}

/* the frame's talk button — the viewer's own, re-scoped for the feed */
.view-map .feed-talk{
  position:absolute;right:20px;bottom:46px;z-index:4;
  display:flex;align-items:center;gap:6px;
  height:34px;min-width:34px;padding:0 11px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);
  font-size:14px;line-height:1;border:none;cursor:pointer;
  font-family:inherit;
}
.view-map #feed.is-talking .feed-talk{pointer-events:none}
/* the viewer's anatomy, copied faithfully (owner: "just copy that"):
   a fixed full-screen wrap that ignores touches while closed, and the
   sheet ABSOLUTE inside it, footed by fitFeedSheet()'s keyboard maths */
.view-map #feed-talk-wrap{
  position:fixed;inset:0;z-index:40;overflow:hidden;pointer-events:none;
}
.view-map #feed-talk-wrap.is-talking{pointer-events:auto}
.view-map #feed-sheet{
  position:absolute;left:var(--edge);right:var(--edge);bottom:0;
  max-width:calc(var(--stage-max) - 2 * var(--edge));margin:0 auto;   /* the column, on a wide screen */
}
.view-map #feed-talk-wrap.is-talking #feed-sheet{transform:translateY(0)}

.view-map .feed-days{display:flex;flex-wrap:wrap;gap:7px;justify-content:center}
.view-map .feed-days button{
  appearance:none;border:1.5px solid var(--ink-faint);border-radius:9px;
  background:var(--cart);color:var(--ink-label);cursor:pointer;
  font-family:"American Typewriter","Courier New",monospace;
  font-size:12px;padding:6px 10px;
}
.view-map .feed-days button.now{border-color:var(--red);color:var(--red)}

/* THE BUTTON BAND, on the parchment at the foot of the one sheet. It
   is a flex child of .sheet-wrap now, under .paper, so it rides the
   same piece of paper the map is drawn on. The safe-area gap moved to
   .stage's own padding, so the whole sheet ends above the home
   indicator and this band is never under it. */
.view-map .pagerwrap{
  position:relative;width:100%;height:var(--pager);flex:none;
  /* the band sits 21px up off the sheet's foot (owner tuned it in the
     ?edit room before that room was removed: band-lift=21). It lifts
     the plaques AND the inked rule above them together, because both
     ride this box. */
  transform:translateY(-21px);
}
/* ---------- THE PAGER: FOUR WORDS, NOT FOUR DOTS ----------
   Owner, 21 Aug: "instead of swiping, we have 4 buttons at the
   bottom, so user clicks on each to see the other ones."

   ⚠️ AND THE SWIPE IS GONE, which is the half that matters. The
   four dots were always tappable and nobody ever knew — they were
   9px circles with no words on them, and the row carried a line of
   violet text ASKING for a swipe instead. A control nobody can read
   is not a control. Now every page names itself, the page you are on
   is the bright one with a pencil line under it, and nothing on this
   app is reached by a gesture a reader has to be taught.

   (The row used to hold back until an opening animation had landed.
   That opening was removed on 21 Aug and the rule that replaced it
   is in docs/app/05_DECISIONS.md: nothing is gated on an animation.
   The row is simply there, from the first paint.) */
/* ⚠️ THE ROW IS EMPTY as of 23 Aug — every plaque that stood in it has
   gone to a better seat (the moments into the map itself, the upload
   doors to home's live pill, the real map and the road feed to the two
   square keys in the map's own corner). The box stays because the
   detour page's way back still stands in it, and only then.

   🅿️ A HAND-INKED RULE was drawn along its top, dividing the map from
   its legend the way a real chart fences off its scale box — a
   gradient, so it faded at both ends rather than hitting the torn
   edges with a hard stop. With no legend under it, it was a line
   under nothing. */
/* 🅿️ .pager stood here — the row's own box, absolute in the wrap.
   The empty div and its rules went together on 24 Aug (4.4): an
   invisible box over .det-back would swallow the detour's back taps,
   so the div could never outlive its display:none partner below. */
/* 🅿️ THE HINT WAS HERE and it is gone with the swipe (21 Aug).
   It read "swipe to see the moments →" in 25.9px violet with a
   beckoning arrow, and it earned every one of its three sizings
   (14 -> 18 -> 21.6 -> 25.9) because testers kept walking past it.
   A row of named buttons answers that problem outright, so the words
   asking for a gesture that no longer exists went with it.

   The note it left behind, worth keeping: THE ANNOTATION VIOLET — the you-can-touch-this ink
   (the law since Aug 6) — bigger, with a beckoning arrow. The quiet
   14px brown shipped and testers walked right past it (owner, Aug 8):
   nobody realized the pages swipe. 18 -> 21.6 the same day, his
   "twenty percent bigger" — the pager row holds it fine.
   21.6 -> 25.9 on Aug 13, the SAME ask a second time, and lifted
   10px off its centred seat in the row ("move it a little bit
   higher"). The lift is a transform rather than a change to the
   row, so the pager dots that share this seat do not move with it —
   he asked for the words, not the row. --pager is 40px and the line
   is ~31px at this size, so the text still sits inside its own band
   even after the lift. The buttons below wear the same violet for
   the same reason. */
/* THE WAY BACK from the detour page (owner, Aug 12: "when you press back,
   it brings back the main map"). It takes the pager row's own place while
   the detour is up — the row is where a reader's thumb already is — and
   wears the violet, like every other thing in this app that answers a
   press. The phone's own back gesture closes the page too (a history
   entry, see openDetour), so this is the visible half of one promise. */
/* It takes the hint's own treatment on Aug 13, by his instruction —
   "same place, same color, do the same to that one". Same 25.9 and
   the same 10px lift, because these two words stand in exactly the
   same seat and a reader who leaves the detour must not see the
   furniture jump. This one is a real button, so the lift carries its
   tap target up with the text rather than leaving it behind. */
.view-map .det-back{
  position:absolute;inset:0;display:none;align-items:center;
  justify-content:center;gap:8px;
  background:none;border:0;padding:0;font-family:inherit;
  font-size:25.9px;color:var(--mark);cursor:pointer;
  transform:translateY(-10px);
}
.view-map .stage.is-detour .det-back{display:flex}
/* AND THE BAND COMES BACK FOR THE DETOUR PAGE, and only for it. The
   row it was built for emptied on 23 Aug and the band went to 0 with
   it, but this page's way back still stands in it — and it must stand
   ABOVE the bottom bar rather than under it. 58 is the height it has
   had since the plaques were drawn (owner, 21 Aug). */
.view-map .stage.is-detour{--pager:58px}
/* A TRIP WITH NO DETOUR SHOWS NEITHER THE DOOR NOR THE ROOM (Aug 21).
   The markup below is the owner's own rally — his authored map draws
   its own side road, the way it draws its own gator — so it stays
   written where it is drawn and is simply never shown to anybody
   else's trip. The page's code asks the same question (see DETOUR),
   so nothing behind these two is built either. */
.view-map .stage.no-detour #detour-flag, .view-map .stage.no-detour #detour{display:none}
/* ...and the ghost road with them (23 Aug): the shortcut he turned
   away from is part of the SEQUOIA detour's story, hand-placed in the
   markup, and it was quietly showing on every other trip's map too —
   one trip's business on the shared paper. A trip with no detour has
   no road it didn't take. */
.view-map .stage.no-detour #road-skipped{display:none}
/* AND A TRIP WITH NO ROAD FEED SHOWS NO DOT FOR ONE (21 Aug). The
   dash-cam page needs a camera on a windscreen, so it belongs to
   the one trip that has the rig and to the person whose trip it
   is — see roadFeed in trip/model.js. The page starts SHUT and is
   opened only by an answer that really came back and really said
   "yours"; a read that failed leaves it shut, because a missing
   page is a far smaller wrong than a stranger's windscreen. */
/* (The rule that hides it is with the corner keys, where the button
   now lives — see .stage.no-feed #feed-switch.) */
/* 🅿️ .stage.no-upload hid the upload plaque from anybody but the trip's
   owner (21 Aug: "shows up only when the user is watching their own
   trips"). The plaque went on 23 Aug, and the sheet it opened — with
   the class's last togglers — on 24 Aug (D5): nothing writes or
   reads no-upload any more. */
/* 🅿️ THE PAGES AS LITTLE INKED SIGNS stood here — .tab, its stamped
   you-are-here state, its press, and the five-plaque squeeze (owner,
   21 Aug: "make them look like part of the map"). The row emptied on
   23 Aug and no element wore the class anywhere; the rules went with
   the machinery on 24 Aug (4.4). THE LOOK LIVES ON: the corner keys
   (.corner-key below) wear the same inked-plaque dress, and the
   considered exception to the violet law — a plaque on parchment is
   brown ink, never a sticker — travelled there with it. */

/* ============ THE LAYER SWITCH (owner, 23 Aug) ============
   See the markup for why it exists and why it does not look like the
   control he pointed at. The frame is #gmap's own geometry, so this
   lies over the real map exactly as it lies over the drawn one; it
   takes no pointer of its own, only the button inside it does, or it
   would swallow every tap meant for the paper. */
.view-map #layers{
  position:fixed;left:0;right:0;top:var(--top);
  max-width:var(--stage-max);margin:0 auto;
  height:var(--sheet-h);          /* the stage's one sheet height — see .stage */
  z-index:7;pointer-events:none;
}
/* The pager's plaque, wearing its own weight: this one is not one of a
   row, so it carries the inked fill a stamped plaque has rather than
   the open outline of a waiting one — on open paper an outline alone
   reads as a label, and this is a thing you press. */
/* THE CORNER KEYS: the layer switch and, under it, the road feed. Two
   squares in a column in the top corner the road leaves empty. */
.view-map .corner-key{
  position:absolute;                /* seated by seatLayers(), in the map's own units */
  pointer-events:auto;
  -webkit-appearance:none;appearance:none;cursor:pointer;
  font-family:inherit;font-size:14px;line-height:1.15;
  color:var(--ink);
  background:rgba(255,248,228,.86);
  border:1.5px solid rgba(94,63,38,.75);border-radius:6px;
  box-shadow:inset 0 0 0 1px rgba(94,63,38,.28), 0 1px 3px rgba(43,42,38,.18);
  /* SQUARE, his ask of 23 Aug ("make it square", then "make the road
     feed button square"), and the shape the control he pointed at has.
     64 is comfortably over the 44px floor a lone button on open paper
     has to keep for a thumb, and it holds "real map" and "road feed"
     on two lines and "journey" on one without the box ever changing. */
  width:64px;height:64px;padding:4px;
  display:flex;align-items:center;justify-content:center;text-align:center;
  transition:background .18s,border-color .18s;
}
.view-map .corner-key:active{transform:translateY(.5px)}
/* THE STAMPED, YOU-ARE-HERE KEY — the pager's own treatment, kept: an
   inked fill, a second ring, darker letters. It is what says the road
   feed is up and that pressing this again is the way back off it. */
.view-map .corner-key[aria-current="true"]{
  background:rgba(94,63,38,.16);
  border-color:rgba(94,63,38,.9);
  box-shadow:inset 0 0 0 1px rgba(94,63,38,.35);
}
/* nothing to switch to on a demo or a remembered trip, and nothing to
   switch it over while another sheet is up — paint() says which */
.view-map #layers.away{display:none}
/* AND THE ROAD FEED IS THE TRIP OWNER'S ALONE (21 Aug: "shows up only
   when the user is watching their own trips"). Same start, same door:
   SHUT until an answer comes back and names this reader as the owner —
   see askWhoseTrip(), which answers for this in one breath. */
.view-map .stage.no-feed #feed-switch{display:none}
/* (The inked rule that divided the map from that row went with the
   last plaque in it — see .pager above.) */

/* ---------- THE WAY OUT ----------
   (owner, Aug 21: "i don't have any button to get back home when i
   click on that trip".) A trip used to BE the app, so there was
   nowhere to go back to; now a person has a home with their trips on
   it and this map needs a door out of it. Named in the plan as the
   "exit trip" control (05_DECISIONS.md).

   ⚠️ OUTSIDE THE STAGE: the map takes the pointer the moment a
   finger lands on it, so anything laid over the paper INSIDE the stage
   never hears its own tap. This is a sibling of the stage, fixed over
   the top-left.

   Top-left is where a way back belongs, and it is the one corner both
   views leave empty — the journey's title and the moments' both start
   well clear of it. Violet, like every other thing in this app that
   answers a press.

   It holds back with the pager row through the opening, and the
   detour page hides it outright: that page has its own way back, and
   two back controls in one room is one too many. */
.view-map .go-home{
  position:fixed;z-index:40;
  /* at the page's edge on a phone; at the COLUMN's edge on a wide screen */
  left:max(calc(var(--edge) + 2px), calc(50% - var(--stage-max) / 2 + var(--edge) + 2px));
  top:calc(env(safe-area-inset-top) + 4px);
  padding:6px 11px;text-decoration:none;
  font-family:inherit;font-size:19px;line-height:1;color:var(--mark);
  -webkit-tap-highlight-color:transparent;
  transition:opacity .5s;
}
.view-map .go-home:active{opacity:.55}
.view-map .stage.is-detour ~ .go-home{display:none}

/* 🅿️ THE SHARE BUTTON'S LOOK STOOD HERE from 21 Aug: fixed over the
   top-RIGHT, the mirror of the back link, violet like everything that
   answers a press, with the share glyph every phone already knows AND
   the word beside it, because a control with no words on it is not a
   control. The button moved to the trip's own card on home on 23 Aug
   (owner: "this share button should be moved to the trip pill") and
   its rules went with it. share/link.js, which does the actual
   sharing, never moved and never changed. */

/* 🅿️ THE EDIT ROOM (?edit=1) was removed on 21 Aug once its last
   tool had done its job. It held, in turn: the doodle/compass drag
   handles (retired Aug 5), the treasure-X strength/size sliders and
   their "export spots" button (Aug 5, baked and gone), and the map
   tuner — four steppers and a copy button that sized the town labels
   and lifted the button band, which the owner used to pick
   label-size=15.3 and band-lift=21 before removing the whole room.
   All of it is in git history; the baked values live in the trip
   file, generated.js and .pagerwrap above. */

/* ---------- the big view ----------
   A polaroid lying on the dark, with room around it on every side.

   It filled the screen until Aug 5, and the owner was right about that: the
   only way out was a close button sitting on a photo, which is exactly where
   a close button cannot be seen. The dark margin IS the way out now — a tap
   anywhere off the print closes it — and the print reads as a print.

   Plain HTML rather than anything inside the SVG: a <video> has to be a real
   element to play on iOS, and shadows and rounded corners are ordinary CSS
   here while inside SVG they would be a filter, which has been caught doing
   nothing at all on the owner's phone (docs/app/10_PITFALLS.md, THE MAP
   AND SVG: never recolour art with a CSS filter).

   It sits OUTSIDE .stage, so a swipe in here never reaches the map.

   --frame is the polaroid's border, and layoutCard() reads this very property
   when it sizes the print, so the number lives in ONE place. */
/* ⚠️ AND THE BOTTOM BAR GOES AWAY WHILE A PHOTOGRAPH IS OPEN (23 Aug,
   the day the map page grew one). The bar is fixed at z-index 20 and
   this dark is 10, so without this it would be drawn ON TOP of the
   photograph — four places inked over somebody's picture. Reading a
   photograph is a one-way job with its own way out, which is the same
   reason the bar stays off the door and off Create (see bar.js). The
   bar is a later sibling of this element, which is what makes one
   line of CSS enough. */
.view-map .viewer.is-open ~ #bar{display:none}
.view-map .viewer{
  --frame:13px;
  position:fixed;inset:0;z-index:10;display:none;overflow:hidden;
  align-items:center;justify-content:center;
  background:rgba(38,34,24,.88);
}
.view-map .viewer.is-open{display:flex}

/* (No close button. There was an X in the dark until Aug 5 — the owner
   removed it: tapping anywhere off the print already closes, and Escape
   still works for a keyboard.) */

/* ---------------------------------------------------------- THE PACK
   The moments viewer is a PACK OF CARDS held in the hand (owner,
   Aug 11, after a sandbox built and blessed at mockups/stack-test/).
   Every card is a whole polaroid — cream, picture, chip, written lip —
   built once and complete from the first moment. Swiping MOVES cards;
   it never loads, swaps or redraws anything.

   WHAT CAME OUT WITH THIS, and must not be reinvented:
     the GHOST FLIGHT (a frozen clone flying while the real card turned
       over underneath) and its five rounds of near-misses;
     shot-img / shot-img-b, the two picture layers that cross-faded;
     .shot-under u1/u2, the fanned cards that were dressing only, and
       the veil that had to be painted over them;
     the modulo wrap that made one spot loop forever.
   None of that is being fixed — it is being DELETED. Every one of
   those parts existed to keep a STAND-IN in sync with the truth, and
   an architecture with no stand-ins cannot have that disease.

   MOTION'S SINGLE SOURCE OF TRUTH: every choreographed duration is
   declared here and only here; viewer/motion.js reads these very
   tokens to arm its safety-net timeouts, so the pair can never drift
   (they used to be twin literals, a CSS curve and a hand-matched ms
   number beside it). */
.view-map .viewer{
  --t-home:.34s;      /* a hesitant drag settling back into the pile */
  --t-arrive:.52s;    /* a fresh pack gliding in on a forward deal */
  --t-carousel:.58s;  /* THE ROPE — both packs of a backward crossing */
  --t-return:.48s;    /* an un-dealt card coming home along its exit */
}

/* The pack. Every card is absolute inside this one box; the box takes
   the pack's width and viewer/layout.js writes its height from the
   tallest card. The height EASES between pack sizes in step with a
   crossing — an instant resize recentred the box and stepped every
   card mid-flight. */
.view-map .shot-stack{
  position:relative;
  /* ⚠️ z-index is not decoration here, exactly as in .shot-media below.
     The pile hands every card a real z-index (and 99 to one in flight),
     and without a stacking context of its own those numbers are counted
     against everything in the viewer — including the comments sheet at
     3, which the top card then covered (owner, Aug 11: "the comments
     open under the slides"). One line, and the pack's whole ladder is
     contained beneath the sheet. */
  z-index:1;
  opacity:0;
  transition:opacity .25s ease,
             height var(--t-arrive) cubic-bezier(.22,.7,.25,1);
}
.view-map .shot-stack.is-ready{opacity:1}

/* One polaroid. Deeper at the bottom than on the other three sides,
   the way a polaroid is — that lip is where the caption is written.
   Its resting pose (the hand-stacked tilt) lives on --rest*, so a drag
   can come home to it and a card keeps its own lean wherever it sits
   in the pile. */
.view-map .shot{
  position:absolute;left:0;top:0;
  background:var(--cart);border-radius:2px;
  box-shadow:0 20px 46px rgba(0,0,0,.5);
  padding:var(--frame) var(--frame) 0;
  display:flex;flex-direction:column;
  transform:translate(var(--rest-x,0), var(--rest-y,0)) rotate(var(--rest-r,0));
  will-change:transform;
}

/* Motion states. Dragging is 1:1 with the finger — no easing while a
   finger is down, ever. The flight's pace and curve are written by JS
   per flick (--fly-t / --fly-ease): the card must LEAVE at the speed
   the finger gave it. The lifted shadow stays for the whole flight —
   the card is in the air. */
.view-map .shot.is-drag, .view-map .shot.is-fly{box-shadow:0 26px 54px rgba(0,0,0,.55)}
.view-map .shot.is-drag{transition:none}
.view-map .shot.is-fly{transition:transform var(--fly-t,.6s) var(--fly-ease,linear)}
.view-map .shot.is-home{transition:transform var(--t-home) cubic-bezier(.2,.9,.25,1.18)}
.view-map .shot.is-teleport{transition:none}
/* the arrival: a fresh pack glides in as ONE unit, every card carrying
   the same off-screen offset, the way a hand sets a new pack down. No
   overshoot: a wiggle at rest reads as someone bumping the table. */
.view-map .shot.is-arrive{transition:transform var(--t-arrive) cubic-bezier(.22,.7,.25,1)}
/* THE ROPE (owner, Aug 11: the two packs "kind of kiss"): a backward
   crossing couples BOTH packs to one curve, one duration, one starting
   frame — equal distances on an identical profile keep the gap constant
   by construction, "as if they're connected to a same rope". */
.view-map .shot.is-carousel{transition:transform var(--t-carousel) cubic-bezier(.5,.08,.25,1)}
/* the un-deal: a dismissed card returns along its OWN exit path,
   reversed — same family of ease as an arriving pack */
.view-map .shot.is-return{transition:transform var(--t-return) cubic-bezier(.2,.7,.25,1)}
/* a dismissed pack rests at its poses, invisible, ready for its next
   visit — visibility, never display: the cards stay measurable and
   their pictures stay decoded */
.view-map .shot.is-away{visibility:hidden}

/* The word shown during a real wait. Cream on the dark, in the
   caption's own hand, held in the middle of the room where the print
   is about to be. Two beats of delay before it fades in: a fast open
   must never flash it, or the hint becomes the flicker it was added to
   prevent. pointer-events none — a tap during the wait still closes. */
.view-map .shot-wait{
  position:absolute;left:0;right:0;top:50%;
  transform:translateY(-50%);
  text-align:center;font-size:18px;color:var(--cart);
  opacity:0;pointer-events:none;
  transition:opacity .3s ease;
}
.view-map .viewer.is-waiting .shot-wait{opacity:.72;transition-delay:.45s}

/* THE ONE PLAYER — rides OVER the top card's still, exactly its size.
   object-fit cover: the still and the film share one declared ratio,
   so cover crops nothing and hairline rounding never shows cream. */
.view-map .viewer .player{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;pointer-events:none;background:transparent;
  z-index:1;
}

/* The window the picture sits in. It is cut to the SHOT'S OWN SHAPE, so the
   picture fills it edge to edge and the only cream showing is the frame
   around it — even on three sides, deeper at the bottom. That is what makes
   it read as a polaroid.

   It was one fixed shape for a while, and the owner was right about that
   (Aug 5): a 4:3 upright photo looked like a print and everything else —
   a tall video, a wide photo — sat inside it with lopsided cream bands and
   looked wrong. A real print is the size of its own picture.

   Only the HEIGHT eases, because within a group the width does not change —
   measureGroup() gives every shot at a spot the same one. Height alone reads
   as paper being cut to fit; height and width together read as the print
   jumping about (owner, Aug 5).

   ⚠️ z-index:0 is not decoration. It makes this window a world of its own, so
   the layer numbers below count only against EACH OTHER. Without it they are
   measured against everything in the viewer, and the comment button — above
   the picture, as it must be — was also above the comments sheet, and sat
   there in the middle of the reading (owner's screenshot, Aug 5). Anything
   added inside this window from now on is contained by this one line. */
/* The window the picture sits in. It is cut to the SHOT'S OWN SHAPE, so
   the picture fills it edge to edge and the only cream showing is the
   frame around it — even on three sides, deeper at the bottom. That is
   what makes it read as a polaroid.

   ⚠️ z-index:0 is not decoration. It makes this window a world of its
   own, so the numbers inside count only against EACH OTHER. Without it
   the comment button — above the picture, as it must be — was also above
   the comments sheet, and sat there in the middle of the reading
   (owner's screenshot, Aug 5). Anything added inside this window from
   now on is contained by this one line.

   NO TRANSITION on the height any more: the card is cut to its own
   picture at build and never changes shape again. Easing belonged to
   the old world where ONE window had to morph between shots. */
.view-map .shot-media{
  position:relative;z-index:0;flex:none;background:var(--cart);
  touch-action:none;box-shadow:inset 0 0 0 1px rgba(43,42,38,.14);
  overflow:hidden;
}

/* NOTHING IS CROPPED (owner, Aug 9 night: "the whole image should be
   visible, no part of it being cropped"): the picture's own declared
   ratio shapes the card before a byte arrives, and viewer/layout.js
   then writes the real box in PIXELS. Every card wears ONE picture,
   always — a photo its own, a video its FRAME-ZERO still, cut in the
   page by viewer/frame0.js. The window is the one player's landing pad. */
.view-map .shot-pic{
  display:block;width:100%;
  background:var(--cart);
  pointer-events:none;
}

/* An image and a video each come with gestures of their own — iOS will pick
   a photo up to drag it, and offers to save it on a press. Both of those
   start by taking the touch away from us, which is why swiping on the photo
   used to do nothing while swiping on the caption worked. */
.view-map .shot-media img, .view-map .shot-media video{
  touch-action:none;-webkit-user-drag:none;
  -webkit-touch-callout:none;-webkit-user-select:none;user-select:none;
}

/* 🅿️ .shot-play WAS THE BIG VIEW'S OWN PLAY BADGE — the polaroid's
   circle and triangle at 74px, over the middle of a film that was not
   running. Nothing has worn it for some time: the pack draws its own
   (viewer/cards.js) and the little card draws its own
   (trip/render/thumb.js). Swept 23 Aug, on the day the app went
   looking for rules nobody wears. */

/* WHAT KIND OF THING IS THIS (owner, Aug 9): a video's poster is a
   perfect photo impersonator until playback starts, so every shot
   names its own kind in the picture's top corner — a camera for a
   photo, a film camera + the clip's length for a video. The talk
   badge's dress, mirrored to the opposite corner. NEVER a play
   triangle: these videos drive themselves, and a ▶ here would read
   as a control that must be pressed (owner's words). Information
   only — it takes no taps. */
.view-map .shot-kind{
  position:absolute;right:8px;top:8px;z-index:4;
  display:flex;align-items:center;gap:6px;
  height:34px;min-width:34px;padding:0 8px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);
  font-size:14px;line-height:1;pointer-events:none;
}
/* THE WAY OUT — the kind chip's dress on the opposite shoulder, so the
   two corners read as a pair rather than as furniture dropped on the
   picture. Unlike the chip it DOES take taps: it is the one control on
   the print, and it says the same thing the dark says. */
.view-map .viewer .shot-close{
  position:absolute;left:8px;top:8px;z-index:4;
  display:flex;align-items:center;justify-content:center;
  width:34px;height:34px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);
}
/* with the sheet up, everything outside it means put the sheet away —
   this button included, or a tap there would close the reading and the
   print in one breath */
.view-map .viewer.is-talking .shot-close{pointer-events:none}

.view-map .shot-kind .k-video{display:none}
.view-map .shot-kind.is-video .k-photo{display:none}
.view-map .shot-kind.is-video .k-video{display:block}
.view-map .kind-secs:empty{display:none}

/* Reading what was said, from the picture itself — the owner's own shape for
   it (Aug 5), and the one every phone already knows. It sits IN the picture's
   bottom corner rather than in the lip, where the caption and the shot
   counter are already competing for a narrow line. */
.view-map .viewer .shot-talk{
  position:absolute;right:8px;bottom:8px;z-index:4;
  display:flex;align-items:center;gap:6px;
  height:34px;min-width:34px;padding:0 11px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);
  font-size:14px;line-height:1;
}
.view-map .talk-count:empty{display:none}
/* WHO POSTED IT (owner, 23 Aug): the poster's face and name, the kind
   chip's dress on the picture's bottom-left shoulder — the one corner
   the close ✕, the kind chip and the comments badge leave free. Takes
   no taps (the card's gestures pass through it). A long name is
   clamped by the layout, never cut by a count of letters. */
.view-map .shot-who{
  position:absolute;left:8px;bottom:8px;z-index:4;
  display:flex;align-items:center;gap:7px;
  height:34px;max-width:58%;padding:0 11px 0 4px;border-radius:17px;
  background:rgba(43,42,38,.55);color:var(--cart);
  font-family:var(--read, inherit);font-size:13px;line-height:1;pointer-events:none;
}
/* ⚠️ display:flex above DEFEATS the hidden attribute — an author rule
   always beats the browser's own [hidden]{display:none} — so a shot
   nobody signed (the owner's whole road before accounts: the key
   door stamped no author) wore an EMPTY chip, a bare cream circle on
   a dark pill (owner's screenshot, 24 Aug). The shell met this exact
   trap and keeps a global guard (app.css, its one justified
   !important); this page guards the one element that hides. */
.view-map .shot-who[hidden]{display:none}
.view-map .shot-who .who-face{
  flex:none;width:26px;height:26px;border-radius:50%;
  background:var(--cart) center/cover no-repeat;color:var(--ink);
  display:flex;align-items:center;justify-content:center;
  font-weight:600;font-size:12px;
}
.view-map .shot-who .who-name{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* With the sheet up, the button that opens it is usually underneath it — but
   on a short screen it can still peek out, and a tap there would close the
   sheet and reopen it in the same breath. Everything outside the sheet means
   put it away, this button included. */
.view-map .viewer.is-talking .shot-talk{pointer-events:none}

/* Where the shot was taken, and which shot this is. Written in the print's
   deep bottom lip, the way a caption is.

   Two lines (owner, Aug 5): the place and which day of the trip it was on the
   first, the whole date and the time underneath. The shot counter sits beside
   both rather than joining the queue, so a long date never pushes it away. */
/* ⭐ A COLUMN OF TWO ROWS since 29 Aug, not a row of two columns. The
   counter joins the place's row and the date row runs the whole width
   beneath it — his own re-draw of the lip. The lip's HEIGHT is
   measured by layoutCard (shotCap.offsetHeight) and everything above
   re-cuts itself to whatever it comes to, so this can change shape
   without a single number below caring. */
.view-map .shot-cap{
  flex:none;display:flex;flex-direction:column;
  gap:3px;padding:9px 3px 15px;
  color:var(--ink);touch-action:none;
}
.view-map .shot-top{
  display:flex;align-items:baseline;justify-content:space-between;
  gap:0 10px;min-width:0;
}
/* the second row starts with the day and runs on under the counter —
   the width the old layout was giving away */
.view-map .shot-under{display:flex;align-items:baseline;gap:9px;min-width:0}
/* Both lines are kept to ONE line each, so the lip is always exactly the same
   height. The print changes width from shot to shot, and a place name that
   wrapped on the narrow ones would change how much room the picture has —
   which would change the width again. */
/* every written thing in the lip +30% (owner, Aug 6 eve, with the
   labels' bump): place 17 -> 22.1, day 13 -> 16.9, when 12.5 -> 16.3,
   counter 12 -> 15.6. The lip is MEASURED (layoutCard reads
   shotCap.offsetHeight), so the room and every print re-cut
   themselves to the taller lip on their own — no number below
   depends on these.

   ⚠️ THESE FOUR NUMBERS ARE NOW THE WHOLE TRUTH, AND THIS IS THE ONLY
   PLACE THEY LIVE (owner, 27 Aug). Between Aug 9 and today a line too
   long for its card was SHRUNK to fit rather than ellipsed, so the
   size a caption ended up at fell out of two accidents — the length of
   its town's name and the width of its own print, which follows the
   photograph's shape — and no two piles agreed: five sizes across six
   of his piles, 11.1px to 22.1px, some of them unreadable on a phone.
   His call, shown both shapes with their numbers: "one size
   everywhere, and we go with this size .. the bigger one .. if some
   cards are more narrow and some of it doesn't fit, we trim and add
   three dots". So the ellipsis below is not a net under a failure any
   more — it IS the answer for a name too long for its card, and
   nothing anywhere changes these sizes at runtime. ⚠️ Do not bring
   the shrinking back; it was removed on purpose, whole, and
   viewer/caption-fit.js is in git history if the reasoning is ever
   wanted. */
/* ⭐ THE TOWN IS THE ONLY PART THAT MAY BE CUT. It is its own span
   with the ellipsis on it and the country sits outside that span, so
   a narrow print reads "Chianciano Te…, Italy" and never "Chianciano
   Term…". His rule from the little cards, applied to the big one: the
   country is the half that says which side of a border he is on, and
   half a country says nothing. (29 Aug: "if the first line, city is
   too long, we can trim it so we have chianca te... , italy".) */
.view-map .shot-place{
  font-size:22.1px;display:flex;align-items:baseline;
  min-width:0;flex:1 1 auto;
}
.view-map .shot-town{
  min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.view-map .shot-country{flex:none;white-space:nowrap}
.view-map .shot-day{font-size:16.9px;color:var(--ink-soft);white-space:nowrap;flex:none}
.view-map .shot-when{
  font-size:16.3px;color:var(--ink-soft);
  min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.view-map .shot-nav{display:flex;align-items:center;gap:2px;flex:none}
.view-map .shot-count{font-size:15.6px;color:var(--ink-soft);white-space:nowrap}
/* the arrows say "you can swipe" and take no taps — see the template */
/* ⭐ NARROWER SINCE 29 AUG — 34px each to 21. They are a signpost and
   take no taps (owner, Aug 11), so the width was pure padding around
   a chevron: twenty-six pixels of it, on the one line the place name
   was asking for room on. The chevrons themselves are unchanged. */
.view-map .viewer .shot-step{
  display:inline-block;text-align:center;
  color:var(--ink-soft);font-size:22px;line-height:1;
  width:21px;height:30px;
  opacity:.55;pointer-events:none;
}
/* a lone card has nowhere to swipe, so the signpost would be a lie —
   dressCard marks THE CARD (each knows its own pack; a deal can walk
   piles of different depths). ⚠️ The rule read `.viewer.is-single`
   until 24 Aug — a class the code never put THERE — so every lone
   card wore arrows pointing at nothing; his catch, off a one-shot
   pile's lip. */
.view-map .viewer .shot.is-single .shot-nav{display:none}

/* ---------- the notes, on a sheet that comes up over the print ----------
   What people wrote about the SPOT, not about one photo of it (owner,
   Aug 4) — so the list does not change as the shots are swiped.

   They lived on the lower half of the print itself until Aug 5. The owner
   asked for the phone shape everyone already knows instead: a sheet rising
   from the bottom over the picture, which stays where it is behind it. The
   print does NOT move out of its way and nothing is resized to make room —
   his words were no jumps and no movement, and the rise is the only thing
   that travels.

   Still no ruled lines on it: they were tried once as the map's notebook
   paper and read as chaos under a photo (owner, Aug 4).

   Its height and its footing are written in pixels by fitSheet(), because
   the keyboard is what decides where the bottom of the screen is. */
.view-map .notes-sheet{
  position:absolute;left:0;right:0;bottom:0;z-index:3;
  max-width:var(--stage-max);margin:0 auto;        /* the stage's column on a wide screen */
  display:flex;flex-direction:column;
  background:var(--cart);border-radius:18px 18px 0 0;
  box-shadow:0 -14px 34px rgba(0,0,0,.42);
  transform:translateY(101%);
  transition:transform .34s cubic-bezier(.22,.61,.36,1);
}
/* ============ A PHOTO'S COMMENTS SIT ABOVE THE BAR ============
   (3 Sep 2026, his phone: "the input field and the send button are cut
   off".) The sheet was inside .viewer, which is z-index 10 and under
   the bar's 20, so the bar drew over its writing row. .viewer is
   positioned AND z-indexed, so it is a stacking context and nothing
   inside it can climb out — the sheet had to move into a wrap of its
   own, which is exactly what the road feed's comments already have
   (#feed-talk-wrap above). The two are the same arrangement on
   purpose.
   ⚠️ The PRINT still sits under the bar, as it always has: only the
   sheet came forward. */
.view-map #talk-wrap{
  position:fixed;inset:0;z-index:40;overflow:hidden;pointer-events:none;
}
.view-map #talk-wrap.is-talking{pointer-events:auto}
.view-map #talk-wrap.is-talking .notes-sheet{transform:translateY(0)}

/* The head is also the handle: it answers a drag downwards, so the sheet can
   be put away the way it arrived. touch-action:none, or the browser claims
   that drag as a scroll before we see it. */
.view-map .sheet-head{
  flex:none;padding:9px 0 9px;text-align:center;touch-action:none;
  border-bottom:1px solid rgba(43,42,38,.14);
}
.view-map .sheet-grip{
  width:38px;height:4px;border-radius:2px;margin:0 auto 7px;
  background:rgba(43,42,38,.22);
}
.view-map .sheet-title{font-size:17px;color:var(--ink)}

.view-map .notes-list{
  flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
  overscroll-behavior:contain;padding:6px 15px 10px;touch-action:pan-y;
}
/* A name and what they said, with a letter standing in for the face nobody
   signed in to give us. No hearts and no replies (owner, Aug 5). */
/* ⚠️ .note-row, NOT .note (3 Sep 2026). The shell's stylesheet has a
   bare `.note` of its own — the little caption under a screen's title —
   and the day the map is a screen the two meet: measured, it would have
   landed margin, font-size, line-height and colour on every comment row
   on this map. The row is renamed rather than the shell's caption
   because this is the side that is moving house, and .note-row sits
   naturally beside .note-face, .note-body and .note-who anyway. */
.view-map .note-row{display:flex;gap:10px;padding:9px 0}
.view-map .note-face{
  flex:none;width:32px;height:32px;border-radius:50%;
  background:var(--ink-faint);color:var(--cart);
  display:grid;place-items:center;line-height:1;
  font-family:var(--read);font-size:14px;font-weight:600;
}
/* the writer's own picture, when they have one and the database hands it
   out (sgw_faces, 0007 — only for people who have written something);
   the first letter of the name stands in otherwise (owner, 22 Aug: "I
   want the user profile pic to show up not the first letter") */
.view-map .note-face.has-photo{background-size:cover;background-position:center}
.view-map .note-body{flex:1 1 auto;min-width:0;font-family:var(--read)}
/* A NAME IN FARSI (owner, Aug 9 night, screenshot: "56مرادm") is a
   right-to-left run, and unisolated it grabs the timestamp's digits
   across the bidi boundary — "56m" split around the name. Each piece
   lives in its own direction bubble now (unicode-bidi), so a name
   written in any script can never reorder its neighbours; a message
   wholly in an RTL script also lays out as its writer meant it
   (plaintext: the first strong letter decides the paragraph). */
.view-map .note-who{font-size:14px;font-weight:600;color:var(--ink);
  unicode-bidi:isolate}
/* the owner's own rows (Aug 9): the annotation violet says "the app's
   person" — name in violet, the car for a face, a violet ring on the
   circle. No bigger and no bolder than anyone else's words. */
.view-map .note-row.is-owner .note-who{color:var(--mark)}
.view-map .note-row.is-owner .note-face{box-shadow:0 0 0 1.5px var(--mark)}
.view-map .note-when{font-size:12px;color:var(--ink-faint);margin-left:7px;
  unicode-bidi:isolate}
.view-map .note-text{
  font-size:15px;color:var(--ink-soft);margin:1px 0 0;line-height:1.38;
  overflow-wrap:break-word;
  unicode-bidi:plaintext;text-align:start;
}
.view-map .notes-empty{
  font-family:var(--read);padding:14px 0;font-size:14px;color:var(--ink-faint);
}

/* Writing one. The thing they want to say — nothing else to fill in.
   The NAME is the profile's since 22 Aug (owner: "name should come from
   user profile"), so the name box that stood here from Aug 5 is gone; a
   signed-out reader sees "Sign in to comment" in this seat instead
   (.notes-guest). The viewer's sheet shows ONE of the two, by the class
   the page puts on it once the account has answered — neither until
   then, so a signed-in person never sees the sign-in line flash first.
   The feed's sheet is the owner's alone and shows its form always. */
.view-map #notes-sheet:not(.is-known) .notes-add{display:none}
.view-map #notes-sheet:not(.is-guest) .notes-guest{display:none}
.view-map .notes-guest{
  flex:none;margin:0;
  padding:14px 15px calc(14px + env(safe-area-inset-bottom));
  border-top:1px solid rgba(43,42,38,.14);
  font-family:var(--read);font-size:15px;text-align:center;color:var(--ink-faint);
}
/* violet — the you-may-press-this ink (Aug 6) */
.view-map .notes-guest a{color:var(--mark);text-decoration:underline;text-underline-offset:3px}
.view-map .notes-add{
  flex:none;display:flex;flex-direction:column;gap:8px;
  padding:9px 15px calc(9px + env(safe-area-inset-bottom));
  border-top:1px solid rgba(43,42,38,.14);
}
.view-map .notes-say{display:flex;gap:8px;align-items:flex-end}
/* Typed in the same face they will be READ in, so nothing changes shape when
   it is sent. */
.view-map .notes-add textarea{
  -webkit-appearance:none;appearance:none;
  /* 16, never less: iOS AUTO-ZOOMS into any input under 16px the moment
     it is focused, and the zoomed viewport turns every sheet measurement
     to garbage (owner's cut-off screenshots, Aug 6). */
  font-family:var(--read);font-size:16px;color:var(--ink);
  background:#FFFDF3;border:1px solid rgba(43,42,38,.18);border-radius:9px;
  padding:8px 11px;width:100%;
}
.view-map .notes-add textarea{resize:none;line-height:1.35;height:40px;max-height:104px}
.view-map .notes-add textarea::placeholder{color:var(--ink-faint)}
/* ⚠️ #talk-wrap, NOT .viewer, since 3 Sep: the sheet moved out of the
   viewer so it could sit above the bar, and a rule rooted at .viewer
   would have stopped reaching it — silently, which is how a dead rule
   hides (10_PITFALLS.md). The wrap is now the sheet's own ancestor and
   the pairs below read as the two sheets they are. */
.view-map #talk-wrap .notes-send{
  font-size:15px;color:var(--cart);background:var(--red);
  border-radius:9px;padding:9px 16px;flex:none;height:40px;
}
.view-map #talk-wrap .notes-send[disabled]{opacity:.4}
/* the feed sheet's send: the SAME red pill, written as its own scoped
   copy — de-scoping the viewer's rule once let ".viewer button"'s
   background:none outrank it and the send went invisible on the cream
   (owner, Aug 6, loudly — and the docs warned about exactly this). */
.view-map #feed-talk-wrap .notes-send{
  -webkit-appearance:none;appearance:none;border:0;
  font-family:inherit;cursor:pointer;display:grid;place-items:center;
  font-size:15px;color:var(--cart);background:var(--red);
  border-radius:9px;padding:9px 16px;flex:none;height:40px;
}

/* the mic beside send (owner, Aug 6): a voice instead of typing.
   Quiet ink until it records, the route red while it counts — the
   /go mic's manners in a comment's size. Every rule scoped for BOTH
   sheets, the .notes-send way (the cream trap). */
.view-map #talk-wrap .notes-mic, .view-map #feed-talk-wrap .notes-mic{
  -webkit-appearance:none;appearance:none;font-family:inherit;
  cursor:pointer;display:grid;place-items:center;padding:0;
  flex:none;min-width:44px;height:40px;color:var(--ink-soft);
  background:#FFFDF3;border:1px solid rgba(43,42,38,.18);
  border-radius:9px;
}
.view-map .notes-add .notes-mic svg{width:21px;height:21px}
/* every state rule needs a #feed-talk-wrap twin: that sheet's base
   rules carry an id, and an id outguns any pile of classes — the
   cream trap's mirror image (owner, Aug 6: the feed's send never
   turned into toss) */
.view-map .notes-add.is-recording .notes-mic, .view-map #feed-talk-wrap .notes-add.is-recording .notes-mic{
  background:#F3E4D8;border-color:var(--red);color:var(--red);
  padding:0 12px;
}
.view-map .notes-add.is-recording .notes-mic svg{display:none}
.view-map .notes-add .mic-count{
  display:none;font-family:var(--read);font-size:15px;color:var(--red);
}
.view-map .notes-add.is-recording .mic-count{display:block}
.view-map .notes-add.is-sending .notes-mic{opacity:.45;pointer-events:none}
/* while a voice is being taken, words wait their turn */
.view-map .notes-add.is-recording textarea{opacity:.45;pointer-events:none}
.view-map .notes-add.is-recording .notes-send, .view-map #feed-talk-wrap .notes-add.is-recording .notes-send{display:none}

/* "toss" takes send's seat while recording — the /go escape hatch */
.view-map #talk-wrap .notes-toss, .view-map #feed-talk-wrap .notes-toss{
  -webkit-appearance:none;appearance:none;border:0;
  font-family:inherit;cursor:pointer;place-items:center;
  font-size:15px;color:var(--ink-soft);
  background:rgba(120,100,60,.18);border-radius:9px;
  padding:9px 14px;flex:none;height:40px;display:none;
}
.view-map .notes-add.is-recording .notes-toss, .view-map #feed-talk-wrap .notes-add.is-recording .notes-toss{display:grid}

/* a voice note's row: a violet pill that plays it — violet is the
   app's you-can-touch-this ink (the song name, the ▶, the scrubber) */
.view-map #talk-wrap .note-audio, .view-map #feed-talk-wrap .note-audio{
  -webkit-appearance:none;appearance:none;border:0;cursor:pointer;
  font-family:var(--read);font-size:14px;font-weight:600;
  color:var(--cart);background:var(--mark);border-radius:999px;
  padding:7px 15px;margin-top:4px;display:inline-grid;
  place-items:center;
}

/* Controls are plain buttons; each rule above says what colour it wants.
   ⚠️ A rule here beats a lone class, which is how the notes line once
   shipped as cream ink on the cream card. Anything inside the card must be
   written as `.viewer .thing`.

   ⚠️ AND #talk-wrap IS NAMED BESIDE IT SINCE 3 SEP. The comments sheet
   moved out of .viewer that day so it could sit above the bottom bar,
   and its three buttons — the mic, toss and send — were dressed by
   THIS rule through the viewer. Dropping out of it would have left
   them wearing the browser's own grey chrome, which is exactly the
   silent kind of break a dead rule makes (10_PITFALLS.md: a rule
   aimed at an element no code marks, and nothing to catch it). */
.view-map .viewer button, .view-map #talk-wrap button{
  -webkit-appearance:none;appearance:none;border:0;background:none;
  font-family:inherit;cursor:pointer;display:grid;place-items:center;
}
