/* Replica, the face Cryptoflip uses, so the two sites read as one hand.
   Served from this origin rather than linked, because there is no CDN
   copy of it and a missing brand font falls back to something that looks
   like a different company. */
@font-face {
  font-family: Replica;
  src: url("assets/ReplicaPro.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: Replica;
  src: url("assets/Replica-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Cryptoflip's palette, pulled toward purple rather than kept at its neutral
   greys. The brand ramp here ends on a violet, and a violet accent sitting on
   a blue-grey surface is the pair that makes a dark page look dirty; carrying
   a little of the accent's hue into the surfaces is what makes the whole thing
   read as one colour instead of two.

   Every surface is on the same hue, around 265 degrees, and only lightness
   changes between them. The card is the one exception at a hair more
   saturation, so an edge is legible without needing a brighter border. */
:root {
  color-scheme: dark;
  /* Neutral. Every surface here is grey, and the only colour on the page comes
     from the wash at the top and the brand ramp on the mark.

     Tinting the surfaces themselves was tried and is wrong: it cannot be kept
     subtle, because a tint that is visible at the top of the page is still
     there at the bottom where there is no wash to justify it, and the page
     reads as purple rather than as dark with a light on it. Colour belongs to
     the light, not to the paper. */
  --bg-color: #050506;
  --bg-secondary: #17181c;
  --bg-gradient-bottom: #141519;
  --bg-dot-color: rgba(255, 255, 255, 0.06);
  --card-bg: #26282e;
  --card-border: #34373f;
  --header-bg: linear-gradient(180deg, #0a0a0d 0%, #070709 100%);
  --header-border: #31343b;
  --text: #e0e0e0;
  --text-muted: #9a9da3;
  --text-secondary: #82858c;
  --text-dark: #797d86;
  /* Cryptoflip's brand gradient, the actual declaration rather than
     anything matched by eye. It is in `ChatPanel.vue`, on the send
     button's hover state, and it is the only place the three stops
     appear together. */
  --brand-pink: #ff1a88;
  --brand-rose: #ff4db8;
  --brand-magenta: #e619ff;
  --brand-ramp: linear-gradient(
    135deg,
    #ff1a88 0%,
    #ff4db8 50%,
    #e619ff 100%
  );
  --primary: #e619ff;
  --brand-accent-soft: rgba(230, 25, 255, 0.1);
  --brand-accent-border: rgba(230, 25, 255, 0.26);
  --button-bg: #2c2f36;
  --button-border: #3b3f48;
  --border-radius: 8px;

  /* The terminal face, in one place because more than one thing on the
     page is supposed to look like it came out of a shell. Cascadia first:
     it ships with Windows Terminal, so the machine this software is for
     already has it and nobody downloads anything. */
  --mono: "Cascadia Code", "Cascadia Mono", "JetBrains Mono",
    "Geist Mono", Consolas, ui-monospace, "SF Mono", monospace;

  /* Aliases, so the rest of the stylesheet did not have to be rewritten
     line by line to change what it points at. */
  --bg: var(--bg-color);
  --elev: var(--card-bg);
  --elev-hi: var(--button-bg);
  --line: var(--card-border);
  --muted: var(--text-muted);
  --faint: var(--text-dark);
  --accent: var(--primary);
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

* {
  box-sizing: border-box;
}

/* No dot grid. It was Cryptoflip's, and against this page's single
   column of centred text it read as graph paper behind a document
   rather than as a surface. The grain below does the same job without
   drawing a lattice the eye tries to line the content up with. */
body {
  margin: 0;
  /* So the wash above measures itself against the page rather than the
     window. */
  position: relative;
  /* `min-height` and `no-repeat` together are what keep the ramp from
     drawing a line across the page.

     The gradient is sized to this element's box. When the content is
     shorter than the window the box ends partway down, and the default
     `repeat` starts the ramp over from transparent right there, which
     reads as a hard seam with a lighter band under it. Filling the
     viewport gives the ramp somewhere to finish, and no-repeat means a
     page that is taller still ends on the flat colour rather than a
     second copy of the ramp. */
  min-height: 100vh;
  /* The ramp runs the whole height rather than finishing in the first
     300px. Over a short distance the eye reads it as an edge with a
     lighter block under it; stretched across the page there is nowhere
     for a line to form and it just reads as depth. */
  background: linear-gradient(
      180deg,
      transparent 0%,
      var(--bg-gradient-bottom) 100%
    ),
    var(--bg-color);
  background-repeat: no-repeat;
  color: var(--text);
  font: 16px/1.65 Replica, Inter, "Segoe UI", system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* A wash behind the top of the page, so the hero is not a flat rectangle
   of near black. In the brand violet now rather than the terminal green
   the page used to borrow from the app's own palette. */
body::before {
  content: "";
  /* Absolute, not fixed. Fixed pins it to the window, so a wash this
     wide followed the reader down and put a violet cast over the help
     and the form as well. It belongs to the top of the document, and it
     should scroll off the way anything else at the top does. */
  position: absolute;
  inset: 0 0 auto 0;
  /* Three overlapping washes rather than one, because a single radial has a
     single centre and the eye finds it. Two off-centre lobes in different
     hues, plus a wide low one to tie them together, never resolve into a
     shape you can point at, which is what "diffused" actually means here.
     None of them is above 12% alpha; the diffusion comes from the overlap,
     not from any one of them being strong.

     The explicit mid stops matter as much as the colours. A two-stop radial
     falls off linearly and that straight ramp is visible as a soft ring on a
     dark page; easing it out through a half-alpha stop at 40% removes the
     ring without making the wash any smaller. */
  /* A bloom, not a tint. It has to be strong enough to be a thing you look at
     and finished early enough that the page is black by the screenshot, which
     is what `height` is for: nothing below 900px gets any of this, so the
     bottom of the page is the base colour and nothing else.

     `circle` rather than the ellipses that were here. An ellipse stretched to
     the width of the window has a horizon rather than a centre, and the ask
     was for something that spreads in a circle. */
  height: 900px;
  background:
    /* Pink, low and left of centre. Sitting on screen rather than above it, so
       there is an actual arc of it visible rather than the bottom edge of
       something happening off the top of the page. */
    radial-gradient(
      circle 620px at 33% 60px,
      rgba(255, 64, 180, 0.20) 0%,
      rgba(255, 64, 180, 0.10) 30%,
      rgba(255, 64, 180, 0.03) 58%,
      transparent 78%
    ),
    /* Magenta, the other side and a little higher, so the two lobes do not
       read as a mirrored pair. */
      radial-gradient(
        circle 660px at 69% 10px,
        rgba(230, 25, 255, 0.21) 0%,
        rgba(230, 25, 255, 0.10) 32%,
        rgba(230, 25, 255, 0.03) 60%,
        transparent 80%
      ),
    /* The violet core, dead centre and mostly above the fold. This is the one
       carrying the brightness; the two above only colour its edges. */
      radial-gradient(
        circle 780px at 50% -110px,
        rgba(170, 70, 255, 0.30) 0%,
        rgba(170, 70, 255, 0.15) 34%,
        rgba(170, 70, 255, 0.05) 62%,
        transparent 82%
      ),
    /* A wide, weak deep violet under all of it, to carry the falloff out to
       the edges of the window so the three above do not end on visible arcs. */
      radial-gradient(
        circle 1250px at 50% -260px,
        rgba(112, 40, 215, 0.17) 0%,
        rgba(112, 40, 215, 0.08) 40%,
        rgba(112, 40, 215, 0.025) 66%,
        transparent 88%
      );
  pointer-events: none;
}

/* No dither. There used to be a fractal-noise layer over the whole page here.
 *
 * It was doing a second job besides texture: noise breaks up the flat steps a
 * gradient makes when it crosses very few levels of a dark colour, which is
 * why banding is not visible in a grainy render and is in a clean one. Taking
 * it out means the washes above have to avoid banding on their own, which is
 * what the extra mid stops in each of them are for: three overlapping ramps
 * that each cross a small range beat one that crosses a large one.
 */

/* Everything real on the page sits in `.wrap`. */
.wrap {
  z-index: 1;
}

.wrap {
  position: relative;
  max-width: 880px;
  margin: 0 auto;
  /* No bottom padding. It was here to stop the screenshot ending flush with
     the document when the shot was the last element; the footer is last now
     and carries its own bottom padding, so this only ever added a second
     helping of empty space under it. */
  padding: 0 24px;
}

header {
  padding: 96px 0 0;
  text-align: center;
}

/* The wordmark is the one place that is not Replica and not the
   terminal's monospace.

   Monospace is wrong for it: it makes the name look like something you
   type rather than something you install, and it puts the logo in the
   same voice as the install line ten pixels below it. A tight geometric
   sans, tracked in, reads as a product name. Grotesques first, since
   every platform has one that fits the brief without a download.

   Light rather than heavy. At this size the weight was doing the work a
   52px word does on its own, and a slab of bold white beside a saturated
   tile fought it for the eye. Thin lets the tile lead. */
.mark {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-family: "Archivo", "Inter Tight", "Helvetica Neue", "Segoe UI",
    system-ui, sans-serif;
  font-size: 52px;
  font-weight: 300;
  letter-spacing: -0.03em;
  color: #fff;
}

/* The word carries the same ramp as the tile, so the two halves of the
   mark are one object rather than an icon sitting next to a label. On its
   own span, not on `.mark`: a gradient clipped to the whole row would
   stretch across the tile as well and leave the letters showing only its
   tail end. */
.mark .word {
  background: var(--brand-ramp);
  background-size: 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* The ramp ends at #e619ff, a violet that is dark against near black.
     A soft bloom under the glyphs keeps the last letters from sinking
     into the page the way flat gradient text does. */
  filter: drop-shadow(0 0 18px rgba(230, 25, 255, 0.28));
}

/* Served at 180 and drawn at 44, so it stays sharp on a retina screen
   without shipping the 512 the grain would cost a third of a megabyte at. */
.mark .tile {
  width: 44px;
  height: 44px;
  border-radius: 10px;
}

h1 {
  margin: 22px 0 0;
  font-size: clamp(28px, 5vw, 42px);
  line-height: 1.18;
  letter-spacing: -0.022em;
  font-weight: 700;
}

.sub {
  margin: 16px auto 0;
  max-width: 560px;
  color: var(--muted);
  font-size: 17.5px;
}

/* ---- install ---- */

/* Wide enough for the whole one-liner. It used to stop at 640px, which
   cut the command off mid-word at `master/ins`, and a truncated install
   line makes the page look like it is hiding something. 780px is what
   the command measures at this size, and it still sits inside the 832px
   the column gives it. */
.install {
  margin: 40px auto 0;
  max-width: 780px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 6px 6px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--elev);
  text-align: left;
}

.install code {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text);
  /* Kept scrollable for the narrow window where it still does not fit.
     The Copy button is how anyone actually takes it, and a scrollbar
     sitting under the line just made the block look broken. */
  scrollbar-width: none;
}

.install code::-webkit-scrollbar {
  display: none;
}

/* Below the fold of a phone the one-liner cannot fit on a line at all,
   so let it wrap rather than scroll off the side. */
@media (max-width: 640px) {
  .install {
    flex-direction: column;
    align-items: stretch;
    padding: 14px;
    gap: 12px;
  }

  .install code {
    white-space: pre-wrap;
    word-break: break-all;
  }
}

/* Only ever shown to someone who cannot run this yet, so it is quiet:
   they are being told no, and a loud box saying so twice is worse. */
.platform-note {
  max-width: 560px;
  margin: 14px auto 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-secondary);
}

.platform-note[hidden] {
  display: none;
}

.install code .dim {
  color: var(--faint);
}

/* A clipboard, not the word "Copy". The word was competing with the
   command for the same reading; the icon is understood without being
   read at all, and it buys the line back the width the label was using. */
.copy {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--elev-hi);
  color: var(--muted);
  cursor: pointer;
  transition: background 160ms var(--ease), color 160ms var(--ease),
    border-color 160ms var(--ease);
}

.copy:hover {
  background: var(--brand-accent-soft);
  border-color: var(--brand-accent-border);
  color: #fff;
}

.copy svg {
  width: 15px;
  height: 15px;
  fill: currentColor;
}

/* Both icons live in the button and one is hidden, rather than the mark
   being swapped in on click. Swapping means the tick has no glyph to
   draw the first time it is needed, which shows as a flicker. */
.copy .tick,
.copy.copied .clip {
  display: none;
}

.copy.copied .tick {
  display: block;
}

.copy.copied {
  color: var(--brand-rose);
  border-color: var(--brand-accent-border);
}

/* Clipboard access denied, which happens on an insecure origin and when
   the permission is refused. The icon stays, so the button still says
   what it is, and the title carries the fallback. */
.copy.failed {
  color: var(--brand-pink);
}

.buttons {
  margin: 22px 0 0;
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 12px 22px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--elev);
  color: var(--text);
  font-size: 14.5px;
  font-weight: 600;
  text-decoration: none;
  transition: background 160ms var(--ease), border-color 160ms var(--ease);
}

/* Colour only. Nothing on this page moves under the cursor. */
.btn:hover {
  background: var(--elev-hi);
  border-color: var(--brand-accent-border);
}

/* The brand gradient, not a flat accent. Dark text on it was the last
   of the lime the page used to use, and lime on magenta is not a
   near miss, it is a different brand. */
.btn.primary {
  background: var(--brand-ramp);
  border-color: transparent;
  color: #fff;
  background-size: 140% 140%;
  background-position: 0% 50%;
  transition: background-position 220ms var(--ease),
    box-shadow 220ms var(--ease);
}

/* Slide the ramp rather than swapping the colour. There is nothing to
   swap to that is still this gradient, and a hover that changes hue
   reads as a different button. */
.btn.primary:hover {
  background-position: 100% 50%;
  box-shadow: 0 6px 22px rgba(230, 25, 255, 0.28);
}

.btn svg {
  width: 17px;
  height: 17px;
  fill: currentColor;
}

/* Set as a shell comment, because that is what it is: the note you would
   read above the command before running it. The terminal face and the
   hash do the work a label would otherwise have to. */
.fineprint {
  margin: 22px 0 0;
  color: var(--faint);
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: -0.005em;
}

.fineprint::before {
  content: "# ";
  color: var(--brand-accent-border);
}

/* ---- screenshot ---- */

/* A small radius, not a soft one. The picture inside is a window with square
   corners of its own, and the more this frame rounds the more the two disagree
   at every corner. 6px reads as a mounted screenshot; 14px read as a card that
   happened to have a screenshot in it. */
.shot {
  margin: 64px 0 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  background: var(--elev);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}

.shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- sections ---- */

.sec {
  margin: 88px 0 0;
}

.sec > h2 {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: -0.018em;
}

.sec-lede {
  margin: 10px 0 0;
  max-width: 560px;
  color: var(--muted);
  font-size: 15px;
}

/* ---- help ---- */

.keys {
  margin: 22px 0 0;
  display: grid;
  gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.keyset {
  padding: 20px 22px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--elev);
}

.keyset h3 {
  margin: 0 0 14px;
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 400;
  color: var(--muted);
}

.keyset table {
  width: 100%;
  border-collapse: collapse;
}

.keyset td {
  padding: 7px 0;
  font-size: 14px;
  vertical-align: baseline;
  border-top: 1px solid rgba(255, 255, 255, 0.05);
}

.keyset tr:first-child td {
  border-top: 0;
}

.keyset td:first-child {
  width: 46%;
  padding-right: 14px;
  white-space: nowrap;
}

.keyset td:last-child {
  color: var(--muted);
}

/* A key, drawn as a key. The heavier bottom border is the whole trick:
   it reads as the edge of something with depth without a shadow or a
   gradient, either of which would fight the cards it sits inside. */
kbd {
  display: inline-block;
  padding: 2px 6px;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--elev-hi);
  color: var(--text);
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.5;
  white-space: nowrap;
}

/* ---- features ---- */

.features {
  margin: 22px 0 0;
  display: grid;
  gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

.feature {
  padding: 22px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--elev);
}

.feature h3 {
  margin: 0 0 8px;
  font-size: 15.5px;
  letter-spacing: -0.01em;
}

.feature p {
  margin: 0;
  color: var(--muted);
  font-size: 14.5px;
  line-height: 1.6;
}

.feature code {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--text);
  background: var(--elev-hi);
  border-radius: 4px;
  padding: 1px 5px;
  /* Paths are long and the cards are narrow. Breaking anywhere is uglier
     than breaking at a separator, so only the separators give. */
  word-break: break-word;
}

/* ---- feedback ---- */

.feedback {
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

.feedback .sec-lede {
  margin-left: auto;
  margin-right: auto;
  max-width: 480px;
}

.feedback form {
  margin: 26px 0 0;
  text-align: left;
}

/* The message is in the terminal face and the contact line is not. What
   someone reports is usually a command and its output, and body text
   mangles the alignment that makes a paste readable. An address is prose. */
.feedback textarea,
.feedback input {
  display: block;
  width: 100%;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--elev);
  color: var(--text);
  font: inherit;
  font-size: 14.5px;
  transition: border-color 160ms var(--ease), background 160ms var(--ease);
}

.feedback textarea {
  min-height: 130px;
  resize: vertical;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.6;
}

.feedback textarea::placeholder,
.feedback input::placeholder {
  color: var(--faint);
}

.feedback textarea:focus,
.feedback input:focus {
  outline: none;
  border-color: var(--brand-accent-border);
  background: var(--elev-hi);
}

.feedback-row {
  margin: 12px 0 0;
  display: flex;
  gap: 10px;
  align-items: stretch;
}

.feedback-row input {
  flex: 1;
  min-width: 0;
}

.feedback-row .btn {
  flex: none;
  padding: 0 24px;
}

@media (max-width: 560px) {
  .feedback-row {
    flex-direction: column;
  }

  .feedback-row .btn {
    justify-content: center;
    padding: 13px 24px;
  }
}

/* Off screen rather than `display: none`, so the label is still read out.
   A textarea whose only description is a placeholder loses that
   description the moment anything is typed into it. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* The honeypot. Not `display: none`, because the crawlers that fill forms
   look for that; a field pushed out of the layout is filled by a bot and
   never seen by a person. */
.feedback .trap {
  position: absolute;
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}

.feedback-status {
  margin: 14px 0 0;
  font-family: var(--mono);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
}

.feedback-status[hidden] {
  display: none;
}

.feedback-status.ok {
  color: var(--brand-rose);
}

.feedback-status.bad {
  color: var(--brand-pink);
}

/* ---- footer ---- */

footer {
  margin: 64px 0 0;
  padding: 24px 0 40px;
  border-top: 1px solid var(--line);
  color: var(--faint);
  font-size: 13.5px;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

footer a {
  color: var(--muted);
  text-decoration: none;
}

footer a:hover {
  color: var(--accent);
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0.01ms !important;
  }
}

/* ---- interior pages ---- */

/* The hero's 96px of top padding is for a page whose whole job is the logo.
   An interior page starts with a back-link and a title, and wants less air
   above it than that. */
.page-head {
  padding: 56px 0 0;
  text-align: left;
}

.page-head h1 {
  margin: 26px 0 0;
  font-size: clamp(30px, 4.4vw, 38px);
}

.page-head .sub {
  margin: 12px 0 0;
  max-width: 560px;
}

.page-head .sub a {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--brand-accent-border);
}

.page-head .sub a:hover {
  color: var(--text);
}

/* The mark as a link home. Smaller than the hero's, because here it is
   navigation rather than the thing you came to see. */
.mark-sm {
  font-size: 26px;
  gap: 10px;
  text-decoration: none;
}

.mark-sm .tile {
  width: 26px;
  height: 26px;
  border-radius: 7px;
}

/* No bloom at this size. The drop-shadow the hero wordmark carries is sized
   for 52px type and reads as a smudge under 26px. */
.mark-sm .word {
  filter: none;
}

/* ---- faq ---- */

.faq {
  margin-top: 48px;
}

.qa {
  border-top: 1px solid var(--line);
}

.qa details {
  border-bottom: 1px solid var(--line);
}

.qa summary {
  padding: 16px 34px 16px 0;
  position: relative;
  cursor: pointer;
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -0.012em;
  list-style: none;
  transition: color 160ms var(--ease);
}

/* Both, because Safari still wants the webkit pseudo-element and everything
   else honours `list-style: none`. */
.qa summary::-webkit-details-marker {
  display: none;
}

.qa summary:hover {
  color: var(--brand-rose);
}

/* The marker is drawn rather than typed. A rotating chevron built from two
   borders is one element and scales with the text; a glyph would need a font
   that has it. */
.qa summary::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 22px;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--faint);
  border-bottom: 2px solid var(--faint);
  transform: rotate(45deg);
  transform-origin: center;
  transition: transform 200ms var(--ease), border-color 160ms var(--ease);
}

.qa details[open] summary::after {
  transform: rotate(-135deg);
  border-color: var(--brand-rose);
}

.qa details p {
  margin: 0 0 20px;
  max-width: 640px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.7;
}

.qa details code {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--text);
  background: var(--elev);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 1px 5px;
  word-break: break-word;
}
