/* The showcase: the device sitting on the screen, and nothing else.

   The body is drawn from the real thing: a black slab, the glass set into it
   with an even bezel and a deeper chin, a capacitive Home circle in the chin,
   and three keys on the edges -- the two page keys opposite each other, and
   Power above the right one. That is the X4 Pro's entire physical input; Back
   and Confirm have no pins on this board at all.

   The keys carry no labels, because the device's carry none. */

:root {
  /* Room kept above and below the device, read back by fit(): none by
     default; the showcase keeps a strip for its corner bar and scroll cue, a
     tester's device one for its three buttons. */
  --reserve-top: 0px;
  --reserve-bottom: 0px;
  --bg: #1c1c1f;      /* the surface it sits on: dark, but not the device */
  --body: #121215;
  --bodyTop: #26262b;
  --bodyBot: #08080a;
  --keyFace: #34343b;
  --keyEdge: #4a4a53;
  --ink: #e9e5dc;
  --glow: 255, 214, 160;   /* the pulse behind a key: warm, like the frontlight */
  /* Buttons, Pablo's rule of 2026-10-16 (peter/design/BUTTON_SHADOW.md): one
     token, a top highlight, a bottom edge, a contact shadow and a glow in the
     page's own accent; pressed flips the inset, disabled and ghost get none. */
  --btn-hi: rgb(255 255 255 / .18);
  --btn-glow: rgba(var(--glow), .6);
  --btn-shadow: inset 0 1px 0 var(--btn-hi), inset 0 -2px 0 rgb(0 0 0 / .2),
    0 1px 2px rgb(0 0 0 / .4), 0 8px 24px -6px var(--btn-glow);
  --btn-shadow-hover: inset 0 1px 0 var(--btn-hi), inset 0 -2px 0 rgb(0 0 0 / .2),
    0 1px 2px rgb(0 0 0 / .4), 0 10px 28px -6px var(--btn-glow);
  --btn-shadow-soft: inset 0 1px 0 var(--btn-hi), 0 1px 2px rgb(0 0 0 / .2);
  --btn-shadow-down: inset 0 2px 0 rgb(0 0 0 / .2);

  /* The furniture around the glass. showcase.js reads these to work out how
     much room is left for the panel, so they live in one place. */
  --bezel: 14px;      /* left, right and top of the glass */
  --gutter: 104px;    /* room beside the body for the key labels, while they show */
  --chin: 74px;       /* the bottom bezel, which carries the Home circle */
  --radius: 30px;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* `hidden` means hidden, whatever display a class gives the element: the
   install button is an .opt.primary, and inline-block beat the attribute. */
[hidden] { display: none !important; }

html, body {
  margin: 0; height: 100%; background: var(--bg); color: var(--ink);
  /* A phone must not rubber-band or double-tap-zoom while somebody is swiping
     the panel; both read as the emulator being unresponsive. */
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, sans-serif;
  overscroll-behavior: none; touch-action: manipulation;
}

body {
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  /* The page scrolls: the device is the first screenful and the letter is
     under it. overscroll-behavior keeps the rubber-band off while swiping. */
  overflow-y: auto; overflow-x: hidden;
  /* The notch, the Dynamic Island and the home indicator. Without this the
     device's top corner sits UNDER the island in standalone mode, which is
     where this page spends most of its life. */
  padding: max(env(safe-area-inset-top), 8px) max(env(safe-area-inset-right), 10px)
           max(env(safe-area-inset-bottom), 8px) max(env(safe-area-inset-left), 10px);
  background-image: radial-gradient(110% 75% at 50% 14%, #26262a 0%, var(--bg) 68%);
}

#boot { opacity: .7; }

#shell {
  display: flex; flex-direction: column; align-items: center;
  width: 100%; gap: 14px;
}
/* The device gets the first screenful to itself, and the letter starts below
   the fold -- so somebody who opens this sees the thing, not the essay. */
.stage {
  display: flex; align-items: center; justify-content: center;
  width: 100%;
  padding-top: var(--reserve-top); padding-bottom: var(--reserve-bottom);
  /* svh, not vh: on a phone browser vh counts the chrome that is about to slide
     away, so the device would be sized for a viewport that is not there. */
  min-height: calc(100svh - 26px);
}
@supports not (height: 100svh) { .stage { min-height: calc(100vh - 26px); } }

/* --- the device ----------------------------------------------------------- */

.device {
  position: relative;
  padding: var(--bezel) var(--bezel) 0;
  background: linear-gradient(180deg, var(--bodyTop) 0%, var(--body) 14%, var(--bodyBot) 100%);
  border-radius: var(--radius);
  box-shadow:
    0 1px 0 rgba(255,255,255,.09) inset,     /* the lit top edge */
    0 0 0 1px rgba(0,0,0,.8),                /* the seam */
    0 30px 70px rgba(0,0,0,.62),             /* the shadow it casts */
    0 6px 18px rgba(0,0,0,.5);
}

/* isolate: the light overlay blends with the glass under it and nothing else. */
.screen { position: relative; line-height: 0; isolation: isolate; }
#panel {
  display: block; background: #fff; touch-action: none; border-radius: 4px;
  /* Unlit e-ink is not white: it is light grey, and it is the frontlight that
     makes it look like paper. The overlay below adds that back. */
  filter: brightness(.86);   /* replaced by showcase.js once the lamp is read */
  box-shadow: 0 0 0 1px rgba(0,0,0,.85), 0 2px 6px rgba(0,0,0,.55) inset;
}

/* The frontlight: a wash over the glass, coloured by the firmware's own warmth
   and brightness. `screen` blending lightens rather than tints, which is what
   a light behind the page does. */
.light {
  position: absolute; inset: 0; border-radius: 4px; pointer-events: none;
  mix-blend-mode: multiply; opacity: 0;
  /* The gradient itself is set by showcase.js from the firmware's brightness
     and warmth; the fade is here so switching the lamp looks like a lamp. */
  transition: opacity .45s ease;
}

.chin { height: var(--chin); display: flex; align-items: center; justify-content: center; }

/* --- the keys ------------------------------------------------------------- */

/* Positions are fractions of the body's own height, so they hold wherever the
   panel is scaled to: the page keys opposite each other, Power above the right
   one. That is where they sit on the device. */
.key {
  position: absolute; padding: 0; border: 0; background: none;
  cursor: pointer; touch-action: none;
}
.key::before {                      /* the part you can see */
  content: ""; position: absolute; top: 0; bottom: 0;
  background: linear-gradient(90deg, var(--keyEdge), var(--keyFace) 60%, #1d1d21);
  box-shadow: 0 1px 0 rgba(255,255,255,.07) inset, 0 0 0 1px rgba(0,0,0,.7);
}
/* The glow: OUTSIDE the key and the shape of the key. A box-shadow on the
   visible part does both -- it is drawn outside the box, and it follows the
   box's own corners, so the page keys glow as rectangles and the Home key as a
   circle without either of them being lit from inside. */
.key::before { animation: pulse 3.4s ease-in-out infinite; }
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 6px 0 rgba(var(--glow), .10),
                         0 1px 0 rgba(255,255,255,.07) inset, 0 0 0 1px rgba(0,0,0,.7); }
  50%      { box-shadow: 0 0 16px 3px rgba(var(--glow), .55),
                         0 1px 0 rgba(255,255,255,.07) inset, 0 0 0 1px rgba(0,0,0,.7); }
}
@media (prefers-reduced-motion: reduce) {
  .key::before { animation: none; box-shadow: 0 0 10px 1px rgba(var(--glow), .3); }
}

.key:active::before { background: linear-gradient(90deg, #4a4a52, #35353b 60%, #232327); }
.key:active::before { animation: none; box-shadow: 0 0 18px 4px rgba(var(--glow), .7); }

/* The two page keys, opposite each other. The hit area is wider than the tab
   so a thumb on a phone can find it; only the tab is drawn. */
.key.side { width: 30px; height: 13%; top: 34%; }
.key.side::before { width: 9px; }
.key.side.left { left: -9px; }
.key.side.left::before { left: 0; border-radius: 4px 0 0 4px; }
.key.side.right { right: -9px; }
.key.side.right::before { right: 0; border-radius: 0 4px 4px 0; }

/* Power: above the right page key, and shorter. */
.key.power { width: 30px; height: 7%; top: 16%; right: -9px; }
.key.power::before { width: 9px; right: 0; border-radius: 0 4px 4px 0; }

/* Home: capacitive, printed in the chin. A circle, unlabelled, as on the
   device -- it is a dimple in the plastic, not a key that travels. */
.key.home {
  position: relative; width: 46px; height: 46px; border-radius: 999px;
}
.key.home::before {
  inset: 0; width: auto; border-radius: 999px;
  background: radial-gradient(circle at 50% 36%, #35353b 0%, #1c1c20 100%);
  box-shadow: 0 0 0 1px rgba(0,0,0,.8), 0 1px 0 rgba(255,255,255,.05) inset;
}
/* Asleep, the power key is the only one that does anything -- the touch panel
   is unpowered, as it is on the device -- so it says so and the others go
   quiet. */
.device.asleep .key.side::before,
.device.asleep .key.home::before { animation: none; }
.device.asleep .key.power::before { animation-duration: 2.1s; }

/* A key being held. The hold is a real gesture with a real dwell, so the key
   says it is counting rather than looking like a slow tap. */
.key.holding::before {
  animation: none;
  box-shadow: 0 0 0 2px rgba(var(--glow), .9), 0 0 22px 5px rgba(var(--glow), .6);
}

/* --- the page's own chrome ------------------------------------------------ */

.chrome { max-width: 600px; }
.hint { opacity: .45; font-size: 12px; margin: 0 0 10px; line-height: 1.5; }
.opts { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.opt {
  font: inherit; font-size: 12px; color: var(--ink); background: transparent;
  border: 1px solid #33333a; border-radius: 999px; padding: 6px 14px; cursor: pointer;
  box-shadow: var(--btn-shadow-soft);
}
.opt:active { background: #26262b; box-shadow: var(--btn-shadow-down); }
.opt:disabled { box-shadow: none; }
.credit { opacity: .3; font-size: 11px; margin: 12px 0 0; letter-spacing: .06em; }

/* A phone gets a smaller body, but keeps the words: the device is the first
   screenful and everything else is under it, so there is room. Without the
   hint nobody would find the swipe in from the left edge. */
@media (max-width: 620px), (max-height: 760px) {
  :root { --bezel: 10px; --chin: 56px; --radius: 24px; }
  .key.home { width: 38px; height: 38px; }
  #shell { gap: 10px; }
}

/* --- the letter and the postbox ------------------------------------------- */

/* The letter is the one thing on the page written to the visitor, and at 13px
   and 72% it read as a footnote -- people scrolled past it. Body-copy size,
   near-full contrast, and a measure that suits it. */
.letter {
  margin: 30px auto 0; max-width: 600px; text-align: left;
  color: rgba(233,229,220,.9); font-size: 17px; line-height: 1.7;
}
.letter p { margin: 0 0 14px; }
.letter .sign { opacity: .9; font-style: italic; margin-top: 18px; }

.ideas { margin: 22px auto 0; max-width: 560px; text-align: left; }
.ideas h2 { font-size: 14px; margin: 0 0 10px; opacity: .8; letter-spacing: .02em; }
.field { display: block; font-size: 12px; opacity: .6; margin: 0 0 10px; }
.field input, .field textarea {
  display: block; width: 100%; margin-top: 4px; box-sizing: border-box;
  font: inherit; font-size: 14px; color: var(--ink); background: #232327;
  border: 1px solid #35353c; border-radius: 10px; padding: 9px 11px;
  -webkit-user-select: text; user-select: text;
}
.field textarea { resize: vertical; min-height: 84px; }
.field input:focus, .field textarea:focus { outline: none; border-color: #55505c; background: #26262b; }
.ideas .send { padding: 9px 18px; font-size: 13px; }
.ideas .send[disabled] { opacity: .5; }
.thanks { font-size: 13px; line-height: 1.6; color: rgba(255,214,160,.85); margin: 12px 0 0; }
/* Seen only by the robots that fill it in. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }

/* The page scrolls now: there is a letter under the device. The panel still
   must not scroll the page while a finger is swiping it -- that is handled by
   touch-action on the canvas itself. */
@media (max-width: 620px), (max-height: 760px) {
  .letter { font-size: 16px; }
  .ideas { font-size: 13px; }
}

/* --- what the keys are ----------------------------------------------------- */
/*
   White text outside the body with a hairline running to the key it names.
   They go after two minutes: somebody who has pressed a key twice does not
   need a diagram, and the device is the thing to look at.

   The gutter they sit in is reserved by fit() while `labelled` is set, so the
   panel shrinks a little to make room and grows back when they go.
*/
.tags { position: absolute; inset: 0; pointer-events: none; transition: opacity .9s ease; }
body:not(.labelled) .tags { opacity: 0; }

.tag {
  position: absolute; display: flex; align-items: center; gap: 0;
  color: #fff; font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap; text-shadow: 0 1px 3px rgba(0,0,0,.8);
}
.tag .lead { display: block; width: 22px; height: 1px; background: rgba(255,255,255,.75); }
.tag span { opacity: .92; }
.tag.left span, .tag.right span, .tag.power span { padding: 0 6px; }

.tag.power { top: 19.5%; left: 100%; transform: translateY(-50%); }
.tag.right { top: 40.5%; left: 100%; transform: translateY(-50%); }
.tag.left  { top: 40.5%; right: 100%; transform: translateY(-50%); }
.tag.home  { top: 100%; left: 50%; transform: translateX(-50%); flex-direction: column; gap: 0; }
.tag.home .lead { width: 1px; height: 16px; }
.tag.home span { padding-top: 5px; }

/* The one thing somebody actually needs to be told first: it is OFF. Beside
   the power key where there is room for it, and above the device where there
   is not. Gone the moment the device is on, because then it is not true. */
.note {
  position: absolute; top: 19.5%; left: calc(100% + 30px); width: 196px; margin: 14px 0 0;
  font-size: 11.5px; line-height: 1.55; letter-spacing: 0; text-transform: none;
  color: rgba(255,255,255,.72); text-align: left; white-space: normal;
  text-shadow: 0 1px 3px rgba(0,0,0,.8);
  transition: opacity .7s ease;
}
.note em { font-style: normal; color: #fff; }
body:not(.asleep) .note { opacity: 0; }

.tag .narrow { display: none; }

/* --- the two gestures ------------------------------------------------------ */

.coach {
  position: absolute; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 8px;
  padding: 7px 13px; border-radius: 999px; white-space: nowrap;
  background: rgba(18,18,21,.9); color: #fff; font-size: 11.5px;
  box-shadow: 0 2px 14px rgba(0,0,0,.5), 0 0 0 1px rgba(255,255,255,.12) inset;
  pointer-events: none; transition: opacity .6s ease;
  max-width: 96vw;
}
.tag.home { transition: opacity .5s ease; }
.coach[hidden] { display: none; }
.coach.fading { opacity: 0; }
/* Straddling the edge of the glass, half on the bezel: it covers a sliver of
   the screen rather than the page, and unlike a mark floating outside the body
   it cannot be pushed off the top of a short window. */
.coach.top { top: var(--bezel); transform: translate(-50%, -50%); }
.coach.bottom { bottom: var(--chin); transform: translate(-50%, 50%); }

/* A chevron that keeps pointing the way the finger goes. */
.arrow {
  display: block; width: 9px; height: 9px; flex: 0 0 9px;
  border-right: 2px solid #fff; border-bottom: 2px solid #fff;
}
.arrow.down { transform: rotate(45deg); animation: nudgeDown 1.5s ease-in-out infinite; }
.arrow.up { transform: rotate(-135deg); animation: nudgeUp 1.5s ease-in-out infinite; }
@keyframes nudgeDown {
  0%, 100% { transform: rotate(45deg) translate(-2px, -2px); opacity: .55; }
  50%      { transform: rotate(45deg) translate(2px, 2px); opacity: 1; }
}
@keyframes nudgeUp {
  0%, 100% { transform: rotate(-135deg) translate(-2px, -2px); opacity: .55; }
  50%      { transform: rotate(-135deg) translate(2px, 2px); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .arrow.down, .arrow.up { animation: none; }
}

/* No room beside the device for the note (fit() decides): above it. */
body.noteAbove .note {
  top: auto; bottom: calc(100% + 14px); left: 50%; transform: translateX(-50%);
  width: min(320px, 82vw); margin: 0; text-align: center; font-size: 11px;
}

/* A phone has no room beside the device for a sentence, so the note goes above
   it and the side labels lose a word. */
@media (max-width: 620px) {
  :root { --gutter: 58px; }
  .tag { font-size: 10px; letter-spacing: .06em; }
  .tag .lead { width: 12px; }
  .tag .wide { display: none; }
  .tag .narrow { display: inline; }
  .note {
    top: auto; bottom: calc(100% + 14px); left: 50%; transform: translateX(-50%);
    width: min(320px, 82vw); margin: 0; text-align: center; font-size: 11px;
  }
  .coach { font-size: 11px; padding: 6px 11px; }
}

/* --- a new build ----------------------------------------------------------- */

.updatePill {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom) + 14px); z-index: 10;
  font: inherit; font-size: 12px; color: #fff; cursor: pointer;
  padding: 9px 16px; border-radius: 999px; border: 0;
  background: rgba(28,28,32,.95);
  box-shadow: 0 4px 18px rgba(0,0,0,.55), 0 0 0 1px rgba(var(--glow), .45) inset;
  /* Its own breath, not the keys': theirs animates a box-shadow shaped for a
     key and would repaint this one's ring. */
  animation: breathe 3.4s ease-in-out infinite;
}
@keyframes breathe {
  0%, 100% { opacity: .82; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .updatePill { animation: none; } }
.updatePill[hidden] { display: none; }

/* --- signed in, sharing, and wanting one ----------------------------------- */

.signedIn { font-size: 13px; opacity: .75; margin: 12px 0 0; }
.signedIn a, .gateAlt a, .fineNote a,
/* saints.html: its gate carries the one link a signed-out tester must find, and
   an unstyled <a> is the browser default blue on near-black -- 1.81 against a
   background this dark, where 4.5 is the floor. Same treatment as every other
   link on the site rather than a colour invented for this page. */
.lede a, .foot a { color: rgb(var(--glow)); }

.want { margin: 34px auto 6px; max-width: 560px; }
.wantBtn {
  display: inline-block; text-decoration: none; cursor: pointer;
  font-size: 19px; font-weight: 700; letter-spacing: .02em; color: #1c1409;
  padding: 15px 34px; border-radius: 999px;
  background: linear-gradient(180deg, #ffd9a6, #f0b86e);
  box-shadow: var(--btn-shadow);
}
.wantBtn:hover { box-shadow: var(--btn-shadow-hover); }
.wantBtn:active { transform: translateY(1px); box-shadow: var(--btn-shadow-down); }
/* Sign in beside it: the same size, outlined, so "I want one!" stays the one
   a newcomer's eye lands on. Two lines on a narrow phone, "I want one!" first. */
.wantRow { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
.wantBtn.alt {
  color: #ffd9a6; background: rgba(40,40,46,.8); box-shadow: var(--btn-shadow-soft);
  border: 2px solid rgba(var(--glow), .6); padding: 13px 32px;
}
.want p { font-size: 13px; opacity: .6; margin: 12px auto 0; max-width: 400px; }

.opt.primary {
  background: rgba(var(--glow), .92); color: #1c1409; border-color: transparent; font-weight: 600;
  text-decoration: none; display: inline-block; box-shadow: var(--btn-shadow);
}
.opt.primary:hover { box-shadow: var(--btn-shadow-hover); }
.opt.primary:active { background: rgb(var(--glow)); box-shadow: var(--btn-shadow-down); }
.opt.quiet { opacity: .6; }
.opt.wide { display: block; width: 100%; padding: 11px 18px; font-size: 14px; text-align: center; }
a.opt { text-decoration: none; display: inline-block; }

/* A sheet: one card over a dimmed page. Share, the iPhone note, the app menu. */
.sheet {
  position: fixed; inset: 0; z-index: 20;
  display: flex; align-items: center; justify-content: center;
  padding: max(env(safe-area-inset-top), 16px) 16px max(env(safe-area-inset-bottom), 16px);
  background: rgba(8,8,10,.72);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  -webkit-user-select: text; user-select: text;
}
.sheet[hidden] { display: none; }
.sheetCard {
  width: min(420px, 100%); max-height: 100%; overflow-y: auto;
  background: #232327; border-radius: 18px; padding: 22px 20px 18px;
  box-shadow: 0 20px 60px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.08) inset;
  text-align: center;
}
.sheetCard h2 { font-size: 18px; margin: 0 0 6px; }
.sheetCard h3 { font-size: 14px; margin: 0 0 6px; opacity: .9; }
.sheetSub { font-size: 13px; opacity: .65; margin: 0 0 14px; line-height: 1.55; }
.sheetRow { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.qr { display: block; margin: 4px auto 10px; width: 232px; height: 232px; border-radius: 10px;
      image-rendering: pixelated; }
.shareUrl { font-size: 12px; opacity: .55; margin: 0; word-break: break-all; }

/* --- full screen ------------------------------------------------------------ */

.fullExit {
  display: none; position: fixed; z-index: 15;
  top: calc(env(safe-area-inset-top) + 10px); right: calc(env(safe-area-inset-right) + 10px);
  width: 34px; height: 34px; border-radius: 999px; border: 0; cursor: pointer;
  font: 20px/34px ui-sans-serif, system-ui, sans-serif; color: #fff;
  background: rgba(40,40,46,.55);
}
body.full { overflow: hidden; }
body.full .chrome { display: none; }
body.full .fullExit { display: block; }
body.full .stage { min-height: calc(100vh - 16px); }

/* --- a tester's own device: the device, and nothing else -------------------- */

body[data-mode="app"] {
  overflow: hidden; height: 100vh; height: 100dvh;
  justify-content: center;
}
/* Nothing on this page may scroll, not even programmatically: overflow:hidden
   still lets a focused field or a tapped menu item scroll the page, and it
   did -- the device slid 41px off the left edge. clip does not allow it. The
   key labels are never shown here, so they go from the layout altogether;
   invisible, they were still the thing overhanging the right edge. */
html.app, html.app body { overflow: hidden; overflow: clip; }
body[data-mode="app"] .tags { display: none; }
body[data-mode="app"] #shell { flex: 1; min-height: 0; }
body[data-mode="app"] .stage { flex: 1; min-height: 0; height: 100%; }

/* Three dots in the corner, quiet until it is needed: amber when something the
   device wrote has not reached the account yet. */
.menuBtn {
  position: fixed; z-index: 12; cursor: pointer;
  bottom: calc(env(safe-area-inset-bottom) + 10px); right: calc(env(safe-area-inset-right) + 10px);
  display: flex; gap: 3px; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 999px; border: 0;
  background: rgba(40,40,46,.45); opacity: .55;
}
.menuBtn i { display: block; width: 4px; height: 4px; border-radius: 99px; background: #fff; }
.menuBtn:active { opacity: 1; }
body.unsaved .menuBtn { opacity: .95; box-shadow: 0 0 0 2px rgba(var(--glow), .8); }
.menuCard { text-align: left; }
.menuCard h2, .menuCard .saveState { text-align: center; }
.saveState { font-size: 12px; opacity: .6; margin: 0 0 14px; }
.installBox { background: #1c1c1f; border-radius: 12px; padding: 12px 14px; margin: 0 0 16px; }
.installBox.lead { box-shadow: 0 0 0 1px rgba(var(--glow), .7); }
.installBox p { font-size: 13px; line-height: 1.55; margin: 0 0 8px; opacity: .85; }
.shareGlyph {
  display: inline-block; width: 12px; height: 14px; vertical-align: -2px; margin: 0 1px;
  border: 1.5px solid currentColor; border-top: 0; border-radius: 0 0 2px 2px; position: relative;
}
.shareGlyph::before { content: ""; position: absolute; left: 50%; top: -5px; width: 1.5px; height: 10px;
                      background: currentColor; transform: translateX(-50%); }
.menuCard .ideas { margin: 0 0 6px; }
.menuCard .ideas h3 { font-size: 14px; }
.menuCard .ideas .sheetSub { margin-bottom: 8px; }

/* The sign-in that stands in front of the device when nobody is signed in. */
.gatePanel {
  width: min(380px, 100%); margin: auto; padding: 24px 6px;
  -webkit-user-select: text; user-select: text;
}
.gatePanel h1 { font-size: 22px; margin: 10px 0 4px; }
.gatePanel form, .join form { text-align: left; }
.gateIcon { display: block; margin: 0 auto; border-radius: 18px; }
.gateAlt { font-size: 13px; opacity: .75; margin: 14px 0 0; }
.formErr { font-size: 13px; color: #f0a592; margin: 10px 0 0; }
.field small { display: block; font-size: 11.5px; opacity: .7; margin-top: 4px; }

/* --- "I want one!" ------------------------------------------------------------ */

body.page {
  display: block; overflow-y: auto; -webkit-user-select: text; user-select: text;
  touch-action: auto;
}
.join { max-width: 560px; margin: 0 auto; padding: 18px 4px 30px; }
.join h1 { font-size: 26px; line-height: 1.25; margin: 14px 0 12px; }
.join .lede { font-size: 16px; line-height: 1.65; color: rgba(233,229,220,.88); text-align: left; margin: 0 0 22px; }
.join h2 { font-size: 17px; margin: 0 0 12px; }
.join h3 { font-size: 13px; letter-spacing: .08em; text-transform: uppercase; opacity: .6; margin: 22px 0 6px; }
.card { background: #232327; border: 1px solid #33333a; border-radius: 16px; padding: 18px 18px 14px; margin: 0 0 18px; text-align: left; }
.card h2 { text-align: center; }
.opt-label { opacity: .6; }
.fine, .devices { text-align: left; margin: 26px 0 0; }
.fine p { font-size: 13.5px; line-height: 1.65; opacity: .72; margin: 0 0 10px; }
.fineNote { font-size: 13px; line-height: 1.6; opacity: .7; margin: 0 0 10px; }
.device-card {
  display: block; text-decoration: none; color: var(--ink);
  background: #232327; border: 1px solid rgba(var(--glow), .5); border-radius: 16px; padding: 16px 18px;
}
.device-card strong { display: block; font-size: 17px; margin-bottom: 4px; }
.device-card span { display: block; font-size: 13.5px; line-height: 1.55; opacity: .8; }
.device-card em { display: block; font-style: normal; margin-top: 8px; color: rgb(var(--glow)); font-size: 13px; }
.coming { list-style: none; padding: 0; margin: 0 0 10px; display: grid; gap: 8px;
          grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.coming li { background: #202024; border: 1px dashed #3a3a42; border-radius: 12px; padding: 10px 14px; }
.coming strong { display: block; font-size: 14px; }
.coming span { font-size: 12px; opacity: .55; }

/* --- the Reconciliation Guide: where it lives --------------------------------- */

.guideBox { background: #1c1c1f; border-radius: 12px; padding: 12px 14px; margin: 0 0 16px; }
.guideBox h3 { margin: 0 0 4px; }
.guideBox p { font-size: 13px; line-height: 1.55; margin: 0; opacity: .85; }
.sheetRow.left { justify-content: flex-start; }
.guideCard { text-align: left; }
.guideCard h2 { text-align: center; }
.choice {
  display: block; width: 100%; text-align: left; cursor: pointer; margin: 0 0 10px;
  font: inherit; color: var(--ink); background: #1c1c1f;
  border: 1px solid #3a3a42; border-radius: 14px; padding: 12px 14px;
}
.choice:hover, .choice:focus-visible { border-color: rgba(var(--glow), .8); outline: none; }
.choice strong { display: block; font-size: 15px; margin-bottom: 4px; }
.choice span { display: block; font-size: 12.5px; line-height: 1.55; opacity: .78; }
.pairCode {
  font: 600 17px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .06em;
  text-align: center; margin: 6px 0 10px; padding: 10px; border-radius: 10px;
  background: #1c1c1f; -webkit-user-select: all; user-select: all; word-break: break-all;
}
.pinInput, .codeInput {
  display: block; width: 100%; margin-top: 4px; font: inherit; font-size: 18px; letter-spacing: .2em;
  color: var(--ink); background: #1c1c1f; border: 1px solid #35353c; border-radius: 10px; padding: 10px 12px;
  -webkit-user-select: text; user-select: text;
}
.codeInput { font-size: 15px; letter-spacing: .06em; text-transform: uppercase;
             font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.sheetSub.small { font-size: 12px; opacity: .55; }
.sheetSub a { color: rgb(var(--glow)); }

/* --- a tester's device: language and Settings, bottom left ------------------ */

.deviceBar {
  position: fixed; z-index: 12; display: flex; gap: 8px; align-items: center;
  bottom: calc(env(safe-area-inset-bottom) + 10px); left: calc(env(safe-area-inset-left) + 10px);
}
.barBtn {
  height: 38px; min-width: 38px; border-radius: 999px; border: 0; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font: 600 12px/1 ui-sans-serif, system-ui, sans-serif; letter-spacing: .04em; color: #fff;
  background: rgba(40,40,46,.55); opacity: .75; padding: 0 12px;
}
.barBtn:active { opacity: 1; }
.barBtn.gear { padding: 0; width: 38px; }
.barBtn.gear.asleep { opacity: .3; }
.barBtn.gear.nudge { animation: nudgeGear .35s ease-in-out 3; }
@keyframes nudgeGear { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }
.langMenu {
  position: absolute; bottom: 46px; left: 0; width: min(290px, 86vw);
  background: #232327; border-radius: 14px; padding: 6px;
  box-shadow: 0 12px 40px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.08) inset;
}
.langMenu button {
  display: block; width: 100%; text-align: left; cursor: pointer; font: inherit; color: var(--ink);
  background: none; border: 0; border-radius: 10px; padding: 9px 11px;
}
.langMenu button.on { background: rgba(var(--glow), .16); }
.langMenu button:active { background: #2c2c32; }
.langMenu strong { display: block; font-size: 14px; }
.langMenu span { display: block; font-size: 11.5px; opacity: .6; margin-top: 1px; }
.langMenu .langNow { font-size: 11px; line-height: 1.45; opacity: .5; margin: 6px 10px 4px; }

/* How the Reconciliation Guide is kept here, above the device while it is open. */
.guideNote {
  position: fixed; z-index: 11; left: 50%; transform: translateX(-50%);
  top: calc(env(safe-area-inset-top) + 8px); width: min(440px, 92vw); margin: 0;
  padding: 8px 12px; border-radius: 12px; text-align: center;
  font-size: 12px; line-height: 1.45; color: #fff;
  background: rgba(28,28,32,.92); box-shadow: 0 4px 18px rgba(0,0,0,.45), 0 0 0 1px rgba(var(--glow), .35) inset;
  pointer-events: none;
}

/* --- privacy and consent ------------------------------------------------------ */

.policy h2 { text-align: left; margin: 26px 0 8px; }
.policy p, .policy li { text-align: left; font-size: 15px; line-height: 1.65; color: rgba(233,229,220,.88); }
.policy ul { padding-left: 20px; margin: 0 0 10px; }
.policy li { margin: 0 0 8px; }
.policy a, .consentNote a, .check a, .credit a { color: rgb(var(--glow)); }
.consentNote { font-size: 13px; line-height: 1.55; opacity: .8; margin: 4px 0 10px; }
.check { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; line-height: 1.5;
         margin: 0 0 12px; cursor: pointer; }
.check input { width: 20px; height: 20px; flex: 0 0 20px; margin: 1px 0 0; accent-color: rgb(var(--glow)); }
.credit.small { font-size: 10.5px; opacity: .35; margin-top: 4px; }
input:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid rgba(var(--glow), .9); outline-offset: 2px;
}

/* --- two languages ------------------------------------------------------------- */
/* Long passages are written out in both; the one not wanted is hidden. The
   language is on <html lang>, set by i18n.js before anything is drawn. */
html[lang="es"] [data-lang="en"], html:not([lang="es"]) [data-lang="es"] { display: none !important; }

/* --- the showcase: room for the corner bar and the scroll cue ------------------ */
/* The bottom one also clears the HOME tag, which hangs 36px under the chin. */
body:not([data-mode]):not(.page) { --reserve-top: 52px; --reserve-bottom: 72px; }
body[data-mode="app"] { --reserve-bottom: 40px; }

/* The corner bar: the page's language on the left, a small way to get one on
   the right. Out of the device's way, in reach of a thumb. */
.cornerBar {
  position: fixed; z-index: 14; left: 0; right: 0; top: 0;
  display: flex; justify-content: space-between; align-items: center;
  padding: calc(env(safe-area-inset-top) + 8px) calc(env(safe-area-inset-right) + 10px) 8px
           calc(env(safe-area-inset-left) + 10px);
  pointer-events: none; transition: background .3s ease, box-shadow .3s ease;
}
body.scrolled .cornerBar { background: rgba(24,24,27,.95); box-shadow: 0 2px 14px rgba(0,0,0,.5); }
#letter, #want { scroll-margin-top: 72px; }
.cornerBar > * { pointer-events: auto; }
body.page .cornerBar { position: static; justify-content: flex-end; padding: 0 0 6px; }
.langSwitch {
  font: 600 15px/1 ui-sans-serif, system-ui, sans-serif; color: var(--ink); cursor: pointer;
  min-height: 40px; padding: 0 16px; border-radius: 999px;
  background: rgba(40,40,46,.8); border: 1px solid #44444c;
}
.langSwitch:active { background: #2c2c32; }
.wantSmall {
  font: 700 14px/1 ui-sans-serif, system-ui, sans-serif; text-decoration: none; color: #1c1409;
  min-height: 40px; display: inline-flex; align-items: center; padding: 0 16px; border-radius: 999px;
  background: linear-gradient(180deg, #ffd9a6, #f0b86e);
  box-shadow: 0 4px 14px rgba(var(--glow), .25);
}
.cornerActions { display: inline-flex; gap: 8px; }
.signInSmall {
  font: 700 14px/1 ui-sans-serif, system-ui, sans-serif; text-decoration: none; color: #ffd9a6;
  min-height: 40px; display: inline-flex; align-items: center; padding: 0 16px; border-radius: 999px;
  background: rgba(40,40,46,.8); border: 1px solid rgba(var(--glow), .55);
}
.signInSmall:active { background: #2c2c32; }
/* Three pills across a 360px phone, "Iniciar sesión" the longest of them. */
@media (max-width: 420px) {
  .langSwitch, .signInSmall, .wantSmall { padding: 0 11px; }
  .cornerActions { gap: 6px; }
}
body.full .cornerBar, body.full .scrollCue { display: none; }

/* "There is more below": bottom centre of the first screen, gently moving,
   gone once the page is scrolled. */
.scrollCue {
  position: fixed; z-index: 13; left: 50%; transform: translateX(-50%);
  bottom: calc(env(safe-area-inset-bottom) + 10px);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  font: 600 15px/1.2 ui-sans-serif, system-ui, sans-serif; color: #fff; cursor: pointer;
  padding: 8px 18px 10px; border-radius: 16px; border: 0;
  background: rgba(28,28,32,.85); box-shadow: 0 4px 18px rgba(0,0,0,.45);
  transition: opacity .4s ease;
}
.scrollCue .arrow { width: 12px; height: 12px; border-width: 0 3px 3px 0; margin-top: 2px; }
.scrollCue.gone { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .scrollCue .arrow { animation: none; } }

/* --- how to use it: big, numbered, one thing a line ----------------------------- */
.howto { max-width: 600px; margin: 8px auto 18px; text-align: left; }
.howto h2 { font-size: 22px; margin: 0 0 12px; text-align: center; }
.howto ol, .steps { counter-reset: step; list-style: none; padding: 0; margin: 0; }
/* CHILD combinator, not descendant. saints.html nests the saint picker
   inside a step, and with `.steps li` every one of its 148 rows counted as
   a step: 148 numbered badges, each indented 50px, and the step after it
   numbered 150. Audited before changing it -- index.html's .howto and
   join.html's two .steps lists all have their <li> as direct children, so
   nothing else moves. */
.howto ol > li, .steps > li {
  counter-increment: step; position: relative; padding: 10px 0 10px 50px; min-height: 44px;
  font-size: 18px; line-height: 1.5; color: var(--ink); border-bottom: 1px solid #2c2c32;
}
.howto ol > li::before, .steps > li::before {
  content: counter(step); position: absolute; left: 0; top: 8px;
  width: 36px; height: 36px; border-radius: 999px; display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 18px; color: #1c1409; background: rgb(var(--glow));
}
.howtoNote { font-size: 16px; opacity: .75; margin: 12px 0 0; text-align: center; }
.steps { max-width: 560px; margin: 0 auto 22px; text-align: left; }
.steps > li { font-size: 17px; }

/* --- larger, for older hands and eyes ------------------------------------------- */
/* Every button a real target (48px), every label readable, every field at 17px
   -- which is also what stops an iPhone zooming in when a field is tapped. */
.opts .opt, .ideas .send, .sheetRow .opt, .opt.wide {
  min-height: 48px; font-size: 16px; padding: 10px 20px;
}
.opts { gap: 10px; }
.ideas h2 { font-size: 20px; opacity: 1; }
.field { font-size: 16px; opacity: .9; }
.field input, .field textarea { font-size: 17px; padding: 12px 14px; }
.thanks { font-size: 16px; }
.signedIn { font-size: 16px; }
.want p { font-size: 16px; opacity: .8; }
.sheetSub { font-size: 15px; }
.sheetCard h2 { font-size: 20px; }
.join h1 { font-size: 28px; }
.join .lede { font-size: 18px; }
.check { font-size: 15px; }
.check input { width: 24px; height: 24px; flex-basis: 24px; }
.gateAlt { font-size: 16px; }
.field small { font-size: 14px; }
.policy p, .policy li { font-size: 17px; }

/* "Show password" / "Show PIN" under a secret field: a big box, easy to hit. */
.reveal {
  display: flex; align-items: center; gap: 10px; min-height: 44px; margin: -4px 0 10px;
  font-size: 16px; color: var(--ink); opacity: .9; cursor: pointer; user-select: none;
}
.reveal input { width: 22px; height: 22px; margin: 0; accent-color: #f0b86e; cursor: pointer; }

/* Where notes go, at the top of the menu that opens after sign-up. */
.feedbackTip {
  font-size: 16px; line-height: 1.45; text-align: left; margin: 0 0 14px; padding: 12px 14px;
  border-radius: 12px; background: rgba(var(--glow), .1); border: 1px solid rgba(var(--glow), .35);
}

/* --- thanks and the contributors' leaderboard (thanks.js, leaderboard.html) -- */
.thanksBody { font-size: 18px; line-height: 1.5; margin: 6px 0 12px; }
.thanksBody q { font-style: italic; color: #fff; }
.thanksSign { font-size: 16px; opacity: .75; margin: 0 0 16px; text-align: right; }
.thanksSheet a.opt { display: inline-flex; align-items: center; text-decoration: none; }

.contributors { max-width: 600px; margin: 26px auto 10px; padding-top: 18px; border-top: 1px solid #33333a; }
.contributors h2 { font-size: 18px; margin: 0 0 4px; }
.contribSub { font-size: 15px; opacity: .7; margin: 0 0 10px; }
.contribNames { font-size: 17px; line-height: 1.7; margin: 0 0 6px; }

.board { max-width: 640px; }
.boardMe { background: rgba(var(--glow), .1); border: 1px solid rgba(var(--glow), .35);
           border-radius: 12px; padding: 12px 14px; margin: 0 0 16px; font-size: 16px; }
.boardMe p { margin: 0 0 8px; }
.boardMe .opt { min-height: 44px; font-size: 15px; padding: 0 18px; }
.boardNotYet, .boardState { font-size: 16px; opacity: .8; }
.boardWrap { overflow-x: auto; }
.boardTable { width: 100%; border-collapse: collapse; font-size: 16px; }
.boardTable th, .boardTable td { text-align: left; padding: 10px 8px; border-bottom: 1px solid #33333a; }
.boardTable th { font-size: 13px; font-weight: 600; opacity: .6; }
.boardTable .num { text-align: right; }
.boardTable tr.me td { background: rgba(var(--glow), .14); font-weight: 700; }
.boardTable .you { font-size: 12px; font-weight: 600; padding: 1px 8px; border-radius: 99px;
                   background: #f0b86e; color: #1c1409; vertical-align: middle; }

/* --- saints.html: a tester giving a saint a portrait -------------------------
   Its own components, because nothing else on the site crops a picture. The
   page itself is body.page + .join, like the leaderboard and the policy: it is
   a long form and MUST scroll. It was briefly data-mode="app", which is the
   DEVICE page -- pinned to the viewport, "nothing on this page may scroll, not
   even programmatically" -- so the crop step and the Send button could not be
   reached at all. */

.pickList { list-style: none; margin: 10px 0 0; padding: 0; max-height: 46vh; overflow-y: auto; }
.pickList li { margin: 0 0 6px; }
.pickList button {
  width: 100%; text-align: left; font: inherit; font-size: 15px; cursor: pointer;
  color: rgba(233,229,220,.92); background: rgba(255,255,255,.04);
  border: 1px solid rgba(255,255,255,.10); border-radius: 10px; padding: 10px 12px;
}
.pickList button:hover, .pickList button:focus-visible { background: rgba(255,255,255,.09); }
.pickList .none { font-size: 15px; color: rgba(233,229,220,.62); padding: 6px 2px; }
.picked { font-size: 15px; color: rgb(var(--glow)); margin: 10px 0 0; }

.warn {
  font-size: 14px; line-height: 1.55; margin: 10px 0 0; padding: 10px 12px;
  border-radius: 10px; color: rgba(233,229,220,.88);
  background: rgba(255,196,0,.10); border: 1px solid rgba(255,196,0,.28);
}
.say { font-size: 15px; margin: 14px 0 0; color: rgb(var(--glow)); }
.say.bad { color: rgb(255,138,128); }

/* The crop box and, beside it, the same picture as the panel will actually draw
   it. Wraps rather than sitting side by side on a phone, where the crop wants
   the width more than the comparison does. */
.cropWrap { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; margin: 12px 0 0; }
.cropWrap canvas { touch-action: none; border-radius: 12px; background: #000; max-width: 100%; }
#crop { cursor: grab; border: 1px solid rgba(255,255,255,.14); }
#crop:active { cursor: grabbing; }
.previewWrap { display: flex; flex-direction: column; gap: 6px; }
.previewLabel { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; opacity: .55; }
.zoom { display: flex; align-items: center; gap: 10px; margin: 14px 0 0; font-size: 15px; }
.zoom input { flex: 1; }
.why { font-size: 14px; margin: 14px 0 0; color: rgba(233,229,220,.62); }

/* --- the screenshots, in place of the device ------------------------------- */
/*
   The front page used to run the firmware here and visitors took it for the
   device. These are pictures and are meant to read as pictures: no keys drawn
   around them, no chin, nothing to press. The one thing kept from the device is
   the white glass, because what the screen looks like is the whole point.

   Sizing follows the panel's own 480x800, so a screenshot is never stretched:
   capped by height on a short viewport and by width on a narrow one, whichever
   bites first.
*/
/* The extra bottom room is for the scroll cue, which is fixed to the bottom of
   the first screen. The device that used to stand here left it empty; the
   sign-up does not, and was landing exactly under the cue. */
.shotStage {
  flex-direction: column; justify-content: center; gap: 10px;
  padding-bottom: calc(var(--reserve-bottom) + 26px);
}

.shots { width: 100%; max-width: 560px; margin: 0 auto; text-align: center; }

/* Sized by HEIGHT first: the panel is a tall 480x800, so driving it from the
   width overflowed the viewport on a laptop and pushed the sign-up under the
   scroll cue. Width follows from the shape, and is clamped on a narrow phone. */
.shotFrame {
  position: relative;
  overflow: hidden;
  margin: 0 auto;
  height: min(52svh, 470px);
  aspect-ratio: 480 / 800;
  max-width: 82vw;
  border-radius: 10px;
  background: #fff;
  border: 1px solid #3a3a42;
  box-shadow: 0 14px 34px rgba(0, 0, 0, .45);   /* lifted, as a photograph sits */
}
@supports not (aspect-ratio: 1) {
  .shotFrame { width: min(82vw, 300px); height: min(54svh, 500px); }
}

.shotTrack {
  display: flex;
  margin: 0; padding: 0; list-style: none;
  height: 100%;
  transition: transform .45s cubic-bezier(.4, 0, .2, 1);
}
@media (prefers-reduced-motion: reduce) { .shotTrack { transition: none; } }

.shot {
  position: relative; flex: 0 0 100%; height: 100%;
  display: flex; flex-direction: column;
}
.shot img {
  display: block; width: 100%; flex: 1 1 auto; min-height: 0;
  object-fit: contain;
  image-rendering: -webkit-optimize-contrast;  /* the panel's pixels are the point */
}

/* Under the picture, never over it. Laid across the glass it hid the bottom of
   the calendar and read as part of the screen, which is the confusion this
   whole page is fixing. */
.shot .cap {
  flex: 0 0 auto;
  padding: 7px 10px 8px;
  font: 600 13px/1.3 ui-sans-serif, system-ui, sans-serif;
  color: #16161a;
  background: #fff;
  border-top: 1px solid #e6e3dc;
}

.shotBar {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  margin: 14px auto 0;
}
.shotArrow {
  font: 400 24px/1 ui-sans-serif, system-ui, sans-serif;
  color: var(--ink); background: transparent;
  border: 1px solid #35353c; border-radius: 999px;
  width: 38px; height: 38px; cursor: pointer; opacity: .75;
}
.shotArrow:hover { opacity: 1; }
.shotArrow:active { transform: translateY(1px); }

.shotDots { display: flex; align-items: center; gap: 8px; }
.shotDot {
  width: 8px; height: 8px; padding: 0;
  border: 0; border-radius: 999px; cursor: pointer; background: #4a4a53;
}
.shotDot.on { background: var(--ink); }

.shotNote { margin: 8px auto 0; max-width: 420px; font-size: 12px; opacity: .55; }

/* Straight under the pictures, where somebody who has just decided will look. */
.want.lead { margin: 0 auto; }
