/* ---------------------------------------------------------------------------
   The WaresHQ theme, shared by every page on the site.

   Every WaresHQ icon is one letter set in Avenir Black on near-black: W for the
   publisher, X for Crosswalk, L for Log Book, G for Gift List. The site makes
   that literal. A field of particles drawn by /field.js holds the letter of
   whatever you are reading about, and the type is set over it and stays
   perfectly still, because the motion is the argument and the words should not
   compete with it.

   One accent per product. A page declares its own with a theme class on the
   body, and field.js moves --accent as the reader moves between sections.
   --------------------------------------------------------------------------- */
:root {
  --bg: #07070a;
  --ink: #f5f5f7;
  --body: #c4c4cb;
  --muted: #8e8e97;
  --dim: #5a5a63;
  --line: rgba(255,255,255,0.09);
  --accent: #ffb800;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Helvetica, Arial, sans-serif;
  /* Avenir Black is the face the icons are rendered in, so the headings are
     set in it wherever the machine has it. */
  --display: "Avenir Next", Avenir, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  color-scheme: dark;
}
.theme-crosswalk { --accent: #7ada8c; }
.theme-logbook { --accent: #ffb800; }
.theme-gift { --accent: #7dd3fc; }

/* Nothing is hidden unless the script is running to reveal it again. With
   JavaScript off, or broken, or still loading, every page reads top to bottom
   like any other document. These come first so the .ready and .in rules below
   still win at equal specificity. */
.js h1 .clip > span { transform: translateY(108%); }
.js .lede { opacity: 0; transform: translateY(14px); }
.js .scrollcue { opacity: 0; }
.js .rise { opacity: 0; transform: translateY(30px); }
.js .fade { opacity: 0; }

* { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Header links point at sections of the same page. Without this they
     teleport, which reads as a page load rather than as a move, and the letter
     arrives already changed instead of flying there. */
  scroll-behavior: smooth;
  scroll-padding-top: 64px;
}
body {
  margin: 0; background: var(--bg); color: var(--ink); overflow-x: hidden;
  font: 17px/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3 { font-family: var(--display); font-weight: 800; letter-spacing: -0.03em; margin: 0; }
a { color: var(--accent); text-decoration: none; transition: opacity 0.25s, color 0.25s; }
a:hover { opacity: 0.7; }
strong { color: var(--ink); font-weight: 600; }
code { font-family: var(--mono); font-size: 0.88em; color: var(--ink);
  background: rgba(255,255,255,0.06); border-radius: 4px; padding: 1px 5px; overflow-wrap: anywhere; }

/* --- the field ------------------------------------------------------------- */
#signal { position: fixed; inset: 0; z-index: 0; display: block; width: 100%; height: 100%; }
/* Scanlines and a vignette: the field is a signal on a screen, and a clean
   gradient on a big display would band without them. */
.scan { position: fixed; inset: 0; z-index: 1; pointer-events: none; opacity: 0.5;
  background: repeating-linear-gradient(to bottom, rgba(255,255,255,0.022) 0 1px, transparent 1px 3px); }
.vig { position: fixed; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(120vw 100vh at 30% 45%, transparent 25%, rgba(7,7,10,0.72) 85%); }
.page { position: relative; z-index: 2; }

/* --- readout, front page only ---------------------------------------------- */
.hud { display: none; }
.js .hud {
  position: fixed; left: 28px; bottom: 26px; z-index: 12; font-family: var(--mono);
  font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim);
  display: grid; gap: 4px; pointer-events: none;
}
.hud b { font-weight: 500; color: var(--accent); transition: color 0.9s var(--ease); }
@media (max-width: 900px) { .js .hud { display: none; } }

/* --- header ---------------------------------------------------------------- */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; padding: 18px 32px;
  transition: background 0.4s, border-color 0.4s;
  /* The default is the solid state, because a page whose script never runs has
     nothing to add .stuck and would otherwise have its text scroll under a
     transparent bar. With the script alive it starts clear and earns its
     background on the first scroll. */
  background: rgba(7,7,10,0.88); border-bottom: 1px solid var(--line);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}
.js .nav { background: transparent; border-bottom-color: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; }
.js .nav.stuck { background: rgba(7,7,10,0.88); border-bottom-color: var(--line);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); }
.nav .brand { display: flex; align-items: center; gap: 10px; color: var(--ink); font-weight: 800; font-size: 15px; }
.nav .brand img { width: 20px; height: 20px; border-radius: 6px; display: block; }
.nav .set { display: flex; gap: 24px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; }
.nav .set a { color: var(--muted); }
.nav .set a:hover, .nav .set a.here { color: var(--accent); opacity: 1; }
@media (max-width: 760px) {
  .nav { padding: 14px 20px; }
  .nav .set { gap: 14px; font-size: 10px; }
  /* Five monospace links wrap into an unreadable stack beside the wordmark, so
     a phone keeps the one that is not reachable by scrolling. */
  .nav .set a:not(.keep) { display: none; }
}

/* --- the screenful: a section is one screen and the page's measure ---------- */
/* Both live in one rule on purpose. `.col` is a class and `section` is an
   element, so a padding shorthand split across the two hands the whole thing to
   the class and silently drops the vertical rhythm.

   Stacked is the default, because it is what a phone gets. The letter takes the
   top quarter of the screen and the copy begins below it, which is why the top
   padding is in svh: it is measured against the same height the letter is
   placed in. */
.right { margin-left: 0; max-width: none; }
section.col {
  position: relative; max-width: 1240px; margin: 0 auto;
  min-height: 100svh; display: flex; align-items: flex-end;
  padding: 44svh 22px 11vh;
}
section.col.close { padding-bottom: 7vh; }
/* A tablet held upright has room for a big letter, which leaves too much air
   under it at the same proportion a phone uses. */
@media (min-width: 600px) { section.col { padding-top: 38svh; } }

/* Side by side only once there is a column to spare beside the letter. A phone
   on its side is not that: it keeps the stacked layout, where the letter is
   sized from the height and stays out of the way. isWide() in field.js tests
   the same width, and the two have to agree. */
@media (min-width: 1000px) {
  section.col { align-items: center; padding: 120px 40px; }
  section.col.close { padding-bottom: 120px; }
  .right { margin-left: auto; max-width: 560px; }
}

/* --- hero type ------------------------------------------------------------- */
.kicker { font-family: var(--mono); font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--dim); margin: 0 0 26px; }
h1 { font-size: clamp(2.05rem, 4.6vw, 3.9rem); line-height: 1.02; margin-bottom: 26px; }
h1 .clip { display: block; overflow: hidden; }
h1 .clip > span { display: block; transition: transform 1.05s var(--ease); }
.ready h1 .clip > span { transform: none; }
.ready h1 .clip:nth-child(2) > span { transition-delay: 0.08s; }
.ready h1 .clip:nth-child(3) > span { transition-delay: 0.16s; }
.lede { color: var(--muted); font-size: 1.14rem; max-width: 52ch; margin: 0 0 32px;
  transition: opacity 1s 0.45s, transform 1s 0.45s var(--ease); }
.ready .lede { opacity: 1; transform: none; }
.scrollcue { display: none; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--dim); align-items: center; gap: 10px; transition: opacity 1s 1.1s; }
.js .scrollcue { display: flex; }
.ready .scrollcue { opacity: 1; }
.scrollcue i { display: block; width: 34px; height: 1px; background: var(--accent); transform-origin: left;
  animation: pull 2.4s var(--ease) infinite; }
@keyframes pull { 0%, 100% { transform: scaleX(0.3); opacity: 0.4; } 50% { transform: scaleX(1); opacity: 1; } }

/* The product's own line above a heading: a rule, then its name and platforms. */
.glyphtag {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--pa, var(--accent)); display: flex; align-items: center; gap: 12px; margin: 0 0 18px;
  flex-wrap: wrap;
}
.glyphtag::before { content: ""; width: 26px; height: 1px; background: currentColor; display: block; flex: none; }

/* A product's mark beside its name, in the hero of its own page. */
.mark { display: flex; align-items: center; gap: 18px; margin-bottom: 26px; }
.mark img, .mark svg { width: 66px; height: 66px; border-radius: 15px; display: block; flex: none;
  box-shadow: 0 18px 44px rgba(0,0,0,0.6); }
.mark .name { font-family: var(--display); font-weight: 800; font-size: 1.5rem; letter-spacing: -0.02em; line-height: 1.1; }
.mark .sub { color: var(--muted); font-size: 14.5px; }

/* --- a ground for type that has nothing else behind it --------------------- */
/* On a phone the letter sits above the copy, but scrolling slides the copy up
   through it, and type with nothing behind it shows the field changing letters
   straight through the words. A card already puts a blurred ground between the
   two, which is why the product sections never had this problem. The blocks
   that are bare type, the two heroes, the closing note, the constraints intro
   and the 404, get the same ground here. A wide screen does not need it: there
   the letter is beside the column rather than underneath it. */
/* Above the breakpoint .ground draws nothing, so a block using it for spacing
   keeps whatever margins its contents already had. */
@media (max-width: 999px) {
  .ground {
    background: rgba(9, 9, 12, 0.86);
    backdrop-filter: blur(12px) saturate(130%);
    -webkit-backdrop-filter: blur(12px) saturate(130%);
    border-radius: 20px;
    padding: 26px 22px 24px;
    margin: 0 -6px;
  }
  /* The ground is the panel, so anything inside one stops being its own: two
     surfaces with a gap between them would just show the field through the
     gap, which is the thing this is here to stop. */
  .ground > .card { background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; border: 0; padding: 0; }
  .ground > .rules { background: transparent; border: 0; border-radius: 0; gap: 0; }
  .ground > .rules > div { background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; padding: 22px 0 0; }
  .ground > .rules > div + div { border-top: 1px solid var(--line); padding-top: 22px; margin-top: 22px; }
  .ground > .rules > div:first-child { padding-top: 4px; }
}

/* --- cards ----------------------------------------------------------------- */
.card {
  border: 1px solid var(--line); border-radius: 18px; padding: 34px 34px 30px;
  background: rgba(10,10,13,0.62); backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  transition: border-color 0.5s, transform 0.6s var(--ease), opacity 0.6s;
}
.card:hover { border-color: color-mix(in srgb, var(--pa, var(--accent)) 42%, transparent); }
.card .top { display: flex; align-items: center; gap: 16px; margin-bottom: 18px; }
.card img.ic, .card svg.ic { width: 60px; height: 60px; border-radius: 14px; display: block; flex: none;
  box-shadow: 0 16px 40px rgba(0,0,0,0.6); }
.card h2 { font-size: 1.6rem; }
.card h3 { font-size: 1.1rem; margin-bottom: 8px; }
.card .where { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--dim); margin-top: 4px; }
.card p { color: var(--body); margin: 0 0 18px; }
.card p:last-child { margin-bottom: 0; }
.card ul { list-style: none; margin: 0 0 20px; padding: 0; border-top: 1px solid var(--line); }
.card li { padding: 10px 0; border-bottom: 1px solid var(--line); display: flex; gap: 14px;
  font-size: 14.5px; color: var(--muted); }
.card li b { font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--dim); width: 96px; flex: none; font-weight: 500; padding-top: 2px; }
.card li em { font-style: normal; color: var(--pa, var(--accent)); }
.card .go { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--pa, var(--accent)); }
.card.soon { filter: saturate(0.5) brightness(0.84); }
/* The one highlighted phrase on a page, used for the widget's own words. */
.glow { color: var(--accent); }
/* A label in its own column costs the value beside it half the screen, so on a
   narrow one the label sits above its value and the sentence gets the width. */
@media (max-width: 560px) {
  .card { padding: 26px 24px 24px; }
  .card li { flex-direction: column; gap: 3px; }
  .card li b { width: auto; padding-top: 0; }
}

/* A plain grid of statements, used for feature lists on the product pages. */
.grid { display: grid; gap: 14px; margin: 0 0 28px; }
.doc .card { padding: 26px 28px; }
.doc .card h3 { margin-top: 0; }
/* .rules inside a document: its heading rule only sets a bottom margin, so the
   26px top .doc h3 carries has to go, and the mono label is a flex item that
   must not be squeezed by a long heading beside it. */
.doc .rules { margin: 0 0 28px; }
/* Section 02 alternates a .rules block with the screenshot of what it just
   described, so a block that follows a figure needs the air that a figure's
   own bottom margin does not give it. */
.doc .shot + .rules { margin-top: 36px; }
.doc .rules h3 { margin-top: 0; flex-wrap: wrap; }
.doc .rules h3 span { flex: none; }
@media (min-width: 720px) { .grid.two { grid-template-columns: 1fr 1fr; } }

/* --- long form: everything below a product page's hero --------------------- */
/* The letter has faded by here, so the reading column centres itself rather
   than staying out to one side where the field used to be. */
.doc { position: relative; max-width: 760px; margin: 0 auto; padding: 0 22px 12vh; }
.doc section { padding: 6vh 0 0; }
.doc h2 { font-size: clamp(1.45rem, 2.6vw, 1.95rem); line-height: 1.1; margin: 0 0 8px; }
.doc h3 { font-size: 1.08rem; margin: 26px 0 6px; }
.doc p { color: var(--body); margin: 0 0 16px; }
.doc p.meta, .doc p.note { color: var(--muted); font-size: 15px; }
.doc ul { margin: 0 0 18px; padding-left: 20px; color: var(--body); }
.doc li { margin-bottom: 9px; }
.doc li::marker { color: var(--dim); }
.doc .lead { font-size: 1.1rem; color: var(--muted); margin-bottom: 26px; max-width: 58ch; }
/* The rule above a heading carries the section number, the way the front page
   numbers its sections. */
.doc .head { display: flex; align-items: baseline; gap: 16px; border-top: 1px solid var(--line);
  padding-top: 22px; margin-bottom: 18px; }
.doc .head .no { font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em; color: var(--accent); flex: none; }
/* A document that is not selling anything, such as a privacy policy, carries
   the theme without the particle field: the reader came for the text, the page
   should not make them scroll past a title screen to reach it, and a long read
   should not be running a canvas. The product's own icon carries the letter
   instead. */
.doc .title { padding: 128px 0 6px; }
.doc .title .mark { margin-bottom: 22px; }
.doc h1 { font-size: clamp(1.75rem, 3.4vw, 2.5rem); line-height: 1.06; margin-bottom: 10px; }
@media (min-width: 1000px) { .doc .title { padding-top: 152px; } }

table { border-collapse: collapse; width: 100%; margin: 0 0 20px; font-size: 15px; }
th, td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { color: var(--dim); font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; font-weight: 500; }
td { color: var(--body); }
@media (max-width: 560px) { th, td { padding: 10px 8px; } }

/* --- buttons --------------------------------------------------------------- */
.acts { display: flex; flex-wrap: wrap; gap: 12px; margin: 0 0 22px; }
.btn {
  display: inline-block; border: 1px solid var(--line); color: var(--ink); padding: 13px 24px;
  border-radius: 999px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.14em;
  text-transform: uppercase; background: rgba(255,255,255,0.03);
  transition: background 0.35s, color 0.35s, border-color 0.35s;
}
.btn:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
.btn.prominent { border-color: var(--accent); color: var(--accent); }
.btn.prominent:hover { background: var(--accent); color: #07070a; }
.btn .tag { color: var(--dim); letter-spacing: 0.1em; }
.btn:hover .tag { color: inherit; }

/* --- the screenshot, the one thing allowed out of the column --------------- */
/* A terminal full of text is unreadable at reading width, so above 900px the
   figure centres itself on the window and grows, and its caption stays in the
   column. The radius is a percentage of each side so the window's corners keep
   their shape at every width it is drawn at. */
.shot { margin: 0 0 10px; }
.shot a { display: block; }
.shot a:hover { opacity: 1; }
.shot a:hover img { filter: brightness(1.06); }
.shot img { display: block; width: 100%; height: auto; border-radius: 1.66% / 2.49%;
  border: 1px solid var(--line); }
.shot figcaption { color: var(--muted); font-size: 15px; margin-top: 14px; }
@media (min-width: 900px) {
  .shot { width: min(1100px, calc(100vw - 60px)); margin-left: 50%; transform: translateX(-50%); }
  .shot figcaption { max-width: 716px; margin: 14px auto 0; padding: 0 22px; }
}


/* --- reveal and decode ----------------------------------------------------- */
.rise { transition: opacity 0.85s var(--ease), transform 0.85s var(--ease); }
.rise.in { opacity: 1; transform: none; }
/* For anything positioned by a transform of its own, such as the screenshot
   that breaks out of the reading column. */
.fade { transition: opacity 0.9s var(--ease); }
.fade.in { opacity: 1; }
.scramble { font-variant-ligatures: none; }

/* --- a row of statements, used for the three constraints ------------------- */
.rules { display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 18px; overflow: hidden; }
.rules > div { background: rgba(9,9,12,0.78); padding: 26px 28px; backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
.rules h3 { font-size: 1.05rem; margin-bottom: 8px; display: flex; align-items: baseline; gap: 12px; }
.rules h3 span { font-family: var(--mono); font-size: 11px; color: var(--accent); letter-spacing: 0.1em; }
.rules p { color: var(--muted); font-size: 14.5px; margin: 0; }

.close h2 { font-size: clamp(1.7rem, 3.6vw, 2.7rem); line-height: 1.08; margin-bottom: 20px; max-width: 20ch; }
.close p { color: var(--muted); margin-bottom: 28px; }

/* --- footer ---------------------------------------------------------------- */
/* The rule above a footer is a separator, so it needs air above it as well as
   below. Without the margin it butts straight against whatever ends the page,
   which on the front page and the 404 is a button. */
footer { border-top: 1px solid var(--line); margin-top: 52px; padding: 28px 0 56px;
  font-family: var(--mono); font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim);
  display: flex; flex-wrap: wrap; gap: 10px 24px; }
footer a { color: var(--dim); }
footer a:hover { color: var(--accent); opacity: 1; }
footer .sp { flex: 1; }
.doc footer { margin-top: 8vh; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  h1 .clip > span, .lede, .scrollcue, .rise { transition: none !important; transform: none !important; opacity: 1 !important; }
  .scrollcue i { animation: none; }
  * { transition-duration: 0.01ms !important; }
}
