/* ShotWork site styles.
 *
 * One stylesheet, plain CSS, no preprocessor. Read top to bottom:
 * tokens, then base elements, then the page furniture, then the
 * components each homepage section uses.
 *
 * FONTS: system stack for now. CLAUDE.md requires any real webfont to be
 * self-hosted with a commercial licence -- that decision is not made yet,
 * so this uses whatever the visitor's OS already has.
 */

/* --- Tokens -------------------------------------------------------
 * Every colour and measurement the site uses is named here. Changing
 * the accent is a one-line edit.
 *
 * These are the brand palette, matching the app's splash screen and the
 * SVGs in static/brand/.
 *
 * One departure: the brand's muted text is #5E6875, which lands at
 * 3.15:1 on this background and fails AA for body text. Every secondary
 * paragraph on the site uses it, so it is lifted to #78828F, which keeps
 * the same character and passes.
 */
:root {
  --bg:      #161818;   /* the brand background */
  --deep:    #0F1111;   /* the darker ground: panels, raised blocks */
  --panel:   #0F1111;
  --line:    #2A2F2F;   /* the hex cell stroke, reused as the site's rules */
  --text:    #94A0B2;   /* slate */
  --muted:   #78828F;   /* lifted from the brand's #5E6875, which fails AA */
  --accent:  #3DF26C;   /* dark grounds only */
  --accent-ink: #06240F; /* text drawn on top of the accent */
  --accent-on-light: #0B8F3E; /* the green for light grounds */

  --wrap:    68rem;     /* maximum content width */
  --radius:  6px;
  --head-height: 60px;

  --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;
}

/* --- Base ---------------------------------------------------------- */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  position: relative;   /* the honeycomb layer anchors to the page */
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin: 0 0 0.6em;
  font-weight: 600;
  text-wrap: balance;
}

h1 { font-size: clamp(2.1rem, 5vw, 3.2rem); }
h2 { font-size: clamp(1.6rem, 3.2vw, 2.2rem); }
h3 { font-size: 1.25rem; }

p { margin: 0 0 1em; }

a { color: var(--text); }

.muted { color: var(--muted); }
.small { font-size: 0.88rem; }

/* A short paragraph under a heading. Kept narrow so lines stay readable. */
.lede {
  font-size: clamp(1.05rem, 2vw, 1.2rem);
  color: var(--muted);
  max-width: 56ch;
}

/* Horizontal padding + max width. Wraps the content of every section. */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* Present for screen readers, invisible on screen. Used for the
 * included/not-included words in the comparison table, so the meaning
 * does not rest on a tick mark alone. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Keyboard users can jump straight past the nav. */
.skip { position: absolute; left: -9999px; }

.skip:focus {
  left: 1rem;
  top: 1rem;
  background: var(--accent);
  color: var(--accent-ink);
  padding: 0.5rem 1rem;
  z-index: 10;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* --- Buttons ------------------------------------------------------- */

.btn {
  display: inline-block;
  padding: 0.7rem 1.4rem;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 1rem;
  font-family: inherit;
  text-decoration: none;
  text-align: center;
  border: 1px solid var(--accent);
  cursor: pointer;
}

.btn:hover { filter: brightness(1.08); }

.btn-ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--line);
}

.btn-ghost:hover { border-color: var(--muted); filter: none; }

.btn-small { padding: 0.4rem 0.9rem; font-size: 0.9rem; }

.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
  filter: none;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin: 2rem 0 1rem;
}

/* The hero shows one primary or the other by width, never both: a phone
 * cannot install a desktop app, so it copies the link instead. */
.narrow-only { display: none; }

/* --- Pre-launch banner --------------------------------------------
 * A thin strip above the header while config.PRELAUNCH is True. It
 * scrolls away with the page; the header below it is what stays put.
 */
.prelaunch-banner {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  backdrop-filter: blur(10px);
  color: var(--text);
  font-size: 0.9rem;
  padding: 0.55rem 0;
  text-align: center;
}

/* --- The honeycomb ---------------------------------------------------
 * A page-level layer, anchored to the very top of the document so it
 * starts above the banner and the header rather than below them. It runs
 * about three screens down and fades out as it goes.
 *
 * Two masks are needed, horizontal and vertical. Compositing two masks on
 * one element is patchy across browsers, so they sit on nested elements
 * instead, one each: the wrapper fades it out downward, the layer inside
 * fades it in from the left.
 *
 * The left 45% stays clear, which is what lets the header and the splash
 * wordmark sit on solid colour, as the brand rules require.
 */
.page-hex-wrap {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2600px;
  z-index: -1;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg,
      #000 0 900px,
      rgba(0,0,0,0.989) 900.00px 1003.92px,
      rgba(0,0,0,0.957) 1003.92px 1107.84px,
      rgba(0,0,0,0.908) 1107.84px 1211.76px,
      rgba(0,0,0,0.844) 1211.76px 1315.68px,
      rgba(0,0,0,0.768) 1315.68px 1419.60px,
      rgba(0,0,0,0.684) 1419.60px 1523.52px,
      rgba(0,0,0,0.593) 1523.52px 1627.44px,
      rgba(0,0,0,0.500) 1627.44px 1731.36px,
      rgba(0,0,0,0.407) 1731.36px 1835.28px,
      rgba(0,0,0,0.316) 1835.28px 1939.20px,
      rgba(0,0,0,0.232) 1939.20px 2043.12px,
      rgba(0,0,0,0.156) 2043.12px 2147.04px,
      rgba(0,0,0,0.092) 2147.04px 2250.96px,
      rgba(0,0,0,0.043) 2250.96px 2354.88px,
      rgba(0,0,0,0.011) 2354.88px 2458.80px,
      rgba(0,0,0,0.000) 2458.80px 2562.72px,
      transparent 2562.72px);
  mask-image: linear-gradient(180deg,
      #000 0 900px,
      rgba(0,0,0,0.989) 900.00px 1003.92px,
      rgba(0,0,0,0.957) 1003.92px 1107.84px,
      rgba(0,0,0,0.908) 1107.84px 1211.76px,
      rgba(0,0,0,0.844) 1211.76px 1315.68px,
      rgba(0,0,0,0.768) 1315.68px 1419.60px,
      rgba(0,0,0,0.684) 1419.60px 1523.52px,
      rgba(0,0,0,0.593) 1523.52px 1627.44px,
      rgba(0,0,0,0.500) 1627.44px 1731.36px,
      rgba(0,0,0,0.407) 1731.36px 1835.28px,
      rgba(0,0,0,0.316) 1835.28px 1939.20px,
      rgba(0,0,0,0.232) 1939.20px 2043.12px,
      rgba(0,0,0,0.156) 2043.12px 2147.04px,
      rgba(0,0,0,0.092) 2147.04px 2250.96px,
      rgba(0,0,0,0.043) 2250.96px 2354.88px,
      rgba(0,0,0,0.011) 2354.88px 2458.80px,
      rgba(0,0,0,0.000) 2458.80px 2562.72px,
      transparent 2562.72px);
}

.page-hex {
  position: absolute;
  inset: 0;
  background-image: url("brand/hex-tile.svg");
  /* Exactly the SVG's own size. Asking for 51.9615 to match the true row
   * pitch of 30 * sqrt(3) meant the browser rescaled every tile, and
   * resampling a 1.5px stroke is a good way to produce seams. The 0.0015px
   * of drift it corrected is invisible; the resampling was not. */
  background-size: 90px 51.96px;
  background-repeat: repeat;
  background-position: 0 0;
  /* No horizontal mask here. The fade lives on .page-hex-fade, one layer
   * only: two masks would quantise twice. The mobile rule further down
   * gives this element a VERTICAL mask instead, which is a different axis
   * and so does not stack with it. */
}

/* The fade, and the banding.
 *
 * Three attempts failed because I was solving the wrong problem. The limit
 * is not the alpha channel's 256 levels. It is the colour being faded: the
 * hex stroke sits 23 levels above the background and the cell fill sits 3.
 * Fade a 3-level colour over any distance and you get 3 bands, however
 * precisely the alpha is computed.
 *
 * So the fade does not try to be smooth. It steps, one step per column of
 * cells across and one per two rows down, which is the honeycomb's own
 * rhythm. The steps are hard-edged and land exactly on cell boundaries, so
 * they read as cells dimming rather than as a gradient breaking up. Spacing
 * is eased rather than linear, so the change is slower at both ends.
 */
.page-hex-fade {
  position: absolute;
  inset: 0;
  -webkit-mask-image: linear-gradient(90deg,
      transparent 0 45%,
      rgba(0,0,0,0.055) calc(45% + 0px) calc(45% + 90px),
      rgba(0,0,0,0.198) calc(45% + 90px) calc(45% + 180px),
      rgba(0,0,0,0.394) calc(45% + 180px) calc(45% + 270px),
      rgba(0,0,0,0.606) calc(45% + 270px) calc(45% + 360px),
      rgba(0,0,0,0.802) calc(45% + 360px) calc(45% + 450px),
      rgba(0,0,0,0.945) calc(45% + 450px) calc(45% + 540px),
      rgba(0,0,0,1.000) calc(45% + 540px) calc(45% + 630px),
      #000 calc(45% + 630px));
  mask-image: linear-gradient(90deg,
      transparent 0 45%,
      rgba(0,0,0,0.055) calc(45% + 0px) calc(45% + 90px),
      rgba(0,0,0,0.198) calc(45% + 90px) calc(45% + 180px),
      rgba(0,0,0,0.394) calc(45% + 180px) calc(45% + 270px),
      rgba(0,0,0,0.606) calc(45% + 270px) calc(45% + 360px),
      rgba(0,0,0,0.802) calc(45% + 360px) calc(45% + 450px),
      rgba(0,0,0,0.945) calc(45% + 450px) calc(45% + 540px),
      rgba(0,0,0,1.000) calc(45% + 540px) calc(45% + 630px),
      #000 calc(45% + 630px));
}

.page-hex-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27200%27%20height%3D%27200%27%3E%3Cfilter%20id%3D%27n%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%271.1%27%20numOctaves%3D%274%27%20stitchTiles%3D%27stitch%27%2F%3E%3C%2Ffilter%3E%3Crect%20width%3D%27200%27%20height%3D%27200%27%20filter%3D%27url%28%23n%29%27%2F%3E%3C%2Fsvg%3E");
  background-size: 200px 200px;
  opacity: 0.05;
  pointer-events: none;
}

/* The one lit cell. Placed by site.js on a real grid centre, and kept
 * clear of the wordmark. Hidden until then, so it never flashes up in the
 * wrong place. */
.page-lit {
  position: absolute;
  width: 56px;
  height: 49.04px;
  background-image: url("brand/hex-lit-cell.svg");
  background-size: 56px 49.04px;
  background-repeat: no-repeat;
  pointer-events: none;
  visibility: hidden;
}

/* --- Header and footer --------------------------------------------- */

.site-head {
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  /* See-through, so the honeycomb is visible from the very top of the
   * page. Safe for the brand rule about the wordmark never sitting on the
   * pattern: the pattern's own mask keeps the left 45% clear, and the
   * wordmark is on the left. */
  background: color-mix(in srgb, var(--bg) 72%, transparent);
  backdrop-filter: blur(10px);
  z-index: 5;
}

.head-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--head-height);
}

/* The wordmark is 868 x 100, so a fixed height makes it very wide. It is
 * sized against the screen instead. */
.wordmark {
  display: inline-flex;
  align-items: center;
  flex: none;
  text-decoration: none;
}

.wordmark img { display: block; }

/* The wordmark stays at every width: on a phone it is the only place the
 * name appears, so swapping it for the hexagon would leave the product
 * unnamed. Sized as a share of the screen with a ceiling, so it shrinks
 * rather than overflowing. */
.wordmark .full { width: min(42vw, 190px); height: auto; }
.wordmark .mark { display: none; }

.site-head nav {
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.site-head nav a {
  text-decoration: none;
  font-size: 0.95rem;
  color: var(--muted);
}

.site-head nav a:hover { color: var(--text); }
.site-head nav a.btn { color: var(--accent-ink); }

.site-foot {
  border-top: 1px solid var(--line);
  margin-top: 4rem;
  padding: 2.5rem 0 2rem;
}

.foot-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2.5rem;
  align-items: baseline;
  justify-content: space-between;
}

.foot-links {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.foot-links a {
  color: var(--muted);
  text-decoration: none;
  font-size: 0.92rem;
}

.foot-links a:hover { color: var(--text); }

/* --- Sections ------------------------------------------------------ */

section { padding: 4.5rem 0; }

.section-head { max-width: 56ch; margin-bottom: 2.5rem; }

/* The sticky header would otherwise cover the top of a jumped-to
 * section. */
[id] { scroll-margin-top: calc(var(--head-height) + 1rem); }

.hero { padding: 5rem 0 2rem; }
.hero h1 { max-width: 18ch; }
.hero .lede { max-width: 54ch; }

.hero-media { padding: 1rem 0 2rem; }

/* A single sentence sitting between the hero and the first real
 * section. Deliberately not a row of chips. */
.presets-line {
  padding: 0 0 3.5rem;
  color: var(--muted);
}

.presets-line p { margin: 0; }

/* --- Captures ------------------------------------------------------
 * A still or loop from the app. The border stops a dark screenshot from
 * bleeding into the dark page.
 */
.capture-live {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--panel);
  margin: 0;
}

.capture-live img,
.capture-live video { display: block; width: 100%; height: auto; }

/* --- How it works --------------------------------------------------
 * Three numbered steps. The numbering is real information here: this is
 * a sequence, not a list of features.
 */
.steps-flow {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4rem;
}

.step {
  display: grid;
  grid-template-columns: 1fr 1.25fr;
  gap: 3rem;
  align-items: center;
}

/* Every other step puts the capture on the left, so the page does not
 * read as three identical rows. */
.step:nth-child(even) .step-text { order: 2; }

.step-text { max-width: 48ch; }
.step-text p { margin: 0; color: var(--muted); }

.step-text h3 {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  margin-bottom: 0.5em;
}

.step-number {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--line));
  border-radius: 50%;
  width: 1.9rem;
  height: 1.9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}

/* --- Tables --------------------------------------------------------
 * The comparison and pricing tables are real tables: they are tabular
 * data, and a screen reader should be able to say which column a tick
 * belongs to.
 */
.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
}

caption { text-align: left; }

th, td {
  text-align: left;
  padding: 0.85rem 1.2rem;
  border-bottom: 1px solid var(--line);
  font-weight: 400;
}

thead th {
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  white-space: nowrap;
}

tbody tr:last-child th,
tbody tr:last-child td { border-bottom: 0; }

tbody th { color: var(--text); }

/* The two verdict columns are narrow and centred; the feature name
 * column takes whatever is left. */
.tiers td { text-align: center; width: 8rem; }
.tiers thead th:not(:first-child) { text-align: center; }

.yes { color: var(--accent); font-size: 1.1rem; }
.no { color: var(--muted); }

/* One price, so no price table: the figure, what it entitles, the Buy
 * button, then the plain answer to "why not a subscription". */
.price-block { max-width: 52ch; margin-bottom: 3.5rem; }

.price-figure {
  font-size: 2.8rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1;
  margin-bottom: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.price-block .btn-row { margin: 1.5rem 0 0; }

.why-perpetual {
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
  max-width: 52ch;
}

.why-perpetual p { color: var(--muted); margin: 0; }

.founding {
  margin: 1.5rem 0 0;
  color: var(--accent);
}

/* --- What a license adds -------------------------------------------
 * Four items, each a heading and a sentence. Not cards: they are one
 * list of related things, not four separate objects.
 */
.adds {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 2rem 2.5rem;
}

.adds h3 { margin-bottom: 0.35em; }
.adds p { margin: 0; color: var(--muted); }

/* A short list of plain facts. Ticked, because each one is something you
 * get, not a bullet in an argument. */
.facts {
  list-style: none;
  margin: 2.5rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  max-width: 60ch;
  color: var(--muted);
}

.facts li { padding-left: 1.5rem; position: relative; }

.facts li::before {
  content: "¹3";
  position: absolute;
  left: 0;
  color: var(--accent);
}

/* --- The two promises ----------------------------------------------
 * Deliberately set apart: they are commitments, not features.
 */
.promises {
  margin-top: 2.5rem;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
  border-radius: var(--radius);
  background: var(--panel);
  padding: 1.75rem 2rem;
  max-width: 60ch;
}

.promises h3 { margin-bottom: 0.7em; }

.promises ul {
  margin: 0;
  padding-left: 1.2rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.promises li { color: var(--text); }

/* A column of running text, kept to a readable measure. */
.prose { max-width: 62ch; }
.prose p:last-child { margin-bottom: 0; }

/* --- Downloads ----------------------------------------------------- */

.downloads {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.5rem;
  margin-bottom: 1.5rem;
}

.download {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--panel);
  padding: 1.5rem;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1rem;
}

.download p { margin: 0; }

/* --- Founder line -------------------------------------------------- */

.founder {
  padding: 3rem 0 1rem;
  color: var(--muted);
}

.founder p { margin: 0; max-width: 52ch; }

/* --- Narrow screens ------------------------------------------------
 * The hero swaps its primary button here: below this width the visitor
 * is assumed to be on a phone, where a desktop download is no use.
 */
@media (max-width: 700px) {
  .wide-only { display: none; }
  .narrow-only { display: inline-block; }

  section { padding: 3rem 0; }
  .hero { padding: 2.5rem 0 1.5rem; }
  .presets-line { padding-bottom: 2.5rem; }

  .step { grid-template-columns: 1fr; gap: 1.5rem; }
  /* Stacked, the capture always follows its own text. */
  .step:nth-child(even) .step-text { order: 0; }

  .steps-flow { gap: 3rem; }

  th, td { padding: 0.7rem 0.9rem; }
  .tiers td { width: 5rem; }
}

/* --- Reduced motion ------------------------------------------------
 * Autoplay cannot be stopped from CSS, so site.js pauses the loops. This
 * only removes the smooth-scroll, which is the other moving thing.
 */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
