@property --accent {
  syntax: '<color>';
  inherits: true;
  initial-value: #10b981;
}

/*
 * obelisk.place
 *
 * The marketing site. Deliberately separate from the app in app.obelisk.place:
 * no build step, no framework, no dependencies. A page that describes a product
 * should not be able to break the product.
 *
 * Colours are the app's own, not an approximation of them. #10b981 is the
 * default accent every new experience starts with, and #09090b is the shell the
 * player renders on.
 */

:root {
  --bg:        #09090b;
  --bg-raised: #18181b;
  --line:      #27272a;
  --text:      #fafafa;
  --muted:     #a1a1aa;
  --faint:     #71717a;
  --accent:    #10b981;
  /* Long enough to read as a fade rather than a flicker, short enough that
     the section you are looking at is the colour you are seeing. */
  transition: --accent 700ms cubic-bezier(0.4, 0, 0.2, 1);
  --accent-dim:#34d399;
  --radius:    18px;
  --measure:   34rem;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 17px/1.65 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; }

.wrap { width: 100%; max-width: 62rem; margin: 0 auto; padding: 0 1.5rem; }

/* ── Hero map ───────────────────────────────────────────────────────────── */
/* Sits behind the opening text and fades out before the fold. Masked rather
   than overlaid with a gradient, so the page background shows through instead
   of a dark panel sitting on top of a dark panel. */
.hero-map {
  position: absolute; top: 0; left: 0; right: 0;
  height: 660px; overflow: hidden; z-index: 0;
  pointer-events: none;
  opacity: 0.6;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 38%, rgba(0,0,0,0.35) 72%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 38%, rgba(0,0,0,0.35) 72%, transparent 100%);
}
.hero-map-grid {
  width: 2304px;
  /* Anchored centre so the same part of the city stays in frame as the window
     changes width, and the edges are what get cropped. */
  /* The nudge puts Manhattan on the page's centre line rather than the
     grid's, which are not the same thing once the tile range is rounded. */
  position: absolute; left: 50%; top: -30px;
  transform: translateX(calc(-50% + 216px));
}
.hero-map-grid img { display: block; width: 2304px; height: 768px; }

/* Everything after the map has to sit above it. */
header, main, footer { position: relative; z-index: 1; }
.hero { position: relative; z-index: 1; }

/* ── Header ─────────────────────────────────────────────────────────────── */
header {
  position: sticky; top: 0; z-index: 20;
  background: rgba(9, 9, 11, 0.82);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.bar { display: flex; align-items: center; gap: 1.25rem; height: 62px; }
.brand { display: flex; align-items: center; gap: 0.55rem; text-decoration: none; font-weight: 700; letter-spacing: -0.01em; }
.brand svg { display: block; }
nav { margin-left: auto; display: flex; align-items: center; gap: 1.5rem; }
nav a { color: var(--muted); text-decoration: none; font-size: 0.95rem; }
nav a:hover { color: var(--text); }
.cta {
  /* inline-flex, not the default inline. As a flex child in the header this
     button got the full box model and stood 40px tall; sitting inside a
     paragraph in the hero it was inline, where vertical padding paints but
     does not add height, so the same button measured 33px. Same class, two
     heights, depending only on what it happened to be inside. */
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent); color: #09090b;
  padding: 0.5rem 0.95rem; border-radius: 999px;
  font-weight: 700; font-size: 0.9rem; text-decoration: none;
  white-space: nowrap;
}
.cta:hover { background: var(--accent-dim); }

/* Secondary button. Matched to .cta's height by subtracting the border from the
   vertical padding, so the two do not sit at 40px and 38px next to each other. */
.login {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text); font-weight: 600; font-size: 0.9rem;
  padding: calc(0.5rem - 1px) 0.95rem; border-radius: 999px;
  border: 1px solid var(--line); text-decoration: none;
  white-space: nowrap;
}
.login:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
@media (max-width: 30rem) {
  nav { gap: 0.6rem; }
  /* A phone header fits two things. The two it should fit are the way in for
     someone who already has an account and the way in for someone who does
     not. About and Blog are carried by the footer on every page, so losing
     them here costs nothing. */
  nav a.hide-sm, nav a[href="/about"], nav a[href="/blog"] { display: none; }
  .cta   { padding: 0.45rem 0.8rem; font-size: 0.85rem; }
  .login { padding: calc(0.45rem - 1px) 0.8rem; font-size: 0.85rem; }
}

/* ── Type ───────────────────────────────────────────────────────────────── */
h1 {
  font-size: clamp(2.3rem, 7vw, 3.9rem);
  line-height: 1.04; letter-spacing: -0.035em;
  font-weight: 800; margin: 0 0 1.1rem;
  max-width: 18ch;
}
h2 {
  font-size: clamp(1.5rem, 3.6vw, 2.1rem);
  line-height: 1.15; letter-spacing: -0.025em;
  font-weight: 700; margin: 0 0 0.9rem;
}
h3 { font-size: 1.06rem; font-weight: 700; margin: 0 0 0.35rem; letter-spacing: -0.01em; }
p  { margin: 0 0 1.1rem; max-width: var(--measure); }
.lead { font-size: clamp(1.08rem, 2.3vw, 1.28rem); color: var(--muted); line-height: 1.55; }
.muted { color: var(--muted); }
.faint { color: var(--faint); font-size: 0.86rem; }

/* One page that flows, rather than a stack of boxed-off marketing panels.
   The rhythm comes from spacing, not from borders and background swaps. */
/* Vertical only, as longhand.
   These were shorthands, and a shorthand sets all four sides: "padding: 5.5rem 0"
   also declared padding-left and padding-right as 0. Both of these elements
   also carry .wrap, which sets the horizontal padding that keeps text off the
   edge of the screen, and .block came later in the file so it won. On a wide
   screen the 62rem max-width leaves margins either side and it never showed. On
   a phone there is nothing spare, so every heading, paragraph and the demo
   frame sat flush against the edge of the glass. */
.block { padding-top: 5.5rem; padding-bottom: 5.5rem; }
.block + .block { padding-top: 0; }
.hero { padding-top: 6.5rem; padding-bottom: 5rem; }

/* Vertical rhythm is set for a wide screen, where a section fills a fraction
   of the height and generous gaps read as composure. On a phone each section
   is already most of the screen on its own, so the same gaps become dead space
   somebody has to scroll through to find out there is more. Roughly two thirds
   here, which keeps the separation without the wait. */
@media (max-width: 46rem) {
  .block { padding-top: 3.5rem; padding-bottom: 3.5rem; }
  .hero  { padding-top: 4.5rem; padding-bottom: 3rem; }
  footer { padding: 2rem 0 2.5rem; margin-top: 2.5rem; }
}

.eyebrow {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.19em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--accent) 80%, #ffffff);
  margin: 0 0 1rem;
}

/* ── Steps ──────────────────────────────────────────────────────────────── */
.steps { display: grid; gap: 2rem; margin-top: 2.5rem; }
@media (min-width: 46rem) { .steps { grid-template-columns: repeat(3, 1fr); gap: 2.25rem; } }
.step p { margin: 0; color: var(--muted); font-size: 0.97rem; }
.num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 999px;
  border: 1px solid var(--accent); color: var(--accent);
  font-size: 0.78rem; font-weight: 700; margin-bottom: 0.75rem;
}

/* ── Pricing ────────────────────────────────────────────────────────────── */
.cards { display: grid; gap: 1rem; margin-top: 2.5rem; }
@media (min-width: 40rem) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 68rem) { .cards { grid-template-columns: repeat(4, 1fr); } }
.card ul { flex: 1 1 auto; }
.card {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.6rem 1.5rem;
  display: flex; flex-direction: column;
}
.card.featured { border-color: rgba(16, 185, 129, 0.5); }
/* inline-flex, not block. The height is fixed so featured and unfeatured
   cards line up, and with display:block the label sat against the top of
   that fixed box instead of in the middle of it. */
.tag { display: inline-flex; align-items: center; justify-content: center;
       height: 1.42rem; margin-bottom: 0.9rem; }
.tag-space { display: none; }
@media (min-width: 52rem) {
  .tag-space { display: block; height: 1.42rem; margin-bottom: 0.9rem; }
}
.tag {
  align-self: flex-start; width: fit-content; font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); background: rgba(16, 185, 129, 0.12);
  border: 1px solid rgba(16, 185, 129, 0.3);
  padding: 0.2rem 0.5rem; border-radius: 999px;
  line-height: 1;
}
.price { font-size: 2.1rem; font-weight: 800; letter-spacing: -0.03em; margin: 0.3rem 0 0.15rem; }
.per { color: var(--faint); font-size: 0.86rem; margin: 0 0 1.1rem; }
.card ul { list-style: none; padding: 0; margin: 0 0 1.5rem; }
.card li { display: flex; gap: 0.6rem; align-items: flex-start; color: var(--muted); font-size: 0.94rem; padding: 0.3rem 0; }
.card li svg { flex: 0 0 auto; margin-top: 0.35rem; color: var(--accent); }
.card .cta, .card .ghost { margin-top: auto; text-align: center; display: block; }
.ghost {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.5rem 0.95rem; font-weight: 600; font-size: 0.9rem;
  text-decoration: none; color: var(--text);
}
.ghost:hover { border-color: var(--faint); }

/* ── Footer ─────────────────────────────────────────────────────────────── */
footer { border-top: 1px solid var(--line); padding: 2.5rem 0 3.5rem; margin-top: 4rem; }
.footrow { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: center; }
footer a { color: var(--muted); text-decoration: none; font-size: 0.9rem; }
footer a:hover { color: var(--text); }
.footrow .spacer { margin-left: auto; }

/* ── Blog list ──────────────────────────────────────────────────────────── */
/* The page opens on the articles. No heading, no standfirst, because the
   articles say what the blog is better than a sentence about them would. */
.blog-index { padding-top: 4rem; padding-bottom: 5rem; }
@media (max-width: 46rem) { .blog-index { padding-top: 2.5rem; padding-bottom: 3.5rem; } }

/* The masthead sits close to the cards it heads. An earlier version had a
   standfirst under it and a full hero's worth of space below that, which put
   the first article most of a screen away from the thing announcing it. The
   standfirst is back, matching the About page, but the gaps it introduces are
   taken out of the masthead rather than added on top: eyebrow to first card is
   the same distance it was without it. */
.blog-masthead {
  font-size: clamp(2rem, 5vw, 3rem);
  margin: 0 0 0.7rem;
  max-width: none;
}
.blog-standfirst { margin: 0 0 4.5rem; max-width: 46ch; }
@media (max-width: 46rem) {
  .blog-masthead   { margin-bottom: 0.55rem; }
  .blog-standfirst { margin-bottom: 3rem; }
}

/* Off screen, not display:none. Hiding it that way would remove it from the
   accessibility tree too, and a page with no h1 is one a screen reader and a
   crawler both have to guess at. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.posts { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.25rem; }
@media (min-width: 54rem) { .posts { grid-template-columns: repeat(2, 1fr); } }
.posts li {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); overflow: hidden;
  transition: border-color 0.25s ease, transform 0.25s cubic-bezier(0.2, 0.6, 0.3, 1);
}
.posts li:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); transform: translateY(-2px); }
.posts a { text-decoration: none; display: flex; flex-direction: column; height: 100%; }

/* Sized by aspect ratio so a card holds its shape before the image arrives and
   the grid does not jump as thumbnails load. */
.post-thumb {
  display: block; aspect-ratio: 16 / 9;
  background-color: #101012; background-size: cover; background-position: center;
  border-bottom: 1px solid var(--line);
  position: relative;
}
/* A post with no thumbnail yet gets a tint rather than a grey rectangle, so it
   reads as not filled in rather than as a broken image. */
.post-thumb:not([style])::after {
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(circle at 42% 38%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 72%);
}
.post-body { display: block; padding: 1.4rem 1.5rem 1.6rem; }
.post-title {
  display: block; font-size: 1.2rem; font-weight: 700;
  letter-spacing: -0.02em; line-height: 1.25; margin: 0 0 0.5rem; color: var(--text);
}
.posts .muted { display: block; font-size: 0.95rem; line-height: 1.55; }
.posts .eyebrow { display: flex; align-items: center; margin-bottom: 0.6rem; }
.dot-sep { margin: 0 0.45rem; opacity: 0.5; }
.readmore { display: inline-block; margin-top: 1rem; font-size: 0.86rem; font-weight: 700; color: var(--accent); }
.posts a:hover .readmore { text-decoration: underline; }

/* ── Post page ──────────────────────────────────────────────────────────── */
.post { padding-top: 4.5rem; padding-bottom: 4rem; }
.post-back { margin-bottom: 1.75rem; }
.post h1 { font-size: clamp(1.9rem, 4.6vw, 2.9rem); max-width: 22ch; }
.post .lead { margin-bottom: 0; }
.posthead { border-bottom: 1px solid var(--line); padding-bottom: 2rem; margin-bottom: 2.5rem; }
.posthead .eyebrow { display: flex; align-items: center; }
.post-hero {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius); border: 1px solid var(--line);
  margin: 0 0 2.5rem;
}

/* An offer at the end of a post, where somebody who read the whole thing is. */
.post-cta {
  margin-top: 3.5rem; padding: 2rem 1.75rem;
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius); max-width: var(--measure);
}
.post-cta h2 { font-size: 1.25rem; margin: 0 0 0.5rem; }
.post-cta p { color: var(--muted); font-size: 0.95rem; margin-bottom: 1.25rem; }

.post-nav {
  display: grid; gap: 1rem; margin-top: 2.5rem;
  border-top: 1px solid var(--line); padding-top: 2rem;
}
@media (min-width: 46rem) { .post-nav { grid-template-columns: 1fr 1fr; } }
.post-nav a {
  text-decoration: none; color: var(--text); font-weight: 600;
  display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.95rem;
}
.post-nav a:hover { color: var(--accent); }
.post-nav .next { text-align: right; }
.post-nav .faint { font-size: 0.78rem; font-weight: 400; }

/* ── Posts ──────────────────────────────────────────────────────────────── */
.prose { max-width: var(--measure); }
.prose h2 { font-size: 1.35rem; margin: 2.75rem 0 0.8rem; }
.prose p { color: #d4d4d8; }
.prose blockquote {
  margin: 1.8rem 0; padding: 0 0 0 1.1rem;
  border-left: 2px solid var(--accent);
  color: var(--muted); font-style: italic;
}
.prose ul { color: #d4d4d8; padding-left: 1.1rem; margin: 0 0 1.1rem; }
.prose li { margin: 0.35rem 0; }
.backlink { color: var(--muted); text-decoration: none; font-size: 0.9rem; }
.backlink:hover { color: var(--text); }

/* ── Centred blocks ─────────────────────────────────────────────────────── */
/* Applied where a section is about one thing and has nothing beside it, so
   the eyebrow, the heading and the paragraph all sit on the same axis as the
   thing they introduce. Done with a class rather than inline styles, because
   it was three sections and would have been nine attributes. */
.block.centered { text-align: center; }
.block.centered > h2 { max-width: none; margin-left: auto; margin-right: auto; }
/* Direct children only. A .card is a flex column, and auto side margins on a
   flex item shrink it to its content and centre it, so an unrestricted rule
   here quietly re-laid-out every price and caption inside the pricing cards
   while their computed text-align still read "left". */
.block.centered > p  { margin-left: auto; margin-right: auto; }
.block.centered .cards { text-align: left; }

.prose code {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: 5px; padding: 0.1em 0.35em; font-size: 0.9em;
}
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }


/* ── About: a personal piece, set to be read rather than scanned ────────── */
.essay p {
  /* --measure, not a ch value of my own. The site already decided what a line
     of prose should be, and 63ch measured out at 93 characters here, which is
     a third wider than anything is comfortable to read. */
  font-size: 1.09rem; line-height: 1.72; color: #d4d4d8;
  margin: 0 0 1.35rem; max-width: var(--measure);
}
.essay p:last-of-type { margin-bottom: 0; }
/* The one-line paragraph carrying the years of delay. Given room on both
   sides so the pause reads as a pause. */
.essay p.beat { margin: 2.1rem 0 2.1rem; color: var(--text); }
.signature {
  margin-top: 2.4rem; font-weight: 600; color: var(--text);
  font-size: 1.02rem; letter-spacing: -0.005em; line-height: 1.45;
}
.signature span { display: block; font-weight: 400; color: var(--muted); font-size: 0.95rem; }
/* A page of prose, not a page of sections.
   .hero's 5rem bottom padding and .block's 5.5rem top padding are tuned for
   separate sections with their own headings. Stacked under a title that
   introduces the very next sentence, they put 168px between the two, and the
   opening line reads as though it belongs to something else. */
.hero.tight    { padding-bottom: 2rem; }
.block.essay   { padding-top: 0; padding-bottom: 0; }

/* .block.after-essay, not .after-essay. `.block + .block { padding-top: 0 }`
   is two classes and outranks a single one no matter where it sits in the
   file, so the space under the rule silently never applied. */
.block.after-essay {
  margin-top: 3.25rem; padding-top: 2.25rem;
  /* The rule is drawn to the width of the column it divides. As a plain
     border-top it ran the full 59rem of the wrap beneath a 34rem column of
     text, which reads as a stray line rather than a division. */
  border-top: none;
  background-image: linear-gradient(var(--line), var(--line));
  background-repeat: no-repeat;
  background-position: 1.5rem 0;
  background-size: min(var(--measure), calc(100% - 3rem)) 1px;
}
@media (max-width: 46rem) {
  .essay p { font-size: 1.04rem; line-height: 1.68; }
}
