/* ══════════════════════════════════════════════════════════════════
   Queendom Farm — the app.

   Built to be used standing in a field with one hand and a cold thumb, so:
   a fixed bottom bar within thumb reach, targets no smaller than 44px, and
   safe-area insets everywhere because this runs full-screen from the home
   screen and iOS will happily put the tab bar under the gesture indicator.

   The two rules the visual language has to carry, same as the board:
   a proposal must not look like a decision, and a rota must not look like a
   job that finishes.

   THE MOTION RULE. Everything eases on one curve and moves only `transform`
   and `opacity`. Mixing easings is what makes an app feel like a web page,
   and animating anything that hits layout drops a mid-range Android to 30fps
   — which on this app means a list of 31 missions juddering in somebody's
   cold hand. There is exactly one curve here, and a spring used only where a
   thumb is dragging something.
   ══════════════════════════════════════════════════════════════════ */

:root {
  --bg: #12100d;
  --bg-2: #1a1712;
  --panel: #1e1a15;
  --panel-2: #262019;
  --line: #342d23;
  --line-2: #453c2e;
  --ink: #f4efe5;
  --dim: #a99e8b;
  --faint: #7a705f;

  --life: #86b96f;
  --solar: #e3a95c;
  --now: #d76d5d;

  /* ─── the valley ────────────────────────────────────────────────
     Queendom Farm is in Hirschwang, in the valley the Kaiserbrunn spring
     falls through on its way to Vienna — 927 m down to 179 m. That valley
     already has a film about it (~/Projects/valley-of-water), and these two
     values are lifted straight out of its palette rather than picked to
     match it: the water it is named for, and the parchment of the 1873
     archive plates. Water marks anything that recurs — a rota is the farm's
     own version of a thing that keeps arriving — and parchment marks the
     guide's recorded words, which are this farm's archive. */
  --water: #bfeaff;
  --parchment: #f0e2cd;

  --r: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --tab-h: 56px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);

  /* the house curve, and the one spring a thumb is allowed to feel */
  --ease: cubic-bezier(.22,.61,.36,1);
  --spring: cubic-bezier(.32,.72,0,1);
  --dur-fast: .18s;
  --dur: .30s;
  --dur-slow: .5s;

  --shadow: 0 1px 2px rgba(0,0,0,.4), 0 6px 18px -8px rgba(0,0,0,.7);
  --shadow-up: 0 -8px 40px -12px rgba(0,0,0,.85);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* Several things here set `display` (the tab bar, the placing banner) and a
   bare `display:flex` beats the UA stylesheet's rule for [hidden]. Without
   this the "line up the pin" banner is on screen permanently. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  /* The app is full-screen from the home screen; a rubber-band that reveals
     the browser's white ground under a near-black app is the single loudest
     "this is a web page" tell there is. */
  position: fixed; inset: 0;
  /* A column, so the app takes what the sign-in gate leaves rather than a
     flat 100%. See the note on #gate: this is what keeps the tab bar on
     screen no matter what the Hub's auth renders above it. */
  display: flex; flex-direction: column;
}

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }

/* The ground under everything: a warm wash off the top-left, so the near
   black is lit rather than flat. Fixed, so it does not travel with scroll. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(120% 70% at 12% -8%, rgba(227,169,92,.09), transparent 62%),
    radial-gradient(90% 60% at 92% 4%, rgba(134,185,111,.055), transparent 58%);
}

/* ─── shell ──────────────────────────────────────────────────────── */

/* ─── the front door ─────────────────────────────────────────────
   The Commons Hub's sign-in gate renders here and then empties itself. The
   node stays behind, and it used to keep its 24px of padding — 48px of empty
   box above an app that was still sizing itself to a flat `height: 100%`.
   Total: 48px taller than the screen, with the tab bar pushed exactly that
   far below the fold. The menu was not missing, it was underneath the phone.
   Two guards, because either alone is one assumption about somebody else's
   auth code: the empty gate takes no space, and the app takes what is left
   rather than all of it.

   🪤 AND THE GATE BRINGS NO CSS. commons.js writes its own markup and toggles
   a `.hidden` class fourteen times, but the rule that makes `.hidden` mean
   anything lives in the Commons Hub's stylesheet, which this app does not
   load. Without it every step of the passport — the known device, the email,
   the code, the keys — renders stacked on top of each other, all at once, as
   the first thing anybody sees.

   So the passport is dressed here, in its own language rather than the
   farm's: Instrument Serif for the display, JetBrains Mono for the eyebrows,
   the glass card, the pill button in solar gradient, the same house curve.
   These values are ported from commons-flowme/dist/assets/commons.css so the
   door looks the same whichever FlowBond space it is opening — the passport
   is one object, and an object that changes its face at every threshold is
   not a passport, it is a form. What is the farm's, and only the farm's, is
   the ground it stands on and the doorway above it. */

#gate, #doorway {
  /* The Passport's own palette, kept apart from the app's so neither can
     drift into the other. */
  --pp-night:    #060A07;
  --pp-glass:    rgba(18,28,21,.62);
  --pp-paper:    #EDE7D7;
  --pp-dim:      #B9C2B6;
  --pp-ash:      #7E8C81;
  --pp-solar:    #F5B95A;
  --pp-solar-hot:#FFDD97;
  --pp-line:     rgba(237,231,215,.11);
  --pp-line-2:   rgba(237,231,215,.2);
  --pp-serif: "Instrument Serif", "Times New Roman", serif;
  --pp-sans:  "Instrument Sans", system-ui, sans-serif;
  --pp-mono:  "JetBrains Mono", ui-monospace, monospace;
}

/* The ground the door stands on: the farm's warm dark, lifted toward the
   Hub's night so the glass card reads as the same material it does there. */
body:has(#gate:not(:empty))::before {
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(245,185,90,.10), transparent 60%),
    radial-gradient(90% 60% at 50% 110%, rgba(111,217,162,.06), transparent 60%);
}

#doorway {
  flex: none; text-align: center;
  padding: max(6vh, 28px) 18px 0;
  font-family: var(--pp-sans);
  animation: rise var(--dur-slow) var(--ease) both;
}
#doorway[hidden] { display: none; }
#doorway .mark {
  width: 46px; height: 46px; margin: 0 auto 14px;
  display: grid; place-items: center; border-radius: 50%;
  border: 1px solid var(--pp-line-2);
  background: var(--pp-glass);
  backdrop-filter: blur(20px) saturate(1.15);
  -webkit-backdrop-filter: blur(20px) saturate(1.15);
}
#doorway .glyph { width: 22px; height: 22px; color: var(--pp-solar); display: block; }
#doorway .where {
  margin: 0; font-family: var(--pp-serif); font-weight: 400;
  font-size: 30px; line-height: 1.06; letter-spacing: -.012em;
  color: var(--pp-paper);
}
#doorway .place {
  margin: 7px 0 0; font-family: var(--pp-mono);
  font-size: .66rem; letter-spacing: .19em; text-transform: uppercase;
  color: var(--pp-ash);
}

#gate {
  flex: 1; min-height: 0; overflow-y: auto;
  display: grid; place-items: center;
  padding: 22px 18px calc(22px + var(--safe-b));
  font-family: var(--pp-sans);
  color: var(--pp-paper);
}
#gate:empty { display: none; padding: 0; }

/* The bug. Everything else here is dress; this line is the fix. */
#gate .hidden { display: none !important; }

#gate .card {
  width: 100%; max-width: 452px; margin: 0;
  position: relative;
  background: var(--pp-glass);
  backdrop-filter: blur(20px) saturate(1.15);
  -webkit-backdrop-filter: blur(20px) saturate(1.15);
  border: 1px solid var(--pp-line); border-radius: 16px;
  padding: 24px 26px;
  box-shadow: 0 24px 70px -30px rgba(0,0,0,.9);
}
/* The app's mission-card stripe has no business on a passport. */
#gate .card::before { display: none; }

/* `.rv` is the Hub's reveal: hidden until `.in` is added. Without the rule
   the card is simply visible, which is correct — but with it the door opens
   the way it does at the Hub. */
#gate .rv { opacity: 0; transform: translateY(18px);
            transition: opacity var(--dur-slow) var(--ease) var(--d, 0ms),
                        transform var(--dur-slow) var(--ease) var(--d, 0ms); }
#gate .rv.in { opacity: 1; transform: none; }

#gate .eyebrow {
  display: block; font-family: var(--pp-mono); font-size: .66rem;
  letter-spacing: .19em; text-transform: uppercase; color: var(--pp-ash);
}
.areahead { display: flex; align-items: center; gap: 9px; }
.areahead .ic { width: 20px; height: 20px; color: var(--life); }

#gate h2 {
  font-family: var(--pp-serif); font-weight: 400; font-size: 2rem;
  letter-spacing: -.012em; line-height: 1.06; text-wrap: balance;
  color: var(--pp-paper);
}
#gate .muted { color: var(--pp-dim); }
/* The Hub's note is a rule in the margin rather than a box. */
#gate .note {
  border-left: 1px solid var(--pp-line-2);
  padding: 3px 0 3px 16px; margin: 12px 0;
  font-size: .94rem; color: var(--pp-dim);
}
#gate .note b { color: var(--pp-paper); font-weight: 500; }

#gate .field { margin-bottom: 16px; }
#gate .field .eyebrow { margin-bottom: 7px; }
#gate input {
  width: 100%; background: rgba(6,10,7,.6); color: var(--pp-paper);
  border: 1px solid var(--pp-line); border-radius: 11px; padding: 12px 14px;
  font-family: inherit;
  font-size: 16px;   /* 16px or iOS zooms the whole page on focus */
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
#gate input:focus {
  outline: none; border-color: rgba(245,185,90,.55);
  box-shadow: 0 0 0 3px rgba(245,185,90,.12);
}
#gate input::placeholder { color: var(--pp-ash); }
#gate .mono { font-family: var(--pp-mono); }

#gate .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  background: linear-gradient(180deg, var(--pp-solar-hot), var(--pp-solar));
  color: #1B1206; border: none;
  font-family: var(--pp-sans); font-weight: 600; font-size: .93rem;
  padding: 12px 24px; border-radius: 99px; min-height: 0;
  transition: transform var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
#gate .btn:active:not(:disabled) { transform: scale(.97); }
#gate .btn.wide  { width: 100%; }
#gate .btn.link  { background: none; border: none; color: var(--pp-ash);
                   padding: 4px 2px; font-size: .85rem; font-weight: 400;
                   text-decoration: underline; box-shadow: none; }
#gate .btn.ghost { background: transparent; color: var(--pp-paper);
                   border: 1px solid var(--pp-line-2); box-shadow: none; }
#gate .btn.small { padding: 8px 16px; font-size: .82rem; }

#gate .row { display: flex; gap: 10px; align-items: center; }
#gate .orline {
  display: flex; align-items: center; gap: 14px; margin: 22px 0 16px;
  color: var(--pp-ash); font-size: .78rem;
  letter-spacing: .14em; text-transform: uppercase;
}
#gate .orline::before, #gate .orline::after {
  content: ""; flex: 1; height: 1px; background: var(--pp-line);
}
/* Four ways in, two by two, rather than a row that wraps a lonely fourth. */
#gate .keys { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; }
#gate .keys .btn { width: 100%; }
#gate .err { color: #F0937C; font-size: .87rem; margin-top: 12px; min-height: 1.2em; }

@media (min-width: 720px) {
  #doorway .where { font-size: 38px; }
}

.app { display: none; flex: 1; min-height: 0; flex-direction: column;
       position: relative; z-index: 1; }
.app.on { display: flex; }

/* ─── the header, which collapses ────────────────────────────────
   A large title that shrinks into a compact bar on scroll. The collapse is
   driven by a class JS toggles at a threshold, never by a scroll-linked
   style write — a per-frame layout write here is exactly the jank the
   motion rule exists to prevent. */

.bar {
  position: relative; z-index: 30; flex: none;
  padding: calc(var(--safe-t) + 16px) 18px 12px;
  background: linear-gradient(180deg, var(--bg) 60%, rgba(18,16,13,.86) 88%, rgba(18,16,13,0));
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: 2px 12px;
  transition: padding var(--dur) var(--ease);
}
.bar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--line); opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.bar.tight { padding-top: calc(var(--safe-t) + 9px); padding-bottom: 9px; }
.bar.tight::after { opacity: 1; }

.bar h1 {
  grid-column: 1; margin: 0; font-size: 30px; font-weight: 680;
  letter-spacing: -.028em; line-height: 1.12;
  transform-origin: left center;
  transition: transform var(--dur) var(--ease);
}
.bar.tight h1 { transform: scale(.62); }

.bar .sub {
  grid-column: 1; grid-row: 2; color: var(--faint); font-size: 12.5px;
  transform-origin: left top;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.bar.tight .sub { opacity: 0; transform: translateY(-7px); }

/* The ember count is the one number that must never be hidden — it is what
   the whole board pays out in. It rides the collapse instead of leaving. */
.bar .tally {
  grid-column: 2; grid-row: 1 / span 2; align-self: center;
  display: flex; align-items: center; gap: 7px;
}

.bar .embers {
  color: var(--solar); font-size: 13px; font-weight: 640;
  font-variant-numeric: tabular-nums;
  display: flex; align-items: center; gap: 5px;
  padding: 5px 11px; border-radius: 99px;
  background: rgba(227,169,92,.10); border: 1px solid rgba(227,169,92,.22);
  transition: transform var(--dur) var(--spring);
}
.bar .embers.bump { transform: scale(1.16); }

.nosignal {
  display: none; flex: none; margin: 0;
  padding: 7px 18px; font-size: 12.5px; font-weight: 560;
  color: #f2d9b0; background: rgba(227,169,92,.13);
  border-bottom: 1px solid rgba(227,169,92,.24);
  animation: rise var(--dur) var(--ease) both;
}
body.offline .nosignal { display: block; }

/* Views stack; only one is on. The map keeps its DOM alive when hidden so
   MapLibre does not re-initialise (and re-download tiles) on every tab. */
.view { display: none; flex: 1; min-height: 0; position: relative; }
.view.on { display: block; }
.view.scroll {
  overflow-y: auto; -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  padding: 0 15px 30px;
}
.view.flush { padding: 0; }

/* The view that is arriving. Direction comes from which tab you left. */
.view.in-l { animation: slide-l var(--dur) var(--ease); }
.view.in-r { animation: slide-r var(--dur) var(--ease); }
@keyframes slide-l { from { opacity: 0; transform: translate3d(-16px,0,0); } }
@keyframes slide-r { from { opacity: 0; transform: translate3d(16px,0,0); } }

.wrap { max-width: 820px; margin: 0 auto; }

/* Reveal, don't animate: content arrives with opacity and a short lift.
   Staggered in tens — past ~240ms of delay it reads as lag, not rhythm. */
.rise { animation: rise var(--dur-slow) var(--ease) both; animation-delay: var(--d, 0ms); }
@keyframes rise { from { opacity: 0; transform: translate3d(0,10px,0); } }

/* ─── pull to refresh ────────────────────────────────────────────── */

.ptr {
  position: absolute; left: 50%; top: 0; z-index: 20;
  width: 30px; height: 30px; margin-left: -15px;
  border-radius: 50%; background: var(--panel-2);
  border: 1px solid var(--line-2); box-shadow: var(--shadow);
  display: grid; place-items: center;
  transform: translate3d(0, calc(var(--pull, 0px) - 42px), 0) rotate(var(--spin, 0deg));
  opacity: clamp(0, calc(var(--pull, 0px) / 46), 1);
  pointer-events: none;
}
.ptr::before {
  content: ""; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--solar); border-top-color: transparent;
}
.ptr.settling { transition: transform var(--dur) var(--spring), opacity var(--dur) var(--ease); }
.ptr.spinning::before { animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ─── tab bar ────────────────────────────────────────────────────── */

.tabs {
  position: relative; z-index: 40; flex: none;
  height: calc(var(--tab-h) + var(--safe-b));
  padding-bottom: var(--safe-b);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: rgba(22,19,15,.86);
  backdrop-filter: saturate(170%) blur(20px);
  -webkit-backdrop-filter: saturate(170%) blur(20px);
  border-top: 1px solid var(--line);
}
/* The indicator slides between tabs rather than blinking on — it is what
   tells a thumb the two views are side by side, not stacked. */
.tabs::before {
  content: ""; position: absolute; top: -1px; height: 2px; border-radius: 99px;
  width: calc(100% / 4); left: 0; background: var(--life);
  transform: translate3d(calc(var(--tabi, 0) * 100%), 0, 0) scaleX(.34);
  transition: transform var(--dur) var(--spring);
}
.tabs button {
  position: relative;
  background: none; border: 0; color: var(--faint);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; font-size: 10.5px; letter-spacing: .02em; padding: 0;
  transition: color var(--dur-fast) var(--ease);
}
/* One drawing at three sizes. They take colour from the text around them,
   which is why a selected tab, a section heading and a chip all look like
   they were drawn by the same hand — because they were. */
.ic { width: 22px; height: 22px; display: block; flex: none; }
.tabs button .ic { transition: transform var(--dur) var(--spring); }
.tabs button[aria-selected="true"] { color: var(--life); }
.tabs button[aria-selected="true"] .ic { transform: translateY(-1px) scale(1.14); }
.tabs button:active .ic { transform: scale(.88); }
.tabs button .dot {
  position: absolute; top: 6px; left: 50%; margin-left: 4px;
  min-width: 16px; height: 16px; border-radius: 99px;
  background: var(--now); color: #fff; font-size: 10px; font-weight: 700;
  display: grid; place-items: center; padding: 0 4px;
  box-shadow: 0 0 0 2px rgba(22,19,15,.9);
}

/* ─── today, the head of the board ───────────────────────────────
   The app used to open on a list of 31 things, which is a filing cabinet,
   not a farm. This is the one glance that answers "what is happening here
   today" before a thumb has moved. */

.today {
  position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-xl);
  background:
    radial-gradient(130% 100% at 100% 0%, rgba(227,169,92,.13), transparent 58%),
    linear-gradient(168deg, var(--panel-2), var(--panel) 62%);
  padding: 16px 17px 15px; margin: 4px 0 14px;
  box-shadow: var(--shadow);
}
.today .when { font-size: 11px; text-transform: uppercase; letter-spacing: .12em;
               color: var(--solar); font-weight: 660; }
.today .line { display: flex; align-items: center; gap: 14px; margin-top: 9px; }
.today .say { flex: 1; min-width: 0; }
.today .say b { display: block; font-size: 17.5px; font-weight: 640;
                letter-spacing: -.015em; line-height: 1.28; }
.today .say p { margin: 4px 0 0; color: var(--dim); font-size: 13px; }

/* the ring: done-today against the day's rota */
.ring { flex: none; width: 58px; height: 58px; position: relative; }
.ring svg { width: 100%; height: 100%; transform: rotate(-90deg); display: block; }
.ring circle { fill: none; stroke-width: 5; stroke-linecap: round; }
.ring .track { stroke: var(--line); }
.ring .live {
  stroke: var(--life);
  stroke-dasharray: var(--circ) var(--circ);
  stroke-dashoffset: var(--circ);
  transition: stroke-dashoffset 1.1s var(--ease);
}
.ring b { position: absolute; inset: 0; display: grid; place-items: center;
          font-size: 13px; font-weight: 660; font-variant-numeric: tabular-nums; }

.today .row { margin-top: 13px; }

/* ─── the filter, a segmented control ────────────────────────────── */

.seg {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  position: relative; padding: 3px; margin: 0 0 13px;
  background: rgba(0,0,0,.28); border: 1px solid var(--line);
  border-radius: 11px;
}
.seg::before {
  content: ""; position: absolute; top: 3px; bottom: 3px; left: 3px;
  width: calc((100% - 6px) / var(--segn, 4));
  background: var(--panel-2); border: 1px solid var(--line-2);
  border-radius: 8px; box-shadow: var(--shadow);
  transform: translate3d(calc(var(--segi, 0) * 100%), 0, 0);
  transition: transform var(--dur) var(--spring);
}
.seg button {
  position: relative; z-index: 1;
  background: none; border: 0; color: var(--faint);
  padding: 7px 4px; font-size: 12.5px; font-weight: 560; min-height: 36px;
  border-radius: 8px;
  transition: color var(--dur-fast) var(--ease);
}
.seg button[aria-selected="true"] { color: var(--ink); }
.seg button .n {
  font-variant-numeric: tabular-nums; color: var(--faint);
  font-size: 11px; margin-left: 4px;
}
.seg button[aria-selected="true"] .n { color: var(--solar); }

/* ─── cards ──────────────────────────────────────────────────────── */

.card {
  position: relative;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r); padding: 13px 15px 13px 17px; margin: 0 0 9px;
  box-shadow: var(--shadow);
  transition: transform var(--dur-fast) var(--ease), opacity var(--dur) var(--ease);
}
/* The priority stripe is a gradient inside the radius rather than a flat
   border-left, so the corner does not go square where the stripe meets it. */
.card::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  border-radius: var(--r) 0 0 var(--r);
  background: var(--stripe, var(--line));
}
.card.now { --stripe: linear-gradient(180deg, var(--now), rgba(215,109,93,.35)); }
.card.soon { --stripe: linear-gradient(180deg, var(--solar), rgba(227,169,92,.32)); }
.card.normal { --stripe: linear-gradient(180deg, var(--life), rgba(134,185,111,.3)); }
.card.someday { --stripe: var(--line-2); }

/* A proposal must not look like a decision. Dashed, dimmer ground, and it
   never gets the solid stripe a published mission has. */
.card.draft {
  border-style: dashed; background: var(--panel-2);
  box-shadow: none;
}
.card.draft::before { opacity: .45; }
.card.done { opacity: .48; }
.card.blocked { opacity: .72; }
.card[data-act-able]:active { transform: scale(.988); }

/* struck through the moment a thumb lands, before the server has answered */
.card.doing { opacity: .62; }

.card h3 { margin: 0; font-size: 16px; font-weight: 620; letter-spacing: -.01em;
           display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.card .brief { color: var(--dim); font-size: 13.5px; margin: 6px 0 0; }

.tags { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
/* inline-flex, because `.ic` is a block and an icon inside an inline-block
   tag breaks the line under it: a pill twice as tall as every other pill. */
.tag { display: inline-flex; align-items: center; gap: 4px;
  font-size: 11.5px; padding: 2px 8px; border-radius: 99px;
       border: 1px solid var(--line); color: var(--dim); background: rgba(0,0,0,.22); }
.tag.pri-now { color: var(--now); border-color: rgba(215,109,93,.42); }
.tag.recurs { color: var(--water); border-color: rgba(191,234,255,.3); }
.tag.points { color: var(--solar); border-color: rgba(227,169,92,.36);
              font-variant-numeric: tabular-nums; }

.origin { font-size: 10.5px; padding: 2px 8px; border-radius: 99px;
          font-weight: 660; letter-spacing: .03em; white-space: nowrap; }
.origin.heard { color: #d3e6c6; background: rgba(134,185,111,.17); }
.origin.inferred { color: #f2d9b0; background: rgba(227,169,92,.17); }
.origin.human { color: var(--faint); background: rgba(255,255,255,.05); }

.said { margin: 10px 0 0; padding: 8px 12px; border-left: 2px solid rgba(240,226,205,.28);
        color: var(--parchment); opacity: .78; font-size: 13px; font-style: italic; }
.said .who { display: block; font-style: normal; font-size: 11px;
             color: var(--faint); margin-top: 5px; }

.clips { margin-top: 9px; }
.clips summary { cursor: pointer; font-size: 12.5px; color: var(--faint);
                 list-style: none; padding: 3px 0; }
.clips summary::-webkit-details-marker { display: none; }
.clips summary::before { content: "▸"; display: inline-block; margin-right: 6px; }
.clips[open] summary::before { content: "▾"; }

.blockers { margin-top: 9px; font-size: 12.5px; color: var(--solar); }
.blockers b { font-weight: 600; }

.row { display: flex; gap: 8px; margin-top: 11px; flex-wrap: wrap; align-items: center; }

.btn { border-radius: 10px; padding: 8px 14px; font-size: 13.5px; font-weight: 500;
       border: 1px solid var(--line); background: var(--panel-2); min-height: 38px;
       transition: transform var(--dur-fast) var(--ease),
                   opacity var(--dur-fast) var(--ease),
                   border-color var(--dur-fast) var(--ease); }
.btn:active:not(:disabled) { transform: scale(.94); }
.btn:hover:not(:disabled) { border-color: var(--line-2); }
.btn:disabled { opacity: .4; cursor: default; }
.btn.go { background: var(--life); border-color: var(--life); color: #10210a; font-weight: 640; }
.btn.quiet { background: transparent; color: var(--dim); }
.btn.danger { background: transparent; color: var(--now); border-color: rgba(215,109,93,.32); }
.btn.sm { padding: 5px 11px; min-height: 32px; font-size: 12.5px; }

.done-note { color: var(--life); font-size: 13px; }
.msg { color: var(--now); font-size: 13px; margin-top: 8px; }
.msg[hidden] { display: none; }

/* ─── section headers ────────────────────────────────────────────── */

.sec { margin: 22px 0 0; }
.sec > h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .1em;
            color: var(--faint); margin: 0 0 2px; font-weight: 640;
            display: flex; align-items: center; gap: 8px; }
.sec > h2 .ic { width: 16px; height: 16px; color: var(--life); opacity: .75; }
.sec > h2 .k { margin-left: auto; text-transform: none; letter-spacing: 0;
               font-weight: 500; color: var(--faint); font-variant-numeric: tabular-nums; }
.sec > .blurb { color: var(--faint); font-size: 12.5px; margin: 0 0 10px; }

.banner { border: 1px dashed var(--solar); border-radius: var(--r);
          padding: 13px 15px; margin: 14px 0 4px;
          background: linear-gradient(180deg, rgba(227,169,92,.10), rgba(227,169,92,.03)); }
.banner b { color: var(--solar); }
.banner p { margin: 5px 0 0; color: var(--dim); font-size: 13px; }

.empty { color: var(--faint); padding: 40px 16px; text-align: center; font-size: 14px; }
.empty .ic.big { width: 34px; height: 34px; margin: 0 auto 12px; opacity: .38; }

/* ─── skeletons ──────────────────────────────────────────────────
   The app used to show nothing at all until four RPCs had answered, then
   snap into a full board. On a phone in a field that reads as broken. The
   skeleton is the same shape as what replaces it, so nothing jumps. */

.sk { border-radius: var(--r); background: var(--panel);
      border: 1px solid var(--line); padding: 13px 15px; margin: 0 0 9px; }
.sk i { display: block; height: 11px; border-radius: 6px; margin: 0 0 8px;
        background: linear-gradient(90deg, var(--panel-2) 25%, var(--line) 50%, var(--panel-2) 75%);
        background-size: 280% 100%; animation: sweep 1.35s var(--ease) infinite; }
.sk i:last-child { margin-bottom: 0; }
.sk i.w70 { width: 70%; } .sk i.w45 { width: 45%; } .sk i.w88 { width: 88%; }
.sk i.tall { height: 16px; }
@keyframes sweep { from { background-position: 140% 0; } to { background-position: -40% 0; } }

/* ─── stats ──────────────────────────────────────────────────────── */

.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(88px, 1fr));
         gap: 8px; margin: 12px 0 4px; }
.stat { background: var(--panel); border: 1px solid var(--line);
        border-radius: var(--r); padding: 11px 12px; box-shadow: var(--shadow); }
.stat .n { font-size: 20px; font-weight: 660; font-variant-numeric: tabular-nums;
           letter-spacing: -.02em; }
.stat .l { font-size: 11px; color: var(--faint); margin-top: 1px; }
.stat.a .n { color: var(--solar); }
.stat.b .n { color: var(--life); }

/* progress through a phase */
.meter { height: 4px; border-radius: 99px; background: var(--line); overflow: hidden;
         margin-top: 8px; }
.meter i { display: block; height: 100%; background: var(--life); border-radius: 99px;
           transform-origin: left; animation: fill .9s var(--ease) both; }
@keyframes fill { from { transform: scaleX(0); } }

/* ─── the map ────────────────────────────────────────────────────── */

#mapcanvas { position: absolute; inset: 0; }
.maprap { position: relative; height: 100%; }

.maplibregl-ctrl-attrib { font-size: 10px !important; }
.maplibregl-ctrl-bottom-right,
.maplibregl-ctrl-bottom-left { margin-bottom: 8px; }

/* a pin */
.pin { display: grid; place-items: center; width: 34px; height: 34px;
       border-radius: 50% 50% 50% 6px; transform: rotate(-45deg);
       background: var(--panel); border: 2px solid var(--life);
       box-shadow: 0 3px 10px rgba(0,0,0,.55); cursor: pointer;
       animation: drop-in .45s var(--spring) both; animation-delay: var(--d, 0ms);
       transition: transform var(--dur) var(--spring); }
.pin:active { transform: rotate(-45deg) scale(.88); }
.pin > .ic { transform: rotate(45deg); width: 16px; height: 16px; color: var(--ink); }
.pin.hot { border-color: var(--now); }
.pin.warm { border-color: var(--solar); }
.pin .count { position: absolute; top: -5px; right: -5px; transform: rotate(45deg);
              min-width: 18px; height: 18px; border-radius: 99px; background: var(--now);
              color: #fff; font-size: 10.5px; font-weight: 700; display: grid;
              place-items: center; padding: 0 4px; border: 1.5px solid var(--bg); }
@keyframes drop-in { from { opacity: 0; transform: rotate(-45deg) translate(6px,-6px) scale(.4); } }

/* A hot pin breathes. One pin at a time, and only the ones that are urgent —
   an app where everything pulses is an app nobody looks at. */
.pin.hot::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit;
  border: 2px solid var(--now); animation: halo 2.4s var(--ease) infinite;
}
@keyframes halo {
  0% { opacity: .8; transform: scale(1); }
  70%, 100% { opacity: 0; transform: scale(1.75); }
}

/* What the map is doing. A blank dark rectangle is the same picture whether
   the tiles are still coming, the style failed, or the browser refused a 3D
   canvas — and only one of those is worth waiting for. */
.mapstate {
  position: absolute; left: 50%; top: 46%; z-index: 7;
  transform: translate(-50%, -50%);
  width: min(86%, 330px); text-align: center;
  background: rgba(26,23,18,.93);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 18px 20px; box-shadow: var(--shadow-up);
  font-size: 13px; color: var(--dim); line-height: 1.45;
  animation: rise var(--dur) var(--ease) both;
}
.mapstate b { display: block; color: var(--ink); font-size: 15px;
              font-weight: 620; letter-spacing: -.01em; margin-bottom: 5px; }
.mapstate .spin {
  width: 22px; height: 22px; border-radius: 50%; margin: 0 auto 12px;
  border: 2px solid var(--line-2); border-top-color: var(--solar);
  animation: spin .8s linear infinite;
}

/* the unpinned list, floated over the map */
.unpinned {
  position: absolute; left: 12px; right: 12px; bottom: 12px;
  background: rgba(26,23,18,.9); backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: 12px 14px; max-height: 46%; overflow-y: auto; z-index: 5;
  box-shadow: var(--shadow-up);
  animation: rise var(--dur-slow) var(--ease) both;
}
.unpinned h4 { margin: 0 0 3px; font-size: 13px; font-weight: 620; }
.unpinned p { margin: 0 0 9px; font-size: 12px; color: var(--faint); }
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip { border: 1px solid var(--line); background: var(--panel-2); color: var(--dim);
        border-radius: 99px; padding: 6px 12px 6px 9px; font-size: 12.5px;
        display: inline-flex; align-items: center; gap: 6px;
        transition: transform var(--dur-fast) var(--ease); }
.chip .ic { width: 15px; height: 15px; color: var(--faint); }
.chip[aria-pressed="true"] .ic { color: var(--life); }
.chip:active { transform: scale(.93); }
.chip[aria-pressed="true"] { border-color: var(--life); color: var(--life); }

.placing {
  position: absolute; left: 12px; right: 12px; top: 12px; z-index: 6;
  background: rgba(227,169,92,.94); color: #201703; border-radius: var(--r);
  padding: 11px 14px; font-size: 13.5px; font-weight: 560;
  display: flex; align-items: center; gap: 10px;
  box-shadow: var(--shadow); animation: rise var(--dur) var(--ease) both;
}
.placing .btn { background: rgba(0,0,0,.16); border-color: rgba(0,0,0,.2);
                color: #201703; margin-left: auto; }

/* the crosshair you drop a pin under */
.cross { position: absolute; left: 50%; top: 50%; z-index: 5;
         transform: translate(-50%,-50%); pointer-events: none; }
.cross::before, .cross::after { content: ""; position: absolute; background: var(--solar); }
.cross::before { left: -14px; width: 28px; height: 2px; top: -1px; }
.cross::after { top: -14px; height: 28px; width: 2px; left: -1px; }

/* ─── sheet ──────────────────────────────────────────────────────
   Drag-to-dismiss, because a sheet with a grab handle that cannot be grabbed
   is a screenshot of a native app rather than one. While the thumb is down
   the transition is off and the sheet tracks the finger exactly; on release
   it springs to open or closed. */

.sheet-bg { position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 50;
            opacity: 0; transition: opacity var(--dur) var(--ease);
            backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }
.sheet-bg.on { opacity: 1; }
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 51;
  background: var(--bg-2); border-top: 1px solid var(--line-2);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 8px 16px calc(var(--safe-b) + 20px);
  max-height: 88%; overflow-y: auto; overscroll-behavior: contain;
  box-shadow: var(--shadow-up);
  transform: translate3d(0, 100%, 0);
  transition: transform var(--dur-slow) var(--spring);
}
.sheet.on { transform: translate3d(0, var(--drag, 0px), 0); }
.sheet.dragging { transition: none; }
.sheet .grab { width: 38px; height: 5px; border-radius: 99px; background: var(--line-2);
               margin: 6px auto 12px; cursor: grab; }
.sheet .grab::before { content: ""; position: absolute; left: 0; right: 0; top: 0;
                       height: 44px; } /* the handle's real target */
.sheet h2 { margin: 0 0 4px; font-size: 20px; letter-spacing: -.018em; }

/* ─── toast ──────────────────────────────────────────────────────── */

.toast {
  position: fixed; left: 50%; z-index: 60;
  bottom: calc(var(--tab-h) + var(--safe-b) + 14px);
  max-width: min(92vw, 420px);
  padding: 10px 16px; border-radius: 99px;
  background: rgba(38,32,25,.94); border: 1px solid var(--line-2);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  box-shadow: var(--shadow-up);
  font-size: 13.5px; text-align: center;
  transform: translate3d(-50%, 16px, 0) scale(.94); opacity: 0;
  transition: transform var(--dur) var(--spring), opacity var(--dur) var(--ease);
  pointer-events: none;
}
.toast.on { transform: translate3d(-50%, 0, 0) scale(1); opacity: 1; }
.toast.bad { color: #f7c9c2; border-color: rgba(215,109,93,.45); }
.toast.good { color: #d9ecca; border-color: rgba(134,185,111,.4); }

/* ─── the ember burst ────────────────────────────────────────────
   Doing a job pays embers. The count in the header changing by itself is a
   receipt nobody reads; embers flying up to it is the one moment this app
   is allowed to be pleased with somebody. transform/opacity only, removed
   from the DOM on animationend, and skipped entirely under reduced motion. */

.ember {
  position: fixed; z-index: 55; pointer-events: none;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--solar); box-shadow: 0 0 9px 2px rgba(227,169,92,.6);
  animation: ember 1s var(--ease) both; animation-delay: var(--d, 0ms);
}
@keyframes ember {
  0% { opacity: 0; transform: translate3d(0,0,0) scale(.4); }
  12% { opacity: 1; transform: translate3d(0,0,0) scale(1); }
  100% { opacity: 0; transform: translate3d(var(--dx), var(--dy), 0) scale(.3); }
}

/* ─── forms ──────────────────────────────────────────────────────── */

label.f { display: block; font-size: 11px; color: var(--faint);
          text-transform: uppercase; letter-spacing: .08em; margin: 11px 0 4px; }
.f-in, textarea.f-in, select.f-in {
  width: 100%; font-size: 16px; /* 16px or iOS zooms the whole app on focus */
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: 10px; padding: 10px 11px;
  transition: border-color var(--dur-fast) var(--ease);
}
.f-in:focus { outline: none; border-color: var(--life); }
textarea.f-in { min-height: 76px; resize: vertical; }
.f2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

/* ─── hands ──────────────────────────────────────────────────────── */

.hand { display: flex; align-items: center; gap: 11px; padding: 10px 0;
        border-bottom: 1px solid var(--line); }
.hand:last-child { border-bottom: 0; }
.hand .av { width: 34px; height: 34px; border-radius: 50%; display: grid;
            place-items: center; background: var(--panel-2); border: 1px solid var(--line);
            font-size: 14px; font-weight: 640; color: var(--dim); flex: none; }
.hand .who { flex: 1; min-width: 0; }
.hand .who b { display: block; font-size: 14.5px; font-weight: 580; }
.hand .who span { font-size: 12px; color: var(--faint); }
.hand select { font-size: 12.5px; padding: 5px 8px; border-radius: 8px;
               background: var(--panel-2); border: 1px solid var(--line); }

/* A claim link, shown once so it can be copied. Monospaced because it will
   be read aloud and retyped at least once. */
.mono-code {
  display: block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; letter-spacing: .12em; color: var(--solar);
}
.linkout {
  margin-top: 16px; padding: 14px 15px; border-radius: var(--r);
  border: 1px solid rgba(227,169,92,.3);
  background: linear-gradient(180deg, rgba(227,169,92,.10), rgba(227,169,92,.03));
}
.linkout .eyebrow-lite {
  font-size: 11px; text-transform: uppercase; letter-spacing: .1em;
  color: var(--solar); font-weight: 640; margin-bottom: 7px;
}
.linkout code {
  display: block; word-break: break-all;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; color: var(--ink); line-height: 1.5;
}

.feed { font-size: 13px; color: var(--dim); padding: 7px 0;
        border-bottom: 1px solid var(--line); }
.feed:last-child { border-bottom: 0; }
.feed b { color: var(--ink); font-weight: 560; }
.feed .t { color: var(--faint); font-size: 11.5px; }

@media (min-width: 720px) {
  .bar h1 { font-size: 34px; }
  .view.scroll { padding-left: 22px; padding-right: 22px; }
}

/* ─── reduced motion ─────────────────────────────────────────────
   Not optional. Everything above degrades to the same app with no travel:
   the states are all reachable, they just arrive instead of moving. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .ember { display: none; }
  .pin.hot::after { display: none; }
}

/* ══════════════════════════════════════════════════════════════════
   THE ROUND

   Everything below is the twice-a-day job: the two tiles somebody actually
   looks at, the stepper that walks them through doing one, and the moment
   it pays.

   The tiles are the one place in this app where colour carries meaning on
   its own, so each state also carries a word and an icon — in a field, in
   sun, through a cracked screen protector, hue is the first thing to go.
   ══════════════════════════════════════════════════════════════════ */

/* ─── the streak, in the header ─────────────────────────────────── */
.streak-chip {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 12px; font-weight: 640; font-variant-numeric: tabular-nums;
  color: var(--solar); padding: 2px 8px 2px 6px; border-radius: 99px;
  border: 1px solid rgba(227,169,92,.3); background: rgba(227,169,92,.09);
}
.streak-chip .ic { width: 13px; height: 13px; }
.streak-chip.hot { color: var(--now); border-color: rgba(215,109,93,.4);
  background: rgba(215,109,93,.1); }

.today .streak {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  font-size: 12.5px; color: var(--solar);
}
.today .streak .ic { width: 15px; height: 15px; }
.today .streak b { font-weight: 660; font-variant-numeric: tabular-nums; }
.today .streak .best { color: var(--faint); margin-left: 4px; }
.today .streak.hot { color: var(--now); }

/* The seal replaced an earlier, flatter tile. Its styles came out with it:
   two renderers for the same three states is how two screens end up
   disagreeing about what "held" looks like. See QUEENDOM SLATE below.

   The proof thumbnail stayed, because three screens use it: the seal, the
   sheet a finished round opens into, and the panel's feed. It went out with
   the rest once and every photograph on the board vanished with it. */
.shot {
  display: block; width: 40px; height: 40px; border-radius: 8px; flex: none;
  background: var(--panel-2) center/cover no-repeat;
  border: 1px solid var(--line-2); opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.shot.loaded { opacity: 1; }
.shot.gone { opacity: .25; }
.shot.big { width: 100%; height: 190px; border-radius: var(--r); }

/* ─── doing one: the stepper ────────────────────────────────────── */
.sheet .blurb.sub { margin: -4px 0 14px; color: var(--dim); font-size: 13px; }
.sheet .blurb.tight { margin: -4px 0 8px; font-size: 12px; color: var(--faint); }
.what { margin: 0 0 14px; }
.what > summary { font-size: 12.5px; color: var(--dim); cursor: pointer;
  padding: 7px 0; list-style: none; }
.what > summary::-webkit-details-marker { display: none; }
.what > summary::before { content: "▸ "; color: var(--faint); }
.what[open] > summary::before { content: "▾ "; }
.what > p { margin: 0 0 4px; color: var(--dim); font-size: 13.5px; }

.steps { list-style: none; margin: 0 0 16px; padding: 0;
  display: flex; flex-direction: column; gap: 10px; }
.steps li {
  display: grid; grid-template-columns: 1fr 76px; grid-template-rows: auto auto;
  gap: 2px 12px; align-items: center;
  padding: 12px; border-radius: var(--r); border: 1px solid var(--line);
  background: var(--panel); opacity: .5;
  transition: opacity var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.steps li.on { opacity: 1; border-color: var(--line-2); }
.steps li.ok { opacity: 1; }
.steps li b { font-size: 14.5px; font-weight: 620; }
.steps li span { font-size: 12.5px; color: var(--faint); }
.steps li .thumb { grid-row: 1 / 3; grid-column: 2; }

.thumb {
  width: 76px; height: 76px; border-radius: 12px; overflow: hidden;
  border: 1px dashed var(--line-2); background: var(--panel-2);
  display: grid; place-items: center; position: relative; cursor: pointer;
}
.thumb .ic { width: 22px; height: 22px; color: var(--faint); }
.thumb.has { border-style: solid; border-color: var(--life); }
.thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb .re {
  position: absolute; inset: auto 0 0 0; background: rgba(0,0,0,.62);
  color: #fff; font-size: 10px; padding: 3px 0; text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 3px;
}
.thumb .re .ic { width: 11px; height: 11px; color: #fff; }

.row.big { flex-direction: column; align-items: stretch; gap: 8px; }
.btn.grand {
  min-height: 56px; font-size: 16px; border-radius: var(--r);
  display: flex; align-items: center; justify-content: center; gap: 9px;
}
.btn.grand .ic { width: 20px; height: 20px; }

/* The upload, which on a farm connection is the part with a wait in it. */
.up { height: 3px; border-radius: 99px; background: var(--line); overflow: hidden;
  margin-top: 10px; }
.up i { display: block; height: 100%; background: var(--life);
  transform: scaleX(.04); transform-origin: left;
  transition: transform .3s var(--ease); }

.proof { display: grid; gap: 10px; margin: 12px 0; }
.proof figure { margin: 0; }
.proof figcaption { font-size: 11px; color: var(--faint); margin-top: 5px;
  text-transform: uppercase; letter-spacing: .1em; }

/* ─── the moment it pays ────────────────────────────────────────── */
.reward {
  position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
  background: rgba(8,7,5,.72); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity var(--dur) var(--ease); padding: 24px;
}
.reward.on { opacity: 1; }
.reward .card {
  width: 100%; max-width: 320px; text-align: center; padding: 26px 20px 20px;
  border-radius: var(--r-xl); background: var(--panel);
  border: 1px solid var(--line-2); box-shadow: var(--shadow);
  transform: translate3d(0,14px,0) scale(.96);
  transition: transform var(--dur-slow) var(--spring);
}
.reward.on .card { transform: none; }
.reward .big { font-size: 46px; font-weight: 700; color: var(--solar);
  font-variant-numeric: tabular-nums; letter-spacing: -.03em; line-height: 1; }
.reward .for { font-size: 13px; color: var(--dim); margin-top: 6px; }
.reward ul { list-style: none; margin: 18px 0; padding: 0;
  border-top: 1px solid var(--line); }
.reward li { display: flex; justify-content: space-between; padding: 8px 2px;
  border-bottom: 1px solid var(--line); font-size: 13px; color: var(--dim); }
.reward li b { color: var(--ink); font-variant-numeric: tabular-nums; }
.reward li.late { color: var(--faint); }
.reward .btn { width: 100%; justify-content: center; }

/* ─── the crew ──────────────────────────────────────────────────── */
.hand.rank { gap: 9px; }
.hand.rank .pos { width: 18px; text-align: center; color: var(--faint);
  font-size: 12px; font-variant-numeric: tabular-nums; flex: none; }
.hand.rank.me .av { background: var(--life); color: #10210a; }
.hand.rank .pts { font-size: 16px; font-weight: 660; color: var(--solar);
  font-variant-numeric: tabular-nums; flex: none; }
/* 🪤 Not `.bar` — that is the app's header, and a week's progress line
   inheriting a sticky header's padding is a 22px pill where a 3px rule was
   meant to be. */
.hand .who b { display: flex; align-items: center; gap: 6px; }
.hand .who .fl { color: var(--solar); font-weight: 500; font-size: 11.5px;
  display: inline-flex; align-items: center; gap: 2px; }
.hand .who .fl .ic { width: 12px; height: 12px; }
.hand .who .wk { display: block; height: 3px; border-radius: 99px;
  background: var(--line); margin: 6px 0 5px; overflow: hidden; }
.hand .who .wk i { display: block; height: 100%; background: var(--solar);
  transform-origin: left; border-radius: 99px; }

.badges { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 10px; }
.badge { text-align: center; padding: 12px 6px; border-radius: var(--r);
  border: 1px dashed var(--line); opacity: .42; }
.badge .ic.big { width: 26px; height: 26px; margin: 0 auto 7px; display: block; }
.badge b { display: block; font-size: 12.5px; font-weight: 600; }
.badge span { display: block; font-size: 10.5px; color: var(--faint); margin-top: 2px; }
.badge.got { opacity: 1; border-style: solid; border-color: rgba(227,169,92,.4);
  background: rgba(227,169,92,.06); }
.badge.got .ic { color: var(--solar); }

hr.rule { border: 0; border-top: 1px solid var(--line); margin: 30px 0 0; }
.eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: .14em;
  color: var(--faint); margin: 16px 0 0; }

/* ─── the chips a lead picks with ───────────────────────────────── */
.chip.on { border-color: var(--life); color: var(--life);
  background: rgba(134,185,111,.09); }
.chip.on .ic { color: var(--life); }
.chip i { font-style: normal; color: var(--faint); font-size: 11px; }
.chip.on i { color: rgba(134,185,111,.75); }
label.f i { font-style: normal; color: var(--faint); text-transform: none;
  letter-spacing: 0; }
.tag.points.boosted { color: var(--now); border-color: rgba(215,109,93,.45);
  background: rgba(215,109,93,.08); }
.boostnote { display: flex; align-items: center; gap: 6px; color: var(--solar) !important; }
.boostnote .ic { width: 13px; height: 13px; flex: none; }
.round.boosted > h2 { color: var(--solar); }
.feed .shots { display: flex; gap: 5px; margin-top: 7px; }
.feed .btn.sm { margin-top: 8px; }

/* A grid item's default `min-width: auto` is its content's minimum, so two
   tiles whose top row is a name, the hours and an icon refuse to go below
   about 220px each — and 2 × 220 + the gap is wider than a phone. The whole
   document then scrolls sideways, which on a fixed-position app reads as the
   tab bar being cut in half. Both halves of the fix are needed: the track
   may shrink, and the text inside it may be clipped. */
.seals { min-width: 0; }
.seal, .seal > span { min-width: 0; }
.seal .when { overflow: hidden; }
.seal .state, .seal .foot { overflow-wrap: anywhere; }
.steps li { min-width: 0; }
.steps li b, .steps li span { overflow-wrap: anywhere; }
.hand .who b, .hand .who span { overflow-wrap: anywhere; }

/* A checkbox is not a field label — it should read as a sentence somebody
   is agreeing to, not as a heading in small caps. */
label.check { display: flex; align-items: center; gap: 9px; margin: 12px 0 4px;
  font-size: 13.5px; color: var(--ink); text-transform: none; letter-spacing: 0; }
label.check input { width: 18px; height: 18px; accent-color: var(--life); flex: none; }
.sheet .eyebrow-sm { font-size: 11px; text-transform: uppercase; letter-spacing: .14em;
  color: var(--faint); margin: 0 0 2px; }
.sheet h2 + .eyebrow-sm { margin-top: 8px; }
.heads-up { display: flex; gap: 8px; align-items: flex-start; margin: 0 0 14px;
  padding: 10px 12px; border-radius: var(--r); font-size: 13px;
  border: 1px solid rgba(227,169,92,.34); background: rgba(227,169,92,.07);
  color: var(--solar); }
.heads-up .ic { width: 16px; height: 16px; flex: none; margin-top: 1px; }

.tag .ic { width: 12px; height: 12px; flex: none; opacity: .8; }

/* ══════════════════════════════════════════════════════════════════
   QUEENDOM SLATE

   The board had the right bones and the wrong surface: it read as a web
   page about a farm rather than as something you carry. This layer is the
   surface. Four ideas, and everything below is one of them.

   1 · IT IS MADE OF SOMETHING. Grain over the whole app, a vignette at the
       edges, and cards that are lit rather than filled. A flat #1e1a15
       rectangle is the single loudest "template" tell there is.
   2 · THE ANIMALS ARE IN IT. The round is drawn over two and a half seconds
       of the actual sheep, shot in the actual stable. No illustration of a
       farm beats one second of the farm.
   3 · A ROUND IS A TRIAL, NOT A ROW. Corner brackets, a watermark glyph, a
       wax seal when it is done. The three states are three different
       objects, not three colours of the same object.
   4 · THE MOTION RULE STILL HOLDS. One curve, transform and opacity only,
       and every glow, pulse and sweep stops dead under
       `prefers-reduced-motion`. An app used in a field at 5am with one bar
       does not get to be expensive.
   ══════════════════════════════════════════════════════════════════ */

:root {
  --rune:  #9fd8ff;          /* the lit edge of a thing that wants pressing */
  --gold:  #e9c46a;
  --wax:   #c1543f;
  --ink-2: rgba(244,239,229,.78);

  /* One grain, used twice: over the ground, and inside a card. Generated
     rather than fetched — a texture that is a request is a texture that is
     missing for the first second. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.42'/%3E%3C/svg%3E");
}

/* The ground: grain, and a vignette that keeps the eye off the corners. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background-image: var(--grain);
  opacity: .16; mix-blend-mode: overlay;
}
.app { position: relative; z-index: 2; }
.bar, .tabs { position: relative; z-index: 31; }

.view.scroll::after {
  content: ""; position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(130% 88% at 50% 42%, transparent 52%, rgba(6,5,4,.55));
}

/* ─── the display face ──────────────────────────────────────────
   Instrument Serif italic, and only ever on a quest title. One face used
   in one place is a voice; the same face on six things is a theme. */
.serif { font-family: "Instrument Serif", Georgia, serif; font-style: italic;
  font-weight: 400; letter-spacing: -.005em; }

.eyebrow-rune {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .18em;
  color: var(--rune); opacity: .85;
  display: flex; align-items: center; gap: 7px;
}
.eyebrow-rune::before, .eyebrow-rune::after {
  content: ""; height: 1px; flex: 1;
  background: linear-gradient(90deg, transparent, rgba(159,216,255,.42));
}
.eyebrow-rune::before { background: linear-gradient(90deg, rgba(159,216,255,.42), transparent); flex: 0 0 14px; }

/* ─── corner brackets ───────────────────────────────────────────
   Four <use> of one 28px drawing. `pointer-events: none` because they sit
   over a card somebody taps. */
.corners { position: absolute; inset: 0; pointer-events: none; color: var(--gold); }
.corners .ic { position: absolute; width: 22px; height: 22px; opacity: .5; }
.corners .ic:nth-child(1) { top: 7px; left: 7px; }
.corners .ic:nth-child(2) { top: 7px; right: 7px; transform: scaleX(-1); }
.corners .ic:nth-child(3) { bottom: 7px; left: 7px; transform: scaleY(-1); }
.corners .ic:nth-child(4) { bottom: 7px; right: 7px; transform: scale(-1); }

/* ══════════════════════════════════════════════════════════════════
   THE QUEST BANNER
   ══════════════════════════════════════════════════════════════════ */

.quest {
  position: relative; overflow: hidden;
  border-radius: var(--r-xl); margin: 4px 0 0;
  border: 1px solid var(--line-2);
  background: var(--slate, #16130f);
  box-shadow: var(--shadow), inset 0 1px 0 rgba(244,239,229,.05);
  isolation: isolate;
}
.quest .qv {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
  /* The stable is warm and bright and the type is not. Pull it down and
     cool it slightly so white text has somewhere to sit. */
  filter: saturate(.72) brightness(.52) contrast(1.06);
  transform: scale(1.04);
}
.quest .qshade {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(10,9,7,.30) 0%, rgba(10,9,7,.72) 46%, rgba(10,9,7,.94) 100%),
    radial-gradient(120% 90% at 78% 12%, rgba(233,196,106,.16), transparent 62%);
}
.quest .qgrain {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background-image: var(--grain); opacity: .22; mix-blend-mode: overlay;
}
.quest .qbody { position: relative; z-index: 3; padding: 15px 16px 16px; }

.quest .qhead { display: flex; align-items: center; gap: 13px; margin: 11px 0 0; }

/* The portrait. A real animal, in a frame, looking at you — the one thing
   on this screen that no amount of iconography could have done. */
.portrait {
  flex: none; width: 58px; height: 58px; border-radius: 50%;
  overflow: hidden; position: relative; display: block;
  border: 1.5px solid rgba(233,196,106,.55);
  box-shadow: 0 0 0 4px rgba(18,16,13,.75), 0 6px 18px -8px #000;
}
.portrait img { width: 100%; height: 100%; object-fit: cover; display: block;
  transform: scale(1.06); }
.portrait::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: linear-gradient(160deg, rgba(255,255,255,.14), transparent 46%);
}

.quest h2 {
  margin: 0; font-size: 27px; line-height: 1.06; color: var(--ink);
  text-shadow: 0 2px 14px rgba(0,0,0,.6);
}
.quest .qsub { margin: 11px 0 0; font-size: 13px; color: var(--ink-2); }
.quest .qsub b { color: var(--ink); font-weight: 600; }

/* The counter, bottom right of the banner: what today is worth in total. */
.quest .qtotal {
  position: absolute; z-index: 3; right: 15px; top: 14px;
  display: flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 640; color: var(--gold);
  font-variant-numeric: tabular-nums;
  padding: 5px 10px 5px 8px; border-radius: 99px;
  background: rgba(10,9,7,.55); border: 1px solid rgba(233,196,106,.28);
  backdrop-filter: blur(3px);
}
.quest .qtotal .ic { width: 13px; height: 13px; }

/* ══════════════════════════════════════════════════════════════════
   THE SEALS — one per shift
   ══════════════════════════════════════════════════════════════════ */

.seals { display: grid; grid-template-columns: 1fr 1fr; gap: 11px; margin-top: 11px; }
@media (max-width: 370px) { .seals { grid-template-columns: 1fr; } }

.seal {
  -webkit-appearance: none; appearance: none; text-align: left; color: var(--ink);
  position: relative; overflow: hidden; isolation: isolate;
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  min-height: 158px; padding: 14px 13px 13px;
  border-radius: var(--r-lg); border: 1px solid var(--line);
  background:
    linear-gradient(180deg, rgba(244,239,229,.045), transparent 42%),
    var(--panel);
  box-shadow: inset 0 1px 0 rgba(244,239,229,.05), 0 4px 14px -10px #000;
  transition: transform var(--dur-fast) var(--ease),
              border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.seal:active { transform: scale(.972); }
.seal > * { position: relative; z-index: 2; }

/* The watermark: the shift's own glyph, enormous and nearly gone. */
.seal .mark {
  position: absolute; z-index: 0; right: -16px; bottom: -20px;
  width: 116px; height: 116px; opacity: .06; color: var(--ink);
  transform: rotate(-8deg);
}
.seal .corners { z-index: 1; }
.seal .corners .ic { width: 16px; height: 16px; opacity: .3; }

.seal .when {
  display: flex; align-items: center; gap: 6px;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .13em;
  color: var(--faint);
}
.seal .when .ic { width: 14px; height: 14px; }
.seal .name { font-size: 17px; line-height: 1.1; margin: 1px 0 0; }
.seal .hours { font-size: 11px; color: var(--faint);
  font-variant-numeric: tabular-nums; letter-spacing: .02em; }

.seal .state { margin-top: auto; font-size: 14.5px; font-weight: 600;
  display: flex; align-items: center; gap: 6px; }
.seal .state .ic { width: 16px; height: 16px; }
.seal .foot { font-size: 11.5px; color: var(--faint); }

/* 🪤 Not `.reward` — that is the full-screen quest-complete overlay, which
   is `position: fixed` at `opacity: 0` until it is shown. Naming the points
   chip the same thing made it invisible in every tile, with a bounding box
   the right size and in the right place, which is the hardest kind of
   missing thing to see. Same family of mistake as `.bar`. */
.seal .prize {
  display: inline-flex; align-items: center; gap: 4px; margin-top: 3px;
  align-self: flex-start;
  font-size: 13px; font-weight: 700; color: var(--gold);
  font-variant-numeric: tabular-nums;
  padding: 3px 9px 3px 7px; border-radius: 99px;
  background: rgba(233,196,106,.09); border: 1px solid rgba(233,196,106,.26);
}
.seal .prize .ic { width: 13px; height: 13px; }

/* ── open, and it is this shift's hours ──
   The one object on the screen that is lit. Everything else is matte. */
.seal.open.live {
  border-color: rgba(159,216,255,.5);
  background:
    radial-gradient(110% 80% at 50% 118%, rgba(159,216,255,.16), transparent 70%),
    linear-gradient(180deg, rgba(159,216,255,.06), transparent 44%),
    var(--panel);
}
.seal.open.live .state { color: var(--rune); }
.seal.open.live .when .ic, .seal.open.live .corners { color: var(--rune); }
.seal.open.live .corners .ic { opacity: .6; }
.seal.open.live::after {
  content: ""; position: absolute; z-index: 1; left: 12%; right: 12%; bottom: -1px;
  height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--rune), transparent);
  animation: breathe 3.4s var(--ease) infinite;
}
@keyframes breathe { 0%,100% { opacity: .38; transform: scaleX(.76); }
                     50%     { opacity: 1;   transform: scaleX(1); } }

/* A sweep of light across the lit tile, once every few seconds. It is the
   only thing in the app that moves on its own, which is exactly why it is
   the thing a thumb goes to. */
.seal.open.live::before {
  content: ""; position: absolute; z-index: 1; inset: 0;
  background: linear-gradient(105deg, transparent 32%, rgba(159,216,255,.13) 48%, transparent 62%);
  transform: translateX(-120%);
  animation: sweep 5.2s var(--ease) infinite;
}
@keyframes sweep { 0%, 62% { transform: translateX(-120%); }
                   92%, 100% { transform: translateX(120%); } }

.seal.open.past { border-style: dashed; border-color: rgba(215,109,93,.45); }
.seal.open.past .state { color: var(--now); }
.seal.open.soon { opacity: .9; }
.seal.open.soon .state { color: var(--dim); }

/* ── held ── */
.seal.held { border-color: rgba(233,196,106,.45); }
.seal.held .state { color: var(--gold); }
.seal.held .state::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--gold);
  animation: throb 1.7s var(--ease) infinite; flex: none;
}
@keyframes throb { 0%,100% { opacity: .35; transform: scale(.82); }
                   50% { opacity: 1; transform: scale(1); } }

/* ── done: it gets a stamp ── */
.seal.done {
  background: linear-gradient(180deg, rgba(134,185,111,.05), transparent 40%), var(--bg-2);
  border-color: rgba(134,185,111,.26);
}
.seal.done .state { color: var(--life); }
.seal.done .stamp {
  position: absolute; z-index: 3; right: 9px; top: 9px;
  width: 46px; height: 46px; color: var(--life); opacity: .5;
  transform: rotate(-11deg);
}
.seal.done .mark { opacity: .04; }
.seal.waiting { border-color: rgba(191,234,255,.34); }
.seal.waiting .state { color: var(--water); }

.seal .shots { display: flex; gap: 5px; margin-top: 2px; }

/* ══════════════════════════════════════════════════════════════════
   THE LEVEL — a diamond in the header
   ══════════════════════════════════════════════════════════════════ */

.lvl {
  position: relative; width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
}
.lvl svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.lvl svg circle { fill: none; stroke-width: 2.5; stroke-linecap: round; }
.lvl .track { stroke: var(--line); }
.lvl .arc { stroke: var(--gold); transition: stroke-dashoffset .9s var(--ease); }
.lvl i {
  position: absolute; inset: 6px; border-radius: 6px; transform: rotate(45deg);
  background: linear-gradient(140deg, rgba(233,196,106,.22), rgba(233,196,106,.06));
  border: 1px solid rgba(233,196,106,.42);
}
.lvl b { position: relative; font-size: 14px; font-weight: 700; color: var(--gold);
  font-variant-numeric: tabular-nums; }

/* `.rank` already means a row on the crew board, so the name of a rank is
   its own class rather than a second meaning for the first one. */
.rankname { font-size: 11px; color: var(--faint); letter-spacing: .02em; }
.rankname b { color: var(--gold); font-weight: 600; }

/* ══════════════════════════════════════════════════════════════════
   QUEST COMPLETE
   ══════════════════════════════════════════════════════════════════ */

.reward .card {
  position: relative; overflow: hidden;
  background: linear-gradient(180deg, #221d16, #15120e);
  border: 1px solid rgba(233,196,106,.4);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6), 0 30px 70px -30px #000,
              0 0 60px -18px rgba(233,196,106,.35);
}
.reward .card::before {
  content: ""; position: absolute; inset: 0; background-image: var(--grain);
  opacity: .2; mix-blend-mode: overlay; pointer-events: none;
}
/* One sweep of light across the slab as it lands. */
.reward .card::after {
  content: ""; position: absolute; inset: -40%;
  background: linear-gradient(100deg, transparent 40%, rgba(255,255,255,.14) 50%, transparent 60%);
  transform: translateX(-70%); pointer-events: none;
}
.reward.on .card::after { animation: shine 1.1s var(--ease) .18s both; }
@keyframes shine { to { transform: translateX(70%); } }

.reward .rune {
  width: 62px; height: 62px; margin: 0 auto 10px; color: var(--gold);
  opacity: .9;
}
.reward.on .rune { animation: stamp .6s var(--spring) both; }
@keyframes stamp { from { transform: scale(1.7) rotate(-18deg); opacity: 0; } }

.reward .title {
  font-size: 11px; text-transform: uppercase; letter-spacing: .22em;
  color: var(--gold); margin-bottom: 12px;
}
.reward .big { font-size: 52px; }
.reward .for { font-size: 15px; color: var(--ink); font-style: italic;
  font-family: "Instrument Serif", Georgia, serif; }
.reward .lvlup {
  margin: 14px 0 0; padding: 9px 12px; border-radius: var(--r);
  background: rgba(233,196,106,.1); border: 1px solid rgba(233,196,106,.34);
  font-size: 13px; color: var(--gold);
}
.reward .lvlup b { font-family: "Instrument Serif", Georgia, serif; font-style: italic;
  font-size: 16px; }

/* ══════════════════════════════════════════════════════════════════
   EVERYTHING STOPS
   ══════════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  .seal.open.live::after, .seal.open.live::before,
  .seal.held .state::before, .reward.on .card::after,
  .reward.on .rune { animation: none; }
  .seal.open.live::before { display: none; }
  .quest .qv { display: none; }
}

/* The second block on Today, when the rounds already had the top of the
   screen. Quieter than the first one by construction: no date, smaller
   ring, a quiet button. */
.today.second { background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: 14px; margin-top: 16px; }
.ring.small { width: 52px; height: 52px; }
.ring.small b { font-size: 12px; }
.asks { margin: 9px 0 0; font-size: 12px; color: var(--faint);
  display: flex; align-items: center; gap: 6px; }
.asks .ic { width: 14px; height: 14px; }
.reward .foot-note { margin: 12px 0 0; font-size: 12px; color: var(--faint); }

/* ── banner and seal corrections ──────────────────────────────── */

/* The photograph needs room to be a photograph. Under this it is a texture
   behind a headline, which is not why it is there. */
.quest { min-height: 178px; display: flex; align-items: flex-end; }
.quest .qbody { width: 100%; padding-top: 52px; }
.quest h2 { font-size: 25px; }
/* Enough light to see a sheep in a barn, and enough shade under it to read
   a headline. The balance lives here rather than in the encode, so it can
   be moved without re-cutting the clip. */
.quest .qv { filter: saturate(.78) brightness(.62) contrast(1.05); }
.quest .qshade {
  background:
    linear-gradient(180deg, rgba(10,9,7,.16) 0%, rgba(10,9,7,.58) 44%, rgba(10,9,7,.95) 100%),
    radial-gradient(120% 90% at 78% 8%, rgba(233,196,106,.14), transparent 60%);
}

.seal .when { justify-content: flex-start; }
.seal .nowdot {
  font-style: normal; font-size: 9px; letter-spacing: .12em;
  color: var(--rune); border: 1px solid rgba(159,216,255,.4);
  padding: 1px 5px; border-radius: 99px; margin-left: 2px;
}
.seal .state { font-size: 19px; font-weight: 400; letter-spacing: -.01em; }
.seal.done .state, .seal.held .state { font-size: 18px; }

/* The rank sits at the end of the row and must not be shaved by the gutter. */
.bar { padding-right: 16px; }
.bar .tally { gap: 8px; }
.lvl { width: 38px; height: 38px; }

/* ─── the publish sheet ─────────────────────────────────────────
   The two decisions get the room. Cadence is four targets a thumb can hit
   without looking, not a select nobody reads the options of. */

.chips.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.chip.big {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 11px 12px; min-height: 58px; text-align: left; border-radius: var(--r);
  line-height: 1.25;
}
.chip.big b { font-size: 14px; font-weight: 600; color: var(--ink); }
.chip.big i { font-style: normal; font-size: 11px; color: var(--faint); }
.chip.big.on { border-color: var(--life); background: rgba(134,185,111,.1); }
.chip.big.on b { color: var(--life); }
.chip.big.on i { color: rgba(134,185,111,.8); }

/* What it pays, with the mycelium's number beside the box rather than
   inside it — a suggestion you can see and ignore. */
.pay { display: grid; grid-template-columns: 96px 1fr; gap: 10px; align-items: stretch; }
.pay .f-in { font-size: 20px; font-weight: 680; text-align: center;
  font-variant-numeric: tabular-nums; }
.myc {
  display: flex; align-items: center; gap: 9px; text-align: left;
  padding: 8px 12px; border-radius: var(--r); font-size: 12.5px;
  color: var(--dim); background: rgba(159,216,255,.05);
  border: 1px dashed rgba(159,216,255,.32);
  transition: transform var(--dur-fast) var(--ease);
}
.myc:active { transform: scale(.97); }
.myc .ic { width: 20px; height: 20px; color: var(--rune); flex: none; }
.myc b { color: var(--rune); font-size: 15px; font-weight: 700;
  font-variant-numeric: tabular-nums; }

.sheet h2.serif { font-size: 25px; margin: 2px 0 0; }
.sheet .row.big .row { margin-top: 0; }

/* ─── the rank card ─────────────────────────────────────────────
   The one place the level is a name rather than a number. */
.ranker {
  position: relative; display: flex; align-items: center; gap: 15px;
  padding: 16px 17px; margin: 4px 0 0;
  border-radius: var(--r-xl); border: 1px solid var(--line-2);
  background:
    radial-gradient(90% 120% at 12% 0%, rgba(233,196,106,.12), transparent 60%),
    linear-gradient(180deg, #211c16, #17140f);
  box-shadow: inset 0 1px 0 rgba(244,239,229,.05);
  overflow: hidden;
}
.ranker::before {
  content: ""; position: absolute; inset: 0; background-image: var(--grain);
  opacity: .2; mix-blend-mode: overlay; pointer-events: none;
}
.ranker .lvl { width: 62px; height: 62px; flex: none; }
.ranker .lvl b { font-size: 21px; }
.ranker .lvl i { inset: 10px; border-radius: 9px; }
.ranker .who { flex: 1; min-width: 0; position: relative; }
.ranker h2 { margin: 3px 0 0; font-size: 26px; line-height: 1.08; color: var(--ink); }
.ranker .meter { margin-top: 9px; height: 4px; border-radius: 99px;
  background: rgba(244,239,229,.09); overflow: hidden; }
.ranker .meter i { display: block; height: 100%; border-radius: 99px;
  background: linear-gradient(90deg, rgba(233,196,106,.6), var(--gold)); }
.ranker p { margin: 7px 0 0; font-size: 12px; color: var(--faint); }
.ranker p b { color: var(--gold); font-weight: 640; }
.ranker .eyebrow-rune { color: var(--gold); }
.ranker .eyebrow-rune::before, .ranker .eyebrow-rune::after {
  background: linear-gradient(90deg, rgba(233,196,106,.4), transparent); }

.worth-line { margin: 8px 0 15px; display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: var(--dim); }
.worth-line .ic { width: 15px; height: 15px; color: var(--gold); }
.worth-line b { color: var(--gold); font-size: 16px; font-weight: 700;
  font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════════
   THE FRONT DOOR

   The one screen a stranger sees. It has to do three things before they
   scroll: say where this is, show them the animals, and make clear that
   the thing behind it is a job somebody does rather than an app somebody
   signs up for.

   It scrolls, which nothing else in this app does — the board is a fixed
   shell and this is a page. `#doorway` therefore takes over the scroll
   while it is on screen, and gives it back the moment it is gone.
   ══════════════════════════════════════════════════════════════════ */

#doorway {
  position: relative; z-index: 3;
  flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
  /* The rules above this one dressed a small centred card: a flat padding,
     a fixed height and `text-align: center`. A page is none of those, and
     the centring in particular turned every paragraph into a wedding
     invitation. */
  padding: 0 0 calc(var(--safe-b) + 28px);
  text-align: left;
  animation: none;
}
#doorway:empty { display: none; }

.land-hero {
  position: relative; overflow: hidden; isolation: isolate;
  min-height: 74vh; display: flex; align-items: flex-end;
  padding: 0 20px 26px;
}
.land-v {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  z-index: 0; filter: saturate(.76) brightness(.58) contrast(1.05);
  transform: scale(1.06);
}
.land-shade {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(180deg, rgba(10,9,7,.46) 0%, rgba(10,9,7,.30) 26%,
                    rgba(10,9,7,.82) 68%, var(--bg) 100%),
    radial-gradient(90% 60% at 80% 6%, rgba(233,196,106,.16), transparent 62%);
}
.land-hero .qgrain { z-index: 2; }
.land-top { position: relative; z-index: 3; width: 100%; max-width: 560px; }

.land-top .mark {
  width: 46px; height: 46px; margin: 0 0 20px;
  display: grid; place-items: center; border-radius: 14px;
  border: 1px solid rgba(233,196,106,.36); background: rgba(10,9,7,.5);
  color: var(--gold);
}
.land-top .mark .glyph { width: 27px; height: 27px; }
.land-top .eyebrow-rune { color: var(--gold); margin: 0 0 12px; }
.land-top .eyebrow-rune::after { background: linear-gradient(90deg, rgba(233,196,106,.4), transparent); }
.land-top .eyebrow-rune::before { background: linear-gradient(90deg, rgba(233,196,106,.4), transparent); }

.land-top h1 {
  margin: 0; font-size: 46px; line-height: .98; letter-spacing: -.01em;
  text-shadow: 0 3px 20px rgba(0,0,0,.6);
}
.land-top .lede {
  margin: 14px 0 0; font-size: 16px; line-height: 1.45; color: var(--ink-2);
  max-width: 34ch;
}

/* The pulse. Real or absent: it is hidden until farm_pulse() answers, and
   it never invents a number to fill the space. */
.pulse {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  margin: 0 20px; border-radius: var(--r-lg); overflow: hidden;
  background: var(--line); border: 1px solid var(--line);
}
.pulse div { background: var(--bg-2); padding: 14px 10px; text-align: center; }
.pulse b { display: block; font-size: 22px; font-weight: 700; color: var(--gold);
  font-variant-numeric: tabular-nums; line-height: 1.1; }
.pulse span { display: block; font-size: 10.5px; color: var(--faint); margin-top: 3px;
  text-transform: uppercase; letter-spacing: .1em; }
.pulse.quiet { grid-template-columns: 1fr; }
.pulse.quiet div { text-align: left; padding: 15px 16px; }
.pulse.quiet b { font-size: 16px; color: var(--ink); font-family: "Instrument Serif", Georgia, serif;
  font-style: italic; font-weight: 400; }
.pulse.quiet span { text-transform: none; letter-spacing: 0; font-size: 12.5px;
  color: var(--dim); margin-top: 4px; }

.land-what { padding: 30px 20px 8px; display: grid; gap: 24px; }
.land-what article { max-width: 46ch; }
.land-what .ic-wrap { display: flex; gap: 6px; color: var(--life); margin-bottom: 11px; }
.land-what .ic-wrap .ic { width: 21px; height: 21px; }
.land-what h2 { margin: 0; font-size: 17px; font-weight: 620; letter-spacing: -.01em; }
.land-what p { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: var(--dim); }

.land-door { padding: 26px 20px 0; }
.land-door .btn.grand { width: 100%; }
.land-note { margin: 13px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--faint);
  max-width: 44ch; }

.land-foot { padding: 30px 20px 0; }
.land-foot p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--faint);
  max-width: 44ch; border-top: 1px solid var(--line); padding-top: 16px; }

/* The gate is the Hub's, and it only appears once somebody has said they
   have a way in. Before that it is not on the page at all, rather than
   hidden underneath it: an email box below the fold is still the first
   thing a scroll finds. */
#gate { position: relative; z-index: 3; }
body:not(.door-open) #doorway ~ #gate { display: none; }
/* Only the call to action goes; the page keeps its foot, so opening the
   door does not leave a hole where the bottom of the page was. */
body.door-open #doorway .land-door { display: none; }

@media (min-width: 720px) {
  .land-what { grid-template-columns: repeat(3, 1fr); gap: 30px; }
  .land-what article { max-width: none; }
  .land-top h1 { font-size: 62px; }
  .pulse, .land-what, .land-door, .land-foot { max-width: 980px; margin-inline: auto; }
}
@media (prefers-reduced-motion: reduce) { .land-v { display: none; } }

/* The old doorway centred its mark with `margin: 0 auto`. The landing is a
   page with a left edge, and everything on it lines up with that edge. */
#doorway .land-top .mark { margin: 0 0 20px; border-radius: 14px; }

.invited {
  display: inline-flex; align-items: center; gap: 7px; margin: 0 0 14px;
  padding: 6px 13px 6px 10px; border-radius: 99px; font-size: 12.5px;
  color: var(--life); background: rgba(134,185,111,.12);
  border: 1px solid rgba(134,185,111,.4);
}
.invited .ic { width: 15px; height: 15px; }
