/* G DALY LABS — Qallaline
   ------------------------------------------------------------------
   A tile world. Everything sits on a whole-tile grid with visible grout,
   one manganese ink draws every line on the site, and colour is glaze:
   earned, never spread. The grid is admitted rather than hidden — the
   headline crosses its seams the way a painted tile panel does.

   Two rules the layout never breaks:
     1. A breakpoint changes the NUMBER of tiles, never their size. The
        module is the same physical square on a phone as on a desktop.
     2. State reads as form as well as colour — a cut corner, a flooded
        glaze, a hairline — so the page survives greyscale.
   ------------------------------------------------------------------ */

:root {
  /* The module. Fixed on purpose: see rule 1. */
  --tile: 76px;
  --grout: 1px;

  /* Lime plaster, and the one ink. */
  --plaster: #EDEDE7;
  --plaster-deep: #E1E1D9;
  --ink: #1A1714;
  --ink-soft: #55504A;

  /* Glazes off the kiln. */
  --cobalt: #12406E;
  --verdigris: #186A63;
  --ochre: #C98A1B;
  --oxblood: #5A2A3C;

  /* Locastic's own blue, not one of ours, because the tile belongs to them.
     Sampled from locastic.com by painted area — it covers 150,000 square
     pixels of their home page against 3,200 for the next blue, and it is the
     logo and the button. Unaltered: the tile takes ink rather than plaster,
     because plaster on it measures 2.55 and ink measures 5.95. Darkening
     somebody's brand colour to suit our text would be the wrong fix. */
  --locastic: #0099FF;

  --measure: 34rem;
  --bay: 1216px;   /* 16 modules exactly, so both edges land on grout */

  color-scheme: light;
  /* A phone held outdoors in Sousse daylight. Light, and high contrast. */
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--plaster);
  color: var(--ink);
  font-family: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  font-variation-settings: 'wdth' 100;
  font-size: 1.0625rem;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

/* The browser's own surfaces belong to the design too. */
::selection { background: var(--ochre); color: var(--ink); }
:focus-visible { outline: 3px solid var(--cobalt); outline-offset: 3px; }

img { max-width: 100%; height: auto; display: block; }
strong { font-weight: 700; }

.skip {
  position: absolute; left: -9999px; top: 0;
  background: var(--ink); color: var(--plaster);
  padding: .75rem 1rem; z-index: 40; font-weight: 600;
}
.skip:focus { left: 0; }

/* Said to a screen reader, shown to nobody. */
.vh {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── The grout field ────────────────────────────────────────────────
   Hairlines at tile pitch, drawn in the one ink at low opacity. This is
   the ground everything else sits on. */
.field,
.panel {
  background-image:
    repeating-linear-gradient(to right,  rgba(26,23,20,.11) 0 var(--grout), transparent var(--grout) var(--tile)),
    repeating-linear-gradient(to bottom, rgba(26,23,20,.11) 0 var(--grout), transparent var(--grout) var(--tile));
  /* The seam has to land on the text edge, or crossing it reads as an
     accident rather than the point. This resolves to the container's own
     left edge at every width. */
  background-position-x: max(20px, calc(50% - (var(--bay) / 2) + 20px));
  background-position-y: 0;
}

/* ── Bar ──────────────────────────────────────────────────────────── */
.bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem;
  max-width: var(--bay); margin: 0 auto;
  padding: 1.5rem 20px;
}
.bar-mark img { width: 104px; }
.bar-nav { display: flex; gap: 1.25rem; }
.bar-nav a {
  color: var(--ink); text-decoration: none;
  font-size: .8125rem; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  padding-bottom: 2px; border-bottom: 2px solid transparent;
}
.bar-nav a:hover { border-bottom-color: var(--ink); }

/* Every section stays reachable on a phone. This used to hide "What we
   build" below 33rem to stop the bar wrapping, which is not a fix: it is a
   section the visitor can no longer get to. The type and the gaps tighten
   instead, and the mark gives up a few pixels. */
@media (max-width: 33rem) {
  .bar { padding-inline: 16px; gap: .75rem; }
  .bar-mark img { width: 82px; }
  .bar-nav { gap: .7rem; }
  /* nowrap, or the longest label folds and the bar grows a line. */
  .bar-nav a { font-size: .6875rem; letter-spacing: .04em; white-space: nowrap; }
}

/* The narrowest phones still in use. One more step down rather than hiding
   anything. */
@media (max-width: 22.5rem) {
  .bar-mark img { width: 72px; }
  .bar-nav { gap: .55rem; }
  .bar-nav a { font-size: .625rem; letter-spacing: .02em; }
}

/* ── Field (hero) ─────────────────────────────────────────────────── */
.field { padding-block: clamp(2rem, 7vw, 5.5rem) clamp(3rem, 9vw, 7rem); position: relative; }

/* ── The tile under the hand ───────────────────────────────────────
   A single module lights under the pointer and snaps to the grout, because
   in a tiled wall the unit that catches the light is a tile, never a blob.
   It lags slightly, so it reads as glaze catching up rather than a cursor
   with a shadow.

   One element for the whole page, placed in document coordinates, so it
   travels across section boundaries instead of being handed off between six
   copies of itself.

   Pointer devices only: there is no hover on a phone, and a touch that
   leaves a lit square behind it looks like a fault. It sits behind
   everything and takes no clicks. */
.hand {
  position: absolute; top: 0; left: 0; z-index: 0;
  pointer-events: none;
  width: var(--tile); height: var(--tile);
  background: var(--cobalt);
  opacity: 0;
  transition: transform .3s cubic-bezier(.2,.8,.25,1),
              opacity .35s ease,
              background-color .3s ease;
}
.hand.is-lit { opacity: .14; }
.hand.on-ink { background: var(--plaster); }
.hand.on-ink.is-lit { opacity: .07; }

/* Content sits above the lit tile in every tiled section. */
.field-inner, .panel-inner { position: relative; z-index: 1; }

.field-inner {
  max-width: var(--bay); margin: 0 auto; padding-inline: 20px;
  /* On a phone the tile panel goes last. Ornament must not push the one
     action on the screen below the fold. */
  display: flex; flex-direction: column;
}
/* On a phone the panel goes last and sits against the right edge of the
   column, so the left side stays a clean stack of text and the tiles read
   as a corner of wall rather than a block interrupting the reading line.
   Above 64rem the grid takes over and places it beside the headline. */
.tilework { order: 5; align-self: flex-end; }

@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  .hand { display: none; }
}

/* ── Tilework ──────────────────────────────────────────────────────
   Nine modules, four of them fired. On a wide screen the panel sits beside
   the headline; on a phone it drops under the text, because a tile wall
   with no room is just a stripe. */
.tilework {
  display: grid;
  grid-template-columns: repeat(3, var(--tile));
  grid-auto-rows: var(--tile);
  gap: var(--grout);
  margin-top: clamp(2rem, 5vw, 3rem);
  width: max-content;
}
.t { display: block; background: transparent; }

/* ── A tile you can touch ──────────────────────────────────────────
   The pointer light is a desktop pleasure and touch had none. Press a tile
   and it fires: the glaze floods up from the bottom and settles. Any tile,
   plain or already coloured, so the whole panel is live under a thumb.

   touch-action stays auto — the page must still scroll normally through
   the panel; this only listens, it never swallows the gesture. */
.t { transition: transform .18s ease; }
.t.is-struck { animation: strike .7s cubic-bezier(.2,.8,.25,1); }

@keyframes strike {
  0%   { transform: scaleY(.82); }
  40%  { transform: scaleY(1.06); }
  100% { transform: scaleY(1); }
}
/* The flash sits behind, so a plain tile shows colour for a moment and a
   glazed one deepens. */
.t { position: relative; overflow: hidden; }
.t::after {
  content: ''; position: absolute; inset: 0;
  background: var(--strike, var(--cobalt));
  transform: scaleY(0); transform-origin: bottom;
  opacity: .9;
}
.t.is-struck::after { animation: strike-fill .7s cubic-bezier(.2,.8,.25,1); }
@keyframes strike-fill {
  0%   { transform: scaleY(0); }
  38%  { transform: scaleY(1); }
  100% { transform: scaleY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .t.is-struck, .t.is-struck::after { animation: none; }
}
.t-cobalt    { background: var(--cobalt); }
.t-verdigris { background: var(--verdigris); }
.t-ochre     { background: var(--ochre); }
.t-oxblood   { background: var(--oxblood); }
.t-ink       { background: var(--ink); }
/* Fired in sequence whenever the panel comes into view, not once at load:
   a wall being glazed is worth seeing again on the way back up.

   Armed only by script, and only below the fold, so the resting state in
   the stylesheet is the finished one. No script, no observer, reduced
   motion, a failed observer — the tiles are simply there. */
.tilework.is-armed .t { transform: scaleY(0); transform-origin: bottom; }
.tilework.is-armed.is-playing .t {
  animation: glaze-fill .5s cubic-bezier(.2,.8,.25,1) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}

@media (min-width: 64rem) {
  .field-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) max-content;
    grid-template-areas: 'lede tiles' 'stand tiles' 'foot tiles';
    align-items: start;
    column-gap: clamp(2rem, 5vw, 4rem);
  }
  .lede  { grid-area: lede; }
  .stand { grid-area: stand; }
  .field-foot { grid-area: foot; }
  .tilework { grid-area: tiles; align-self: center; margin-top: 0; }
}

/* The headline crosses tile seams instead of sitting inside one. Each
   line is its own block so the crossing is deliberate, not accidental. */
.lede {
  margin: 0;
  font-size: clamp(2.6rem, 9.2vw, 6.4rem);
  line-height: .93;
  letter-spacing: -0.035em;
  font-weight: 700;
  font-variation-settings: 'wdth' 112;
  /* No balancing here: each line is its own block, so the breaks are already
     decided in the markup. Balance only re-splits phrases that were whole. */
}
.lede span { display: block; }

/* The headline is not revealed, uncovered or wiped in. Two attempts at it
   both ended with the H1 hidden behind something: a clip-path cut the
   descenders off the box, and an overlay froze mid-cover in a background
   tab, where animations do not advance. The most important text on the
   page is painted by the stylesheet and stays painted. The tile wall beside
   it carries the entrance. */

.lede-b { color: var(--cobalt); }
.lede-c { font-variation-settings: 'wdth' 92; font-weight: 500; }
.lede-c em { font-style: normal; color: var(--cobalt); font-weight: 700; font-variation-settings: 'wdth' 108; }

.stand {
  margin: clamp(1.5rem, 3vw, 2.25rem) 0 0;
  max-width: var(--measure);
  font-size: 1.0625rem;
  color: var(--ink-soft);
}

.field-foot {
  margin-top: clamp(2rem, 5vw, 3rem);
  display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem 2rem;
}

.jump {
  display: inline-flex; align-items: center; gap: .5rem;
  color: var(--ink); text-decoration: none;
  font-size: .875rem; font-weight: 600;
  border-bottom: 2px solid var(--ink); padding-bottom: 3px;
}
.jump svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }

/* ── Glaze: colour floods a tile the way it floods in the kiln ────── */
.act {
  position: relative; isolation: isolate;
  display: inline-flex; flex-direction: column; gap: .15rem;
  padding: 1.05rem 1.6rem;
  text-decoration: none;
  border: var(--grout) solid var(--ink);
  /* Two modules tall, and never less than three wide. */
  min-height: calc(var(--tile) * 2);
  min-width: calc(var(--tile) * 3);
  justify-content: center;
}
.act-k { font-weight: 700; font-size: 1.0625rem; letter-spacing: -0.01em; }
.act-v { font-size: .8125rem; opacity: .8; font-variant-numeric: tabular-nums; }

.glaze { color: var(--plaster); background: var(--ink); }
.glaze::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  transform: scaleY(1); transform-origin: bottom;
  transition: transform .42s cubic-bezier(.2,.8,.25,1);
}
.glaze-oxblood::before   { background: var(--oxblood); }
.glaze-cobalt::before    { background: var(--cobalt); }
.glaze-verdigris::before { background: var(--verdigris); }
.glaze-ochre::before     { background: var(--ochre); }
a.glaze:hover::before, a.glaze:focus-visible::before { transform: scaleY(0); }

/* The cut corner: state carried by form, so this still reads with no
   colour at all. */
a.glaze:hover, a.glaze:focus-visible {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%);
}

/* ── The one moment ────────────────────────────────────────────────
   Glaze filling the tiles as the panel arrives, the way it runs in a kiln,
   and again every time it arrives.

   The safety property is the whole design here: the stylesheet paints these
   tiles coloured, and script only ever borrows the colour in order to hand
   it back. Nothing a visitor can already see is taken away — arming skips
   anything on screen — and a failsafe un-arms everything if the observer
   never runs at all. */
.facts.is-armed li::before { transform: scaleY(0); }
.facts.is-armed.is-playing li::before {
  animation: glaze-fill .55s cubic-bezier(.2,.8,.25,1) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}

/* ── Setting a tile ────────────────────────────────────────────────
   One grammar for the whole page, two expressions of it. Tiles glaze: the
   colour floods up from the bottom. Everything else sets: it arrives from
   just below and settles, the way a piece is pressed into the bed.

   Armed only by script, so the stylesheet's resting state is the finished
   one. No script, a failed observer, reduced motion — every block is simply
   there, in place, at full opacity. */
.reveal.is-armed .r-item { opacity: 0; transform: translateY(18px); }
.reveal.is-armed.is-playing .r-item {
  animation: set-in .62s cubic-bezier(.2,.8,.25,1) both;
  animation-delay: calc(var(--i, 0) * 85ms);
}

/* Slower on a phone, and further apart.
   A block crosses the trigger while it is still low on the screen, and by
   the time a thumb has carried it up to where somebody is looking, a
   half-second animation has already finished. Desktop scrolls in bigger
   jumps and does not have the problem. */
@media (max-width: 48rem) {
  .tilework.is-armed.is-playing .t {
    animation-duration: .8s;
    animation-delay: calc(var(--i, 0) * 105ms);
  }
  .facts.is-armed.is-playing li::before {
    animation-duration: .85s;
    animation-delay: calc(var(--i, 0) * 120ms);
  }
  .reveal.is-armed.is-playing .r-item {
    animation-duration: .8s;
    animation-delay: calc(var(--i, 0) * 115ms);
  }
}

@keyframes set-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

@keyframes glaze-fill {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* ── Panels ───────────────────────────────────────────────────────── */
.panel { padding-block: clamp(3.5rem, 9vw, 7rem); border-top: var(--grout) solid var(--ink); position: relative; }
.panel-inner { max-width: var(--bay); margin: 0 auto; padding-inline: 20px; }

.panel-head { max-width: 46rem; margin-bottom: clamp(2rem, 5vw, 3.25rem); }
.panel-head h2 {
  margin: 0;
  font-size: clamp(1.85rem, 5vw, 3.1rem);
  line-height: 1.02; letter-spacing: -0.03em; font-weight: 700;
  font-variation-settings: 'wdth' 108;
  text-wrap: balance;
}
.panel-head p { margin: 1rem 0 0; max-width: var(--measure); color: var(--ink-soft); }

/* Ink panel: the one dark region, so the page has a floor as well as a
   ground. Same grout, drawn in plaster instead. */
.panel-ink {
  background-color: var(--ink);
  color: var(--plaster);
  background-image:
    repeating-linear-gradient(to right,  rgba(242,237,228,.10) 0 var(--grout), transparent var(--grout) var(--tile)),
    repeating-linear-gradient(to bottom, rgba(242,237,228,.10) 0 var(--grout), transparent var(--grout) var(--tile));
}
.panel-ink .panel-head p { color: rgba(242,237,228,.72); }

/* ── Proof ────────────────────────────────────────────────────────── */
.proof { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: start; }
@media (min-width: 60rem) { .proof { grid-template-columns: minmax(0, 21rem) minmax(0, 1fr); } }

/* The product, drawn. Its own palette, because it is a picture of
   another thing and must not be repainted in ours. */
.screen {
  background: #0C1A12;
  border: var(--grout) solid var(--ink);
  padding: 1.1rem 1rem 1.25rem;
  color: #E8F2E9;
  font-size: .8125rem;
  min-width: 0;
}
.screen-bar { display: flex; align-items: center; margin-bottom: 1.1rem; }
.screen-logo { font-weight: 800; letter-spacing: -.02em; font-size: .95rem; }
.screen-logo i { font-style: normal; font-weight: 500; opacity: .66; margin-left: .3em; letter-spacing: .16em; font-size: .62rem; }
.screen-q { margin: 0; font-weight: 700; font-size: 1rem; }
.screen-s { margin: .15rem 0 1rem; opacity: .6; font-size: .75rem; }
.screen-days { display: flex; gap: .4rem; margin-bottom: 1rem; }
.screen-days span {
  flex: 1; text-align: center; padding: .45rem .2rem;
  border-radius: 10px; background: rgba(232,242,233,.06);
  font-size: .62rem; opacity: .75;
}
.screen-days b { display: block; font-size: .9rem; font-weight: 700; }
.screen-days .is-on { background: #E8F2E9; color: #0C1A12; opacity: 1; }
.screen-card { background: rgba(232,242,233,.07); border-radius: 14px; padding: .75rem .85rem; margin-bottom: .6rem; }
.screen-card.is-quiet { opacity: .55; }
.screen-row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.screen-time { font-weight: 700; font-variant-numeric: tabular-nums; }
.screen-need { font-size: .62rem; font-weight: 700; background: #C6F04B; color: #0C1A12; padding: .2rem .5rem; border-radius: 99px; }
.screen-full { font-size: .62rem; font-weight: 600; opacity: .6; }
.screen-club { margin: .2rem 0 .55rem; font-size: .72rem; opacity: .62; }
.screen-slots { display: flex; gap: .3rem; }
.screen-slots i { width: 100%; height: 5px; border-radius: 99px; background: rgba(232,242,233,.18); }
.screen-slots i.is-taken { background: #C6F04B; }

/* ── The fourth ────────────────────────────────────────────────────
   The whole product in one loop: three places taken, one open, someone
   takes it, the game is full. It is the thing 4TH exists to do, so the
   page shows it rather than describing it.

   Plain keyframes on the document clock. No observer, no timeline, nothing
   that can fail to start — and the resting state, which is what anyone with
   reduced motion sees, is the honest one: a place still open. */
/* The two chips share one cell, so the row does not reserve space for the
   one that is hidden and jump when they swap. The cell is as wide as the
   longer of them, which is the point. */
.screen-swap { display: grid; }
.screen-swap > * { grid-area: 1 / 1; justify-self: end; }

.screen-done {
  font-size: .62rem; font-weight: 700;
  background: rgba(232,242,233,.14); color: #E8F2E9;
  padding: .2rem .5rem; border-radius: 99px;
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .screen-slots i.is-filling {
    background: linear-gradient(#C6F04B, #C6F04B) left / 0% 100% no-repeat,
                rgba(232,242,233,.18);
    animation: fourth-fills 7s cubic-bezier(.2,.8,.25,1) infinite;
  }
  .screen-need { animation: need-goes 7s steps(1, end) infinite; }
  .screen-done { animation: done-comes 7s steps(1, end) infinite; }
}

@keyframes fourth-fills {
  0%, 34%   { background-size: 0% 100%; }
  46%, 88%  { background-size: 100% 100%; }
  100%      { background-size: 0% 100%; }
}
/* Swapped rather than dissolved: a chip that is half one word and half
   another for a second reads as a rendering fault. */
@keyframes need-goes { 0%, 43% { opacity: 1 } 44%, 92% { opacity: 0 } 93%, 100% { opacity: 1 } }
@keyframes done-comes { 0%, 43% { opacity: 0 } 44%, 92% { opacity: 1 } 93%, 100% { opacity: 0 } }

.proof-body { min-width: 0; }
.proof-lead { margin: 0 0 1.75rem; font-size: 1.1875rem; line-height: 1.5; max-width: var(--measure); }

.facts { list-style: none; margin: 0 0 1.75rem; padding: 0; display: grid; gap: var(--grout); }
@media (min-width: 34rem) { .facts { grid-template-columns: 1fr 1fr; } }
.facts li {
  position: relative; isolation: isolate;
  padding: 1rem 1.1rem;
  border: var(--grout) solid var(--ink);
  min-height: calc(var(--tile) * 1.25);
  color: var(--plaster);
}
.facts b { display: block; font-size: .9375rem; letter-spacing: -.01em; }
.facts span { display: block; margin-top: .2rem; font-size: .8125rem; line-height: 1.45; }
/* Ochre is a light glaze: it takes the ink, not the plaster. */
.facts li.is-dark { color: var(--ink); }


.out {
  display: inline-flex; align-items: center; gap: .5rem;
  background: var(--ink); color: var(--plaster);
  text-decoration: none; font-weight: 700;
  padding: .95rem 1.35rem;
  border: var(--grout) solid var(--ink);
}
.out svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.out:hover, .out:focus-visible {
  background: var(--plaster); color: var(--ink);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%);
}

/* ── Partners ─────────────────────────────────────────────────────
   A partner is one tile: the mark, and the one sentence that says what the
   relationship actually is. A mark with nothing attached is a logo wall,
   which is the category default this page refuses. The grid takes more when
   there are more, and one does not look stranded. */
.partners { display: grid; gap: var(--grout); }
@media (min-width: 46rem) { .partners { grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr)); } }

.partner {
  border: var(--grout) solid var(--ink);
  padding: clamp(1.4rem, 3vw, 2rem);
  display: flex; flex-direction: column; gap: 1.1rem;
  min-width: 0;
  /* A partner's tile wears the partner's colour, exactly as they use it. */
  background: var(--locastic);
  color: var(--ink);
}
.partner-link {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .75rem 1rem; text-decoration: none; color: var(--ink);
}
.partner-go {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .8125rem; font-weight: 600; color: rgba(26,23,20,.72);
  border-bottom: 2px solid transparent; padding-bottom: 2px;
}
.partner-go svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.partner-link:hover .partner-go, .partner-link:focus-visible .partner-go {
  color: var(--ink); border-bottom-color: var(--ink);
}
.partner-mark { display: inline-flex; align-items: center; min-height: 40px; }
.partner-mark img { height: 34px; width: auto; }
/* The name set as the mark would be, for as long as there is no mark. */
.partner-name {
  font-size: 1.5rem; font-weight: 700;
  letter-spacing: -.03em;
  font-variation-settings: 'wdth' 108;
}
.partner p { margin: 0; color: rgba(26,23,20,.8); font-size: .9375rem; max-width: var(--measure); }

/* ── The plaster panel ─────────────────────────────────────────────
   An inscription set into the tiled wall: a slab of lime plaster
   interrupting the dark field, carrying the one sentence that says what the
   studio does. Tile halls do exactly this — the field is tile, the statement
   is plaster, and the contrast is the point.

   It sits off the left edge of the content column by one module, so the
   plaster laps over the grout instead of lining up politely inside it. */
.says {
  background: var(--plaster);
  color: var(--ink);
  padding: clamp(1.75rem, 4vw, 3rem);
  max-width: 44rem;
  margin-bottom: clamp(2rem, 5vw, 3rem);
  border: var(--grout) solid var(--ink);
}
/* The lap is whatever room actually exists to the left of the content
   column, capped at one module. A flat -1 module pulled the panel to -56px
   on every viewport between 819px and about 1330px — off the screen edge
   and clipped — because below the bay width there is no margin to lap into.
   This resolves to a full module on a wide screen and to nothing at all on
   a narrow one, with no breakpoint to get wrong, and it never comes closer
   than 16px to the viewport edge.

   The 10px is the scrollbar. 100vw counts it and layout does not, so every
   calculation off 100vw runs about half a scrollbar too generous — which is
   how the panel still landed 9px from the edge after the first fix. */
@media (min-width: 52rem) {
  .says {
    margin-left: calc(-1 * min(var(--tile), max(0px, (100vw - var(--bay)) / 2 - 10px)));
    padding-left: calc(min(var(--tile), max(0px, (100vw - var(--bay)) / 2 - 10px)) + 1rem);
  }
}
.says h2 {
  margin: 0 0 1rem;
  font-size: clamp(1.85rem, 5vw, 3.1rem);
  line-height: 1.02; letter-spacing: -0.03em; font-weight: 700;
  font-variation-settings: 'wdth' 108;
}
/* Set large: this is the claim, not a caption under one. */
.says p {
  margin: 0;
  font-size: clamp(1.0625rem, 2vw, 1.3125rem);
  line-height: 1.5;
  color: var(--ink);
  max-width: 32rem;
}

/* The list reads as an index rather than a row of pills: one per line,
   ruled, numbered by nothing, scanned in a second. */
.kit {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0;
  max-width: 44rem;
}
@media (min-width: 40rem) { .kit { grid-template-columns: 1fr 1fr; column-gap: clamp(1.5rem, 4vw, 3rem); } }
.kit li {
  padding: .85rem 0;
  border-top: var(--grout) solid rgba(242,237,228,.22);
  font-size: .9375rem; font-weight: 600;
  letter-spacing: .01em;
  color: rgba(242,237,228,.92);
  display: flex; align-items: center; gap: .7rem;
}
/* A fired tile in front of each, the module at its smallest. */
.kit li::before {
  content: ''; flex: none;
  width: 10px; height: 10px;
  background: var(--ochre);
}
.kit li:nth-child(2)::before { background: var(--cobalt); }
.kit li:nth-child(3)::before { background: var(--verdigris); }
.kit li:nth-child(4)::before { background: var(--ochre); }
.kit li:nth-child(5)::before { background: var(--cobalt); }

/* ── How a project runs ──────────────────────────────────────────
   A sequence, so it is set as one: a single ruled column read top to
   bottom, numbered down the side. It was three cards side by side, which
   says "three unrelated features" rather than "first this, then that" —
   and boxes invite the kind of writing that fills them. */
.run { list-style: none; counter-reset: step; margin: 0; padding: 0; max-width: 46rem; }
.run li {
  counter-increment: step;
  display: grid;
  grid-template-columns: 2.75rem minmax(0, 1fr);
  gap: 0 1.25rem;
  padding: 1.4rem 0;
  border-top: var(--grout) solid rgba(26,23,20,.22);
}
.run li:last-child { border-bottom: var(--grout) solid rgba(26,23,20,.22); }
.run li::before {
  content: counter(step, decimal-leading-zero);
  grid-row: 1 / span 2;
  font-size: .8125rem; font-weight: 700; letter-spacing: .1em;
  color: var(--verdigris);
  font-variant-numeric: tabular-nums;
  padding-top: .2rem;
}
.run h3 { margin: 0 0 .3rem; font-size: 1.0625rem; letter-spacing: -.015em; font-weight: 700; }
.run p { margin: 0; font-size: .9375rem; color: var(--ink-soft); }

/* ── Close ────────────────────────────────────────────────────────── */
.panel-close { background-color: var(--plaster-deep); }
.close-h {
  margin: 0;
  font-size: clamp(2rem, 6.5vw, 4rem);
  line-height: 1; letter-spacing: -.035em; font-weight: 700;
  font-variation-settings: 'wdth' 110;
  text-wrap: balance;
}
.close-p { margin: 1.25rem 0 0; max-width: var(--measure); color: var(--ink-soft); }
.close-acts { margin-top: clamp(1.75rem, 4vw, 2.5rem); display: flex; flex-wrap: wrap; gap: var(--grout); }
.act-wide { min-width: 16rem; }
.act-plain { color: var(--ink); background: transparent; }
.act-plain:hover, .act-plain:focus-visible {
  background: var(--ink); color: var(--plaster);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%);
}
.close-where { margin: 1.75rem 0 0; font-size: .875rem; color: var(--ink-soft); }

/* ── Foot ─────────────────────────────────────────────────────────── */
.foot {
  border-top: var(--grout) solid var(--ink);
  padding: 2rem 20px;
  max-width: var(--bay); margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 1rem;
}
.foot-mark { width: 84px; opacity: .75; }
.foot p { margin: 0; font-size: .8125rem; color: var(--ink-soft); }

/* Rule 1, enforced: narrow screens drop tile COUNT, never tile size.
   The one exception is a phone too narrow to hold two modules plus its
   gutters, where the module steps down a single notch rather than
   letting the page scroll sideways. */
@media (max-width: 26rem) {
  :root { --tile: 64px; }
}

/* The tile panel takes three modules out of the line, so the display drops a
   step to keep "We build software" whole. A headline that breaks mid-phrase
   because an ornament moved in is the ornament winning.

   This lives at the end of the file on purpose: the same selector earlier in
   the cascade loses to the base rule below it, which is exactly what happened
   the first time. */
@media (min-width: 64rem) {
  .lede { font-size: clamp(2.6rem, 5.6vw, 4.9rem); }
}
