/* Fegnome Alchemy -- phone-first (390x844 baseline). No CDN. No Google
   Fonts. Three SELF-HOSTED faces out of fonts/, relative url() only, and
   they are the reason the page still makes zero outbound requests in the
   full edition and exactly one -- the email POST -- in the demo.

   THE TYPEFACES (v1.5). Both editions ship the same three files and both
   licence texts; see "Typefaces" in the README for source, tag and bytes.

     Be Vietnam ExtraBold   fonts/BeVietnam-800.woff2       4,488 B
       The wordmark face fegnome.com self-hosts, and now the wordmark face
       here: the header lockup finally reads in the brand's own letters
       instead of the system sans at 800. Copyright 2019 The Bevietnam
       Project Authors, SIL OFL 1.1, no Reserved Font Name -- the licence
       is in fonts/OFL-BeVietnam.txt and the copyright line is the one in
       the font's own name table. Subset from the upstream 90,072-byte
       ExtraBold TTF to the fourteen letters the lockup spells and nothing
       else, because 90 KB for "Fegnome Alchemy" is not a typeface, it is
       a tax. The service mark is NOT in Be Vietnam at any weight, so the
       sup falls through to the system sans on purpose.

     JetBrains Mono 400     fonts/JetBrainsMono-400.woff2  35,428 B
     JetBrains Mono 500     fonts/JetBrainsMono-500.woff2  36,120 B
       The Fegnome app's data face, and every figure in this game that
       sits in a column or changes under the player now uses it with
       tabular figures: the two header counts, the four tile corners, the
       quick-slot corners, the Library page number, the weekly pip and
       the whole Instrumentation table. Tabular means a count going from
       9 to 10 moves nothing beside it. 400 is text, 500 is a figure.
       Copyright 2020 The JetBrains Mono Project Authors, SIL OFL 1.1, no
       Reserved Font Name, fonts/OFL-JetBrainsMono.txt.

   Fraunces -- the app's editorial serif, and the third face in the sibling
   demo -- is deliberately NOT here. Nothing in this page is editorial
   enough to carry it, and a serif in a 74px tile is a layout risk for no
   gain. Two families, three files, 76,036 bytes. */

/* Be Vietnam ExtraBold -- SIL OFL 1.1, fonts/OFL-BeVietnam.txt */
@font-face{
  font-family:'Be Vietnam';
  src:url('fonts/BeVietnam-800.woff2') format('woff2');
  font-weight:800; font-style:normal; font-display:swap;
}
/* JetBrains Mono -- SIL OFL 1.1, fonts/OFL-JetBrainsMono.txt */
@font-face{
  font-family:'JetBrains Mono';
  src:url('fonts/JetBrainsMono-400.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'JetBrains Mono';
  src:url('fonts/JetBrainsMono-500.woff2') format('woff2');
  font-weight:500; font-style:normal; font-display:swap;
}
:root{
  --bg:#0e1a15; --bg2:#13241d; --panel:#1a2e25; --panel2:#21392e;
  --line:#2d5040; --line2:#3d6b55;
  /* Secondary text was too dark to read on a phone: --faint scored 3.3:1
     on --panel2, well under WCAG AA. Both secondary tokens are brightened
     and the three levels stay distinct (15.8 / 9.9 / 7.0 on --bg2). No
     background changed. */
  --ink:#eaf3ee; --dim:#b8d0c5; --faint:#95b1a4;
  --gold:#e8c34a; --grn:#4fc98a; --red:#e0685f; --blu:#6fb0e8; --pur:#b48be8;
  /* the site's brand magenta, lifted from #a44364 to clear AA on the
     dark bar while holding the same hue -- the wordmark's "Fe" and nothing
     else, exactly as the site reserves it */
  --magenta:#e88aa4;
  --sativa:#e8a13d; --indica:#9b7be0; --hybrid:#4fc98a;
  --r:14px; --tap:44px;
  /* The two self-hosted families and what a reader sees for the few
     hundred milliseconds of font-display:swap. The fallbacks are not the
     design; --brand's fallback is what the wordmark WAS before v1.5. */
  --brand:'Be Vietnam',system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,
         'Liberation Mono',monospace;
  /* ZONES, and the 50/50 rule ------------------------------------------
     While the player is playing, the BOTTOM HALF of the viewport belongs
     to the codex tiles. Everything else has to fit in the top half, so
     every region above the codex is budgeted rather than left to grow:

       header        52px   (unchanged content)
       bench         72px   (two compact slots, sprite + name)
       result zone  180px   (fixed -- see below)
       quick bar     44px   (four pins)
       target strip  44px   (today's target + this week's family)

     That is 392px of a 422px half at 390x844, and the codex's own
     search-and-sort bar is counted in the BOTTOM half as the first row of
     the tile zone. The result zone is the only elastic one: it takes what
     is left of the half after the four fixed rows and the gaps between
     them (218px, measured, with a few px of slack), capped at 180 so a
     tall phone hands the surplus to the tiles instead. Nothing below it
     ever moves -- not between empty, a hit and a dead end, and not when
     "more" opens, because "more" is an overlay. */
  --zone-result:clamp(150px,calc(50vh - 218px),180px);
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;height:100%;max-width:100%;overflow-x:hidden}
img{max-width:100%}
body{
  background:var(--bg); color:var(--ink);
  font:15px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif;
  overscroll-behavior:none; display:flex; flex-direction:column;
  padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
#boot{margin:auto;color:var(--dim);font-size:14px}
button{font:inherit;color:inherit;border:0;background:none;cursor:pointer}
input,select{font:inherit}

/* ── header ─────────────────────────────────────────────── */
/* The bar sits ABOVE the sheets (z 40). A sheet is a full-screen fixed
   overlay, so with one open the backdrop used to cover the header: the first
   press on any tool was eaten by the backdrop, which dismissed the sheet and
   did nothing else. The tools are always live now, and each one closes
   whatever is open before it acts -- see closeAllSheets() in game.js. */
#bar{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--bg2);
     border-bottom:1px solid var(--line);flex:0 0 auto;max-width:100%;overflow:hidden;
     position:relative;z-index:45}
/* THE BRAND LOCKUP. The site sets the mark at 34px wide -- 45.5px tall on
   its 136x182 viewBox -- beside a 24px wordmark, so the mark stands 1.9x
   the wordmark's type size. The header here is 52px tall with 8px of
   padding, so the mark takes the 34px of content height that leaves and
   the wordmark takes 34/1.9 = 18px: the same lockup, scaled to the bar.
   The treatment is the site's as well -- magenta "Fe", ink "gnome", the
   service mark small and muted -- and as of v1.5 in the brand's own
   letters: Be Vietnam ExtraBold is self-hosted in fonts/ and the honest
   difference is gone. The service mark has no glyph in Be Vietnam at any
   weight, so the sup alone falls through to the system sans, which is
   what fegnome.com does with it too. The mark's own two colours come out
   of the SVG and are never restyled. */
.brand{display:flex;align-items:center;gap:7px;min-width:0;flex:0 1 auto;overflow:hidden}
.mark{flex:0 0 auto;width:auto;height:34px;display:block}
.wordmark{display:flex;flex-direction:column;justify-content:center;gap:1px;
          min-width:0;white-space:nowrap;overflow:hidden}
.wordmark .wm{font-size:18px;line-height:1;font-weight:800;letter-spacing:-.005em;
              font-family:var(--brand)}
.wordmark .fe{color:var(--magenta)}
.wordmark .gn{color:var(--ink)}
.wordmark sup{font-size:.34em;color:var(--faint);vertical-align:super}
.wordmark b{font-size:9px;line-height:1;font-weight:800;color:var(--gold);
            letter-spacing:.18em;text-transform:uppercase;
            font-family:var(--brand)}
.score{display:flex;margin-left:auto;text-align:right;flex:0 0 auto}
/* FOUND and POSSIBLE SO FAR are one block of two small lines, and since
   v1.5 they are the ONLY thing in here: the hit rate used to be a second
   column and is now read in the "more" overlay and in Instrumentation.
   The stack is still no taller than the single number it replaced, so the
   header height -- which every zone below it is measured from -- does not
   move a pixel, and the room the third column was costing goes into the
   two figures: both are the same size at 360 as they are at 430, their
   captions are a little larger, and the space between number and caption
   is no longer being scraped for pixels. */
.scstack{display:flex;flex-direction:column;justify-content:center;gap:1px;
         text-align:right;flex:0 0 auto}
.scline{display:flex;align-items:baseline;justify-content:flex-end;gap:5px;
        white-space:nowrap;line-height:1}
/* THE FIGURES ARE MONO. JetBrains Mono at 500 with tabular figures: every
   digit is 0.6em wide whatever it is, so FOUND going from 99 to 100 grows
   the stack by exactly one digit and nothing under it twitches, and going
   from 8 to 9 moves nothing at all. The system sans it replaces was
   0.628em per tabular digit, so the mono is also very slightly narrower. */
.scline b{font-size:17px;font-family:var(--mono);font-weight:500;
          font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.scline i{font-style:normal;font-size:10px;color:var(--faint);
          text-transform:uppercase;letter-spacing:.07em}
.scline.sub b{font-size:12px;color:var(--gold)}
.scline.sub i{font-size:9.5px}
.tools{display:flex;gap:6px;flex:0 0 auto}
.icn{width:34px;height:34px;border-radius:10px;background:var(--panel);border:1px solid var(--line);
     display:grid;place-items:center;font-size:15px;color:var(--dim)}
.icn:active{background:var(--panel2)}

main{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
main[hidden],#bar[hidden]{display:none}
/* the three views inside <main> set display, so [hidden] needs saying */
#bench[hidden],#result[hidden],#codexWrap[hidden],#library[hidden],#strain[hidden],
#tree[hidden]{display:none}

/* ── bench ──────────────────────────────────────────────── */
/* 72px, sprite and name side by side. The slots used to be 96px tall with
   the name stacked under a 44px sprite -- 121px of bench for two words. A
   row lays the same two facts out in half the height, and the whole slot is
   still one tap and still a drop target. */
#bench{padding:7px 12px;flex:0 0 auto;background:var(--bg2);border-bottom:1px solid var(--line)}
.slots{display:flex;align-items:center;gap:9px;max-width:100%}
.slot{flex:1 1 0;min-width:0;max-width:100%;overflow:hidden;height:56px;border-radius:12px;border:2px dashed var(--line2);
      background:var(--panel);display:flex;flex-direction:row;align-items:center;
      justify-content:center;gap:7px;padding:4px 7px;transition:border-color .12s,background .12s}
.slot.filled{border-style:solid;border-color:var(--line2);background:var(--panel2)}
.slot.over{border-color:var(--gold);background:#2a3d2c}
.slot .ph{font-size:11px;color:var(--faint);text-align:center;line-height:1.25}
.slot img,.slot svg{width:32px;height:32px;display:block;flex:0 0 auto}
.slot .nm{font-size:11.5px;text-align:left;line-height:1.15;min-width:0;
          overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.op{font-size:18px;color:var(--faint);width:16px;text-align:center;flex:0 0 auto}

/* 180px, and four rows in it, whatever landed: the headline, the citation,
   Fegnome's line, and a pinned foot with "more" on the left and at most two
   actions on the right. Everything longer than that -- the history and
   awards block, the siblings of a cross, the rest of a dead end's answer,
   the running score -- is behind "more", which opens as an OVERLAY over the
   tiles and moves nothing. The panel used to grow with whatever it had to
   say and then, capped at 32vh, still ate 270px of an 844px phone. */
.result{flex:0 0 auto;height:var(--zone-result);
        margin:6px 12px 0;border-radius:var(--r);padding:6px 11px;
        border:1px solid var(--line);background:var(--panel);
        display:flex;flex-direction:column;overflow:hidden}
.reshead{flex:0 0 auto}
.resbody{flex:1 1 auto;min-height:0;overflow:hidden}
/* the citation is at most two lines and Fegnome gets one: the zone is a
   fixed height, so the clamps are what keep the foot where it is */
#result .why{margin:2px 0 0;font-size:11.5px;line-height:1.3;color:var(--dim);
             overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;
             -webkit-box-orient:vertical;overflow-wrap:anywhere}
/* a name is a link, and inside a clamped box a link is a box item: without
   this an eight-syllable strain starting near the right margin hangs off
   the edge of a 360px screen instead of wrapping */
#result .why .slink{overflow-wrap:anywhere}
/* a dead end's one fact keeps its own colour inside the zone */
#result .why.fact{margin-top:6px;padding-top:6px;border-top:1px solid #4a3330;
                  color:#d8b8b2;-webkit-line-clamp:2}
/* one line, clamped the same way the citation is. white-space:nowrap with
   an ellipsis clips the PAINT but not the box: a name near the right
   margin still reported itself past the edge of a 360px screen. */
/* THE CLUE gets two lines, because it is the line that tells you what to
   do next and a clue with its second half cut off is not one. The four
   rows still add up inside the same fixed zone: the headline and the
   citation were both set a notch smaller to pay for it. */
#result .gnome{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
               margin:4px 0 0;font-size:12px;line-height:1.3;
               overflow:hidden;overflow-wrap:anywhere}
#result .gnome .gwho{display:inline;margin-right:5px}
.resfoot{flex:0 0 auto;margin-top:5px;padding-top:5px;border-top:1px solid var(--line);
         display:flex;align-items:center;justify-content:space-between;gap:8px;
         min-height:38px}
/* "more" is a quiet control on the left of the foot, where the running
   score used to be. It is in every state, so the foot is one shape. */
.morebtn{flex:0 0 auto;font-size:12px;color:var(--dim);padding:6px 11px;
         border-radius:999px;background:#0e1a15;border:1px dashed var(--line2)}
.morebtn:after{content:" \203A";color:var(--faint);font-weight:700}
.morebtn.open:after{content:" \2039"}
.morebtn:active{background:var(--panel2)}
.result.miss .morebtn{border-color:#5c3a38}
.result.hit .resfoot{border-top-color:#2f6b45}
.result.miss .resfoot{border-top-color:#4a3330}
.result.pick{border-color:var(--line2);background:var(--panel)}
.result.pick .kindword{font-size:11.5px}
.result.idle{border-style:dashed;border-color:var(--line2);background:#16281f}
.result.idle h3{color:var(--faint);font-weight:600}
/* the foot is pinned to the bottom of the zone: the running score on the
   left, at most two actions on the right, and both are there in every state
   so the zone reads the same height whatever landed in it */
.resacts{display:flex;gap:6px;flex:0 0 auto}
.resacts button{font-size:12px;padding:6px 10px;border-radius:9px;background:var(--panel2);
                border:1px solid var(--line2);color:var(--dim);white-space:nowrap}
.resacts button.go{background:#27381f;border-color:var(--line2);color:var(--ink)}
.resacts button:active{background:#2b4a3b}
.resacts button[hidden]{display:none}
.result.hit{border-color:#3f9b58;background:linear-gradient(180deg,#1d3a2a,#162c20)}
.result.miss{border-color:#5c3a38;background:#2a1e1d}
.result h3{margin:0 0 1px;font-size:15.5px;display:flex;align-items:center;gap:8px}
.result h3 img,.result h3 svg{width:26px;height:26px}
.badge{font-size:10px;letter-spacing:.1em;text-transform:uppercase;padding:2px 6px;border-radius:999px;
       background:#3f9b58;color:#0b1a10;font-weight:800}
.badge.warn{background:#7a4a44;color:#f3d7d2}
.badge.sel{background:var(--blu);color:#0b1a24}
/* a tile you SELECTED is not a discovery: no Verified badge, no green
   panel, no citation -- the box says what you put on the bench and what
   the record still has in it */
.badge.pick{background:var(--panel2);color:var(--dim);border:1px solid var(--line2)}
.badge.poly{background:var(--pur);color:#190b24}
.why{margin:4px 0 0;font-size:12.5px;color:var(--dim)}
.why b{color:var(--ink)}

/* A printed strain name is a door to its library card. It keeps the colour
   of the text around it -- a paragraph of blue links reads as a form, not a
   sentence -- and says it is tappable with a faint dotted rule. */
.slink{color:inherit;text-decoration:none;border-bottom:1px dotted var(--line2)}
.slink:active{color:var(--gold);border-bottom-color:var(--gold)}
.result h3 .slink{display:flex;align-items:center;gap:8px;min-width:0;border-bottom:0}
.result h3 .slink span{overflow:hidden;text-overflow:ellipsis;
                        border-bottom:1px dotted var(--line2)}
.nbtxt .slink,.altxt .slink,.toast .slink{border-bottom-style:dotted}
/* THE QUALIFIER, as a control. It reads as part of Fegnome's sentence --
   same size, same colour, no button chrome shouting over the line -- and
   says it is tappable with a gold underline and a little chevron. The tap
   target is the whole phrase. */
.qual{display:inline;font:inherit;color:var(--gold);padding:0;
      border-bottom:1px solid var(--gold);border-radius:0;background:none}
.qual:after{content:" \203A";font-weight:700;opacity:.85}
.qual:active{background:#27381f}
/* the bar the filter draws for itself: the FIRST ROW OF THE TILE AREA, so
   it scrolls with the tiles and no zone above the codex moves */
.qualbar{grid-column:1/-1;display:flex;align-items:center;justify-content:space-between;
         gap:8px;margin:0 0 8px;padding:7px 10px;border-radius:10px;
         background:#0e1a15;border:1px dashed var(--gold);font-size:11.5px;
         color:var(--dim)}
.qualbar b{color:var(--gold);font-weight:700}
.qualoff{flex:0 0 auto;font-size:11.5px;color:var(--dim);padding:4px 9px;
         border-radius:999px;background:var(--panel2);border:1px solid var(--line2)}
.qualoff:active{background:var(--panel)}
.tags{margin:6px 0 0;display:flex;flex-wrap:wrap;gap:5px}
.tag{font-size:12px;padding:3px 8px;border-radius:999px;background:#0e1a15;
     border:1px solid var(--line);color:var(--dim)}
.deadcount{font-size:12px;color:var(--dim);margin:5px 0 0}
/* ── the "more" overlay ─────────────────────────────────────
   Absolutely positioned inside the codex zone, so it covers the tile area
   and exactly the tile area: the bench, the result, the quick bar and the
   strip stay visible and live above it, and no zone edge moves. It closes
   on the cross, on a tap anywhere outside it, and on the next result. */
.resmore{position:absolute;inset:0;z-index:35;display:flex;flex-direction:column;
         background:var(--bg)}
.resmore[hidden]{display:none}
.morecard{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
          -webkit-overflow-scrolling:touch;position:relative;
          padding:12px 14px calc(22px + env(safe-area-inset-bottom));
          border-top:1px solid var(--line2);animation:down .16s ease}
.morecard .close{top:4px;right:6px}
.morebody{padding-right:34px}
.morebody .why{margin:4px 0 0;font-size:12.5px;color:var(--dim);line-height:1.45}
.morebody h4{margin:14px 0 4px;font-size:11.5px;color:var(--gold);
             text-transform:uppercase;letter-spacing:.09em}
.morebody h4:first-child{margin-top:0}
.morebody .deadcount{margin:4px 0 0}

/* The siblings of a cross. One documented pair can be on record as the
   parents of sixteen strains: they are all granted and they all count, and
   every one of them is named behind "more", where there is room to.
   In the zone itself they are a count and nothing else. */
.alsolist{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}
.alsolist[hidden]{display:none}
.alsoname{font-size:12px;padding:3px 8px;border-radius:999px;background:#0e1a15;
          border:1px solid var(--line);color:var(--dim)}
.why.fact{margin-top:6px;padding-top:6px;border-top:1px solid #4a3330;color:#d8b8b2}
.why.fact b{color:#f3d7d2}

/* A dead end says two short sentences in the zone -- the verdict, then the
   one fact that changes what you do next -- and keeps the rest of the
   answer behind "more". Nothing is dropped; it is all one tap away. */
.morebody .why.fact:first-of-type{border-top:0;margin-top:4px;padding-top:0}

/* ── history block (breeder / region / awards) ──────────── */
/* Facts only. No review counts, no ratings, no third-party prose: a line
   that has no fact behind it is simply not drawn. */
.hist{margin:7px 0 0}
.hist .hrow{display:flex;gap:8px;margin:0 0 3px;font-size:12.5px;color:var(--ink);line-height:1.4}
.hist .hrow span{flex:0 0 92px;color:var(--faint);font-size:11.5px;letter-spacing:.07em;
                 text-transform:uppercase;padding-top:3px}
.hist .hrow b{color:var(--ink)}
.hist.compact{margin-top:6px;padding-top:6px;border-top:1px solid #2c4a38}
.hist.compact .hline{margin:0;font-size:12.5px;color:var(--dim);line-height:1.45}
.hist.compact .hline b{color:var(--ink)}
.awards{display:flex;flex-direction:column;gap:3px;margin-top:6px}
/* A chip wraps to as many lines as the text needs -- two, usually -- and is
   never clipped. overflow-wrap only fires for a single token longer than the
   line, where breaking it is the only thing left; the length itself is
   capped at a word boundary in awardLine(). */
.award{font-size:12px;line-height:1.35;color:#f0dc9f;background:#2b2d18;
       border:1px solid #5d5222;border-radius:8px;padding:3px 7px;
       min-width:0;max-width:100%;overflow-wrap:break-word}
.award.more{color:var(--dim);background:none;border-color:var(--line);padding:2px 7px}

/* ── quick-load bar ─────────────────────────────────────── */
/* Four slots between the bench and the codex, one slim 44px row of the top
   half. Filled only by the explicit pin control; long-press and drag are
   untouched. */
.quick{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;padding:6px 12px 0;flex:0 0 auto}
.quick[hidden]{display:none}
.qslot{position:relative;height:38px;border-radius:10px;background:var(--panel);
       border:1px solid var(--line);
       display:flex;flex-direction:row;align-items:center;justify-content:center;gap:4px;
       padding:2px 4px;min-width:0;overflow:visible}
.qslot.blank{background:none;border:1px dashed var(--line2);opacity:.85}
.qslot.on{border-color:var(--gold);background:#27381f}
/* held: the press-and-hold that opens the slot's detail sheet, where the
   Pin/Unpin control lives -- an unpin with no trip through the codex */
.qslot.held{border-color:var(--gold);transform:scale(.96)}
.qslot:active{background:var(--panel2)}
.qslot img{width:22px;height:22px;display:block;pointer-events:none;flex:0 0 auto}
.qnm{font-size:9.5px;line-height:1.15;text-align:left;color:var(--dim);min-width:0;
     overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
     word-break:break-word}
/* v1.2: with the bottom corners off a pinned slot the name is the only
   thing left beside the sprite, so it gets the room to be read -- and it
   stops short of the gold count in the top-right, which hangs three
   pixels past the slot's own edge. Measured, not guessed: the count is
   16 px wide at right:-3px, so the text column ends 9 px early and the
   two never share a pixel. verify_codex_grid.mjs checks the text's INK,
   not the box around it. */
.qslot .qnm{padding-right:9px}
.qhint{font-size:11px;color:var(--dim);letter-spacing:.09em;text-transform:uppercase}
.pinbtn{flex:0 0 auto}

/* ── codex ──────────────────────────────────────────────── */
/* The bottom half of the phone, and the anchor for the "more" overlay.
   Its first row is the search-and-sort bar, which is counted in the bottom
   half's budget rather than the top's. */
#codexWrap{flex:1 1 auto;display:flex;flex-direction:column;min-height:0;position:relative}
.codexbar{display:flex;gap:8px;padding:6px 12px;flex:0 0 auto}
#search{flex:1 1 auto;min-width:0;background:var(--panel);border:1px solid var(--line);
        border-radius:10px;padding:6px 10px;color:var(--ink);font-size:13px}
#search::placeholder{color:var(--faint)}
#sortBy{background:var(--panel);border:1px solid var(--line);border-radius:10px;
        padding:6px 6px;color:var(--ink);font-size:12.5px;max-width:46%}
.codex{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;
       display:grid;grid-template-columns:repeat(auto-fill,minmax(80px,1fr));
       gap:8px;padding:10px 12px 24px;align-content:start}
.tile{position:relative;background:var(--panel);border:1px solid var(--line);border-radius:12px;
      padding:7px 4px 9px;display:flex;flex-direction:column;align-items:center;gap:3px;
      min-height:100px;touch-action:manipulation}
.tile:active{background:var(--panel2);transform:scale(.97)}
.tile.sel{border-color:var(--gold);background:#27381f}
.tile.fresh{animation:pop .5s ease}
@keyframes pop{0%{transform:scale(.6);opacity:0}60%{transform:scale(1.08)}100%{transform:scale(1)}}
.tile img,.tile svg{width:37px;height:37px;display:block;pointer-events:none;
                     margin-top:2px}
.tile .nm{font-size:12px;line-height:1.25;text-align:center;color:var(--ink);flex:0 0 auto;
          overflow:hidden;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;
          word-break:break-word}
/* One number per corner, on the tile, the quick slot and the Library card.
   TOP LEFT, green: what you can make on this turn. It is always drawn, so
   there is never a card whose green count is simply missing, and a zero is
   muted rather than absent. TOP RIGHT, gold: documented children still to
   find, drawn only when there are any. Inline (on the strain page) the two
   sit in a row instead, which is what .pips is for. */
.pips{display:flex;align-items:center;gap:4px}
/* MONO, TABULAR, AND 500. A corner count is a figure in a fixed box that
   changes under the player's hand, which is exactly what tabular figures
   are for: the chip stops resizing as 9 becomes 10. The weight drops from
   800 to 500 because 500 is a weight we actually ship -- asking for 800
   would have the browser smear the 500 into a fake bold, which is wider
   and worse. JetBrains Mono 500 on a solid green or gold chip carries. */
.pip{min-width:18px;height:18px;padding:0 3px;flex:0 0 auto;border-radius:999px;
     font-size:10.5px;font-family:var(--mono);font-weight:500;
     font-variant-numeric:tabular-nums;display:inline-grid;place-items:center;
     border:2px solid var(--bg);line-height:1}
.pip.make{background:var(--grn);color:#0b2a1c}
.pip.make.zero{background:#2b3c34;color:var(--faint);border-color:var(--bg)}
.pip.fert{background:var(--gold);color:#22301c}
.pip.wide{font-size:9.5px;padding:0 2px;letter-spacing:-.02em}
.tile .pip,.qslot .pip{position:absolute;top:-8px}
.tile .pip.make,.qslot .pip.make{left:-4px}
.tile .pip.fert,.qslot .pip.fert{right:-4px}
/* the quick slots are half the height of a tile, so their corners are too */
.qslot .pip{top:-6px;min-width:16px;height:16px;font-size:9.5px;padding:0 3px}
.qslot .pip.make{left:-3px}
.qslot .pip.fert{right:-3px}
/* a Library card clips its overflow, so its corners sit just inside it */
.libcard .pip{position:absolute;top:8px}
.libcard .pip.make{left:8px}
.libcard .pip.fert{right:8px}
/* LINE FOUR of the result zone: the terpene order. Names only, most to
   least, never a number — the numbers are what the Fegnome app is for.
   One clamped line, and simply absent when the export has nothing for this
   strain, which is why it lives in a container that can be empty. */
#result .terpline{margin:3px 0 0;font-size:10.5px;line-height:1.3;
                  color:var(--faint);overflow:hidden;display:-webkit-box;
                  -webkit-line-clamp:1;-webkit-box-orient:vertical;
                  overflow-wrap:anywhere}
#result .terpline b{color:var(--dim);font-weight:700}
/* the same order in full on the strain page, where there is room for all
   four, plus the one line that says where the numbers are */
.spterps{margin:2px 0 0;font-size:13px;line-height:1.45;color:var(--ink);
         overflow-wrap:anywhere}
.spterph{margin:14px 0 0;font-size:12px;color:var(--faint);font-weight:700;
         letter-spacing:.04em;text-transform:uppercase}
/* The quiet next step, on every strain page. The game is the family tree;
   the lab report is the app. One line, brand green, no badge wall. */
.funnel{margin:16px 0 2px;padding-top:12px;border-top:1px solid var(--line);
        font-size:12.5px;text-align:center;line-height:1.45}
.funnel a{color:var(--grn);border-bottom:0;text-decoration:none}
/* the underline goes on the two words that are the destination, not on
   the whole sentence: a line of green rule reads as an advert */
.funnel a b{color:var(--grn);font-weight:800;text-decoration:underline;
            text-underline-offset:3px}
.funnel a:active{opacity:.7}
/* the one pointer at the app inside play: on the terpene line, quiet, green */
.terpline .tapp{color:var(--grn);border-bottom:0;text-decoration:none;white-space:nowrap;margin-left:6px;font-size:.92em}
.terpline .tapp:active{opacity:.7}
.pipline{display:flex;align-items:center;gap:8px;flex-wrap:wrap;
         font-size:12px;color:var(--dim);margin:8px 0 2px}
/* BOTTOM LEFT (v1.2): the generation, off D.gen[]. Muted, at the weight of
   the other three corners, and never blank -- g0 for a landrace, g? for the
   handful whose export recorded no generation at all. */
.gpip{position:absolute;bottom:4px;left:5px;min-width:14px;height:14px;
      padding:0 3px;border-radius:4px;background:#23302b;color:#8b9e94;
      font-size:9px;font-family:var(--mono);font-weight:500;
      font-variant-numeric:tabular-nums;display:inline-grid;place-items:center;
      line-height:1;letter-spacing:0}
.gpip.wide{font-size:8px;padding:0 2px;letter-spacing:-.02em}
.libcard .gpip{bottom:7px;left:8px}
/* BOTTOM RIGHT (v1.2): the Select mark left the bottom left and joined the
   shelf letter, immediately to its left, so one corner carries everything
   this strain can still be pressed for. The gold corner is crosses, this is
   phenotypes, and they ask the player for different things. It is HIDDEN at
   zero rather than muted: the corner already has a letter in it, and nothing
   left to open is not worth a bubble. The offsets are the chip's own width
   plus its inset plus the gap, so the pair never overlaps. */
.selmark{position:absolute;bottom:4px;right:22px;min-width:15px;height:13px;
         padding:0 3px;border-radius:999px;background:#2f5f86;color:#cfe3f5;
         font-size:9px;font-family:var(--mono);font-weight:500;
         font-variant-numeric:tabular-nums;display:inline-grid;place-items:center;
         line-height:1;opacity:.92}
.selmark.wide{font-size:8px;padding:0 2px;letter-spacing:-.02em}
.libcard .selmark{bottom:7px;right:25px}
/* Shelved: a strain with nothing left leaves the working codex in 300 ms.
   Short, not celebratory -- it is finished, not lost. */
/* JUST FOUND. A bulk phenotype open can put dozens of tiles into the grid
   at once; these are the ones that just landed. A short glow says it
   happened, and the ring stays until the next action so they can still be
   found afterwards -- blue, because gold is already the selected tile. */
.tile.justnew{border-color:var(--blu);box-shadow:0 0 0 1px rgba(111,176,232,.35) inset}
.tile.justnew{animation:justlit 1.6s ease-out}
@keyframes justlit{
  0%{box-shadow:0 0 0 3px rgba(111,176,232,.55);background:#1d3346}
  60%{box-shadow:0 0 0 2px rgba(111,176,232,.3)}
  100%{box-shadow:0 0 0 1px rgba(111,176,232,.35) inset}
}
.tile.shelved{animation:shelf .3s ease forwards;pointer-events:none}
@keyframes shelf{to{transform:scale(.72) translateY(12px);opacity:0}}
/* the sentinel the grid watches: a full-width row with no ink in it, so
   the observer has something to see coming without the layout noticing */
.codexmore{grid-column:1/-1;height:1px}
.shelfnote{color:var(--faint);font-size:11.5px;padding:10px 6px 2px}
.shelfnote b{color:var(--dim)}
.twocount b{color:var(--ink)}
/* THE SHELF CHIP. The dot in the bottom-right corner of a card was a
   colour with no legend; it is a letter now. The COLOUR is still the type
   -- hybrid, indica, sativa -- and the letter is the vault the strain had
   to have open to be makeable: L Landrace, M Mystery-Cut, D Deep. The
   tile grew four pixels of bottom padding to keep it clear of a three-line
   name. Since v1.2 the blue Select mark sits immediately to its left in the
   same corner, and the bottom LEFT belongs to the generation. */
.kindchip{display:inline-grid;place-items:center;min-width:14px;height:14px;
          border-radius:4px;font-size:9px;font-weight:800;color:#0b1b14;
          line-height:1;letter-spacing:0}
.tile .kindchip{position:absolute;bottom:4px;right:5px}
/* a pinned slot is under half a tile's height: it keeps the sprite, the
   name and the two TOP counts, and nothing in the bottom corners at all */
.libcard .kindchip{position:absolute;bottom:7px;right:8px}
.kindword{color:var(--dim);margin-left:5px}
/* the P left the chip; a phenotype says so in words instead */
.phenoof{color:var(--faint);font-size:12px}
#spMeta .kindchip,#shMeta .kindchip{vertical-align:-2px}
.trbtxt .kindchip{vertical-align:-2px;margin-right:2px}
.t0{background:var(--hybrid)} .t1{background:var(--indica)} .t2{background:var(--sativa)}
.empty{grid-column:1/-1;color:var(--dim);font-size:12.5px;padding:16px 4px;text-align:center}


/* ── Library: the codex read as a book ──────────────────── */
.lib,.strainpg{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
.libhead{display:flex;align-items:center;gap:9px;padding:12px 12px 6px;flex:0 0 auto}
.libhead h2{margin:0;font-size:19px}
.libcount{font-size:12px;color:var(--dim);flex:1 1 auto;min-width:0;
          overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.libhead .chip{flex:0 0 auto;white-space:nowrap}
.libfilters{display:flex;flex-direction:column;gap:6px;padding:0 12px 8px;flex:0 0 auto}
/* these wrap rather than scroll sideways: a horizontal scroller would run
   tiles off the edge of a 360 px screen, which is the bug this build fixes */
.frow{display:flex;flex-wrap:wrap;gap:6px}
.frow .chip{flex:0 0 auto;white-space:nowrap;font-size:12px;padding:6px 10px}
.libgrid{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;
         display:grid;grid-template-columns:repeat(2,1fr);gap:10px;
         padding:0 12px 24px;align-content:start;
         scroll-snap-type:y proximity;scroll-padding-top:4px}
/* the Library keeps its corners inside the card, so it needs no top room */
.libcard{position:relative;scroll-snap-align:start;text-decoration:none;color:inherit;
         background:var(--panel);border:1px solid var(--line);border-radius:14px;
         padding:14px 8px 21px;display:flex;flex-direction:column;align-items:center;gap:5px;
         min-height:152px;overflow:hidden}
.libcard:active{background:var(--panel2);transform:scale(.985)}
.libcard img{width:64px;height:64px;display:block}
.libnm{font-size:13px;line-height:1.2;text-align:center;color:var(--ink);
       overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
       word-break:break-word}
.libmeta{font-size:12px;line-height:1.25;text-align:center;color:var(--faint);
         overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
/* the two corners took the top of the card, so the page number moves to the
   foot of it and the trophy chip drops one row */
.libpg{position:absolute;bottom:7px;left:9px;font-size:10.5px;color:var(--faint);
       font-family:var(--mono);font-variant-numeric:tabular-nums}
.libtrophy{position:absolute;top:32px;right:7px;font-size:10.5px;color:#f0dc9f;
           background:#2b2d18;border:1px solid #5d5222;border-radius:999px;padding:1px 6px}

/* ── a single strain's page ─────────────────────────────── */
.spgtop{display:flex;align-items:center;gap:9px;padding:8px 12px 0;flex:0 0 auto}
.spwhere{font-size:11.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.spgbody{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:10px 14px 28px}
.sphead{display:flex;gap:13px;align-items:center}
.sphead h2{margin:0;font-size:21px;line-height:1.15}
.sphead p{margin:3px 0 0;font-size:12.5px;color:var(--dim)}
.spico img{width:72px;height:72px;display:block}
.spgbody h3{margin:18px 0 6px;font-size:12.5px;color:var(--gold);
            text-transform:uppercase;letter-spacing:.09em}
.spgbody .note{font-size:12px}
.pnote{margin:0 0 6px;font-size:13px;color:var(--dim)}
.pnote b{color:var(--ink)}
.pcross{margin:0 0 7px;display:flex;flex-wrap:wrap;align-items:center;gap:6px}
.pop{color:var(--faint);font-size:13px}
.pchip{display:inline-flex;align-items:center;gap:6px;max-width:100%;
       text-decoration:none;color:var(--dim);background:var(--panel);
       border:1px solid var(--line);border-radius:999px;padding:4px 10px 4px 5px;
       font-size:12px;min-height:32px}
.pchip.have{color:var(--ink);border-color:var(--line2)}
.pchip:active{background:var(--panel2)}
.pchip img{width:22px;height:22px;flex:0 0 auto}
.pchip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}
.spgbody .sheetact{margin-top:20px}
.spgbody .primary[disabled],.spgbody .ghost[disabled]{opacity:.5}

/* ── the lineage tree ───────────────────────────────────── */
/* One inline SVG, panned and zoomed by a transform on a single group, so a
   150-node tree is one repaint and not 150 DOM moves. Everything in it is
   derived from the codex at open time; nothing here is stored. */
.treepg{flex:1 1 auto;display:flex;flex-direction:column;min-height:0}
/* two fixed rows -- controls, then the count -- so the stage is the same
   height whatever the counts say, and the opening zoom is predictable */
.treetop{display:flex;flex-direction:column;gap:4px;padding:8px 10px 6px;flex:0 0 auto}
.trrow{display:flex;align-items:center;gap:8px}
.treeacts{display:flex;gap:6px;margin-left:auto;flex:0 0 auto}
.treeacts .chip{padding:6px 9px;font-size:12px;white-space:nowrap}
.trstage{flex:1 1 auto;min-height:0;position:relative;overflow:hidden;
         background:radial-gradient(120% 90% at 50% 0%,#14271f,#0e1a15);
         border-top:1px solid var(--line);border-bottom:1px solid var(--line);
         touch-action:none;cursor:grab}
.trstage:active{cursor:grabbing}
#trSvg{width:100%;height:100%;display:block;user-select:none}
.trempty{position:absolute;inset:auto 16px 50% 16px;text-align:center;color:var(--dim);
         font-size:13px}
.trempty[hidden]{display:none}

.tedge{fill:none;stroke:#2d5040;stroke-width:1.6}
.tedge.lit{stroke:var(--gold);stroke-width:2.6}
.tedge.ghost{stroke:#2d5040;stroke-dasharray:4 5;opacity:.7}
#trSvg.focus .tedge:not(.lit){opacity:.3}
#trSvg.focus .tnode:not(.lit){opacity:.5}
#trSvg.focus .tghost{opacity:.35}
.tnode{cursor:pointer}
.tdisc{fill:#1a2e25;stroke:#2d5040;stroke-width:1.5}
.tnode.root .tdisc{stroke:var(--grn)}
.tnode.lit .tdisc{stroke:var(--gold);stroke-width:2.5;fill:#27381f}
.tnode.sel .tdisc{stroke:var(--gold);stroke-width:3;fill:#2f4526}
.tname{fill:var(--dim);font-size:11px;text-anchor:middle;font-family:inherit}
.tnode.lit .tname,.tnode.sel .tname{fill:var(--ink)}
#trSvg.far .tname{display:none}
/* the streak ring, drawn over the root band: one notch per consecutive
   day the target was reached, and the count in the middle. It is derived
   from the save like everything else on this canvas and stores nothing. */
.tring{fill:none;stroke:var(--gold);stroke-width:1.2;opacity:.45}
.tnotch{stroke:var(--gold);stroke-width:2.2;opacity:.8;stroke-linecap:round}
.tstreaktxt{fill:var(--gold);font-size:12px;font-weight:700;text-anchor:middle;
            font-family:inherit}
.thit{fill:none;pointer-events:all}
.tslot{fill:none;stroke:var(--line2);stroke-width:1.5;stroke-dasharray:3 4;opacity:.75}
.tghost .tmore{fill:var(--faint);font-size:8.5px;text-anchor:middle;font-family:inherit}
.tghost .tmore.can{fill:var(--grn);font-size:10px}
.tghost{cursor:pointer}
.tnode.fresh .tin{animation:tpop .5s cubic-bezier(.2,1.6,.4,1)}
@keyframes tpop{0%{opacity:0;transform:scale(.3)}60%{transform:scale(1.18)}
                100%{opacity:1;transform:scale(1)}}

.trbar{display:flex;align-items:center;gap:9px;flex:0 0 auto;
       padding:9px 11px calc(10px + env(safe-area-inset-bottom));
       background:var(--panel);border-top:1px solid var(--line2)}
.trbar[hidden]{display:none}
.trbar img{width:34px;height:34px;flex:0 0 auto}
.trbtxt{min-width:0;flex:1 1 auto;display:flex;flex-direction:column;line-height:1.25}
.trbtxt b{font-size:13px;overflow:hidden;overflow-wrap:break-word;
          display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.trbtxt span{font-size:11.5px;color:var(--dim);overflow:hidden;
             display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.trbar .chip{flex:0 0 auto;white-space:nowrap;padding:8px 10px}

/* ── discovery animation layer ──────────────────── */
.fx{position:fixed;inset:0;pointer-events:none;z-index:55;overflow:hidden}
.fx .fxi{position:fixed;left:0;top:0;will-change:transform,opacity;
         transform-origin:50% 50%;filter:drop-shadow(0 4px 10px #0009)}
.fx .fxs{position:fixed;left:0;top:0;width:7px;height:7px;border-radius:999px;
         will-change:transform,opacity}
.gnome{margin:8px 0 0;font-size:12.5px;color:var(--dim);font-style:italic;
       display:flex;align-items:baseline;gap:6px}
.altxt .gwho{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
             color:var(--gold);margin-right:5px}
.gnome .gwho{font-style:normal;font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;
             color:var(--gold);flex:0 0 auto}

/* ── sheets ─────────────────────────────────────────────── */
.sheet{position:fixed;inset:0;background:#000a;display:flex;align-items:flex-end;z-index:40}
.sheet[hidden]{display:none}
.sheetcard{background:var(--bg2);border-top:1px solid var(--line2);border-radius:18px 18px 0 0;
           width:100%;max-height:88vh;overflow-y:auto;padding:18px 16px calc(20px + env(safe-area-inset-bottom));
           position:relative;animation:up .18s ease}
@keyframes up{from{transform:translateY(24px);opacity:.4}to{transform:none;opacity:1}}
@keyframes down{from{transform:translateY(-16px);opacity:.4}to{transform:none;opacity:1}}
.sheetcard.tall{max-height:92vh}
.close{position:absolute;top:8px;right:10px;width:34px;height:34px;border-radius:10px;
       font-size:22px;color:var(--dim);line-height:1}
.sheetcard h2{margin:0 0 10px;font-size:19px;padding-right:36px}
.sheetcard h3{margin:16px 0 4px;font-size:13px;color:var(--gold);text-transform:uppercase;letter-spacing:.08em}
.sheetcard p,.sheetcard li{font-size:13.5px;color:var(--dim)}
.sheetcard b{color:var(--ink)}
.sheetcard ol{padding-left:18px;margin:6px 0}
.sheetcard li{margin-bottom:6px}
.sheethead{display:flex;gap:12px;align-items:center}
.sheethead h2{margin:0}
.sheethead p{margin:2px 0 0;font-size:12px}
.sheetico img,.sheetico svg{width:56px;height:56px}
.origin{font-size:12.5px;font-style:italic;color:var(--dim);margin:10px 0 0}
.lineage{margin:8px 0 0;font-size:13px}
.sheetact{display:flex;gap:8px;margin-top:14px;flex-wrap:wrap}
.primary,.ghost,.danger{padding:11px 14px;border-radius:11px;font-size:14px;font-weight:600;min-height:var(--tap)}
.primary{background:var(--gold);color:#22301c}
.primary[disabled]{background:#3a4a3f;color:var(--dim)}
.ghost{background:var(--panel);border:1px solid var(--line);color:var(--ink)}
/* a pin control lit up is a pin that is currently in the quick bar */
.ghost.on{border-color:var(--gold);background:#27381f}
.danger{background:#40201e;border:1px solid #6b3330;color:#f0bcb6;font-size:12px;padding:9px 12px}
.note{font-size:12px;color:var(--dim);margin:8px 0 0}
.fine{font-size:12px;color:var(--faint);margin-top:12px}
.mono{font-family:var(--mono);font-size:12px;color:var(--faint)}
/* v2.3 options on the Instrumentation sheet */
.optHead{font-size:13px;letter-spacing:.08em;text-transform:uppercase;color:var(--gold);margin:6px 0 8px}
.opt{display:flex;align-items:center;gap:10px;min-height:var(--tap);font-size:14px;color:var(--ink);cursor:pointer}
.opt input{width:22px;height:22px;accent-color:var(--gold);flex:none}
.optFine{margin:2px 0 14px}
#statTable{width:100%;border-collapse:collapse;margin-top:4px}
#statTable td{padding:6px 2px;border-bottom:1px solid var(--line);font-size:13px}
/* the whole right-hand column of Instrumentation is figures in a column,
   which is the case tabular mono exists for -- including the hit rate,
   which since v1.5 is read here and in the "more" overlay and nowhere
   else. 500 is the heaviest mono shipped, so it is what "bold" means. */
#statTable td:last-child{text-align:right;font-family:var(--mono);font-weight:500;
                         font-variant-numeric:tabular-nums;color:var(--ink)}
#statTable td:first-child{color:var(--dim)}
#statTable tr.head td{color:var(--gold);font-size:12px;text-transform:uppercase;letter-spacing:.08em;
                      border-bottom-color:var(--line2);padding-top:14px}

/* ── the strip: both errands, one 44px row ──────────────── */
/* A ZONE: fixed height whatever the names and counts say. Today's target
   used to be a 64px card and this week's family a 38px one on the row
   under it -- 110px of the top half. They are one row now: the target
   takes the room, the family is an icon and a count, and each one still
   opens exactly the sheet it opened before. */
.strip{display:flex;align-items:stretch;gap:8px;margin:6px 12px 0;height:38px;flex:0 0 auto}
.strip[hidden]{display:none}
.daily{display:flex;align-items:center;gap:7px;padding:0 9px;
       height:100%;min-width:0;flex:1 1 auto;overflow:hidden;
       border-radius:10px;background:var(--panel);border:1px solid var(--line)}
.daily[hidden]{display:none}
.daily.done{border-color:#3f9b58;background:linear-gradient(180deg,#1d3a2a,#162c20)}
.daily img{width:22px;height:22px;flex:0 0 auto}
.daily b{font-size:11.5px;line-height:1.2;min-width:0;flex:1 1 auto;
         overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.daily #dayState{font-size:10.5px;line-height:1.2;color:var(--faint);flex:0 0 auto;
                 white-space:nowrap}
.daily.done #dayState{color:#7fc79b}
/* written on every render, read by the target sheet, no room on the strip */
.dwhere{display:none}
.dshare{display:none}
/* "Guide me": the still-to-find count on a strain page is a question, and
   this is the button that answers it for the price of a hint. */
.guideline{margin:7px 0 0;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.guidebtn{flex:0 0 auto;font-size:12px;padding:7px 11px;border-radius:999px;
          background:#27381f;border:1px solid var(--line2);color:var(--ink)}
.guidebtn:active{background:#33492a}
.guideline span{flex:1 1 140px;min-width:0;font-size:11.5px;color:var(--faint);
                line-height:1.35}

/* the streak: consecutive UTC days the target was reached. It is gold
   because it is the one number here that is about turning up. */
.dstreak{flex:0 0 auto;font-size:10px;font-weight:700;letter-spacing:.04em;
         padding:2px 6px;border-radius:999px;color:#241d05;background:var(--gold)}
.dstreak[hidden]{display:none}
.tgstreak{margin:0 0 10px;font-size:12px;color:var(--gold)}
.daily:active{background:var(--panel2)}

/* This week's family: the right-hand end of the strip. It is a direction,
   not a second errand, so it is the sprite and the count and nothing else
   -- the name and the wording are on the parent's page, which is what it
   opens. The text is kept and hidden rather than dropped: it is what the
   card announces to a screen reader. */
.weekly{display:flex;align-items:center;gap:6px;padding:0 8px;height:100%;
        overflow:hidden;border-radius:10px;background:var(--bg2);
        border:1px solid var(--line);flex:0 0 auto}
.weekly[hidden]{display:none}
.weekly.done{border-color:#7a6a2a;background:#231f12}
.weekly img{width:20px;height:20px;flex:0 0 auto}
/* the wording is written on every render and read out as the card's own
   aria-label; the strip shows the sprite and the count */
.wtxt{display:none}
.wkpip{flex:0 0 auto;font-size:10.5px;font-family:var(--mono);
       font-variant-numeric:tabular-nums;color:var(--dim);
       padding:2px 6px;border-radius:999px;background:var(--panel);border:1px solid var(--line)}
.weekly.done .wkpip{color:var(--gold);border-color:#7a6a2a}
.weekly:active{background:var(--panel)}

/* ── v2.0: the mission in hand, a third cell on the same 38px row ──── */
/* Same shape as the weekly card: a mark and a count, flex:0 0 auto, so the
   row's height never moves and the daily target gives up a few pixels of
   name instead. The mark is the chapter's badge glyph in its own colour. */
.mission{display:flex;align-items:center;gap:5px;padding:0 8px;height:100%;
         overflow:hidden;border-radius:10px;background:var(--bg2);
         border:1px solid var(--line2);flex:0 0 auto;cursor:pointer}
.mission[hidden]{display:none}
.msglyph{font-size:17px;line-height:1;flex:0 0 auto}
.mspip{flex:0 0 auto;font-size:10.5px;font-family:var(--mono);
       font-variant-numeric:tabular-nums;color:var(--ink);
       padding:2px 6px;border-radius:999px;background:var(--panel);border:1px solid var(--line)}
.mission.done .mspip{color:var(--gold);border-color:#7a6a2a}
.mission:active{background:var(--panel)}

/* ── v2.0: the Chapters sheet ──────────────────────────────────────── */
.chcard{margin:0 0 9px;border-radius:12px;background:var(--panel);
        border:1px solid var(--line);overflow:hidden}
.chcard.on{border-color:var(--line2)}
.chcard.done{border-color:#7a6a2a}
.chcard.locked{opacity:.72}
.chhead{display:flex;align-items:center;gap:10px;width:100%;padding:10px 12px;text-align:left}
.chglyph{font-size:24px;line-height:1;width:30px;text-align:center;flex:0 0 auto}
.chname{flex:1 1 auto;min-width:0;display:flex;flex-direction:column}
.chname i{font-style:normal;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.chname b{font-size:15.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chstate{flex:0 0 auto;font-size:11.5px;color:var(--dim);font-family:var(--mono)}
.chcard.done .chstate{color:var(--gold)}
.chbody{padding:0 12px 12px;border-top:1px solid var(--line)}
.chblurb{margin:10px 0 8px;font-size:13px;color:var(--dim);line-height:1.45}
.chbody h3{margin:12px 0 6px}
.msrow{display:flex;gap:9px;padding:8px 0;border-top:1px dashed var(--line)}
.msrow:first-of-type{border-top:0}
.msmark{flex:0 0 18px;font-size:14px;line-height:1.4;color:var(--faint);text-align:center}
.msrow.done .msmark{color:var(--grn)}
.msrow.on .msmark{color:var(--gold)}
.mstxt{flex:1 1 auto;min-width:0}
.mstxt b{display:block;font-size:13.5px;line-height:1.35}
.msrow.done .mstxt b{color:var(--dim);font-weight:600}
.msrow.next .mstxt b{color:var(--faint);font-weight:600}
.mssay{margin:5px 0 7px;font-size:12.5px;line-height:1.45;color:var(--dim)}
.mssay .gwho{margin-right:5px}
.msbar{height:6px;border-radius:999px;background:var(--bg);border:1px solid var(--line);overflow:hidden}
.msbar i{display:block;height:100%;background:var(--gold)}
.msn{display:block;margin:4px 0 6px;font-size:11.5px;color:var(--faint)}
.pchip.unknown{border-style:dashed;color:var(--gold);min-width:34px;justify-content:center}
.pchip.plain{color:var(--ink)}
/* the world sketch: no tiles, no fetch, one path and a few dots */
.worldmap{display:block;width:100%;height:auto;margin:4px 0 4px;border-radius:9px;
          background:#0b1612;border:1px solid var(--line)}
.worldmap .land{fill:#1d3328;stroke:#2d5040;stroke-width:.6}
.worldmap .mapreg{fill:#95b1a4;font-size:6.5px;font-family:var(--mono);text-anchor:middle;
                  letter-spacing:.02em}
.worldmap .mappin{fill:#6d8a7d;stroke:#0b1612;stroke-width:1}
.worldmap .mappin.lit{fill:var(--gold)}
.mapkey{margin:2px 0 8px;font-size:11px;color:var(--faint);line-height:1.5}
.mapkey .dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:#6d8a7d;
             margin:0 3px 0 6px;vertical-align:-1px}
.mapkey .dot:first-child{margin-left:0}
.mapkey .dot.lit{background:var(--gold)}
.mapgrp{margin:10px 0 4px;font-size:11px;color:var(--gold);text-transform:uppercase;letter-spacing:.08em}
.maprow{display:flex;align-items:center;gap:8px;margin:0 0 5px;min-width:0}
.maprow .pchip{flex:0 1 auto;min-width:0}
.mapwhere{flex:1 1 auto;min-width:0;font-size:11px;color:var(--faint);
          overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mapn{flex:0 0 auto;font-size:11px;font-family:var(--mono);color:var(--dim)}
/* the badge shelf */
.badges{display:flex;flex-wrap:wrap;gap:10px;margin:4px 0 6px}
.bdg{display:flex;flex-direction:column;align-items:center;gap:3px;width:92px;text-align:center}
.bdgico{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;font-size:26px;
        border:2px dashed var(--line2);color:var(--line2);background:var(--panel)}
.bdg.got .bdgico{border-style:solid;background:var(--panel2)}
.bdg b{font-size:12px;line-height:1.25}
.bdg:not(.got) b{color:var(--faint)}
.bdg i{font-style:normal;font-size:10.5px;color:var(--faint)}

/* today's target sheet */
/* the shape of the recipe, first thing, before any of the facts */
.tgshape{margin:0 0 11px;padding:9px 11px;border-radius:11px;font-size:12.5px;
         line-height:1.45;color:var(--dim);background:#16281f;
         border:1px solid var(--line2)}
.tgshape b{color:var(--ink)}
.tgrow{display:flex;gap:9px;align-items:baseline;margin:0 0 5px;font-size:12.5px;color:var(--dim)}
.tgrow span{flex:0 0 96px;color:var(--faint);font-size:11.5px;letter-spacing:.07em;
            text-transform:uppercase}
.tgrow b{color:var(--ink)}
.tghint{display:block;width:100%;text-align:left;margin-bottom:7px;padding:10px 12px;
        border-radius:12px;border:1px solid var(--line);background:var(--panel);
        font-size:12.5px;color:var(--dim);line-height:1.45}
.tghint.got{border-color:var(--line2);background:var(--panel2);color:var(--ink)}
.tghint.locked{opacity:.6}
.tglab{color:var(--gold);display:block;font-size:11.5px;letter-spacing:.07em;
       text-transform:uppercase;margin-bottom:3px}
.tghint b{color:var(--ink)}
.tghint.next{border-color:var(--gold)}
.tghint.next:active{background:var(--panel2)}

/* ── lab notebook ───────────────────────── */
.nbfilter{display:flex;gap:6px;margin:10px 0 8px}
.chip{font-size:12.5px;padding:6px 11px;border-radius:999px;background:var(--panel);
      border:1px solid var(--line);color:var(--dim)}
.chip.on{background:var(--panel2);border-color:var(--gold);color:var(--ink)}
.nblist{display:flex;flex-direction:column;gap:6px}
.nbrow{display:flex;align-items:center;gap:9px;padding:7px 9px;border-radius:11px;
       background:var(--panel);border:1px solid var(--line)}
.nbrow.hit{border-color:#3f6b4e}
.nbrow.miss{border-color:#5c3a38}
.nbpair{display:flex;align-items:center;gap:2px;flex:0 0 auto}
/* a phenotype opening has one parent, not a pair: same width, one icon */
.nbpair.one{width:46px;justify-content:center}
.nbpair img{width:22px;height:22px}
.nbtxt{min-width:0;flex:1 1 auto}
.nbtxt b{display:block;font-size:12.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;
         white-space:nowrap}
.nbtxt span{font-size:12px;color:var(--dim)}
.nbrow.hit .nbtxt span{color:#7fc79b}
.nbwhen{flex:0 0 auto;font-size:11px;color:var(--dim);text-align:right;line-height:1.25}
.nbempty{font-size:12.5px;color:var(--dim);padding:14px 2px}

/* a pair you have already put on the bench */
.tile.tried{opacity:.62}
/* the green corner owns the top-left now, so the tried tick drops below it */
.tile .triedmark{position:absolute;top:16px;left:3px;width:14px;height:14px;border-radius:999px;
                 background:#3a4a41;color:#cfe3d8;font-size:10px;font-weight:700;display:grid;
                 place-items:center}
.op.tried{color:var(--gold);font-size:11px;width:auto;letter-spacing:.06em;text-transform:uppercase}

/* ── Fegnome, the field assistant ─────────────── */
/* Fegnome sits in the header with the other tools. He used to be a floating
   bottom-right button whose bubble covered the codex; now he owns a slot in
   a bar that already owns its space, and the bubble hangs under it. */
/* the bell, and the unread count it carries */
.alertbtn{position:relative;overflow:visible}
.adot{position:absolute;top:-5px;right:-5px;min-width:17px;height:17px;padding:0 4px;
      border-radius:999px;background:var(--gold);color:#22301c;font-size:11px;font-weight:800;
      display:grid;place-items:center;border:2px solid var(--bg2)}
.adot[hidden]{display:none}

/* Alerts inbox */
.alrow{display:flex;align-items:flex-start;gap:9px;width:100%;text-align:left;
       padding:9px 10px;margin-bottom:6px;border-radius:11px;
       background:var(--panel);border:1px solid var(--line)}
.alrow.unread{border-color:var(--gold);background:var(--panel2)}
.alrow:active{background:var(--panel2)}
.alkind{flex:0 0 auto;font-size:13px;color:var(--gold);line-height:1.5}
.altxt{flex:1 1 auto;min-width:0;font-size:12.5px;color:var(--dim);line-height:1.4}
.altxt b{color:var(--ink)}
.alwhen{flex:0 0 auto;font-size:11px;color:var(--faint);line-height:1.6;white-space:nowrap}

/* Achievements board */
.achrow{display:flex;align-items:flex-start;gap:9px;padding:9px 10px;margin-bottom:6px;
        border-radius:11px;background:var(--panel);border:1px solid var(--line);opacity:.72}
.achrow.got{opacity:1;border-color:#3f9b58;background:linear-gradient(180deg,#1d3a2a,#162c20)}
.achmark{flex:0 0 auto;font-size:14px;color:var(--faint);line-height:1.4}
.achrow.got .achmark{color:var(--gold)}
.achtxt{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:2px}
.achtxt b{font-size:13px;color:var(--ink)}
.achtxt span{font-size:12px;color:var(--dim);line-height:1.35}

.gnomebtn{position:relative;background:var(--panel2);border-color:var(--line2);
          padding:0;overflow:visible}
.gnomebtn[hidden]{display:none}
.gnomebtn img{width:30px;height:30px;display:block;margin-bottom:-2px}
.gnomebtn:active{background:#2b4a3b;transform:scale(.95)}
.gdot{position:absolute;top:-3px;right:-3px;width:11px;height:11px;border-radius:999px;
      background:var(--gold);border:2px solid var(--bg2)}
.gdot[hidden]{display:none}

/* Out of the flow, directly under the header. It used to be a flow row, so
   opening it pushed every zone below it down the screen; #notices is a
   zero-height row in the same place whose stack floats, so the bubble lands
   under the bar, over the bench, and moves nothing. */
#notices{flex:0 0 auto;height:0;position:relative;z-index:30}
#notices .nstack{position:absolute;top:0;left:0;right:0;max-width:100%;pointer-events:none}
/* the bubble has buttons in it, so it catches pointers; a pop-up never does.
   A pop-up floats over live controls, and one that swallowed the press would
   cost the player the tap they meant to make -- it clears on the next press
   anywhere instead, and the press still lands on what is under it. */
#notices .gbubble{pointer-events:auto}
.gbubble{flex:0 0 auto;padding:6px 8px 0;max-width:100%}
.gbubble[hidden]{display:none}
.gcard{background:var(--panel2);border:1px solid var(--line2);border-radius:16px;
       padding:14px 14px 12px;box-shadow:0 10px 30px #0009;position:relative;
       animation:down .16s ease}
.gcard:after{content:"";position:absolute;right:16px;top:-9px;width:16px;height:16px;
             background:var(--panel2);border-left:1px solid var(--line2);
             border-top:1px solid var(--line2);transform:rotate(45deg)}
.gcard .close{top:4px;right:6px}
.ghead{display:flex;align-items:center;gap:9px;margin-bottom:8px}
.ghead img{width:38px;height:38px;flex:0 0 auto}
.ghead div{display:flex;flex-direction:column;line-height:1.15}
.ghead b{font-size:13px}
.ghead span{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint)}
.ghead.big img{width:52px;height:52px}
#gsay{margin:0;font-size:13.5px;color:var(--ink);line-height:1.5}
#gsay b{color:var(--gold)}
#gsay i{color:var(--ink);font-style:italic}
.gact{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.gact .primary,.gact .ghost{padding:9px 12px;font-size:13px;min-height:40px;flex:1 1 auto}
.gact .primary[disabled]{background:#3a4a3f;color:var(--faint)}
#gnote{margin-top:8px}

/* ── toast + tutorial ───────────────────────────────────── */
.toast[hidden]{display:none}
/* Pop-ups live at the TOP, under the bar, where the hand holding the phone
   is not. They are drawn in #notices, a zero-height row, so they float over
   the top of the bench for their seven seconds and no zone underneath them
   shifts. The safe-area inset is already spent by <body>. */
.toast{flex:0 0 auto;margin:6px 12px 0;z-index:30;   /* under the sheets (40) */
       background:var(--panel2);border:1px solid var(--gold);border-radius:12px;padding:11px 13px;
       font-size:13px;line-height:1.4;box-shadow:0 8px 24px #0008;animation:down .18s ease}
.toast b{color:var(--gold)}
.tut{position:fixed;inset:0;background:#000c;z-index:70;display:flex;align-items:center;justify-content:center;padding:20px}
.tut[hidden]{display:none}
.tutcard{background:var(--bg2);border:1px solid var(--line2);border-radius:16px;padding:20px;max-width:340px}
.tutcard h2{margin:0 0 8px;font-size:18px}
.tutcard p{margin:0;font-size:14.5px;color:var(--dim)}
.tutfoot{display:flex;align-items:center;justify-content:space-between;margin-top:18px}
#tutDots{letter-spacing:5px;color:var(--dim);font-size:12px}

/* 320-374 px (iPhone SE and friends): the wordmark is the first thing to go,
   then the hit-rate label, so the counters and both bench slots always fit. */
/* The score captions are bigger now, so the wordmark is the first thing to
   go: FOUND and HIT RATE are numbers the player reads, the logotype is not. */
/* The header is six tools, three numbers and the brand, and something has
   to give before 520px. v1.3 gave up the whole brand block between 375 and
   429 because the old "FA" badge was being clipped to two thirds of itself
   by the bar's own overflow, which reads as a bug rather than as a logo.
   The real company mark is 25px wide at 34px tall -- narrower than that
   badge -- so it now stays on at every width, and it is the game's-name
   line under the wordmark that goes first, then the wordmark's own size,
   and only under 375px does the "Fegnome" name mark step aside for the
   counters. The mark never does. */
/* WHAT THE PHONE BAR CARRIES NOW. v1.3 deleted the whole brand block
   between 375 and 429 to make room; v1.4 got the mark back and paid for
   it by hiding the HIT RATE column below 520. v1.5 deletes the hit rate
   outright, at every width -- it is read in the "more" overlay and in
   Instrumentation -- and the phone bar is no longer scraping for pixels
   to hold six tools, two figures and the lockup at once.
   So the game's-name line comes back at every width it fits: it is
   narrower than the "Fegnome" line above it, so it costs the bar no
   width at all, and the two-line wordmark is still shorter than the mark
   beside it, so it costs no height either. */
@media (max-width:519px){
  .wordmark .wm{font-size:15px}
  .wordmark b{font-size:8.5px;letter-spacing:.15em}
  .tools{gap:4px}
  .icn{width:30px;height:30px}
  .gnomebtn img{width:26px;height:26px}
}
@media (max-width:429px){
  #bar{padding:8px}
  .brand{gap:4px;flex:0 0 auto}
  .mark{height:26px}
  .wordmark .wm{font-size:12.5px}
  .wordmark b{font-size:8px;letter-spacing:.12em}
}
/* 390px -- the baseline phone -- is the narrowest bar the name mark fits
   in: mark, wordmark, two numbers and six tools land on 383 of 390. Below
   that the words come off and the mark carries the brand alone. */
@media (max-width:389px){
  .wordmark{display:none}
}

/* 320-374px: even with the hit rate gone there is no room for a name in
   words, so the mark carries the brand on its own -- it never goes. */
@media (max-width:374px){
  .wordmark{display:none}
  .tools{gap:4px}
  .icn{width:30px;height:30px}
  .gnomebtn img{width:26px;height:26px}
  #bench{padding:7px 8px}
  .slots{gap:8px}
  .op{width:14px;font-size:17px}
  .codex{grid-template-columns:repeat(auto-fill,minmax(74px,1fr));gap:7px;
         padding:10px 8px 20px}
  .codexbar{padding:7px 8px}
  .quick{padding:6px 8px 0;gap:5px}
  .qslot{height:36px}
  .qnm{font-size:9px}
  .strip{margin-left:8px;margin-right:8px}
  .morecard{padding-left:10px;padding-right:10px}
  .libgrid{gap:8px;padding:0 8px 24px}
  .libcard{min-height:146px;padding:12px 6px 20px}
  .libcard img{width:56px;height:56px}
  .libhead,.libfilters{padding-left:8px;padding-right:8px}
  .treetop{padding-left:8px;padding-right:8px}
  .treeacts .chip{padding:6px 8px;font-size:11.5px}
  .spgbody{padding:10px 10px 28px}
}

/* Short phones (and a phone in landscape) have no spare rows at all: below
   about 680px the half is 340px and the four fixed rows are already 212 of
   it. The bench and the quick bar give up a few more pixels each and the
   result zone's floor comes down with them, so the tiles keep their half
   as far down as the layout can carry it. */
@media (max-height:680px){
  :root{--zone-result:clamp(120px,calc(50vh - 196px),180px)}
  .slot{height:48px}
  .slot img,.slot svg{width:28px;height:28px}
  #bench{padding:5px 12px}
  .quick{padding:5px 12px 0}
  .qslot{height:34px}
  .strip{height:34px;margin-top:5px}
  .result{margin-top:5px;padding:7px 10px}
}

@media (min-width:560px){
  body{max-width:520px;margin:0 auto;border-left:1px solid var(--line);border-right:1px solid var(--line)}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}


/* ── the demo's end card ───────────────────────────────────────────
   The card is an ordinary .sheet, so it inherits the whole sheet look;
   only the email row and the honeypot are new. The honeypot is off the
   screen rather than display:none -- a bot that reads the stylesheet
   fills it in either way, and a screen reader skips it on aria-hidden. */
.ecform { margin-top: 14px; border-top: 1px solid var(--line, #24402f); padding-top: 12px; }
.ecform h3 { margin: 0 0 8px; }
.ecrow { display: flex; gap: 8px; flex-wrap: wrap; }
.ecrow input {
  flex: 1 1 180px; min-width: 0; padding: 11px 12px; font: inherit;
  color: inherit; background: #10201a; border: 1px solid #2d5040;
  border-radius: 10px;
}
.ecrow input:focus { outline: 2px solid #4fc98a; outline-offset: 1px; }
.ecrow button { flex: 0 0 auto; }
.ecrow button[disabled] { opacity: .55; }
.echp {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
#ecFound b { color: #e8c34a; }
.chip.locked { opacity: .72; border-style: dashed; }
