/* =====================================================================
 GRFT — THE FRONT DOOR.

 The screen: the hero, the two ways in, the status strip, over a
 rotation of frames from the running city. Under it, the page: what
 the city runs, in its own frames, and the manual.

 Two things this file got wrong when the door was one still screen,
 both fixed here and both worth naming:

 THE PLATE WAS INVISIBLE. brightness(.66) and a six-pixel blur under
 a scrim at .92 is not a dark city, it is a black page with grain on
 it — the best frame the game has, on the screen, unreadable. The
 grade opens up and the scrim became a wipe across the frame: held
 down under the copy, let go over the far side.

 THE PAGE ENDED AT THE FOLD. A reader not ready to press a door had
 nowhere to go and nothing to read. The sections are back under it.

 Every colour and face comes from css/palette.css. The accent appears
 on this screen in the alpha dot, a door under the pointer, a link
 under the pointer, and the section kickers. Everything else is ink.

 The layer sits over the game canvas at z 80; js/landing.js takes it
 away when a door is used, and owns the rotation.
 ===================================================================== */

#landing {
 position: fixed; inset: 0; z-index: 80;
 display: block;
 overflow-y: auto; overscroll-behavior: contain;
 scroll-behavior: smooth;
 background: var(--gr-void);
 color: var(--gr-ink);
 font-family: var(--gr-body);
 -webkit-font-smoothing: antialiased;
 font-synthesis: none;
 opacity: 1; transition: opacity .38s ease;
}
#landing.gone { opacity: 0; pointer-events: none; }
#landing a { color: inherit; text-decoration: none; }
#landing :focus-visible { outline: 2px solid var(--gr-accent); outline-offset: 3px; }

/* ---- the header: pills over the art, never a bar across it ----------
 A solid strip along the top edge would put a band of panel over the frame,
 and the frame is the product. Three floating pieces instead, with a
 translucent fill and a blur so the city reads through them.

 FIXED, AND OVER THE HERO RATHER THAN ABOVE IT. The hero is not pushed
 down — .fd-hero's own top padding is what leaves the room, so the copy
 stays centred in the screen. z 10 clears the hero's z 2 and stays well
 under the wallet chooser's z 120.

 THE FILL FIRMS UP ONCE SCROLLED, and that is not decoration: over the
 hero the plate is graded down under the copy, so links read at .42; over
 the sections below, and over the bright side of a sunset frame, they do
 not. js/landing.js puts .is-scrolled on #landing past the fold.

 POINTER-EVENTS: NONE ON THE BAR, AUTO ON THE PIECES. The header spans the
 full width and is fixed over the hero; without this, the empty air either
 side of the pill would swallow every click meant for the page under it —
 including, at the top of the screen, the hero itself. */
.fd-head {
 position: fixed; top: 0; left: 0; right: 0; z-index: 10;
 display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
 padding: 18px 7vw; pointer-events: none;
}
.fd-head > * { pointer-events: auto; }
#landing .fd-hmark {
 justify-self: start;
 font-family: var(--gr-display); font-weight: 700; font-size: 17px; letter-spacing: .04em;
 color: var(--gr-ink-2); transition: color .16s ease;
}
#landing .fd-hmark:hover, #landing .fd-hmark:focus-visible { color: var(--gr-ink); }
.fd-hpill {
 justify-self: center;
 display: flex; align-items: center; gap: 22px;
 height: 42px; padding: 0 22px;
 border: 1px solid var(--gr-line-2); border-radius: 999px;
 /* .42 AND INK-3 WAS NOT ENOUGH, AND THE PICTURE SAID SO. Over the dark
 plates it read; over the sunset frame, where the sky behind the pill is
 the brightest thing on the screen, four grey capitals at .42 opacity were
 barely there. The plate rotation means the pill is over a bright sky some
 of the time whatever the scroll position, so the base fill carries it and
 the scrolled state is still a step firmer on top. */
 background: rgba(8, 9, 10, .56);
 -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
 transition: background .2s ease, border-color .2s ease;
}
#landing.is-scrolled .fd-hpill { background: rgba(8, 9, 10, .8); border-color: var(--gr-line-3); }
#landing .fd-hpill a {
 font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
 color: var(--gr-ink-2); transition: color .16s ease;
}
#landing .fd-hpill a:hover, #landing .fd-hpill a:focus-visible { color: var(--gr-ink); }
/* THE SEARCH CONTROL, RENDERED ONLY WHEN THERE IS A SEARCH TO OPEN. It opens
 the guide's own search rather than a second box that filters a different
 list; js/guidenav.js holds the route and holds null until that exists. An
 icon that opens nothing is decoration, and decoration shaped like a control
 is worse than a gap. */
.fd-hsearch {
 display: inline-flex; align-items: center; justify-content: center;
 width: 26px; height: 26px; margin: 0 -8px 0 -6px; padding: 0;
 border: 0; border-radius: 999px; background: none; cursor: pointer;
 color: var(--gr-ink-3); transition: color .16s ease, background .16s ease;
}
.fd-hsearch { color: var(--gr-ink-2); }
.fd-hsearch:hover, .fd-hsearch:focus-visible { color: var(--gr-ink); background: rgba(237, 230, 214, .09); }
.fd-hsearch svg { width: 14px; height: 14px; display: block; }
/* ---- the header's right side -----------------------------------------
 IT WAS EMPTY BY DESIGN AND IT STILL REPORTS NOTHING. No presence count, no
 viewers, no live dot: this page shows what the world produced, and none of
 those are. One glyph is the exception, and it shares the column with the
 menu button rather than starting a second row — above 900px the burger is
 display:none and the glyph stands alone; below it they sit side by side,
 which is why this is a flex box and not a bare grid cell. */
.fd-hright { grid-column: 3; justify-self: end; display: inline-flex; align-items: center; gap: 8px; }
.fd-hx { width: 34px; height: 34px; }
.fd-hx svg { width: 14px; height: 14px; }
/* nothing at all until the pill will not fit */
.fd-hburger, .fd-hsheet { display: none; }

@media (max-width: 900px) {
 .fd-head { grid-template-columns: auto 1fr auto; padding: 14px 22px; }
 .fd-hpill { display: none; }
 .fd-hburger {
 display: inline-flex; flex-direction: column; justify-content: center; gap: 5px;
 width: 42px; height: 42px; padding: 0 11px;
 border: 1px solid var(--gr-line-2); border-radius: 999px;
 background: rgba(8, 9, 10, .55);
 -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
 cursor: pointer;
 }
 .fd-hburger span { display: block; height: 1px; background: var(--gr-ink-2); transition: transform .2s ease; }
 .fd-hburger[aria-expanded="true"] span:first-child { transform: translateY(3px) rotate(45deg); }
 .fd-hburger[aria-expanded="true"] span:last-child { transform: translateY(-3px) rotate(-45deg); }
 /* A SHEET, NOT A TAKEOVER. It drops under the header and covers what it
 needs to; the plate stays visible under and beside it, which is the whole
 reason the header floats in the first place. */
 .fd-hsheet {
 grid-column: 1 / -1; margin-top: 12px; padding: 8px;
 display: flex; flex-direction: column; gap: 2px;
 border: 1px solid var(--gr-line-2); border-radius: 14px;
 background: rgba(8, 9, 10, .93);
 -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
 }
 .fd-hsheet[hidden] { display: none; }
 #landing .fd-hsheet a {
 padding: 13px 14px; border-radius: 9px;
 font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--gr-ink-2);
 }
 #landing .fd-hsheet a:hover, #landing .fd-hsheet a:focus-visible { color: var(--gr-ink); background: rgba(237, 230, 214, .08); }
}

/* ---- the screen: one viewport, the door on it ----------------------- */
.fd-top {
 position: relative; overflow: hidden;
 min-height: 100vh; min-height: 100svh;
 display: flex; flex-direction: column; justify-content: center;
}

/* ---- the plates: nine frames, one at a time ------------------------- */
.fd-plates { position: absolute; inset: 0; overflow: hidden; }
.fd-plate {
 position: absolute; inset: -3%;
 background: #05060a center / cover no-repeat;
 /* THE IMAGE IS NAMED INLINE, TWICE — see index.html's .fd-plate elements.
 A browser without image-set() keeps the plain url(); everything since
 ~2021 takes the webp from the image-set() that follows it. No
 User-Agent sniff, no server rewrite, no script.

 IT IS WRITTEN OUT RATHER THAN BUILT FROM CUSTOM PROPERTIES, and that is
 not a style preference. image-set(var(--w) ...) parses, computes, and
 shows up correctly in getComputedStyle — and never loads the image.
 The hero came out plain black. Measured, not assumed: the first cut of
 this change shipped var() and the landing lost its photograph.

 Both bakes are 1280x720 (tools/bake-landing-art.mjs). The box is wider
 than that at large viewports, but the blur below throws away everything
 finer than about 5 px, so those extra pixels never reached the glass. */
 /* a city, legibly. The blur is enough to keep type off texture and no
 more; the brightness is the frame's own. */
 filter: blur(2.5px) saturate(.92) brightness(.94) contrast(1.03);
 transform: scale(1.04);
 opacity: 0; transition: opacity 1.5s ease;
 will-change: opacity;
}
.fd-plate.is-on { opacity: 1; }

/* the copy side goes down so ivory reads without a panel behind it; the
 far side is let go, so the frame behind the doors is a place */
.fd-vignette {
 position: absolute; inset: 0;
 background:
 linear-gradient(100deg, rgba(6,7,9,.90) 0%, rgba(6,7,9,.68) 32%, rgba(6,7,9,.18) 64%, rgba(6,7,9,.34) 100%),
 linear-gradient(to top, rgba(6,7,9,.74) 0%, rgba(6,7,9,0) 30%),
 radial-gradient(130% 100% at 46% 46%, rgba(6,7,9,0) 48%, rgba(6,7,9,.46) 100%);
}
/* a fine grain, so the blur reads as film rather than as a blurred JPEG */
.fd-grain {
 position: absolute; inset: 0; opacity: .07; mix-blend-mode: overlay;
 background-image: url("data:image/svg+xml;utf8, ");
}

/* ---- the hero ------------------------------------------------------- */
/* THE BOTTOM PADDING PAID FOR THE METADATA BLOCK. 150px of air under the
 links was sized for a hero that ended at a link row; the block is 100px of
 real content in the same place now, and leaving both put .fd-top 78px
 taller than the viewport at 1440x900 — which pushed THE CITY BELOW and the
 status strip off the bottom of the screen. Neither of those was asked to
 move, and a scroll cue you have to scroll to find is not a cue. */
/* AND THE TOP PADDING IS WHAT THE FLOATING HEADER SITS IN. The header is
 fixed and takes no space in the flow, so this is the only thing keeping
 OPEN ALPHA out from under the nav pill: 18px of offset plus a 42px pill
 is 60, and the rest is air the eyebrow wanted anyway. */
.fd-hero {
 position: relative; z-index: 2;
 width: min(100% - 12vw, 700px);
 margin: 0 0 0 7vw;
 /* 76 rather than 88: the header took 14px of the hero at the top and the
 status strip sits at the bottom of .fd-top, so the difference came
 straight off the bottom of the screen. 953 before this pass, 953 now. */
 padding: 118px 0 76px;
}
.fd-eyebrow {
 margin: 0 0 26px; display: inline-flex; align-items: center; gap: 9px;
 font-size: 10px; letter-spacing: .22em; text-transform: uppercase; color: var(--gr-ink-3);
}
.fd-dot {
 width: 6px; height: 6px; border-radius: 50%;
 background: var(--gr-accent); box-shadow: 0 0 0 3px var(--gr-accent-a);
}
.fd-title {
 margin: 0; font-family: var(--gr-display); font-weight: 700;
 font-size: clamp(64px, 9.4vw, 132px); line-height: .84; letter-spacing: -.04em;
 color: var(--gr-ink);
}
.fd-enter {
 margin: 18px 0 0; font-size: clamp(17px, 1.5vw, 21px); font-weight: 400;
 letter-spacing: .01em; color: var(--gr-ink-2);
}
.fd-lede {
 margin: 20px 0 0; max-width: 56ch;
 font-size: 14.5px; line-height: 1.66; color: var(--gr-ink-2);
}

/* ---- the row: one door, one document, both sized to their labels -----
 `flex: 1 1 0` is what used to be here, and it was right when the row held
 two doors: CONNECT WALLET is shorter than PLAY AS DEFAULT was, so sizing to
 text made the wallet visibly the smaller of two choices the product refused
 to rank. There is one door now and the other button opens a page, so there
 is no ranking left to avoid — and half of a 700px column is not a button,
 it is a bar. They size to what is written on them.

 EQUAL HEIGHT IS STRUCTURAL, NOT A COINCIDENCE OF PADDING. The row stretches
 its children (flex default), the slot is itself a flex box so it passes the
 stretch through to the button inside it, and .fd-doorname carries an
 explicit line-height so the text box is the same in both whatever else is
 in the button — CONTINUE also carries an address and an arrow. */
.fd-doors { margin-top: 32px; display: flex; flex-wrap: wrap; align-items: stretch; gap: 12px; }
#landing .fd-doorslot { display: flex; min-width: 0; flex: 0 0 auto; }
#landing .fd-doorslot[hidden] { display: none; }
.fd-btn {
 display: inline-flex; align-items: center; justify-content: center; gap: 10px; width: auto;
 box-sizing: border-box; padding: 18px 26px;
 border-radius: var(--gr-btn-radius); border: 1px solid transparent;
 font: inherit; cursor: pointer; white-space: nowrap;
 /* position + overflow so the busy sweep below has a box to travel inside.
 transform is on its own short curve: the press has to answer faster than
 a colour fade, and .18s of ease is already a beat too slow for it. */
 position: relative; overflow: hidden;
 transition: background .18s ease, border-color .18s ease, color .18s ease, transform .07s ease-out, filter .07s ease-out;
}
.fd-btn .fd-doorname { font-size: 12.5px; line-height: 1; letter-spacing: .13em; text-transform: uppercase; }
.fd-btnarrow { font-size: 12px; line-height: 1; opacity: .75; }
.fd-btn b { font-family: var(--gr-data); font-size: 12px; font-weight: 400; letter-spacing: .04em; opacity: .8; }
/* the filled one: ivory on the plate, the darkest ink on it.
 SCOPED UNDER #landing, AND THAT IS THE WHOLE POINT OF THIS LINE. Line 41 of
 this file says `#landing a { color: inherit; }`, which is (1,0,1) against
 this rule's (0,1,0) — so the moment a .fd-btn-primary was an rather than
 a, the id selector won and the label inherited the page's ivory. On
 an ivory button that is an empty cream box.
 It shipped that way: INSTALL METAMASK on the no-wallet gate is the one
 action the one-door front page owes a visitor without an extension, and it
 was a blank rectangle. connectgesture passed it, because it read the href.
 Nobody reads an href. */
/* AND THE FILL IS A TOKEN, NOT A COLOUR. It was var(--gr-ink) — the page's own
 ivory, which made the button the brightest thing on the screen by a wide
 margin. The four --gr-btn-* values in css/palette.css carry the reasoning
 and are read by the manual's PLAY GRFT as well, so the two cannot drift. */
/* AND .fd-walletpick IS IN THE LIST BECAUSE THE SHEET IS NOT IN #landing.
 js/landing.js appends the wallet sheet to document.body, so every rule
 scoped to #landing misses it. Measured, with the install state open:
 INSTALL METAMASK rendered rgb(0,0,238) on transparent — the browser's
 default link blue — and the two buttons beside it rendered as grey OS
 chrome. The shape came through because .fd-btn is unscoped; only the
 colour was scoped away. This is the same bug as the ivory-on-ivory one
 wearing different clothes: a rule that cannot reach the element it names. */
#landing .fd-btn-primary, .fd-walletpick .fd-btn-primary { background: var(--gr-btn-fill); color: var(--gr-btn-ink); border-color: var(--gr-btn-edge); }
#landing .fd-btn-primary:hover, #landing .fd-btn-primary:focus-visible,
.fd-walletpick .fd-btn-primary:hover, .fd-walletpick .fd-btn-primary:focus-visible {
 background: var(--gr-btn-hi); border-color: var(--gr-btn-hi); transform: translateY(-1px);
}
/* the outlined one: the same button, wearing the plate instead of covering it */
/* A DOOR THAT IS NOT OPEN HERE: SPECTATE while SPECTATE_ENABLED has it shut
 (COMING SOON), greyed and not pressable, but still in the row where it will be */
#landing .fd-btn.fd-off { opacity: .42; pointer-events: none; cursor: default; filter: saturate(.4); }
#landing .fd-btn-secondary, .fd-walletpick .fd-btn-secondary {
 background: rgba(10, 11, 13, .34); color: var(--gr-ink); border-color: var(--gr-line-2);
 -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
#landing .fd-btn-secondary:hover, #landing .fd-btn-secondary:focus-visible,
.fd-walletpick .fd-btn-secondary:hover, .fd-walletpick .fd-btn-secondary:focus-visible {
 border-color: var(--gr-ink-2); background: rgba(10, 11, 13, .56); transform: translateY(-1px);
}
/* A PRESS HAS TO LOOK PRESSED, AND IT HAS TO KEEP LOOKING IT.
//
// Two rounds of this now. It was opacity alone, which on a filled ivory button
// over a moving plate is not a state change anybody can see. Then it was
// opacity plus a 12px spinner, and the report came back the same: "it
// eventually works but nothing visibly acknowledges the click."
//
// Three things were wrong with that.
//
// OPACITY IS A SHADE, NOT A STATE. .78 moves every pixel of the button a
// little and none of them much. It also reads as DISABLED rather than as
// working, which is the opposite of the message.
//
// THE ACKNOWLEDGEMENT WAITED FOR click. pointerdown fires first — a whole
// input phase earlier, and on a main thread this busy that is a real gap. The
// tactile half of the answer is now on pointerdown (.pressed) and the working
// half on click (.busy), so the button moves under the finger before anything
// has been decided about wallets.
//
// AND IT FROZE. A spinner whose animation is main-thread stops dead while the
// world builds, so the one thing saying "still working" stops saying it
// exactly when it is needed. transform and opacity animate on the COMPOSITOR:
// the sweep below keeps travelling while the main thread is gone, which is the
// state this page spends its first ten seconds in.
*/
.fd-btn.pressed { transform: translateY(1px) scale(.994); filter: brightness(.94); }
.fd-btn.busy { pointer-events: none; cursor: progress; }
.fd-btn.busy .fd-btnarrow { display: none; }
/* the sweep: a band of the button's own ink crossing it, end to end */
.fd-btn.busy::before {
 content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 100%;
 background: linear-gradient(90deg, transparent, currentColor 45%, currentColor 55%, transparent);
 opacity: .26; pointer-events: none;
 transform: translateX(-100%);
 animation: fd-sweep 1.15s cubic-bezier(.45, 0, .2, 1) infinite;
}
@keyframes fd-sweep { to { transform: translateX(100%); } }
.fd-btn.busy::after {
 content: ''; width: 14px; height: 14px; flex: none;
 border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
 animation: fd-spin .7s linear infinite;
}
@keyframes fd-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
 /* no travel and no spin, but the band still sits across the button so the
 press is as legible as it is for everyone else — the state must not be
 carried by the motion alone */
 .fd-btn.busy::before { animation: none; transform: none; opacity: .18; }
 .fd-btn.busy::after { animation: none; border-top-color: currentColor; opacity: .5; }
 .fd-btn.pressed { transform: none; }
}

/* ONE LINE UNDER THE BUTTONS, AND IT USED TO BE TWO. .fd-terms sat under
 this at 11px in a dimmer ink carrying the disclaimer, and two stacked
 paragraphs of small print under a button is a terms panel however true each
 of them is. Both sentences are in this one now; the rule and the class are
 gone rather than left behind empty. */
.fd-doorsline { margin: 16px 0 0; max-width: 64ch; font-size: 11.5px; line-height: 1.65; letter-spacing: .01em; color: var(--gr-ink-3); }
.fd-doornote { margin: 8px 0 0; font-size: 11px; color: var(--gr-caution); }
.fd-doornote:empty { display: none; }
/* "no wallet found on this browser. install MetaMask" — the one note that
 carries a link, and the link has to look like one or it is another dead end */
.fd-doornote a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.fd-doornote a:hover, .fd-doornote a:focus-visible { color: var(--gr-ink, #ece5d5); }

/* ---- the quiet links: pages to read, never a way in -----------------
 NO RULE UNDER THEM ANY MORE. Each carried a 1px border-bottom, which is an
 underline, and a row of underlined capitals sitting directly beneath a pair
 of buttons reads as a third and fourth thing to press. They are a text row
 now, separated by a middot the way the status strip separates its chips.
 The hover accent and the focus ring are the whole of the affordance, which
 is the right amount for a link that is deliberately not a door. */
.fd-links { margin-top: 24px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
.fd-links .fd-sep { font-size: 10.5px; color: var(--gr-ink-4); opacity: .55; }
#landing .fd-links a {
 font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--gr-ink-3);
 transition: color .16s ease;
}
#landing .fd-links a:hover, #landing .fd-links a:focus-visible { color: var(--gr-accent); }

/* ---- what the page knows about itself -------------------------------
 The hairline is the join: above it the hero makes its case, below it are
 two plain facts about this document. A grid rather than a flex row so the
 two labels sit on one baseline and the two values on another however long
 either value runs — a value that wraps must not drag its neighbour's label
 down with it. Labels are smaller than the values on purpose: the label is
 the thing you read once. */
.fd-meta {
 margin: 26px 0 0; padding-top: 18px; max-width: 660px;
 border-top: 1px solid var(--gr-line);
 /* THREE COLUMNS SINCE THE CHAIN JOINED THEM, and auto-fit rather than a
 fixed three: at 700px they sit in a row, and on a phone the third wraps
 under the first two instead of squeezing all three into 100px each. */
 display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px 34px;
}
.fd-metacell { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.fd-meta dt { font-size: 9.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--gr-ink-4); }
.fd-meta dd {
 margin: 0; display: flex; align-items: center; gap: 9px;
 font-size: 13px; line-height: 1.4; color: var(--gr-ink-2);
}
/* the dot is the alpha dot from the eyebrow, at the size it is there */
.fd-meta dd .fd-dot { flex: none; }

/* ---- the status strip ----------------------------------------------- */
.fd-status {
 position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
 display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
 padding: 16px 7vw 18px;
 font-size: 10px; letter-spacing: .13em; text-transform: uppercase;
 color: var(--gr-ink-4);
 border-top: 1px solid var(--gr-line);
 background: linear-gradient(to top, rgba(6,7,9,.72), rgba(6,7,9,0));
}
.fd-status .fd-sep { opacity: .5; }
.fd-mono { font-family: var(--gr-data); letter-spacing: .06em; text-transform: none; }
#landing .fd-status a:hover { color: var(--gr-accent); }
.fd-status .fd-on { color: var(--gr-accent); }

/* ---- the cue: a page that scrolls has to say so ---------------------- */
.fd-scroll {
 position: absolute; right: 7vw; bottom: 62px; z-index: 2;
 display: inline-flex; align-items: center; gap: 10px;
 font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
 color: var(--gr-ink-3);
 transition: color .18s ease;
}
#landing .fd-scroll:hover { color: var(--gr-accent); }
.fd-scrollarrow {
 display: inline-block; font-size: 13px; line-height: 1;
 animation: fd-nudge 2.4s ease-in-out infinite;
}
@keyframes fd-nudge { 0%, 100% { transform: translateY(-2px); } 50% { transform: translateY(3px); } }

/* ---- the page under the door ---------------------------------------- */
.fd-sec {
 position: relative;
 background: var(--gr-bg);
 border-top: 1px solid var(--gr-line);
 padding-block: clamp(64px, 8vw, 118px);
}
.fd-secshell { position: relative; z-index: 2; width: min(100% - 14vw, 1180px); margin-inline: auto; }
.fd-kicker {
 margin: 0 0 20px; display: inline-flex; align-items: center; gap: 9px;
 font-size: 10px; letter-spacing: .2em; text-transform: uppercase; color: var(--gr-ink-3);
}
.fd-h2 {
 margin: 0; font-family: var(--gr-display); font-weight: 700;
 font-size: clamp(30px, 4.1vw, 52px); line-height: 1.02; letter-spacing: -.02em;
 color: var(--gr-ink); text-wrap: balance;
}
.fd-seclede { margin: 18px 0 0; max-width: 62ch; font-size: 14.5px; line-height: 1.68; color: var(--gr-ink-2); }

/* four systems, one object repeated: same edges, same baselines */
.fd-cards {
 margin-top: clamp(34px, 4vw, 54px);
 display: grid; gap: 20px;
 grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
}
.fd-card {
 display: flex; flex-direction: column; gap: 12px;
 padding: 14px 14px 20px;
 background: var(--gr-panel-a6); border: 1px solid var(--gr-line); border-radius: 3px;
 transition: border-color .18s ease, transform .18s ease;
}
.fd-card:hover { border-color: var(--gr-line-3); transform: translateY(-2px); }
/* the card art is a now, so the flex item is the wrapper rather
 than the image. Blockified by flex anyway; said out loud so a later change
 to .fd-card's display does not quietly reflow the grid. */
.fd-card picture { display: block; min-width: 0; }
.fd-cardimg {
 display: block; width: 100%; height: auto; max-width: 100%;
 aspect-ratio: 16 / 9; object-fit: cover;
 background: var(--gr-void); border-radius: 2px;
}
.fd-cardtitle { margin: 4px 0 0; font-size: 15px; font-weight: 500; letter-spacing: .01em; color: var(--gr-ink); }
.fd-cardcopy { margin: 0; font-size: 12.5px; line-height: 1.62; color: var(--gr-ink-3); flex: 1; }
#landing .fd-cardlink {
 font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--gr-ink-2);
 transition: color .16s ease;
}
#landing .fd-card:hover .fd-cardlink, #landing .fd-cardlink:hover { color: var(--gr-accent); }

/* ---- the manual, over the night plate -------------------------------- */
.fd-manualsec { overflow: hidden; background: var(--gr-void); }
.fd-manualbg {
 position: absolute; inset: 0;
 /* the same two-declaration fallback the hero plates use, and the same
 reason for the small source: 3 px of blur at half brightness. This one
 is below the fold, so it is the browser's lazy background handling that
 keeps it off the first paint, not a timer. */
 background: #05060a center / cover no-repeat;
 background-image: url('../assets/landing/alley-night.jpg');
 background-image: image-set(url('../assets/landing/alley-night.webp') type('image/webp'),
 url('../assets/landing/alley-night.jpg') type('image/jpeg'));
 filter: blur(3px) saturate(.8) brightness(.5) contrast(1.02);
 transform: scale(1.05);
}
.fd-manualsec::after {
 content: ''; position: absolute; inset: 0; z-index: 1;
 background: linear-gradient(90deg, rgba(6,7,9,.94) 0%, rgba(6,7,9,.8) 46%, rgba(6,7,9,.62) 100%);
}
.fd-manualshell {
 display: grid; gap: clamp(34px, 5vw, 72px);
 grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start;
}
.fd-manualcopy { min-width: 0; }
#landing .fd-guide {
 margin-top: 26px; display: inline-block;
 font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--gr-ink);
 padding-bottom: 4px; border-bottom: 1px solid var(--gr-line-3);
 transition: color .16s ease, border-color .16s ease;
}
#landing .fd-guide:hover { color: var(--gr-accent); border-bottom-color: var(--gr-accent); }
.fd-guidelinks { display: flex; flex-direction: column; gap: 1px; background: var(--gr-line); border: 1px solid var(--gr-line); }
.fd-guidelink {
 display: flex; align-items: center; justify-content: space-between; gap: 18px;
 padding: 18px 20px; background: var(--gr-panel-a6);
 transition: background .16s ease;
}
#landing .fd-guidelink:hover { background: var(--gr-panel-hi); }
.fd-guidetext { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.fd-guidetext b { font-size: 13.5px; font-weight: 500; color: var(--gr-ink); }
.fd-guidetext i { font-style: normal; font-size: 11.5px; line-height: 1.5; color: var(--gr-ink-4); }
.fd-arrow { font-size: 13px; color: var(--gr-ink-4); transition: color .16s ease; }
#landing .fd-guidelink:hover .fd-arrow { color: var(--gr-accent); }
.fd-bottom {
 position: relative; z-index: 2;
 width: min(100% - 14vw, 1180px); margin: clamp(44px, 5vw, 72px) auto 0;
 display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px;
 padding-top: 20px; border-top: 1px solid var(--gr-line);
 font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--gr-ink-4);
}
#landing .fd-bottom a:hover { color: var(--gr-accent); }

@media (max-width: 820px) {
 .fd-hero { width: calc(100% - 44px); margin: 0 22px; padding: 96px 0 92px; }
 /* on a phone they stack and each takes the width, so neither is a target
 you have to aim at */
 .fd-doors { flex-direction: column; align-items: stretch; }
 #landing .fd-doorslot { flex: 0 0 auto; }
 #landing .fd-btn { width: 100%; justify-content: center; }
 /* the two columns stay two columns — that pairing IS the block — but at
 346px they need the gap back and the value can afford to be smaller */
 .fd-meta { gap: 18px 20px; padding-top: 18px; margin-top: 26px; }
 .fd-meta dd { font-size: 12px; }
 .fd-lede { font-size: 13.5px; }
 .fd-status { padding: 14px 22px 16px; gap: 8px; font-size: 9px; }
 .fd-links { gap: 16px; }
 .fd-scroll { right: 22px; bottom: 58px; font-size: 9px; }
 .fd-secshell, .fd-bottom { width: calc(100% - 44px); }
 .fd-manualshell { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
 #landing, .fd-btn, .fd-card, .fd-plate { transition: none; }
 #landing { scroll-behavior: auto; }
 #landing .fd-btn:hover, #landing .fd-card:hover { transform: none; }
 /* the rotation is stopped in js/landing.js as well — this is the
 belt: whatever is showing stays showing */
 .fd-scrollarrow { animation: none; }
}

/* THE WALLET CHOOSER'S CSS LIVES WITH THE CHOOSER NOW — js/walletmodal.js
 carries its own, because it opens over the game as well as over this page
 and css/landing.css is not loaded there. The .fd-walletpick rules that were
 here styled the front door's own sheet, which has been deleted along with
 js/locker.js's: one implementation, both hosts, nothing left behind to
 style. */

/* THE GATE — the screen for a visitor who has no wallet, or whose wallet is on
 another network, or who used the guest door that no longer exists. With one
 way in this is the product for those people, so it is a panel with a heading
 and a way forward, not a line of red text under a button. Inline rather than
 a modal: a wall is exactly what it must not be. */
.fd-gate {
 margin: 20px 0 0; max-width: 62ch;
 border: 1px solid var(--gr-line-2, #2c261c); border-left: 2px solid var(--gr-accent, #e0a44a);
 border-radius: 3px; padding: 18px 20px; background: rgba(10, 11, 13, .5);
}
.fd-gate[hidden] { display: none; }
.fd-gate-h { font-family: var(--gr-display); font-size: 15px; letter-spacing: .01em; color: var(--gr-ink); }
.fd-gate-b { margin: 8px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--gr-ink-2); }
.fd-gate-chain {
 margin: 10px 0 0; font-family: var(--gr-data, ui-monospace, monospace);
 font-size: 10.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--gr-ink-3);
}
.fd-gate-acts { margin: 14px 0 0; display: flex; flex-wrap: wrap; gap: 10px; }
.fd-gate-btn { width: auto; padding: 12px 18px; }

/* ---- a note under a row of cards -------------------------------------
 THE ONE LINE ON A SECTION THAT MUST NOT BE OPTIONAL. The $GRFT section's
 not-live disclosure used to be the fourth card, which made it a peer of the
 three claims beside it; as a line under the row it reads as the caveat over
 all of them, which is what it is. Muted, but not so muted that it reads as
 decoration — it is the most load-bearing sentence on that section. */
.fd-secnote {
 margin: 20px 0 0; max-width: 76ch;
 font-size: 12px; line-height: 1.6; letter-spacing: .01em; color: var(--gr-ink-3);
}

/* the chain the install state wants, inside the wallet sheet. It was a line in
 the hero panel that state used to be; the panel is gone and the chain is a
 labelled column in .fd-meta, so this is the one place it still needs saying
 in context: you are being asked to point a wallet at a network. */
.fd-wp-chain {
 margin-top: 4px; text-align: center;
 font-family: var(--gr-data, ui-monospace, monospace);
 font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--gr-ink-4);
}

/* the sheet is outside #landing, so it inherits none of the page's type */
.fd-walletpick { color: var(--gr-ink); font-family: var(--gr-body); }
.fd-walletpick a { text-decoration: none; }

/* ---- the X account, on both of this page's surfaces -------------------
 QUIET, AND THE SAME QUIET IN BOTH PLACES: the glyph takes the muted ink the
 status chips and the quiet links take, and lifts to full ink under the
 pointer rather than to the accent. The accent means "this is on, this is
 yours, this worked" (css/palette.css) and a link to an account is none of
 those, so it is deliberately the one hover on this page that does not use
 it — which also needs the override below, because #landing .fd-status a and
 #landing a:hover would otherwise hand it the accent for free.

 No count, no embed, no brand colour. The mark is currentColor. */
.fd-x {
 display: inline-flex; align-items: center; justify-content: center; gap: 7px;
 transition: color .16s ease;
}
.fd-x svg { width: 11px; height: 11px; display: block; flex: none; }
/* AND THE COLOUR IS SCOPED UNDER #landing, WHICH IS NOT OPTIONAL. Line 41 is
 `#landing a { color: inherit }` at (1,0,1); an unscoped `.fd-x { color }` at
 (0,1,0) loses to it, and the glyph inherits the page's ivory. Measured
 before this line existed: the header mark rendered rgb(237,230,214) — the
 brightest ink on the bar, on the one element that was asked to be quiet.
 Third time this selector has caught something on this page. */
#landing .fd-x { color: var(--gr-ink-3); }
/* in the strip it takes the strip's own tone, so it sits level with the chips
 beside it rather than one step brighter than its neighbours */
#landing .fd-status .fd-x { color: inherit; }
#landing .fd-x:hover, #landing .fd-x:focus-visible,
#landing .fd-status .fd-x:hover, #landing .fd-status .fd-x:focus-visible { color: var(--gr-ink); }

/* ---- the contract address, in the metadata block ----------------------
 FORTY-TWO CHARACTERS IN A COLUMN SIZED FOR "Desktop Chrome, Safari,
 mobile". The three original cells are an auto-fit grid of 150px-minimum
 tracks; an address does not fit one and must not be allowed to force the
 track wider, because that reflows the other three. So the cell spans the
 whole row under them and breaks anywhere — it is a string to copy, not a
 phrase to read, and a mid-address line break costs nothing. */
/* AND [hidden] HAS TO BE SAID OUT LOUD, because `.fd-metacell { display:
 flex }` is an author rule and the browser's own `[hidden] { display: none }`
 is a user-agent one — author wins, whatever the specificity, so the
 attribute alone hides nothing. Measured before this line existed: with the
 cell marked hidden and no address configured, the COPY button still laid
 out at [113,759,53,21] beside an empty CONTRACT label. Fourth time a rule
 on this page has quietly outranked the thing meant to silence an element. */
#landing .fd-metacell[hidden] { display: none; }
.fd-metawide { grid-column: 1 / -1; }
.fd-metawide dd { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
/* user-select: all makes one click take the whole address, for the people who
 never find a copy button; the button is for everyone else */
.fd-addr { font-size: 12px; line-height: 1.5; word-break: break-all; user-select: all; }
#landing .fd-copy {
 flex: none; font-family: var(--gr-mono); font-size: 9.5px; letter-spacing: .16em;
 padding: 4px 9px; border-radius: 999px; cursor: pointer;
 background: rgba(10, 11, 13, .34); color: var(--gr-ink-3);
 border: 1px solid var(--gr-line-2); transition: color .16s ease, border-color .16s ease;
}
#landing .fd-copy:hover, #landing .fd-copy:focus-visible { color: var(--gr-ink); border-color: var(--gr-line-3); }

/* PERFORMANCE MODE at the door (index.html #lp-perfwrap) */
.fd-perf{display:flex;align-items:center;gap:10px;margin:14px 0 0;cursor:pointer;user-select:none;max-width:560px}
.fd-perf input{width:16px;height:16px;margin:0;accent-color:var(--gr-accent,#6fc8dc);flex:none}
.fd-perfk{font-family:var(--gr-mono,ui-monospace,monospace);font-size:11px;letter-spacing:.16em;color:var(--gr-ink,#ede6d6);white-space:nowrap}
.fd-perfl{font-size:12px;line-height:1.35;color:var(--gr-ink-3,#8f8a80)}
