/* Mortise — mortise.so
   A shop drawing sheet: hairline frame, crop marks, hidden lines dashed,
   chain centerlines, and a title block ruled across the foot. The palette,
   accent and joinery vocabulary are inherited from the desktop app; the
   density is not. */

@font-face {
  font-family: "Archivo";
  src: url("/fonts/archivo-latin-var.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  color-scheme: dark;

  --bench: #100f0d;
  --bench-lift: #16130f;

  --scribe-faint: rgba(233, 213, 179, 0.05);
  --scribe: rgba(233, 213, 179, 0.1);
  --scribe-strong: rgba(233, 213, 179, 0.17);

  --ink: #eae6de;
  --ink-2: #a9a396;
  --ink-label: #8c8578;
  --ink-3: #736d61;

  --timber: #d9a05b;
  --timber-dim: #8a6a42;

  /* One face, everywhere. A drawing sheet letters its annotations in the same
     single-stroke gothic it letters everything else — and a system mono would
     hand the sheet's voice to whatever the visitor's OS happens to ship. */
  --sans: "Archivo", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;

  --measure: 1500px;
  --inset: clamp(14px, 2.2vw, 26px);
  --gutter: clamp(22px, 4.6vw, 64px);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100svh;
  background: var(--bench);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Raking light from the upper left, and a matte grain so the near-black reads
   as a surface rather than an absence. Both are fixed to the viewport. */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
body::before {
  background:
    radial-gradient(120% 90% at 8% -10%, rgba(217, 160, 91, 0.055), transparent 62%),
    radial-gradient(90% 70% at 100% 108%, rgba(233, 213, 179, 0.028), transparent 60%);
}
body::after {
  opacity: 0.035;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

::selection { background: rgba(217, 160, 91, 0.26); }

a { color: inherit; text-decoration: none; }

:focus-visible {
  outline: 1px solid var(--timber);
  outline-offset: 6px;
}

/* ---------- the sheet ---------- */

.sheet {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  padding:
    calc(var(--inset) + env(safe-area-inset-top) + var(--gutter) * 0.62)
    calc(var(--inset) + env(safe-area-inset-right) + var(--gutter))
    calc(var(--inset) + env(safe-area-inset-bottom) + var(--gutter) * 0.62)
    calc(var(--inset) + env(safe-area-inset-left) + var(--gutter));
}

/* The frame: one hairline, held off every edge. */
.sheet::before {
  content: "";
  position: absolute;
  inset: var(--inset);
  border: 1px solid var(--scribe);
  pointer-events: none;
}

.marks { position: absolute; inset: var(--inset); pointer-events: none; }
.tick {
  position: absolute;
  width: clamp(16px, 2.4vw, 26px);
  height: clamp(16px, 2.4vw, 26px);
  border: 0 solid var(--scribe-strong);
}
.tick.tl { top: -1px; left: -1px; border-top-width: 1px; border-left-width: 1px; }
.tick.tr { top: -1px; right: -1px; border-top-width: 1px; border-right-width: 1px; }
.tick.bl { bottom: -1px; left: -1px; border-bottom-width: 1px; border-left-width: 1px; }
.tick.br { bottom: -1px; right: -1px; border-bottom-width: 1px; border-right-width: 1px; }

/* ---------- head ---------- */

/* The frame hugs the window; the content it holds does not stretch with it. */
.head, .plate, .block {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
}

.head { flex: none; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
}
.mark { flex: none; fill: var(--timber); }
.wordmark {
  font-size: 0.83rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
}

/* ---------- plate ---------- */

.plate {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.74fr);
  align-items: center;
  gap: clamp(40px, 6vw, 104px);
  padding: clamp(48px, 9vh, 104px) 0;
}

.copy { min-width: 0; }

h1 {
  font-size: clamp(2.7rem, 5.6vw, 5.8rem);
  font-weight: 600;
  line-height: 0.99;
  letter-spacing: -0.035em;
  text-wrap: balance;
  max-width: 15ch;
}

.lede {
  margin-top: clamp(20px, 2.4vw, 30px);
  max-width: 44ch;
  font-size: clamp(1.02rem, 0.95rem + 0.35vw, 1.24rem);
  line-height: 1.55;
  letter-spacing: -0.006em;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ---------- the drawing ---------- */

.drawing {
  display: flex;
  justify-content: center;
  /* The context lines arrive from off-sheet. Without this they would stop
     dead in open field, which reads as a clipped render rather than a crop. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 19%);
  mask-image: linear-gradient(to right, transparent 0, #000 19%);
}
/* Sized off the viewport's height, not its width: the sheet keeps its
   proportion instead of stranding the drawing on a wide display. */
.drawing svg {
  width: auto;
  max-width: 100%;
  height: min(54svh, 512px);
  aspect-ratio: 480 / 560;
}
.drawing g, .detail g { fill: none; }
.drawing path, .detail path { vector-effect: non-scaling-stroke; }

/* Line-weight hierarchy, as a drawing carries it: object lines heaviest,
   then hidden detail, then the construction lines behind everything. */
.d-poche { stroke: none; fill: url(#poche); }
.poche-line { stroke: rgba(233, 213, 179, 0.11); }
.d-post path { stroke: rgba(233, 213, 179, 0.36); stroke-width: 1.4; fill: none; }
.d-rail .body { stroke: rgba(233, 213, 179, 0.34); stroke-width: 1.4; fill: #17140f; }
.d-hidden path { stroke: rgba(233, 213, 179, 0.28); stroke-width: 1; stroke-dasharray: 5 4; }
.d-context path { stroke: rgba(233, 213, 179, 0.22); stroke-width: 1; }
.d-center path { stroke: rgba(233, 213, 179, 0.17); stroke-width: 0.8; stroke-dasharray: 20 5 3 5; }
.d-rail .seat { stroke: var(--timber); stroke-width: 1.4; opacity: 0.9; }

/* ---------- title block ---------- */

.block {
  flex: none;
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: clamp(26px, 5vw, 64px);
  align-items: end;
  padding-top: clamp(16px, 1.8vw, 22px);
  border-top: 1px solid var(--scribe);
}

.cell { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cell-id { justify-self: end; text-align: right; }

.key {
  font-size: 0.7rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-label);
}
.val {
  font-size: 1.02rem;
  letter-spacing: -0.008em;
  color: var(--ink);
  text-wrap: nowrap;
}
.val.dim {
  color: var(--ink-2);
  font-size: 0.9rem;
  letter-spacing: 0.06em;
}

/* ---------- 404: the same sheet, one member short of fitting ----------
   Scoped entirely to -nf / .detail / .back so the homepage sheet
   above is untouched. The drawing reuses the .d-* line-weight hierarchy. */

/* The status annotation sits opposite the wordmark, and drops to its own line
   rather than pushing past the frame when text is enlarged. */
.head-nf {
  display: flex;
  align-items: center;
  gap: 8px 20px;
  flex-wrap: wrap;
}
.head-nf .key { min-width: 0; margin-left: auto; }

.plate-nf {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(26px, 4.6vh, 46px);
  padding: clamp(30px, 6vh, 80px) 0;
}

/* The drawing reads at a fixed size and centred — this sheet has one detail
   on it, not a plate and a claim side by side. */
.detail {
  width: 100%;
  max-width: clamp(300px, 62vw, 520px);
}
.detail svg { display: block; width: 100%; height: auto; }

/* Where the rail should have sat: lighter and finer than a hidden line, so
   the two dashed registers stay distinguishable. */
.d-ghost path {
  stroke: rgba(233, 213, 179, 0.2);
  stroke-width: 1;
  stroke-dasharray: 3 4;
}
/* The accent goes to the offset, not to a seat — there is no seat. */
.d-dim path { stroke: var(--timber); stroke-width: 1.2; opacity: 0.85; }

.plate-nf .copy { text-align: center; }

.plate-nf h1 {
  font-size: clamp(2.15rem, 4.6vw, 3.7rem);
  max-width: 16ch;
  margin-inline: auto;
}
.plate-nf .lede { max-width: 34ch; margin-inline: auto; }

.act { margin-top: clamp(26px, 3.4vw, 38px); }

.back {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-size: 1.02rem;
  letter-spacing: -0.008em;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--scribe-strong);
  transition: border-color 0.35s var(--ease), color 0.35s var(--ease);
}
.back .arrow { color: var(--timber); }
.back:hover { border-bottom-color: var(--timber); }

/* The same title block the homepage rules across its foot, carrying what this
   sheet is instead of what the product is. */
.block-nf { grid-template-columns: auto minmax(0, 1fr); }
.block-nf .cell-id {
  justify-self: end;
  text-align: right;
  border-top: 0;
  padding-top: 0;
}

/* This drawing scales down rather than cropping in, so the section keeps the
   alpha it was drawn at instead of the narrow-viewport crop's lower one. */
.detail .poche-line { stroke: rgba(233, 213, 179, 0.11); }

@media (max-width: 560px) {
  .head-nf .key { text-align: right; }
  .detail { max-width: none; }
  .plate-nf { gap: clamp(22px, 3.6vh, 34px); }
}

/* Landscape phones: the sheet is too short to stack, so the detail and the
   copy sit side by side — the same move the homepage makes at this height. */
@media (max-height: 560px) and (min-width: 620px) {
  .plate-nf {
    display: grid;
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1fr);
    align-items: center;
    gap: clamp(28px, 4vw, 48px);
    padding: 14px 0;
  }
  .plate-nf .copy { text-align: left; }
  .plate-nf h1 { font-size: clamp(1.7rem, 3.2vw, 2.3rem); margin-inline: 0; }
  .plate-nf .lede { margin-top: 12px; margin-inline: 0; }
  .detail { max-width: min(42vw, 360px); }
  .act { margin-top: 20px; }
}

/* ---------- narrow ---------- */

@media (max-width: 880px) {
  .plate {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(30px, 5vh, 52px);
    padding: clamp(30px, 5vh, 64px) 0;
    align-content: center;
  }
  h1 { max-width: 13ch; }
  .lede { max-width: 36ch; }

  /* The drawing becomes a detail crop, ruled edge to edge: bigger in scale
     than a shrunken miniature, and shorter than the whole sheet view. The
     crop edges dissolve rather than cutting the members open. */
  /* The crop magnifies the pattern's pitch, so the section has to sit back
     down behind the linework it belongs behind. */
  .poche-line { stroke: rgba(233, 213, 179, 0.055); }

  .drawing svg {
    width: 100%;
    max-width: none;
    aspect-ratio: auto;
    height: clamp(152px, 22svh, 200px);
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 10%, #000 90%, transparent 100%);
  }
}

@media (max-width: 560px) {
  /* One field per row, in the order the sheet reads them. The status is the
     close: it holds a full line to itself at the default text size, but may
     wrap when text is enlarged rather than forcing horizontal scrolling. */
  .block {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    align-items: start;
  }
  .cell-status .val { text-wrap: wrap; }
  .cell-id {
    justify-self: stretch;
    text-align: left;
    padding-top: 13px;
    border-top: 1px solid var(--scribe-faint);
  }
  .val { font-size: 1.06rem; }
  .val.dim { font-size: 0.86rem; }
}

/* Landscape phones: side by side again, or the sheet stacks past the fold. */
@media (max-height: 560px) and (min-width: 620px) {
  .plate {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.58fr);
    padding: 18px 0;
    gap: 36px;
  }
  h1 { font-size: clamp(2.2rem, 4.4vw, 3rem); }
  .lede { margin-top: 16px; }
  .drawing svg { width: 100%; max-width: none; aspect-ratio: auto; height: min(190px, 44svh); }
  .block { padding-top: 14px; }
}

/* ---------- motion: one authored moment — the joint seating ---------- */

@media (prefers-reduced-motion: no-preference) {
  /* The copy rises once; the drawing has its own moment below; the title
     block simply arrives. Nothing else moves. */
  .copy { animation: rise 0.9s var(--ease) 0.12s both; }
  @keyframes rise {
    from { opacity: 0; transform: translateY(9px); }
  }

  .block { animation: arrive 1.1s var(--ease) 0.75s both; }
  @keyframes arrive { from { opacity: 0; } }

  .d-context path {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    animation: scribe 2.1s var(--ease) 0.5s both;
  }
  @keyframes scribe { to { stroke-dashoffset: 0; } }

  .d-rail { animation: seat 1.25s var(--ease) both; }
  .r1 { animation-delay: 0.86s; }
  .r2 { animation-delay: 1.04s; }
  .r3 { animation-delay: 1.22s; }
  @keyframes seat {
    from { transform: translateX(34px); opacity: 0; }
  }

  .d-rail .seat { animation: contact 1.5s var(--ease) 1.5s both; }
  @keyframes contact { from { opacity: 0; } }

  /* 404: the rail runs in and stops short. Then the ghost and the dimension
     arrive to explain why — the same seat gesture, arrested. */
  .nf-rail { animation-delay: 0.45s; }
  .d-ghost { animation: arrive 1s var(--ease) 1.25s both; }
  .d-dim { animation: arrive 1s var(--ease) 1.5s both; }
  .back .arrow { transition: transform 0.35s var(--ease); }
  .back:hover .arrow { transform: translateX(4px); }
}
