/* FitBuddy, the product. Every colour, radius, size, duration and curve is a
   design system token; this file only arranges them. No raw cubic-bezier, no
   raw ms. Motion: transform and opacity only, and the spring is reserved for
   genuine wins, fired through pendingPop in app.js.

   This began life as prototype B's fork of the class path's stylesheet. The
   comparison is decided and this copy is now canonical; the archive keeps its
   own in app/. */

html,body{height:100%}
body{margin:0;background:var(--bg-sunken);display:flex;justify-content:center}

.app{width:100%;max-width:440px;min-height:100dvh;background:var(--bg-canvas);
  display:flex;flex-direction:column;position:relative;overflow-x:hidden}
@media (min-width:480px){
  .app{border-left:1px solid var(--border-subtle);border-right:1px solid var(--border-subtle)}
}
.boot{padding:40px var(--gutter-mobile);color:var(--text-tertiary)}
.screen{flex:1;display:flex;flex-direction:column;min-height:0}
.grow{flex:1}
.pad{padding-left:var(--gutter-mobile);padding-right:var(--gutter-mobile)}
/* Section rhythm, stated once instead of guessed per screen.
   The header owns no bottom space; the block after it owns its own top space.
   That way a screen whose next block is a section label and a screen whose next
   block is a plain card both start at exactly 20px, instead of one of them
   putting a 56px avatar 2px away from the box below it. */
.head{padding-top:20px;padding-bottom:0}
.head + .pad{padding-top:20px}
/* A section label already carries its own 20px, so it does not add a second. */
.head + .pad > .sechead:first-child{margin-top:0}
.foot{padding-bottom:20px}
.center{text-align:center}
.block{display:block}

/* ---- top bar: chrome, stays still across route changes ------------------ */
.topbar{display:flex;align-items:center;gap:12px;min-height:52px;
  padding:8px var(--gutter-mobile) 0}
.topbar .brand{display:flex;align-items:center;gap:8px}
.topbar .dot{width:9px;height:9px;border-radius:var(--r-full);background:var(--accent)}
.topbar .mark{font-family:var(--font-display);font-weight:900;font-size:18px;
  letter-spacing:-.03em;text-transform:uppercase}
.topmid{flex:1;font-family:var(--font-display);font-weight:700;font-size:15px;
  letter-spacing:-.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.iconbtn{width:var(--hit-min);height:var(--hit-min);margin-left:-12px;display:flex;
  align-items:center;justify-content:center;background:none;border:none;
  color:var(--text-primary);cursor:pointer;border-radius:var(--r-full);
  transition:background var(--t-hover)}
.iconbtn:active{background:var(--surface-press)}
.iconbtn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.linkbtn{background:none;border:none;color:var(--text-accent);font-family:var(--font-sans);
  font-size:13.5px;font-weight:600;cursor:pointer;padding:0;min-height:var(--hit-min)}

/* ---- type roles --------------------------------------------------------- */
.display{font-family:var(--font-display);font-weight:900;font-style:italic;
  text-transform:uppercase;letter-spacing:-.035em;line-height:1;text-wrap:balance;margin:0}
.d-hero{font-size:46px;line-height:.9;letter-spacing:-.045em}
.d-xl{font-size:34px}.d-lg{font-size:30px}.d-md{font-size:26px}.d-sm{font-size:22px}
.title{font-family:var(--font-display);font-weight:700;font-size:17.5px;letter-spacing:-.012em}
.body{font-size:15.5px;line-height:1.55;color:var(--text-secondary);margin:0;text-wrap:pretty}
.body.sm{font-size:13.5px;line-height:1.5}
.ov{font-family:var(--font-mono);font-size:11px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--text-tertiary)}
.ov.live{color:var(--live-text);display:flex;align-items:center;gap:8px}
.meta{font-family:var(--font-mono);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary)}
/* Booked is a state, not a live one. Lime is reserved for right-now. */
.meta.booked{color:var(--text-secondary)}
.data{font-family:var(--font-display);font-weight:900;font-variant-numeric:tabular-nums;
  letter-spacing:-.05em;line-height:.84}
.numlead{font-size:26px;width:34px;display:inline-block}

/* ---- controls ----------------------------------------------------------- */
.btn{display:flex;align-items:center;justify-content:center;width:100%;height:52px;
  border:none;border-radius:var(--r-control);font-family:var(--font-sans);font-size:16px;
  font-weight:700;cursor:pointer;
  transition:transform var(--t-press),background-color var(--t-hover),opacity var(--t-hover)}
.btn:active{transform:scale(var(--press-scale))}
.btn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.btn[disabled]{opacity:.45;cursor:not-allowed}
.btn-primary{background:var(--action-primary-bg);color:var(--action-primary-fg)}
.btn-ghost{background:var(--action-secondary-bg);border:1px solid var(--action-secondary-border);
  color:var(--action-secondary-fg);font-weight:600}
/* The prefix is not decoration: WebKit needed it until Safari 18, and the two
   other glass surfaces in this file carry it while this one did not. Without
   it the button still reads, because --surface-glass is a real colour and not
   a transparency trick, but one of three glass surfaces rendering flat is the
   kind of difference nobody notices until it is on a phone. `ds-check` now
   holds the pair together. */
.btn-glass{background:var(--surface-glass);backdrop-filter:blur(var(--blur-glass));
  -webkit-backdrop-filter:blur(var(--blur-glass));
  border:1px solid var(--border-on-media);color:var(--text-on-media);font-weight:600}
.btn-sm{display:inline-flex;align-items:center;justify-content:center;width:auto;
  /* 44, like the chips. "Small" describes how it looks in a row, not how big
     a target it is allowed to be; the rule has no exemption for secondary
     buttons and this one sits next to Reset. */
  min-height:var(--hit-min);padding:0 16px;font-size:13.5px;border-radius:var(--r-full);
  background:none;border:1px solid var(--action-secondary-border);color:var(--text-primary);
  font-family:var(--font-sans);font-weight:600;cursor:pointer;
  transition:transform var(--t-press),background-color var(--t-hover)}
.btn-sm:active{transform:scale(var(--press-scale))}
.btn-sm:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.btnrow{display:flex;flex-direction:column;gap:12px;
  padding:20px var(--gutter-mobile) calc(20px + env(safe-area-inset-bottom))}
@media (hover:hover){
  .btn-primary:hover{background:var(--action-primary-bg-hover)}
  .btn-ghost:hover,.btn-sm:hover{background:var(--action-secondary-bg-hover)}
  .linkbtn:hover{color:var(--text-link-hover)}
}

.chips{display:flex;gap:8px;flex-wrap:wrap}
/* 44px, not the 40 this used to be. The rule is hit targets are never under
   44 and there is no exemption for chips: these are the session pickers and
   the quick replies, tapped with a thumb, often mid-workout. */
.chip{display:inline-flex;align-items:center;min-height:var(--hit-min);padding:0 16px;
  border-radius:var(--r-full);border:1px solid var(--border-default);background:none;
  color:var(--text-primary);font-family:var(--font-sans);font-size:13.5px;cursor:pointer;
  transition:transform var(--t-press),border-color var(--t-hover),background-color var(--t-hover)}
.chip:active{transform:scale(var(--press-scale))}
/* Chalk, not red. Selection is the one state a screen can hold a lot of at
   once: five chips are lit on the lifting-partner screen and four on the first
   run, and five red outlines is a wall rather than the fingernail red is meant
   to be. Chalk on ink is already how this system draws a filled block that
   means "this one" (the week strip), so a selected chip now reads like a small
   primary button, which is also what it behaves like.

   The pair flips with the theme by design: chalk on ink in the dark, ink on
   chalk in the light, always legible together.

   Border-COLOR only. The old rule went from 1px to 1.5px on selection, which
   grew the box by a pixel and nudged the row every time you tapped. */
.chip[aria-pressed="true"]{border-color:transparent;
  background:var(--action-primary-bg);color:var(--action-primary-fg);font-weight:700}
@media (hover:hover){ .chip[aria-pressed="true"]:hover{background:var(--action-primary-bg-hover)} }
.chip:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (hover:hover){ .chip:hover{border-color:var(--border-strong)} }

/* ---- cards and rows ----------------------------------------------------- */
.card{display:block;width:100%;text-align:left;background:var(--surface-1);
  border:1px solid var(--border-subtle);border-radius:var(--r-card);padding:16px 20px;
  margin-bottom:12px;color:inherit;font-family:inherit;
  transition:transform var(--t-press),background-color var(--t-hover),border-color var(--t-hover)}
.card.tap{cursor:pointer}
.card.tap:active{transform:scale(var(--press-scale-tile))}
.card.tap:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.card.today{border:1.5px solid var(--border-strong)}
.card.sel{border:1.5px solid var(--border-accent);background:var(--surface-selected)}
@media (hover:hover){ .card.tap:hover{background:var(--surface-hover)} }
.cardhead{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.card .meta.block{margin-top:4px}
.card .body.block{margin-top:6px}
.card .meta.live.block{margin-top:12px}
.going{display:flex;align-items:center;gap:12px;padding-top:12px}

.rowline{display:flex;align-items:center;gap:12px;width:100%;padding:12px 0;
  border:none;border-bottom:1px solid var(--border-subtle);background:none;
  color:inherit;font-family:inherit;text-align:left;
  transition:transform var(--t-press),background-color var(--t-hover)}
.rowline:last-child{border-bottom:none}
.rowline.tap{cursor:pointer}
.rowline.tap:active{transform:scale(var(--press-scale-tile))}
.rowline.tap:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}
.rowline.dim{opacity:.55}
@media (hover:hover){ .rowline.tap:hover{background:var(--surface-hover)} }
.rowbody{flex:1;min-width:0}
.rt{display:block;font-size:15px;font-weight:700;font-family:var(--font-sans)}
.rs{display:block;margin-top:4px}
.chev{color:var(--text-tertiary);display:flex;align-items:center;width:18px;flex:none}
.chev.on{color:var(--text-primary)}
/* 20 above, 8 below: the label belongs to the list under it, but 2px was
   close enough to collide with it. */
.sechead{display:flex;align-items:baseline;justify-content:space-between;margin:20px 0 8px}
.sechead b{font-size:13.5px;font-family:var(--font-sans)}
/* A section label can carry the live dot, and it needs room to breathe. */
.sechead b:has(.live-dot){display:inline-flex;align-items:center;gap:8px}

.stake{border:1px dashed var(--border-default);border-radius:var(--r-control);
  padding:12px 16px;font-size:13.5px;line-height:1.5;color:var(--text-secondary)}
.stake.solid{border-style:solid;background:var(--surface-1)}
.stake b{color:var(--text-primary)}
.empty{border:1px dashed var(--border-default);border-radius:var(--r-card);
  padding:24px 20px;text-align:center}
.never{border:1px dashed var(--border-default);border-radius:var(--r-control);
  padding:12px 12px;margin-top:8px;font-size:13.5px;color:var(--text-tertiary);
  text-decoration:line-through}
.pill{font-family:var(--font-mono);font-size:10px;letter-spacing:.08em;
  text-transform:uppercase;padding:4px 8px;border-radius:var(--r-badge);
  background:var(--surface-3);color:var(--text-secondary)}
.pill.booked{background:var(--surface-3);color:var(--text-secondary)}

/* ---- people ------------------------------------------------------------- */
/* Crew hues. Locked lightness and chroma, hue is the only variable, so no face
   ever reads as more important than another. */
.avatar{width:38px;height:38px;border-radius:var(--r-full);background:var(--surface-3);
  display:flex;align-items:center;justify-content:center;font-family:var(--font-display);
  font-weight:700;font-size:13px;color:var(--text-secondary);flex:none}
.avatar.hue-periwinkle{background:color-mix(in oklab,var(--crew-periwinkle) 20%,var(--bg-canvas));color:var(--crew-periwinkle-ink)}
.avatar.hue-teal{background:color-mix(in oklab,var(--crew-teal) 20%,var(--bg-canvas));color:var(--crew-teal-ink)}
.avatar.hue-sage{background:color-mix(in oklab,var(--crew-sage) 20%,var(--bg-canvas));color:var(--crew-sage-ink)}
.avatar.hue-clay{background:color-mix(in oklab,var(--crew-clay) 20%,var(--bg-canvas));color:var(--crew-clay-ink)}
.avatar.hue-plum{background:color-mix(in oklab,var(--crew-plum) 20%,var(--bg-canvas));color:var(--crew-plum-ink)}

/* Presence. A filled lime dot is someone actually here right now, which is the
   one thing lime is for. A hollow ring is a booking that has not become
   presence yet, and it is deliberately colourless. */
.pres{display:flex;align-items:center;gap:8px;flex:none;font-family:var(--font-mono);
  font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--text-secondary)}
.pres.dim{color:var(--text-tertiary)}
.pdot{width:7px;height:7px;border-radius:var(--r-full);
  border:1.5px solid var(--border-strong);display:block;flex:none}
.pdot.here{background:var(--data-today);border-color:var(--data-today)}

/* Register: ticking someone in fills the row chalk, which is the design's
   "large solid blocks are chalk on ink" rule doing the work a checkbox would. */
.tickrow{display:flex;align-items:center;gap:12px;width:100%;min-height:var(--hit-min);
  padding:8px 12px;margin-bottom:8px;border-radius:var(--r-control);
  border:1px solid var(--border-subtle);background:none;color:inherit;
  font-family:inherit;text-align:left;cursor:pointer;
  transition:transform var(--t-press),background-color var(--t-hover),color var(--t-hover)}
.tickrow:active{transform:scale(var(--press-scale-tile))}
.tickrow:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.tickrow.on{background:var(--surface-inverse);color:var(--text-inverse);border-color:transparent}
/* Tint from the row's own text colour so it inverts with the theme instead of
   being a hardcoded dark wash that vanishes on an ink row. */
.tickrow.on .avatar{background:color-mix(in oklab,currentColor 12%,transparent);color:var(--text-inverse)}
/* Derived from the row's own colour, not a fixed dark: on a chalk row that
   reads as soft ink, and on the light theme's ink row it inverts with it. */
.tickrow.on .rs{color:color-mix(in oklab,currentColor 68%,transparent)}
.tickrow .tickmark{width:20px;height:20px;flex:none;color:var(--text-inverse);opacity:0}
.tickrow.on .tickmark{opacity:1}
.avatar.lg{width:56px;height:56px;font-size:17px}
.stack{display:flex;flex:none}
.stack .avatar{box-shadow:0 0 0 2px var(--bg-canvas)}
.stack .avatar+.avatar{margin-left:-12px}
.person{display:flex;align-items:center;gap:12px}
.live-dot{width:8px;height:8px;border-radius:var(--r-full);background:var(--live-mark);
  display:inline-block;animation:fb-pulse var(--t-pulse) infinite}
.streak{display:flex;align-items:baseline;gap:12px}
.stats{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;margin-top:20px}
.stat{background:var(--surface-1);border-radius:var(--r-card);padding:16px 12px}
.stat .data{display:block;font-size:26px}
.stat .ov{display:block;margin-top:8px;font-size:10px;letter-spacing:.1em}

/* ---- form bits ---------------------------------------------------------- */
.switch{display:flex;align-items:flex-start;gap:12px;width:100%;padding:12px 0;
  background:none;border:none;border-bottom:1px solid var(--border-subtle);
  color:inherit;font-family:inherit;text-align:left;cursor:pointer}
.switch:last-of-type{border-bottom:none}
.switch:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}
.sw{width:42px;height:25px;border-radius:var(--r-full);border:2px solid var(--text-primary);
  flex:none;position:relative;margin-top:2px;
  transition:border-color var(--t-hover),background-color var(--t-hover)}
.sw::after{content:"";position:absolute;left:3px;top:3px;width:15px;height:15px;
  border-radius:var(--r-full);background:var(--text-primary);
  transform:translateX(17px);transition:transform var(--t-hover)}
.switch[aria-checked="false"] .sw{border-color:var(--border-default)}
.switch[aria-checked="false"] .sw::after{transform:none;background:var(--border-default)}
.box{width:26px;height:26px;border-radius:var(--r-badge);border:2px solid var(--border-default);
  flex:none;display:flex;align-items:center;justify-content:center;color:var(--text-inverse);
  transition:background-color var(--t-hover),border-color var(--t-hover)}
.box.on{background:var(--text-primary);border-color:var(--text-primary)}
.progress{display:flex;gap:6px;padding:20px var(--gutter-mobile) 0}
.progress i{height:3px;flex:1;border-radius:2px;background:var(--data-empty)}
.progress i.on{background:var(--accent)}
.dots{display:flex;gap:8px}
.dots i{width:26px;height:6px;border-radius:var(--r-full);background:var(--data-empty)}
.dots i.on{background:var(--text-primary)}
.doors{display:flex;flex-direction:column;gap:12px;padding-top:22px}
.doors .card{margin-bottom:0}

/* ---- tab bar: chrome, never animates ------------------------------------ */
.tabbar{display:flex;border-top:1px solid var(--border-subtle);background:var(--surface-1);
  padding-bottom:env(safe-area-inset-bottom);position:sticky;bottom:0;z-index:20}
.tab{flex:1;min-height:64px;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:4px;font-size:11px;font-family:var(--font-sans);
  color:var(--text-tertiary);background:none;border:none;cursor:pointer;
  transition:color var(--t-hover)}
.tab.on{color:var(--text-primary);font-weight:600}
.tab:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}
.tabicon{position:relative;display:flex}
.pip{position:absolute;top:-2px;right:-4px;width:7px;height:7px;border-radius:var(--r-full);
  background:var(--accent);box-shadow:0 0 0 2px var(--surface-1)}

/* ---- alerts, thread, buddy ---------------------------------------------- */
.alert.read{opacity:.6}
.thread{border-left:2px solid var(--border-default);margin-left:8px;padding-left:20px}
.node{position:relative;padding:12px 0}
.node .rt{margin-top:4px}
.node::before{content:"";position:absolute;left:-26px;top:18px;width:12px;height:12px;
  border-radius:var(--r-full);background:var(--bg-canvas);border:2px solid var(--border-default)}
.node.now::before{background:var(--text-primary);border-color:var(--text-primary)}
.node.next::before{border-style:dashed}
.cand{background:var(--surface-1);border:1px solid var(--border-subtle);
  border-radius:var(--r-card);padding:16px 16px;margin-bottom:12px}
.cand.miss{opacity:.55}
.candrow{display:flex;gap:8px;margin-top:12px}
.candrow .btn{height:42px;font-size:14px}

/* ---- consistency data ---------------------------------------------------
   The design system's split: big solid blocks are chalk on ink, with lime for
   today. Red only ever appears in thin shapes, so it lives on the chart bars
   and the heatmap squares, never on the week strip's slabs. */
.weekstrip{display:flex;gap:6px;margin-top:12px}
.wday{flex:1;display:flex;flex-direction:column;align-items:center;gap:8px;
  background:none;border:none;padding:0;color:inherit;font-family:inherit}
.wday .wbar{width:100%;height:44px;border-radius:var(--r-sm);background:var(--data-empty)}
.wday.done .wbar{background:var(--data-done)}
.wday.rest .wbar{background:var(--data-rest)}
.wday.today .wbar{background:var(--data-today)}
.wday.ahead .wbar{background:var(--data-empty)}
.wday .wl{font-family:var(--font-mono);font-size:10px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--text-tertiary)}
.wday.today .wl{color:var(--live-text)}

/* Effort per week. Red is allowed here because a chart bar is a thin shape,
   but only while it stays thin: at full column width these become the red wall
   the design system warns about, so the fill is capped narrow and centred. */
.effort{display:flex;align-items:flex-end;gap:8px;height:96px;margin-top:12px}
.ebar{flex:1;display:flex;flex-direction:column;justify-content:flex-end;
  align-items:center;gap:8px;height:100%}
.ebar .efill{width:100%;max-width:10px;background:var(--accent);
  border-radius:var(--r-full);min-height:3px}
.ebar.none .efill{background:var(--data-empty)}
.ebar .el{font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;
  color:var(--text-tertiary)}
/* The week in progress is marked on its label, not by turning the bar lime.
   Lime is live, and a week is not a live thing. */
.ebar.now .el{color:var(--live-text)}

/* Sixteen weeks of squares. Thin shapes again, so red carries the data. */
/* Columns flex to fill the width rather than sitting at a fixed size and
   stopping short of the container. The column COUNT is what keeps the squares
   small: filling 394px with sixteen weeks gives 24px squares, and two solid
   rows of those is the red wall the system forbids. Twenty-six weeks fills the
   same width at ~12px, which is a thin shape, and shows twice the history. */
.heat{display:flex;gap:4px;margin-top:12px}
.heatcol{display:flex;flex-direction:column;gap:4px;flex:1;min-width:0}
.hsq{width:100%;aspect-ratio:1;border-radius:2px;background:var(--data-empty)}
.hsq.done{background:var(--accent)}
.hsq.rest{background:var(--data-rest)}
.hsq.today{background:var(--data-today)}
.hsq.ahead{background:var(--data-empty)}
.legend{display:flex;align-items:center;gap:12px;margin-top:12px;
  font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary)}
.legend i{width:10px;height:10px;border-radius:3px;display:inline-block;
  margin-right:4px;vertical-align:-1px}
.legend i.d{background:var(--accent)}
.legend i.r{background:var(--data-rest)}
.legend i.t{background:var(--data-today)}

/* Moments: the classes behind the numbers, photographed. */
.moments{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
.moment{aspect-ratio:1;border-radius:var(--r-media)}
.moment .cap{padding:12px}
.moment .cap .rt{font-size:13.5px;color:var(--text-on-media)}

/* ---- media --------------------------------------------------------------
   The design system's atomic unit (MediaTile): photograph, warm grade,
   protection gradient, grain. Never a flat scrim, never a custom one. */
.media{display:block;position:relative;overflow:hidden;background:var(--surface-3);
  border-radius:var(--r-media);box-shadow:var(--sh-media-edge)}
.media.bleed{border-radius:0;margin-left:calc(var(--gutter-mobile) * -1);
  margin-right:calc(var(--gutter-mobile) * -1)}
.media>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:var(--grade-filter)}
.media::after,.media::before{content:"";position:absolute;inset:0;pointer-events:none}
.media::before{background:var(--grade-warm)}
/* The gradient exists to protect text. With nothing sitting on the picture it
   is just mud, so it only appears where there is a caption to protect. */
.media::after{background:none}
/* Anything sitting on a photograph gets the protection gradient: a caption in
   A, and in B the effort stamp as well. */
.media:has(.cap)::after,.media:has(.effortstamp)::after{background:var(--scrim-bottom)}
.media .grain{position:absolute;inset:0;background-image:var(--texture-grain);
  opacity:var(--grain-opacity);mix-blend-mode:overlay;pointer-events:none}
/* Text sits on the photograph, on the protection gradient, never in an inset box. */
.media .cap{position:absolute;left:0;right:0;bottom:0;z-index:2;
  padding:16px var(--gutter-mobile) 20px;color:var(--text-on-media)}
/* Full white, not the .72 these used to carry. Dimming 11px type is safe on a
   flat surface and reckless on a photograph, where the ground underneath is
   whatever the sky was doing that day. Hierarchy here comes from size, weight
   and letterspacing, which hold no matter what is behind them. */
.media .cap .ov{color:var(--text-on-media)}
.media .cap .meta{color:var(--text-on-media)}
.media .cap .display{color:var(--text-on-media)}
.media .tag{position:absolute;top:14px;left:var(--gutter-mobile);z-index:2}
.classhero{aspect-ratio:16/11}
/* Full-bleed to the card frame: pulled out to the card's own padding, top
   corners following the card radius less its border. No inset image. */
.card .media{margin:-16px -20px 12px;aspect-ratio:16/7;box-shadow:none;
  border-radius:calc(var(--r-card) - 1px) calc(var(--r-card) - 1px) 0 0}
@media (hover:hover){
  .card.tap:hover .media>img{transform:scale(1.03)}
  .media>img{transition:transform var(--dur-4) var(--ease-out)}
}

/* Chip that floats over photography: glass, per the blur rule. */
.tag .pill{background:var(--surface-glass);backdrop-filter:blur(var(--blur-glass));
  -webkit-backdrop-filter:blur(var(--blur-glass));color:var(--text-on-media);border:1px solid var(--border-on-media)}
.tag .pill.live{color:var(--live);border-color:transparent}

/* ---- hero (onboarding) --------------------------------------------------- */
.hero{position:relative;flex:1;display:flex;flex-direction:column;overflow:hidden}
.hero>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:var(--grade-filter)}
.hero .grade{position:absolute;inset:0;background:var(--grade-warm)}
.hero .grain{position:absolute;inset:0;background:var(--texture-grain);opacity:var(--grain-opacity)}
.hero .scrim{position:absolute;inset:0;background:var(--scrim-full)}
.hero .fg{position:relative;z-index:3;color:var(--text-on-media)}
.hero .fg .body{color:var(--text-on-media-secondary)}
.hero .fg .ov{color:var(--text-on-media-secondary)}
.hero .grow{position:relative;z-index:3}
.brandrow{display:flex;align-items:center;gap:8px;padding:20px var(--gutter-mobile) 0}
.brandrow .dot{width:9px;height:9px;border-radius:var(--r-full);background:var(--accent)}
.brandrow .mark{font-family:var(--font-display);font-weight:900;font-size:18px;
  letter-spacing:-.03em;text-transform:uppercase}

/* ---- toast --------------------------------------------------------------- */
.toast{position:fixed;left:50%;bottom:calc(88px + env(safe-area-inset-bottom));
  transform:translateX(-50%);background:var(--surface-inverse);color:var(--text-inverse);
  border-radius:var(--r-control);padding:12px 20px;font-size:14px;font-weight:600;
  box-shadow:var(--sh-4);z-index:60;max-width:calc(100% - 44px);text-align:center;
  animation:fb-toast-in var(--t-enter) both}
@keyframes fb-toast-in{from{opacity:0;transform:translateX(-50%) translateY(10px)}
  to{opacity:1;transform:translateX(-50%) translateY(0)}}

/* ============ MOTION =====================================================
   Content enters, chrome does not. Only transform and opacity. Curves and
   durations are tokens. The spring lives in .pop and nowhere else. */

@media (prefers-reduced-motion:no-preference){
  .screen .head,.screen .pad>.card,.screen .btnrow{will-change:auto}

  /* list and card entrance, capped so nothing is ever waiting on motion */
  .stagger>.card,.stagger>.rowline,.stagger>.cand,.thread.stagger>.node{
    animation:fb-fade-up var(--t-enter) both;
    animation-delay:calc(min(var(--i,0),var(--stagger-cap)) * var(--stagger-step))}

  /* route change: content only */
  ::view-transition-old(root){animation:fb-fade-out var(--dur-1) var(--ease-in) both}
  ::view-transition-new(root){animation:fb-fade-up var(--t-enter) both}

  /* the reserved spring: a genuine win, nothing else */
  /* No fill-mode: fb-pop starts at opacity 0, so `both` leaves the element
     invisible if the animation never runs (a backgrounded tab at the moment of
     render, an environment that suppresses animation). Without it the element
     simply renders normally, and the celebration is the only thing lost. */
  .pop{animation:fb-pop var(--t-celebrate)}
  .vt-pop::view-transition-new(root){animation:fb-pop var(--t-celebrate) both}
}
@keyframes fb-fade-out{to{opacity:0}}

@media (prefers-reduced-motion:reduce){
  /* Tokens already zero every duration. Belt and braces for view transitions,
     which are JS-driven and would otherwise slip through. */
  ::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){
    animation:none!important}
  .live-dot{animation:none}
}

/* ==========================================================================
   PROTOTYPE B: crews, challenges, streaks, feed
   ========================================================================== */

/* ---- crew rings: the row along the top of the feed ---------------------- */
/* flex:none matters: the screen is a column flexbox and a scroll container
   with no intrinsic height will happily collapse to nothing. */
.rings{display:flex;flex:none;gap:12px;overflow-x:auto;padding:12px var(--gutter-mobile) 4px;
  scrollbar-width:none}
.rings::-webkit-scrollbar{display:none}
.ring{flex:none;width:70px;background:none;border:none;padding:0;color:inherit;
  font-family:inherit;cursor:pointer;text-align:center}
.ring .rimg{display:block;position:relative;width:64px;height:64px;
  border-radius:var(--r-full);overflow:hidden;margin:0 auto;background:var(--surface-3)}
.ring .rimg img{width:100%;height:100%;object-fit:cover;filter:var(--grade-filter)}
/* The ring itself is the live signal, so it is the one lime thing here. */
.ring .rring{display:block;position:absolute;inset:0;border-radius:var(--r-full);
  border:2px solid var(--border-subtle)}
.ring.on .rring{border-color:var(--live-mark)}
.ring .rn{display:block;margin-top:8px;font-size:11.5px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The caption carries two different facts now, so only the live one is lime.
   "3 today" is a past-tense fact and takes ordinary secondary text. */
.ring .rc{display:block;font-family:var(--font-mono);font-size:10px;
  letter-spacing:.06em;color:var(--text-secondary)}
/* No .live variant: the ring border is the single lime carrier for presence. */
.ring:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}

/* ---- presence ----------------------------------------------------------- */
/* Faces, not a count: the claim of this product is that the people are the
   motivator, so the people are what it shows. */
/* Flex, not grid: the faces row disappears when nobody is in, and a fixed grid
   left a hole where it used to be. */
.hereband{display:flex;align-items:center;gap:12px;width:100%;min-height:44px;
  padding:16px;border:1px solid var(--border-subtle);border-radius:var(--r-card);
  background:var(--surface-1);color:inherit;font-family:inherit;text-align:left;cursor:pointer}
.hereband .bandmain{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.hereband .ov{display:block}
/* The faces are aria-hidden, so this line is the button's accessible name as
   well as its label. It stays visible and it stays in the DOM. */
.hereband .hereline{font-size:13.5px;color:var(--text-secondary)}
.hereband .chev{flex:none;color:var(--text-secondary)}
.hereband:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (hover:hover){ .hereband:hover{border-color:var(--border-strong)} }
.hereband:active{transform:scale(var(--press-scale-tile))}

.herefaces{display:flex;align-items:center;flex-wrap:wrap;gap:0}
/* Overlapped, so a full room reads as a crowd rather than a long row, and the
   ring keeps each face separable against the one behind it. */
.herefaces .avatar{margin-right:-8px;box-shadow:0 0 0 2px var(--bg-app)}
.herefaces .moreface{margin-left:16px}

/* ---- streak banner ------------------------------------------------------ */
/* One owner for the space below it: the next block brings its own top
   margin, and two competing margins here measured 28px, off the scale. */
.streakbar{display:flex;align-items:center;gap:12px;margin:12px 0 0;
  padding:12px 16px;border-radius:var(--r-card);background:var(--surface-1);
  border:1px solid var(--border-subtle)}
/* The illustration sits at the left of the banner. `.streakflame` sizing and
   motion come from the design system (tokens/illustration.css); this only
   places it. */
.streakbar>.streakflame,.streakbar>.flame-none{flex:none}
.flame-none{display:inline-flex;align-items:center;justify-content:center;
  color:var(--text-tertiary);flex:none}
.streakbar.tall{align-items:center;gap:20px;padding:20px 20px}
.streakbar .sl.big{font-size:17px;font-weight:700;color:var(--text-primary);
  font-family:var(--font-sans);margin-top:0}
.streakbar .sn{display:block;font-family:var(--font-display);font-weight:900;font-size:30px;
  font-variant-numeric:tabular-nums;letter-spacing:-.04em;line-height:.9}
.streakbar .sl{display:block;font-size:13.5px;color:var(--text-secondary);margin-top:4px}
.streakbar .right{margin-left:auto;text-align:right;flex:none}
.streakbar>span:nth-child(2){min-width:0}

/* ---- feed post ---------------------------------------------------------- */
.post{border-bottom:1px solid var(--border-subtle);padding-bottom:16px;margin-bottom:16px}
.post:last-child{border-bottom:none}
.byline{display:flex;align-items:center;gap:12px;padding:0 var(--gutter-mobile)}
.byline .bn{display:block;font-size:14.5px;font-weight:700}
.byline .bc{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.07em;
  text-transform:uppercase;color:var(--text-tertiary);margin-top:4px}
.byline>span:nth-child(2){flex:1;min-width:0}
.byline .bt{flex:none;font-family:var(--font-mono);font-size:10px;
  letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}
.post .media{margin-top:12px;border-radius:0;aspect-ratio:4/5;box-shadow:none}
/* Effort sits on the photograph, in tabular Chivo, per the design system. */
.effortstamp{display:block;position:absolute;left:var(--gutter-mobile);bottom:16px;
  z-index:2;color:var(--text-on-media)}
.effortstamp .en{display:block;font-family:var(--font-display);font-weight:900;font-size:34px;
  font-variant-numeric:tabular-nums;letter-spacing:-.04em;line-height:.9}
.effortstamp .eu{font-family:var(--font-mono);font-size:10px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--text-on-media-secondary);margin-top:4px;display:block}
.post .cap2{padding:12px var(--gutter-mobile) 0;font-size:14.5px;line-height:1.5}
.post .noimg{margin:12px var(--gutter-mobile) 0;padding:16px 16px;border-radius:var(--r-card);
  background:var(--surface-1);border:1px solid var(--border-subtle)}
.post .noimg .en{font-family:var(--font-display);font-weight:900;font-size:26px;
  font-variant-numeric:tabular-nums;letter-spacing:-.04em}
.crewcard .cap .rt{display:block}
.crewcard .cap .meta{display:block;margin-top:4px}

/* ---- reactions ---------------------------------------------------------- */
.reacts{display:flex;align-items:center;gap:8px;padding:12px var(--gutter-mobile) 0}
.react{display:flex;align-items:center;gap:8px;height:44px;padding:0 16px;
  border-radius:var(--r-full);background:var(--surface-2);border:1px solid var(--border-subtle);
  color:var(--text-secondary);font-family:inherit;font-size:13.5px;font-weight:600;cursor:pointer;
  transition:transform var(--t-press),background-color var(--t-hover),border-color var(--t-hover)}
.react:active{transform:scale(var(--press-scale-tile))}
.react.on{background:var(--surface-selected);border-color:var(--border-accent);
  color:var(--text-primary)}
.react:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (hover:hover){ .react:hover{border-color:var(--border-strong)} }
.react svg{flex:none}
.react{white-space:nowrap}
/* The count moved off the chip row: three chips and a count do not fit across
   a phone, and the chips were wrapping and crushing their own icons. */
.reactmeta{display:flex;align-items:center;min-height:44px;width:100%;
  padding:0 var(--gutter-mobile);margin-top:4px;background:none;border:none;
  text-align:left;cursor:default;font-family:var(--font-mono);
  font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--text-tertiary)}
button.reactmeta{cursor:pointer;transition:color var(--t-hover)}
button.reactmeta:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-sm)}
@media (hover:hover){ button.reactmeta:hover{color:var(--text-secondary)} }

/* ---- crew cards --------------------------------------------------------- */
.crewcard{position:relative;display:block;width:100%;text-align:left;padding:0;
  border:none;background:none;color:inherit;font-family:inherit;cursor:pointer;
  margin-bottom:12px;transition:transform var(--t-press)}
.crewcard:active{transform:scale(var(--press-scale-tile))}
.crewcard:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:var(--r-media)}
.crewcard .media{aspect-ratio:16/9}
.crewcard .cap .rt{font-size:19px;font-family:var(--font-display);font-weight:700;color:var(--text-on-media)}
.crewcard .cap .meta{color:var(--text-on-media-secondary)}
.crewcover{aspect-ratio:16/10}

/* ---- progress ring ------------------------------------------------------ */
/* A ring is a thin shape, which is exactly where red is allowed to carry data. */
.ringwrap{display:flex;align-items:center;gap:20px;margin-top:16px}
.pring{position:relative;width:104px;height:104px;flex:none}
.pring svg{width:100%;height:100%;transform:rotate(-90deg)}
.pring .track{stroke:var(--data-empty)}
/* No transition on the dash offset: the rule is transform and opacity only,
   and the ring's arrival is already carried by the spring on .pring. */
.pring .fill{stroke:var(--accent);stroke-linecap:round}
/* Constrained to the inside of the ring, so a long unit can never spill past
   the stroke. What sits in here is short by construction, see the view. */
.pring .mid{position:absolute;inset:14px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;overflow:hidden}
.pring .pn{font-family:var(--font-display);font-weight:900;font-size:28px;
  font-variant-numeric:tabular-nums;letter-spacing:-.04em;line-height:.9}
.pring .pu{font-family:var(--font-mono);font-size:10px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--text-tertiary);margin-top:4px}

/* ---- leaderboard -------------------------------------------------------- */
/* Ranks days shown up. Never output, never a body. */
/* Every row shares the same geometry so the rank, avatar and name columns
   never shift; your own row differs by its surface tint and nothing else.
   The earlier boxed treatment (inset border, padding only on the you-row)
   pushed your columns out of line with everyone else's. */
.lbrow{display:flex;align-items:center;gap:12px;padding:12px;
  border-radius:var(--r-control);border-bottom:1px solid var(--border-subtle)}
.lbrow:last-child{border-bottom:none}
.lbrow.you{background:var(--surface-2);border-bottom-color:transparent}
.lbrank{font-family:var(--font-display);font-weight:900;font-size:17px;
  font-variant-numeric:tabular-nums;width:26px;flex:none;color:var(--text-tertiary)}
.lbrow.you .lbrank{color:var(--text-primary)}
.lbname{flex:1;min-width:0;font-size:14.5px;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbsub{display:block;font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-tertiary);margin-top:4px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lbval{font-family:var(--font-display);font-weight:900;font-size:18px;
  font-variant-numeric:tabular-nums;letter-spacing:-.03em}

/* ---- badges and level --------------------------------------------------- */
.badges{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
.badge{display:flex;align-items:center;gap:8px;height:36px;padding:0 12px;
  border-radius:var(--r-full);background:var(--surface-2);
  border:1px solid var(--border-subtle);font-size:13.5px;font-weight:600}
/* Outline icon, taking the chip's own colour like every other icon. */
.badge svg{flex:none;color:var(--text-tertiary)}
.levelbar{height:8px;border-radius:var(--r-full);background:var(--data-empty);
  overflow:hidden;margin-top:12px}
.levelbar i{display:block;height:100%;background:var(--accent);border-radius:var(--r-full)}
.levelrow{display:flex;justify-content:space-between;align-items:baseline;margin-top:8px}

/* ---- composer sheet ----------------------------------------------------- */
.sheet{position:fixed;left:50%;bottom:0;width:100%;max-width:440px;
  transform:translateX(-50%);background:var(--surface-2);
  border-radius:var(--r-sheet) var(--r-sheet) 0 0;box-shadow:var(--sh-5);
  z-index:70;padding:8px var(--gutter-mobile) calc(20px + env(safe-area-inset-bottom));
  max-height:calc(100dvh - 48px);overflow-y:auto;
  animation:fb-sheet-in var(--t-sheet) both}
/* Already open: a re-render for a changed option must not replay the entrance. */
.sheet.still,.backdrop.still{animation:none}
@keyframes fb-sheet-in{from{transform:translateX(-50%) translateY(100%)}
  to{transform:translateX(-50%) translateY(0)}}
.grabber{width:38px;height:4px;border-radius:var(--r-full);background:var(--border-strong);
  margin:6px auto 16px}
.backdrop{position:fixed;inset:0;background:var(--surface-scrim);
  backdrop-filter:blur(var(--blur-glass));-webkit-backdrop-filter:blur(var(--blur-glass));z-index:65;
  border:none;width:100%;animation:fb-fade-in var(--t-enter) both}
@keyframes fb-fade-in{from{opacity:0}to{opacity:1}}
.seg{display:flex;gap:6px;margin-top:12px}
.seg button{flex:1;height:44px;border-radius:var(--r-control);background:var(--surface-3);
  border:1px solid transparent;color:var(--text-secondary);font-family:inherit;
  font-size:13.5px;font-weight:600;cursor:pointer}
.seg button.on{background:var(--surface-inverse);color:var(--text-inverse);border-color:transparent}
.seg button:focus-visible{outline:none;box-shadow:var(--focus-ring)}

/* ---- floating compose button ------------------------------------------- */
.fab{position:fixed;right:calc(50% - 220px + var(--gutter-mobile));
  bottom:calc(84px + env(safe-area-inset-bottom));width:56px;height:56px;
  border-radius:var(--r-full);background:var(--accent);color:var(--text-on-media);border:none;
  display:flex;align-items:center;justify-content:center;box-shadow:var(--sh-4);
  cursor:pointer;z-index:50;transition:transform var(--t-press)}
.fab:active{transform:scale(var(--press-scale-tile))}
.fab:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (max-width:479px){ .fab{right:var(--gutter-mobile)} }

/* ---- motion, now under the law ------------------------------------------
   The prototype phase sprang on everything; the product does not. The spring
   fires only through pendingPop, on a genuine win: a post landing, a reaction
   pressed, a challenge entered, a crew created. Nothing here loops except the
   live dot, whose pulse comes from the design system. */

/* ---- message bubbles ---------------------------------------------------- */
.bubble{max-width:78%;padding:12px 16px;border-radius:var(--r-card);
  font-size:14.5px;line-height:1.5;margin-bottom:12px}
.bubble.them{background:var(--surface-2);border:1px solid var(--border-subtle);
  border-bottom-left-radius:var(--r-sm)}
.bubble.me{background:var(--surface-inverse);color:var(--text-inverse);
  margin-left:auto;border-bottom-right-radius:var(--r-sm)}
.pip.inline{display:inline-block;position:static;width:7px;height:7px;margin-left:6px}
.feedlist{display:block}

/* ---- composer photo picker ---------------------------------------------
   Stands in for the camera roll. Horizontal, thumb-sized, first tile is the
   opt out because "no photo" is a real answer and should not be buried. */
.shots{display:flex;gap:8px;overflow-x:auto;margin-top:12px;padding-bottom:4px;
  scrollbar-width:none}
.shots::-webkit-scrollbar{display:none}
.shot{flex:none;width:72px;height:72px;border-radius:var(--r-control);overflow:hidden;
  padding:0;border:2px solid transparent;background:var(--surface-3);cursor:pointer;
  position:relative;transition:transform var(--t-press),border-color var(--t-hover)}
.shot:active{transform:scale(var(--press-scale-tile))}
.shot:focus-visible{outline:none;box-shadow:var(--focus-ring)}
.shot img{width:100%;height:100%;object-fit:cover;display:block;filter:var(--grade-filter)}
/* Chalk, to match the chips it sits under. This is the same "this one is
   chosen" state as the What and Who sees it rows, three inches away on the
   same sheet, and leaving it red made one selection on the screen shout while
   the others did not. A photo tile is a picture, so the mark is the border
   rather than a fill. */
.shot.on{border-color:var(--action-primary-bg);border-width:2px}
.shot.none{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:6px;color:var(--text-tertiary);font-family:inherit}
.shot.none .meta{font-size:10px}
.shot.none.on{color:var(--text-primary)}
@media (hover:hover){ .shot:hover{border-color:var(--border-strong)} }

/* ---- text field --------------------------------------------------------- */
.field{width:100%;height:52px;padding:0 16px;border-radius:var(--r-control);
  background:var(--surface-2);border:1px solid var(--border-default);
  color:var(--text-primary);font-family:var(--font-sans);font-size:16px;
  transition:border-color var(--t-hover)}
.field::placeholder{color:var(--text-tertiary)}
.field:focus{outline:none;border-color:var(--border-strong);box-shadow:var(--focus-ring)}

/* ---- comments and chat sending ------------------------------------------ */
.crow{display:flex;align-items:flex-start;gap:12px;padding:12px 0;
  border-bottom:1px solid var(--border-subtle)}
.crow:last-child{border-bottom:none}
.crow .avatar{width:32px;height:32px;font-size:11px;flex:none}
.cwho{display:block;font-size:13.5px;font-weight:700;font-family:var(--font-sans)}
.ctext{display:block;margin-top:2px;font-size:14.5px;line-height:1.5;
  color:var(--text-secondary);overflow-wrap:anywhere}
.sendbar{display:flex;gap:8px;align-items:center;
  padding-top:12px;padding-bottom:calc(20px + env(safe-area-inset-bottom))}
.sendbar .field{flex:1;min-width:0}
.sendbtn{width:52px;height:52px;flex:none;display:flex;align-items:center;
  justify-content:center;border:none;border-radius:var(--r-control);
  background:var(--action-primary-bg);color:var(--action-primary-fg);cursor:pointer;
  transition:transform var(--t-press),background-color var(--t-hover)}
.sendbtn:active{transform:scale(var(--press-scale))}
.sendbtn:focus-visible{outline:none;box-shadow:var(--focus-ring)}
@media (hover:hover){ .sendbtn:hover{background:var(--action-primary-bg-hover)} }

/* ---- composer photo: upload preview ------------------------------------- */
.shot .shotlabel{position:absolute;left:0;right:0;bottom:0;padding:4px 0;
  text-align:center;font-family:var(--font-mono);font-size:10px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--text-on-media);background:var(--surface-scrim)}
.shot.preview{display:inline-block}
.shotx{position:absolute;top:4px;right:4px;width:24px;height:24px;display:flex;
  align-items:center;justify-content:center;border:none;border-radius:var(--r-full);
  background:var(--surface-scrim);color:var(--text-on-media);cursor:pointer;padding:0}
.shotx:focus-visible{outline:none;box-shadow:var(--focus-ring)}

/* ---- first run ----------------------------------------------------------
   The crew thumbnail on the join step: a face for the room, at row scale. */
.crewthumb{width:44px;height:44px;flex:none;border-radius:var(--r-control);
  overflow:hidden;background:var(--surface-3);display:block}
.crewthumb img{width:100%;height:100%;object-fit:cover;display:block;
  filter:var(--grade-filter)}

/* ---- alerts ------------------------------------------------------------- */
/* Two icon buttons in the top bar, spaced so neither loses its 44px target. */
.topacts{display:flex;align-items:center;gap:4px}
/* The unread mark on a filed alert. Red is the brand and it is a fingernail
   here: a dot on an avatar, never a field. */
.avatar.unread{position:relative}
.avatar.unread::after{content:"";position:absolute;top:-1px;right:-1px;width:8px;height:8px;
  border-radius:var(--r-full);background:var(--accent);box-shadow:0 0 0 2px var(--bg-canvas)}

/* ---- bottom of a screen -------------------------------------------------- */
/* The gap between the last block and whatever ends the screen. On a tabbed
   screen the bar carries the safe-area inset itself, so only the plain ones
   add it here. */
.screenfoot{flex:none;height:24px}
.screenfoot.safe{height:calc(24px + env(safe-area-inset-bottom))}
/* A button row already ends with its own 20px and the inset, so it does not
   get a second helping. */
.btnrow + .screenfoot{height:0}
