/* ==========================================================================
   MINARANK

   Two rules decide most of this file:
   1. The display face appears above 2rem and nowhere else. Everything at or
      below --fs-h3 is Archivo 700. One face was doing nine jobs.
   2. Body copy is ink. Slate is for captions and metadata only. Grey body
      text is what made the old version read as a wireframe.

   Red marks where ranking happens. Never body text: 4.2:1 on paper, which
   is fine for large type, the wordmark's last letter, and marks.
   ========================================================================== */

/* Cross-document transitions. One at-rule, no JavaScript, no CSP allowance:
   it is a CSS at-rule in a same-origin sheet, and a browser that does not know
   it ignores it, so the degradation is literally nothing.

   OPTED IN, not opted out. The blanket reduced-motion reset further down uses
   *, *::before, *::after, which does NOT match ::view-transition-old(root) or
   ::view-transition-new(root): those live in a separate pseudo tree on the
   root. Declaring this unconditionally would animate for exactly the people
   who asked it not to.

   The header and the WhatsApp button are named because a sticky and a fixed
   element are otherwise part of the root snapshot and cross-fade against
   themselves, which is visible double-header ghosting for the whole
   transition. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  .site-head { view-transition-name: site-head; }
  .wa { view-transition-name: wa; }
}

/* -- shell ---------------------------------------------------------------- */
.wrap { max-width: var(--page-max); margin-inline: auto; padding-inline: var(--margin); }
.grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: var(--gutter); }

.skip { position: absolute; left: var(--margin); top: -100%; z-index: 10;
  background: var(--ink); color: var(--paper); padding: var(--s-3) var(--s-4);
  text-decoration: none; }
.skip:focus { top: 0; }

section { padding-block: var(--pad-section); }

/* The one label style on the site. Three per page, maximum. */
.eyebrow { font-size: var(--fs-label); letter-spacing: var(--track-label);
  text-transform: uppercase; color: var(--slate); font-weight: 600;
  margin-bottom: var(--s-4); }

h2 { font-family: var(--font-display); font-weight: 500; font-size: var(--fs-h2);
  line-height: 1.06; letter-spacing: var(--track-display); }

h3, .faq-q, .ledger h3, .cta {
  font-family: var(--font-body); font-weight: 700; letter-spacing: -0.006em; }

/* balance was on h2 alone, so every other heading on the site still stranded
   its last word on a line of its own. Paragraphs keep pretty: balance is for
   short blocks and browsers stop applying it past a few lines anyway. */
h1, h2, h3, .page-title, .hero-say, .case-name, .af-h, .check p {
  text-wrap: balance; }

/* The header is sticky at 72px. Only #audit and .af-done cleared it, so the
   nav's own /#services link and the footer's #contact both landed their
   heading UNDERNEATH the header. Every fragment target clears it now. */
[id] { scroll-margin-top: calc(72px + var(--s-6)); }

.sec-head { margin-bottom: var(--s-7); }

/* -- header ---------------------------------------------------------------- */
.site-head { position: sticky; top: 0; z-index: 5; background: var(--paper); }
/* The default is the finished state (rule 32): with no script the sticky
   header always has its edge, and the script only takes it away at the top of
   the page where there is nothing to separate it from. It was the other way
   round, so a JS-off visitor scrolled a header with no boundary at all. */
.site-head::after { content: ""; display: block; height: 1px; background: var(--edge);
  transition: opacity 0.3s ease; }
.js .site-head:not(.scrolled)::after { opacity: 0; }
.head-row { display: flex; align-items: center; justify-content: space-between;
  height: 72px; gap: var(--s-5); }
.head-logo { display: block; padding: 4px; margin: -4px; }
.head-nav { display: flex; align-items: center; gap: var(--s-6); }
/* nowrap, because a flex item shrinks before it overflows: with the switcher
   in the row the Albanian CTA hit its min-content width at 720 and broke
   "Nis një projekt" over 2 lines inside a row whose height is fixed at 72px.
   Nothing then reported an overflow, because there was none. It stays one line
   or the measurement is a lie. Below 720 they are display:none anyway. */
.head-nav a { font-size: var(--fs-small); font-weight: 500; text-decoration: none;
  padding-block: 12px; margin-block: -12px; white-space: nowrap; }
/* except in the panel, where the 4 links are a column and may use 2 lines */
.menu-panel a { white-space: normal; }
/* was color: var(--slate), which DIMMED the link on hover: 15.9:1 down to
   5.56:1, so pointing at a nav item made it 3 times quieter. Same idiom as
   .head-cta below, and the colour is not the only signal. */
.head-nav a:hover { text-decoration: underline; text-underline-offset: 4px;
  text-decoration-thickness: 2px; text-decoration-color: var(--red); }
/* The page you are on is marked by a rule under it rather than by weight
   alone. Bold was the only signal and it is nearly invisible at 15px against
   the 500 the others carry: 2 of the 5 nav items are one word long and there
   is nothing to compare the weight against. Ink, not red, so it cannot be
   confused with the red hover on any other item. */
.head-nav a[aria-current="true"] { font-weight: 700;
  box-shadow: inset 0 -2px 0 0 var(--ink); }

/* The CTA is an action, not a fifth destination, and it used to sit in the row
   at the same gap and the same weight as the four links beside it. A hairline
   separates the places you can go from the thing you can do. */
.head-cta { margin-left: var(--s-2); padding-left: var(--s-6);
  border-left: 1px solid var(--edge); font-weight: 700 !important;
  text-decoration: underline !important; text-underline-offset: 4px;
  text-decoration-thickness: 2px; }
.head-cta:hover { text-decoration-color: var(--red) !important; }

/* -- the language switcher, in the header --------------------------------
   It lived only in the footer, so an Italian reader who landed on an English
   page had to scroll the entire document to get out of it. Three static <a>
   pointing at the equivalent page: no script, which is what the CSP and rule
   32 both want, and what the footer copy already was.

   No new vocabulary. The hairline is the CTA's hairline in the same 1px
   --edge, and the language you are reading carries the same 2px ink rule as
   the current nav item, so the row is three declared groups (where you can
   go, what you can read it in, what you can do) rather than eight loose
   words. It sits on the navigation side of the CTA's divider, because a
   switcher is navigation and the CTA stays the last thing in the row.

   Below 720px the row copy goes with the nav links and the copy inside
   .menu-panel takes over: 3 more links in a 72px row that already holds a
   logo, a button and a disclosure is not a phone header, it is a wrapped
   one. Emitted twice for the same reason the nav links are, and by the same
   function.                                                                */
.head-lang { display: flex; align-items: center; gap: var(--s-4);
  font-size: var(--fs-small); font-weight: 500; }
.head-lang a { text-decoration: none; }
.head-lang a:hover { text-decoration: underline; text-underline-offset: 4px;
  text-decoration-thickness: 2px; text-decoration-color: var(--red); }
/* the current language is a span, so it inherits none of .head-nav a's box */
.head-lang [aria-current="page"] { font-weight: 700;
  box-shadow: inset 0 -2px 0 0 var(--ink); }
.head-nav > .head-lang { padding-left: var(--s-6);
  border-left: 1px solid var(--edge); }
.head-nav > .head-lang > * { padding-block: 12px; margin-block: -12px; }
/* in the panel it is a column under a rule, which is the same hairline doing
   the same job turned 90 degrees */
.menu-panel .head-lang { flex-direction: column; align-items: stretch;
  gap: 0; margin-top: var(--s-3); padding-top: var(--s-3);
  border-top: 1px solid var(--edge); }
/* block, so the span is a row like the links are and the whole width of each
   is the target; margin-block: 0 for the same reason .menu-panel a has it */
.menu-panel .head-lang > * { display: block; padding-block: var(--s-3);
  margin-block: 0; }
/* the rule under the current language would run the panel's full width here,
   which reads as a divider rather than a mark. It underlines the word. */
.menu-panel .head-lang [aria-current="page"] { box-shadow: none;
  text-decoration: underline; text-underline-offset: 4px;
  text-decoration-thickness: 2px; }

/* 404.html is hand-authored and carries no header, footer or band, which is
   why gate check 7 exempts it. Its styles were an inline <style> block until
   style-src 'self' was pointed at them. */
/* It has a header and a footer now, so it is a page rather than a splash: the
   88vh centring it used to do would push the band off the bottom. */
.err { max-width: var(--page-max); margin-inline: auto;
  padding-inline: var(--margin); padding-block: var(--pad-hero) var(--pad-major); }
/* The 404's ten results, with nothing on any of them. .serp is reused
   verbatim: it needs a positioned ancestor and nothing else, so this box is
   the entire cost and the hero's own rules cannot regress.

   The min-height is in em and explicit, so the box does not depend on the
   display font having loaded and cannot shift when it does. */
.err-box { position: relative; min-height: 3.4em;
  font-size: clamp(3rem, 10vw, 8rem); }
.err h1 { font-family: var(--font-display); font-weight: 500;
  font-size: 1em; line-height: 1; position: relative; z-index: 1; }
.err h1 span { color: var(--red); }
.err p { color: var(--slate); margin-top: var(--s-5); max-width: 40ch; }
.err a { color: var(--ink); font-weight: 700; }

/* -- the phone menu --------------------------------------------------------
   A <details>, so it needs no script at all: rule 32 is satisfied by the
   element rather than by a rule about it, and with JS off it still opens.

   The 4 links are emitted twice from shell.NAV, once per width. They are NOT
   one list shown two ways: that needs ::details-content with
   content-visibility:visible, which is Chrome 131, Safari 18.4 and Firefox
   139, all 2025. On anything older the non-summary children sit in an
   internal slot no author selector reaches, so the DESKTOP nav becomes a
   closed disclosure and it fails silently. Two lists from one Python list is
   the cheaper mistake.                                                      */
.menu { display: none; position: relative; }
.menu > summary { display: inline-flex; align-items: center; gap: 0.45em;
  cursor: pointer; list-style: none; padding-block: 12px; margin-block: -12px;
  font-size: var(--fs-small); font-weight: 500; }
.menu > summary::-webkit-details-marker { display: none; }
/* the marker is drawn, not a glyph: no font has to carry it and it can turn */
.menu > summary::after { content: ""; width: 9px; height: 9px; flex: none;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 160ms var(--ease-rise); }
.menu[open] > summary::after { transform: translateY(1px) rotate(225deg); }
.menu > summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }
/* Stated, not inherited. A closed <details> hides its non-summary children
   through an internal slot the UA styles, and Chrome still reports a live
   192x126 box for the panel inside it. It is not painted and not hit-tested,
   but "not painted because of a UA slot" is not something to build a header
   on. This says it. */
.menu:not([open]) .menu-panel { display: none; }
.menu-panel { position: absolute; right: 0; top: calc(100% + 18px);
  min-width: 12rem; display: flex; flex-direction: column;
  background: var(--paper); border: 1px solid var(--edge);
  padding: var(--s-3) var(--s-5) var(--s-4); }
/* margin-block: 0 UNDOES .head-nav a's -12px, which is right in a 72px row and
   wrong in a stacked panel: it left each link a 48px box on a 24px pitch, so
   consecutive links overlapped by half and the later one won every hit test.
   Tapping the bottom half of the word "Proof" opened Services, on every phone,
   on all 51 pages. Measured with elementFromPoint at 390px, not by eye. */
.menu-panel a { padding-block: var(--s-3); margin-block: 0; }

/* -- hero ------------------------------------------------------------------ */
.hero { padding-block: var(--pad-hero) var(--pad-major); }
.hero-wrap { position: relative; }
/* the one set of crop marks on the site */
.hero-wrap::before, .hero-wrap::after { content: ""; position: absolute;
  top: -20px; width: 12px; height: 12px; pointer-events: none; }
.hero-wrap::before { left: calc(var(--margin) - 18px);
  border-left: 1px solid var(--rule); border-top: 1px solid var(--rule); }
.hero-wrap::after { right: calc(var(--margin) - 18px);
  border-right: 1px solid var(--rule); border-top: 1px solid var(--rule); }

.hero-title { font-family: var(--font-display); font-weight: 500;
  font-size: var(--fs-hero); line-height: 1; letter-spacing: -0.03em;
  padding-top: 0.22em; }
.wm-box { display: block; position: relative; }

/* ten results, and the wordmark climbs them.
   The hairline was rgba(19,22,28,0.06), which is 1.09:1 on paper: ten lines
   nobody has ever seen. The numerals were rgba(90,96,112,0.5), 2.10:1 at 10px.
   This diagram is the hero's whole argument, so it is drawn in the site's own
   hairline and its own caption colour rather than in 2 numbers typed once. */
.serp { position: absolute; inset: 0; z-index: 0; }
.serp i { position: absolute; left: 0; right: 2.6em; height: 1px;
  font-size: 10px; top: calc(4% + var(--r) * 10.2%);
  background: var(--rule); }
.serp i::after { content: attr(data-n); position: absolute; right: -2em; top: 0;
  transform: translateY(-50%); width: 1.6em; text-align: right;
  font-family: var(--font-body); font-variant-numeric: tabular-nums;
  font-size: 10px; letter-spacing: 0.06em; color: var(--slate); }
/* --r and --i used to be style="" attributes on the elements, which
   style-src 'self' blocks exactly as it blocks an inline <style>. Unset, the
   calc()s below become invalid at computed-value time: the wordmark stops
   climbing and all ten hairlines stack onto one line. The counts are fixed by
   shell.WORDMARK (8) and gen_home's range(10), which is also what the 10.2%
   step already assumes. */
.serp i:nth-child(1) { --r: 0 }  .serp i:nth-child(2) { --r: 1 }
.serp i:nth-child(3) { --r: 2 }  .serp i:nth-child(4) { --r: 3 }
.serp i:nth-child(5) { --r: 4 }  .serp i:nth-child(6) { --r: 5 }
.serp i:nth-child(7) { --r: 6 }  .serp i:nth-child(8) { --r: 7 }
.serp i:nth-child(9) { --r: 8 }  .serp i:nth-child(10) { --r: 9 }

.wm { display: inline-block; white-space: nowrap; position: relative; z-index: 1; }

/* The steady noun against the moving verb. "studio" does NOT climb: the climb
   is the identity and the red k is red because it is last, so a 14-letter
   lockup would put the accent on the 7th character and break rules 2 and 4 at
   once. It sits in the wedge the climb opens under its high right end.

   It lives INSIDE .wm, not beside it, so text-align right lands it under the
   wordmark's own end. As a sibling it right-aligned to the whole page and
   collided with the SERP numerals. */
.wm-studio { display: block; text-align: right; margin-top: -0.14em;
  font-family: var(--font-body); font-weight: 500;
  font-size: clamp(0.85rem, 1.9vw, 1.5rem); letter-spacing: 0.34em;
  text-indent: 0.34em; color: var(--slate); line-height: 1;
  transform: none; }
.wm .l { display: inline-block;
  transform: translateY(calc(var(--i) * -1 * var(--rise-step))); }
.wm .l:nth-child(1) { --i: 0 }  .wm .l:nth-child(2) { --i: 1 }
.wm .l:nth-child(3) { --i: 2 }  .wm .l:nth-child(4) { --i: 3 }
.wm .l:nth-child(5) { --i: 4 }  .wm .l:nth-child(6) { --i: 5 }
.wm .l:nth-child(7) { --i: 6 }  .wm .l:nth-child(8) { --i: 7 }
.wm .k { color: var(--red); margin-left: -0.012em; }

@media (hover: hover) and (pointer: fine) {
  .wm .l { transition: transform 250ms cubic-bezier(0.34, 1.56, 0.64, 1); }
  .wm .l:hover { transform: translateY(calc(var(--i) * -1 * var(--rise-step) - 0.12em)); }
  .wm .l.k:hover { transform: translateY(calc(var(--i) * -1 * var(--rise-step) - 0.2em)); }
}

.hero-say { margin-top: var(--s-8); font-size: clamp(1.375rem, 2.4vw, 2rem);
  line-height: 1.3; max-width: 26ch; letter-spacing: -0.012em; }
.hero-sub { margin-top: var(--s-5); max-width: 54ch; text-wrap: pretty; }

/* availability: a fact that has to be kept true */
.status { display: inline-flex; align-items: baseline; gap: var(--s-3);
  margin-top: var(--s-7); font-size: var(--fs-small); }
.status .dot { width: 7px; height: 7px; background: var(--red);
  border-radius: 50%; transform: translateY(-1px); flex: none; }

/* -- the hero's second row -------------------------------------------------
   The wordmark keeps the full width above: .serp is inset:0 on .wm-box, so its
   ten hairlines and their numerals span the whole container and a column
   BESIDE the h1 lands on top of them at every width. Below it, at none.

   The proportion is .ask-row's 7/5, shared literally rather than restated. The
   comment further down this file says four layout idioms on the homepage and
   no more; this is one of the four, used twice.                             */
.hero-split { margin-top: var(--s-8); display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  column-gap: var(--s-9); align-items: start; }
.hero-split .hero-say { margin-top: 0; }

/* The red rule is .check's rule, the same idiom for the same reason: this is
   the one thing on the page that takes something rather than says something.
   No border, no panel, no shadow. The site has no cards and this is not the
   moment to introduce one. */
.hero-af { border-left: 2px solid var(--red); padding-left: var(--s-5); }
.hero-af .af { margin-top: var(--s-5); }
/* .check p's exact register, not a second one 1.4px away from it. The two
   red-rule blocks on this page are one idiom, and an idiom whose two uses
   disagree about type size is two idioms. */
.af-h { font-size: clamp(1.25rem, 2vw, 1.625rem); line-height: 1.15;
  max-width: 18ch; }
.af-lead { margin-top: var(--s-3); font-size: var(--fs-small);
  color: var(--slate); max-width: 34ch; text-wrap: pretty; }
/* the hero form is 4 stacked fields in a 5fr column, so the two-up pair and
   the 54ch measures the /start/ form uses would both overflow it */
/* Shorter than the /start/ form, at every width. In the 5fr column the form
   was 741px against copy that ends near 500, so it overhung by a third of its
   own height and the hero read bottom-heavy on the right. What gives is the
   space between the controls; the input keeps a 44px thumb target. */
.hero-af .field { margin-top: var(--s-3); }
.hero-af .field label { margin-bottom: 4px; }
.hero-af .field input { padding: 0.58em 0.75em; }
.hero-af .af-say, .hero-af .af-alt, .hero-af .af-fine { max-width: none; }
.hero-af .af-go { margin-top: var(--s-5); }
/* The divider under the button, gone. .af-alt and .af-fine are 2 separate
   keys in 6 modules and /start/'s "fine" is a 4-clause privacy sentence naming
   Web3Forms and a deletion address, so the COPY is never merged. Only the
   boxes are, and only here. */
.hero-af .af-fine { margin-top: var(--s-2); padding-top: 0; border-top: 0; }
.hero-af .af-say { margin-top: var(--s-3); }
.hero-af .af-done { padding-left: 0; border-left: 0; }
.hero-af .af-done h3 { font-size: clamp(1.25rem, 2vw, 1.625rem); }

/* -- services -------------------------------------------------------------- */
.services { padding-block: var(--pad-major); border-top: 1px solid var(--edge); }
/* Five doors, not five paragraphs. Nothing in the nav points at the service
   pages, so the whole row is the link surface and the arrow says so. */
.svc-list { list-style: none; margin: 0; padding: 0; }
.svc-list li { border-top: 1px solid var(--edge); }
.svc-list li:first-child { border-top: 0; }
/* 5/7, not 7/5: the name is short and the sell is the long half */
.svc-list a { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: var(--s-6); align-items: baseline; padding-block: var(--s-6);
  text-decoration: none; color: inherit; }
.svc-list li:first-child a { padding-top: 0; }
.svc-list h3 { font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.75rem, 2.8vw, 2.5rem); letter-spacing: var(--track-display);
  line-height: 1.1; transition: color 160ms ease; }
.svc-list p { text-wrap: pretty; }
.svc-say { font-size: var(--fs-lead); line-height: 1.35; }
.svc-go { margin-top: var(--s-4); font-size: var(--fs-small); color: var(--slate);
  max-width: 46ch; }
/* inline, not flex: a wrapped door line must not strand the arrow to the right */
.svc-go .arrow { width: 0.75em; height: 0.75em; vertical-align: -0.05em;
  margin-left: 0.4em; }
/* One gesture, everywhere the site draws an arrow. It lived on the service
   rows alone, which read as an accident rather than as intent. */
.arrow { transition: transform 160ms var(--ease-rise); }
.svc-list a:hover .arrow, .cta:hover .arrow, .btn:hover .arrow,
.case-said a:hover .arrow, .who-more a:hover .arrow,
.band-cta:hover .arrow { transform: translate(2px, -2px); }
.svc-list a:hover h3 { color: var(--red); }
.svc-list a:hover .svc-go { color: var(--ink); }

.svc-list a:focus-visible { outline: 2px solid var(--ink); outline-offset: 4px; }

/* -- proof ----------------------------------------------------------------- */
.proof { padding-block: var(--pad-major); border-top: 1px solid var(--edge); }
.proof-body > p { max-width: 62ch; margin-bottom: var(--s-5); }
.proof-lead { font-size: var(--fs-lead); }

/* flex-wrap, not a fixed column count: the strip grew from 3 stats to 4 when
   the click rate went back in, and a hard-coded repeat(3) silently stacked it */
.stat-strip { list-style: none; margin: var(--s-7) 0 var(--s-3); padding: var(--s-5) 0 0;
  display: flex; flex-wrap: wrap; gap: var(--s-6) var(--s-8);
  border-top: 1px solid var(--edge); }
.stat-n { display: block; font-family: var(--font-display); font-weight: 500;
  font-size: clamp(2rem, 3.6vw, 3.25rem); line-height: 1;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.stat-l { display: block; margin-top: var(--s-2); font-size: var(--fs-small);
  color: var(--slate); }
.stat-note { font-size: var(--fs-small); color: var(--slate); }

/* the chart breaks the measure: one of three deliberate breaks */
.gsc { margin: var(--s-7) 0 var(--s-6); max-width: 1000px; }
.gsc img { width: 100%; height: auto; border: 1px solid var(--edge); }
.gsc figcaption { margin-top: var(--s-3); font-size: var(--fs-small);
  color: var(--slate); max-width: 58ch; }
.taken { font-size: var(--fs-small); color: var(--slate); margin-top: var(--s-6); }

/* -- work list ------------------------------------------------------------- */
.cases { list-style: none; margin: 0; padding: 0; }
.cases > li { border-top: 1px solid var(--edge); padding-block: var(--s-7); }
.case-name { font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.5rem, 2.4vw, 2rem); letter-spacing: var(--track-display); }
.case-name a { text-decoration: none; }
.case-name a:hover { color: var(--red); }
.case-where { font-size: var(--fs-small); color: var(--slate); margin-top: var(--s-2); }
.case-said { margin-top: var(--s-4); max-width: 60ch; text-wrap: pretty; }
.case-grid { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 6fr);
  gap: var(--s-7); align-items: start; }
/* the same 5/7 as the service doors, for the same reason: the name is short
   and the sell is the long half. NOT .case-grid's 6/6, which is sized for a
   text-and-screenshot pair and a post has no screenshot. Without it the blog
   index was a narrow text column with the right half of the page blank. */
.post-row { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  column-gap: var(--s-6); align-items: start; }

/* -- plates: real screenshots, no frame furniture -------------------------- */
.plate { margin: 0; }
.plate img { width: 100%; height: auto; border: 1px solid var(--edge); }
.plate figcaption { margin-top: var(--s-3); font-size: var(--fs-small);
  color: var(--slate); }

/* -- the ink band ---------------------------------------------------------- */
.band { background: var(--ink); color: var(--paper);
  padding-block: var(--pad-major) var(--s-7); margin-top: var(--pad-section); }
.band h2 { font-size: clamp(2.25rem, 4.4vw, 3.5rem); max-width: 16ch; }
.band-note { color: var(--ink-mute); margin-top: var(--s-5); max-width: 46ch;
  text-wrap: pretty; }
.band-actions { margin-top: var(--s-7); display: flex; gap: var(--s-7);
  flex-wrap: wrap; align-items: baseline; }
.band-alt { color: var(--ink-mute); font-size: var(--fs-small);
  overflow-wrap: anywhere; }
.band-alt a { color: var(--ink-mute); text-underline-offset: 4px; }
.band-alt a:hover { color: var(--paper); }

/* -- footer ---------------------------------------------------------------- */
.site-foot { margin-top: var(--s-9); border-top: 1px solid var(--ink-2);
  padding-top: var(--s-6); }
.foot-index { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s-6); }
.foot-col { display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s-2); }
.foot-h { font-size: var(--fs-small); color: var(--ink-mute);
  margin-bottom: var(--s-2); }
/* info@minarankstudio.com is a 23-character unbreakable string in a column
   that is one of four at 1440 and full width at 390 */
.foot-col a { color: var(--paper); font-size: var(--fs-small); font-weight: 500;
  text-decoration: none; padding-block: 5px; overflow-wrap: anywhere; }
/* was color: var(--rule), the same dimming defect as the nav had, on ink */
.foot-col a:hover { color: var(--paper); text-decoration: underline;
  text-underline-offset: 4px; text-decoration-color: var(--red); }
.foot-meta { display: flex; justify-content: space-between; gap: var(--s-6);
  flex-wrap: wrap; margin-top: var(--s-8); font-size: var(--fs-small);
  color: var(--ink-mute); }
/* The footer switcher had no rules at all: 3 links laid out by .foot-meta's
   flex, in the UA's underline, at the muted colour meant for captions. Same
   shape as the header copy, in the ink band's colours: --paper for the links,
   which is what every other link down here is, and the rule under the current
   language in --paper too, because --ink on ink is a mark nobody can see. */
.foot-lang { display: flex; align-items: center; gap: var(--s-4);
  flex-wrap: wrap; font-weight: 500; }
.foot-lang > * { padding-block: 5px; }
.foot-lang a { color: var(--paper); text-decoration: none; }
.foot-lang a:hover { text-decoration: underline; text-underline-offset: 4px;
  text-decoration-color: var(--red); }
.foot-lang [aria-current="page"] { color: var(--paper); font-weight: 700;
  box-shadow: inset 0 -2px 0 0 var(--paper); }

/* ==========================================================================
   SUBPAGES
   ========================================================================== */
.page-head { padding-block: var(--s-8) var(--s-7); }
.crumbs { display: flex; align-items: center; gap: var(--s-3);
  font-size: var(--fs-small); color: var(--slate); margin-bottom: var(--s-5); }
.crumbs a { color: var(--slate); text-decoration: none; }
.crumbs a:hover { color: var(--ink); }
.page-title { font-family: var(--font-display); font-weight: 500;
  font-size: clamp(2.25rem, 4.6vw, 3.75rem); line-height: 1.04;
  letter-spacing: var(--track-display); text-wrap: balance; max-width: 24ch; }
.standfirst { margin-top: var(--s-5); font-size: var(--fs-lead);
  max-width: 42ch; text-wrap: pretty; }

.lead { font-size: var(--fs-lead); max-width: 60ch; margin-bottom: var(--s-6); }

.prose { grid-column: 1 / 8; max-width: 62ch; }
.prose h2, .studio-prose h2 { font-size: clamp(1.75rem, 2.6vw, 2.25rem);
  margin-top: var(--s-8); margin-bottom: var(--s-4); }
.prose h3, .studio-prose h3 { font-size: var(--fs-h3); margin-top: var(--s-6); margin-bottom: var(--s-3); }
.prose p, .studio-prose p { margin-bottom: var(--s-5); text-wrap: pretty; }
.prose ul, .prose ol { margin: 0 0 var(--s-5); padding-left: 1.25em; }
.prose li { margin-bottom: var(--s-3); text-wrap: pretty; }
.prose li::marker { color: var(--slate); }
.prose a, .studio-prose a { color: var(--ink); text-underline-offset: 3px;
  text-decoration-thickness: 1px; text-decoration-color: var(--slate); }
.prose a:hover, .studio-prose a:hover { text-decoration-color: var(--red); }
.prose > :first-child, .studio-prose > :first-child { margin-top: 0; }
.prose code { font-family: ui-monospace, "Cascadia Mono", Menlo, monospace;
  font-size: 0.9em; background: rgba(19, 22, 28, 0.06); padding: 1px 5px; }

.side { grid-column: 9 / 13; align-self: start; position: sticky;
  top: calc(72px + var(--s-6)); }
.side-block { border-top: 1px solid var(--edge); padding-top: var(--s-4);
  margin-bottom: var(--s-7); }
.side-h { font-size: var(--fs-small); color: var(--slate);
  margin-bottom: var(--s-3); font-weight: 700; }
.side-block p, .side-block li { font-size: var(--fs-small); text-wrap: pretty; }
.side-list { list-style: none; margin: 0; padding: 0; }
/* the padding moves from the li onto the a, so the whole row is the target
   rather than a 16px strip of text inside it */
.side-list li { border-bottom: 1px solid var(--edge); }
.side-list a { display: block; padding-block: var(--s-3); text-decoration: none; }
/* --red-dk, not --red. These inherit --fs-small from .side-block, and red on
   paper is 4.2:1, under the 4.5:1 AA floor for text this size. 6.53:1. */
.side-list a:hover { color: var(--red-dk); }

.ledger { list-style: none; margin: var(--s-6) 0 0; padding: 0; }
.ledger > li { border-top: 1px solid var(--edge); padding-block: var(--s-5); }
.ledger > li:last-child { border-bottom: 1px solid var(--edge); }
.ledger h3 { font-size: var(--fs-h3); margin-bottom: var(--s-3); }
.ledger p { text-wrap: pretty; }

.exclusions { margin-top: var(--s-8); border-top: 1px solid var(--edge);
  padding-top: var(--s-6); padding-bottom: 0; }
.exclusions ul { list-style: none; margin: var(--s-4) 0 0; padding: 0; max-width: 62ch; }
.exclusions li { padding-block: var(--s-3); }

/* Measured 191px between the last block and this heading, on a page whose
   heading rhythm is 64: .faq's own margin, plus section{padding-block} landing
   inside .prose, plus the h2's margin. 96 is a deliberate 1.5x step at the one
   place the page genuinely changes register. */
.faq { margin-top: var(--s-9); padding-block: 0; }
.faq > h2 { margin-top: 0; }
.faq-item { border-top: 1px solid var(--edge); padding-block: var(--s-5); max-width: 66ch; }
.faq-item:last-child { border-bottom: 1px solid var(--edge); }
.faq-q { font-size: var(--fs-h3); margin-bottom: var(--s-3); }

.payoff { margin-top: var(--s-5); font-weight: 700; display: flex;
  align-items: center; gap: var(--s-3); }
.tick { width: 12px; height: 12px; flex: none; }

/* The last-updated line, at the foot of the prose on every page.

   Slate at --fs-small, which is the same treatment .taken and .stat-note
   already get, because it is the same kind of thing: rule 7 gives slate to
   captions and metadata and this is metadata about the page rather than a
   sentence in it. The gap above is --s-8 and not --s-5, so it reads as
   something after the argument instead of the argument's last line. It is a
   date. It is not allowed to compete with the payoff sitting a paragraph
   above it. */
.updated { margin-top: var(--s-8); font-size: var(--fs-small);
  color: var(--slate); }

.fig { margin: 0 0 var(--s-7); max-width: 240px; }
.fig svg { width: 100%; height: auto; }

.tail { margin-top: var(--s-9); }
.tail-inner { border-top: 2px solid var(--ink); padding-top: var(--s-6);
  display: flex; justify-content: space-between; align-items: baseline;
  gap: var(--s-6); flex-wrap: wrap; }
.tail h2 { font-size: clamp(1.625rem, 2.6vw, 2.25rem); max-width: 20ch; }
.cta { font-size: var(--fs-h3); text-decoration: none;
  border-bottom: 2px solid var(--ink); padding-bottom: 2px; white-space: nowrap; }
.cta:hover { border-bottom-color: var(--red); }
.arrow { display: inline-block; width: 0.6em; height: 0.6em;
  margin-left: 0.25em; color: var(--red); }

/* /studio/ sits off the usual axis: the one page written as a person */
/* was grid-column 2/9, which indented the body 99px while the h1 stayed
   flush left. An axis shift that moves half a page is a broken edge. */
.studio-prose { grid-column: 1 / 9; max-width: 62ch; }

/* ==========================================================================
   MOTION. Final states are the CSS default, so no-JS, crawlers and
   reduced-motion get the finished page.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  .js:not(.played) .wm .l { opacity: 0; }
  .play .wm .l { animation: rise 650ms var(--ease-rise) both;
    animation-delay: calc(90ms + var(--i) * 70ms); }
  @keyframes rise {
    from { opacity: 0; transform: translateY(calc(var(--i) * -1 * var(--rise-step) + 0.5em)); }
    to   { opacity: 1; transform: translateY(calc(var(--i) * -1 * var(--rise-step))); }
  }
  .play .wm .k { animation: rise 650ms var(--ease-rise) both, kshift 400ms ease both;
    animation-delay: calc(90ms + var(--i) * 70ms), 1350ms; }
  @keyframes kshift { from { color: var(--ink); } to { color: var(--red); } }

  .js:not(.played) .serp i { transform: scaleX(0); transform-origin: left; }
  .js:not(.played) .serp i::after { opacity: 0; }
  .play .serp i { animation: rule-in 400ms var(--ease-reveal) both;
    animation-delay: calc(var(--r) * 40ms); }
  @keyframes rule-in { to { transform: scaleX(1); } }
  .play .serp i::after { animation: fade-in 300ms ease both;
    animation-delay: calc(200ms + var(--r) * 40ms); }
  @keyframes fade-in { to { opacity: 1; } }

  .js:not(.played) .hero-rest { opacity: 0; transform: translateY(12px); }
  /* was 900ms. The last wordmark letter starts rising at 580ms and the climb
     finishes at 1230ms, so the copy used to land AFTER it, 1350ms past play,
     and play itself waits on document.fonts.ready or an 800ms cap. The form
     beside it has no entrance at all: a conversion element must never be the
     thing that has not arrived yet. */
  .play .hero-rest { animation: rest-in 450ms var(--ease-reveal) 400ms both; }
  @keyframes rest-in { to { opacity: 1; transform: none; } }

  /* the line above it is a fact somebody has to keep true, so it breathes */
  .status .dot { animation: dot-pulse 2.4s var(--ease-reveal) infinite; }
  @keyframes dot-pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.35 } }

  .js [data-reveal] { opacity: 0; transform: translateY(14px);
    transition: opacity 0.45s var(--ease-reveal), transform 0.45s var(--ease-reveal); }
  .js [data-reveal].in-view { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
  }
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 999px) {
  /* The row carries 3 groups now, and Albanian is the longest of the 3
     languages in all of them: "Prova Shërbime Shkrime Studio", the switcher,
     and "Nis një projekt". At 32px gaps that measured 724px against a 720px
     viewport, a 4px horizontal scroll at the one width where the phone menu
     has not taken over yet. The GAP is what gives. Not the type size, which is
     already the smallest on the site, and not the words, which are the nav.

     16px is what the row below 720 already uses, and the groups do not close
     up with it: they are separated by a hairline and its own 24 and 32px of
     padding, not by the gap. Measured at 720, the tightest width the row is
     ever asked to hold everything at: Albanian is the longest of the 3 in all
     3 groups and clears it with 32px to spare. */
  .head-nav { gap: var(--s-4); }
  .head-nav > .head-lang { padding-left: var(--s-5); gap: var(--s-3); }
  .prose, .studio-prose { grid-column: 1 / 13; }
  .side { grid-column: 1 / 13; position: static; margin-top: var(--s-8); }
  /* Below 1000 the sidebar stops being sticky and lands under the article, so
     an index you reach by scrolling past everything it indexes is furniture.
     It is hidden rather than moved: .side is a single grid item, so a
     grid-row on it would drag the other 3 blocks above the prose with it. */
  .side-toc { display: none; }
  .case-grid, .post-row { grid-template-columns: 1fr; }
  .post-row > div + div { margin-top: var(--s-4); }
  .foot-index { grid-template-columns: repeat(2, 1fr); }
  /* the form goes under the copy rather than beside it, and gets the red rule
     across the full measure. It stays above the fold's second screen, which is
     the whole point of it being in the hero. */
  .hero-split { grid-template-columns: 1fr; row-gap: var(--s-8); }
  /* Stacked, the form stops being a column and inherits the whole measure: at
     768 that gave it 691px-wide inputs, so "Your name" was a field you could
     have written a paragraph in. A form needs a measure for the same reason
     prose does. */
  .hero-af { max-width: 30rem; }

  /* Tighter, because it is stacked.
     Beside the copy the form is a column and reads as one. Under the copy it
     measured 731px against an 844px viewport, 0.87 of a screen, which made the
     hero 1665px on a phone: 2 full screens before the proof section starts.

     What changes is the rhythm BETWEEN the controls, never the controls. The
     input stays above 44px because that is the target a thumb needs. */
  .hero-split { row-gap: var(--s-6); }
  .hero-af .field { margin-top: var(--s-3); }
  .hero-af .field label { margin-bottom: 4px; }
  .hero-af .field input { padding: 0.62em 0.7em; }
  .hero-af .af-lead { margin-top: var(--s-2); }
  .hero-af .af { margin-top: var(--s-4); }
  .hero-af .af-go { margin-top: var(--s-5); }
  .hero-af .af-say, .hero-af .af-alt { margin-top: var(--s-3); }
}

@media (max-width: 719px) {
  .svc-list a { grid-template-columns: 1fr; gap: var(--s-4); }
  .stat-strip { gap: var(--s-5); }
  .hero-wrap::before, .hero-wrap::after { display: none; }
  .serp i { right: 0; }
  .serp i::after { content: none; }
  .head-nav { gap: var(--s-4); }
  /* '>' and NOT a descendant selector: the phone menu's copies of these four
     links live inside .menu-panel, and a descendant selector here hides them
     too, which is a menu that opens onto nothing. Gate check 33 fails the
     build if this stops being a child combinator. */
  .head-nav > a:not(.head-cta) { display: none; }
  /* the row copy of the switcher goes with them, and the copy inside
     .menu-panel is what a phone reaches. Same '>' for the same reason. */
  .head-nav > .head-lang { display: none; }
  .menu { display: block; }
  /* the CTA is the only item left in the row here, so it has nothing to be
     divided from and the hairline would read as a stray mark */
  .head-cta { margin-left: 0; padding-left: 0; border-left: 0; }
  .foot-index { grid-template-columns: 1fr; }
  .foot-meta { flex-direction: column; }
  .band-actions { flex-direction: column; gap: var(--s-5); }
}

/* ==========================================================================
   THE ASK, THE CHECK, AND THE WHATSAPP BUTTON
   Four deliberate layout idioms on the homepage and no more: the 7/5 row, the
   who split's empty column, the one wide service row, and a red rule down a
   left edge. Two of them are now used twice: the 7/5 carries the hero split
   and the ask row, and the red rule marks the check block and the hero's
   audit form. One idiom used twice is a system. A FIFTH idiom is still the
   line, and it would still make the page a demo reel.
   ========================================================================== */

/* the hero byline: small type on a hairline, so the hero has three weights */
.hero-who { margin-top: var(--s-6); padding-top: var(--s-4);
  border-top: 1px solid var(--rule); max-width: 62ch;
  font-size: var(--fs-small); color: var(--slate); text-wrap: pretty; }
.hero-who strong { color: var(--ink); font-weight: 700; }

/* the free way to check the claim, promoted out of 15px slate */
.check { margin-top: var(--s-7); border-left: 2px solid var(--red);
  padding: var(--s-2) 0 var(--s-2) var(--s-5); max-width: 54ch; }
.check p { font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.25rem, 2vw, 1.625rem); line-height: 1.25;
  letter-spacing: var(--track-display); margin: 0; }
.check .taken { font-family: var(--font-body); font-size: var(--fs-small);
  color: var(--slate); margin-top: var(--s-3); letter-spacing: 0; }

/* the chart is the widest element on the page */
.gsc { max-width: none; }

/* the ask: one question, one button, then the businesses themselves */
.ask { border-top: 2px solid var(--ink); padding-block: var(--pad-major); }
.ask-row { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  column-gap: var(--s-9); align-items: end; }
.ask-q { font-size: clamp(2.25rem, 4.6vw, 3.75rem); line-height: 1.04;
  max-width: 15ch; }
.ask-note { margin-top: var(--s-5); max-width: 46ch; text-wrap: pretty; }
.ask-go { justify-self: end; }

.btn { display: inline-flex; align-items: center; gap: 0.55em;
  font-weight: 700; font-size: var(--fs-lead); line-height: 1;
  text-decoration: none; padding: 0.85em 1.3em;
  border: 2px solid var(--ink); color: var(--ink);
  transition: background 160ms ease, color 160ms ease; }
.btn:hover { background: var(--ink); color: var(--paper); }
.btn .arrow { color: currentColor; }
/* the global ring is 2px ink at 2px offset, which lands against a 2px ink
   border and is indistinguishable from it */
.btn:focus-visible, button.btn:focus-visible { outline-offset: 4px; }

/* -- the client marks ------------------------------------------------------
   Logos drawn by four different people in four different colours. Each part is
   a mask painted with currentColor, so the row arrives as one set and goes red
   together on hover. Widths are the true aspect at the chosen height, so
   nothing is squashed; heights differ because cap heights do.

   justify-content is flex-start, NOT space-between. space-between spreads a
   wrapped line to the container edges, which threw IGLISI and VICTORIA 523px
   apart at 814 wide and read as a missing logo.

   Adding a business is one file and one rule. Gate check 18 enforces it.     */
.marks { --mgap: var(--s-8);
  list-style: none; margin: var(--s-9) 0 0; padding: var(--s-8) 0 0;
  border-top: 1px solid var(--edge); display: flex; flex-wrap: wrap;
  align-items: center; justify-content: flex-start; gap: var(--s-4) var(--mgap); }
/* Below the width where all four fit on one line, go 2 per row rather than
   letting the wrap strand the last one on a line of its own. Measured: four
   fit from 1063px up, so 1079 is the last width that needs this. */
@media (max-width: 1079px) {
  .marks > li { flex: 0 0 calc(50% - var(--mgap) / 2); }
}
/* These were 12.3px tall at 390: four links to real businesses with no
   vertical hit area at all. WCAG 2.5.8 wants 24. The padding comes out of the
   row gap above, so the rendered row height is unchanged and the hand-tuned
   two-per-row breakpoints still hold. */
.mark-link { display: flex; align-items: center; gap: 8px;
  padding-block: var(--s-3);
  color: var(--ink); text-decoration: none; transition: color 160ms ease; }
.mark-link:hover { color: var(--red); }
.mark-link:focus-visible { outline: 2px solid var(--ink); outline-offset: 10px; }
.mark { display: block; background: currentColor;
  width: calc(var(--mw) * var(--mscale, 1));
  height: calc(var(--mh) * var(--mscale, 1));
  -webkit-mask: var(--mark) no-repeat center / contain;
          mask: var(--mark) no-repeat center / contain; }

.mark-iglisi-watch      { --mark: url("/assets/logo/clients/iglisi-watch.png");
                          --mw: 195px; --mh: 22px; }
.mark-victoria-boutique { --mark: url("/assets/logo/clients/victoria-boutique.svg");
                          --mw: 204px; --mh: 22px; }
.mark-intimo-bruna      { --mark: url("/assets/logo/clients/intimo-bruna.svg");
                          --mw: 200px; --mh: 26px; }
/* two parts: the symbol says nothing, so it gets its own name beside it */
.mark-pro-affy          { --mark: url("/assets/logo/clients/pro-affy.png");
                          --mw: 28px;  --mh: 28px; }
.mark-pro-affy-word     { --mark: url("/assets/logo/clients/pro-affy-word.svg");
                          --mw: 108px; --mh: 28px; }

/* With no mask support an unmasked .mark is a solid ink block, which is worse
   than nothing. Drop it and let the name that is already in the link show. */
@supports not ((-webkit-mask-image: url("/favicon.svg")) or (mask-image: url("/favicon.svg"))) {
  .mark { display: none; }
  .mark-link .sr-only { position: static; width: auto; height: auto; margin: 0;
    overflow: visible; clip: auto; font-weight: 700; }
}

/* The claim. One sentence at display size and one underneath, because a
   position you have to explain is not a position. It was 2 columns of
   supporting detail once and read as an argument being made rather than a
   fact being stated. */
/* --pad-major fired 4 times consecutively on the homepage, against a token
   file whose own comment says "three rhythm weights, not one". This is the
   connective sentence between the proof and the refusal, not a third exhibit,
   so it is the one section that can step down without losing status. */
.place { border-top: 1px solid var(--edge); padding-block: var(--pad-section); }
.place-say { font-size: clamp(2.25rem, 4.6vw, 3.75rem); line-height: 1.04;
  max-width: 22ch; }
.place-more { margin-top: var(--s-6); max-width: 50ch; font-size: var(--fs-lead);
  text-wrap: pretty; }

/* who: asymmetric, column 7 deliberately empty, the refusal is the headline */
.who-say { grid-column: 1 / 7; }
.who-more { grid-column: 8 / 13; align-self: end; }
.who-more p { font-size: var(--fs-small); max-width: 40ch; }

/* .mail no longer appears in the band. Rule 20 is one CTA per page and the
   audit link takes that slot rather than joining it, so the address and
   WhatsApp sit together in .band-alt as the alternates they now are. */

/* -- the free audit form ---------------------------------------------------
   The one place on the site that takes something rather than says something.

   Two submit paths. Without JavaScript it is a native POST and Web3Forms sends
   the visitor back to /start/#sent, where :target alone reveals the
   confirmation: no script runs and the fallback still works, which is the one
   thing every WhatsApp form in this workspace gets wrong. With JavaScript,
   js/main.js intercepts and swaps in place.

   Nothing here uses an inline style attribute, because style-src is 'self'.  */
.audit { margin-bottom: var(--s-9); }
.af { margin-top: var(--s-7); }

/* The honeypot. display:none, NOT off-screen: an off-screen honeypot with a
   label is reachable in a screen reader's browse mode, and a blind visitor who
   ticks it gets their lead silently binned while the page says "Sent". This
   takes it out of the accessibility tree, the tab order and axe's label rule
   at once, and it is what Web3Forms' own documentation uses. */
.af-hp { display: none; }

.af-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.field { display: block; margin-top: var(--s-5); margin-bottom: 0; }
.field label { display: block; font-size: var(--fs-small); font-weight: 700;
  margin-bottom: var(--s-2); }
.field-opt { font-weight: 400; color: var(--slate); }
/* --rule on --paper is 1.22:1 and WCAG 1.4.11 wants 3:1 for the boundary of
   a control. --slate is 5.56:1 and is already in the palette, so a box you can
   type into finally looks like one. */
.field input { width: 100%; font: inherit; font-size: var(--fs-body);
  color: var(--ink); background: var(--paper); padding: 0.7em 0.8em;
  border: 1px solid var(--slate); border-radius: 0;
  caret-color: var(--red);
  transition: border-color 160ms ease; }
/* the UA default is undefined and usually fails contrast. --slate is 5.56:1
   and is already the caption colour, which is what a placeholder is. */
.field input::placeholder { color: var(--slate); opacity: 1; }
/* was --slate, which is the default border at line above: the hover state
   did nothing at all. */
.field input:hover { border-color: var(--ink); }
.field input:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px;
  border-color: var(--ink); }
.field-hint { display: block; margin-top: var(--s-2);
  font-size: var(--fs-small); color: var(--slate); }
/* --red-dk, not --red: this is 15px body text and needs 4.5:1. Colour is
   never the only signal; the sentence underneath is. */
.field-err { display: none; margin-top: var(--s-2); font-weight: 700;
  font-size: var(--fs-small); color: var(--red-dk); }

/* nothing turns red before the visitor has tried to send */
.af.was-validated :invalid ~ .field-err { display: block; }
.af.was-validated :invalid { border-color: var(--red-dk); }

.af-go { margin-top: var(--s-6); margin-bottom: 0; }
button.btn { font-family: inherit; background: none; cursor: pointer; }
button.btn:disabled { opacity: 0.55; cursor: default; }
button.btn:disabled:hover { background: none; color: var(--ink); }

/* One line is always reserved, so the status can appear without pushing the
   page around. While the button is disabled and focus has fallen to the body,
   this is the only thing a screen reader hears. */
.af-say { min-height: 1.6em; margin: var(--s-4) 0 0; font-size: var(--fs-small);
  color: var(--slate); max-width: 54ch; }
.af-say.is-err { color: var(--red-dk); font-weight: 700; }

.af-alt { margin: var(--s-4) 0 0; font-size: var(--fs-small);
  color: var(--slate); max-width: 54ch; }
.af-fine { margin: var(--s-5) 0 0; padding-top: var(--s-4);
  border-top: 1px solid var(--rule); font-size: var(--fs-small);
  color: var(--slate); max-width: 54ch; }

.af-done { display: none; padding-left: var(--s-5);
  border-left: 3px solid var(--red); }
.af-done h3 { font-family: var(--font-display); font-weight: 500;
  font-size: var(--fs-h2); line-height: 1.08;
  letter-spacing: var(--track-display); max-width: 18ch; }
.af-done p { margin-top: var(--s-4); max-width: 54ch; text-wrap: pretty; }
/* :target is the no-JS reveal, .is-sent is the scripted one. The
   confirmation sits BEFORE the form in the source, which is the only reason
   this can be a plain sibling combinator rather than :has(): universal
   support, and no fallback state where both show at once. */
.af-done:target,       .audit.is-sent .af-done { display: block; }
.af-done:target ~ .af, .audit.is-sent .af      { display: none; }
.af-done:focus { outline: none; }
.af-done:focus-visible { outline: 2px solid var(--ink); outline-offset: 6px; }

/* the band's primary action, inverted against the ink */
.band-cta { display: inline-flex; align-items: center; font-weight: 700;
  font-size: var(--fs-lead); line-height: 1; text-decoration: none;
  padding: 0.85em 1.3em; background: var(--paper); color: var(--ink);
  transition: background 160ms ease, color 160ms ease; }
.band-cta:hover { background: var(--red); color: var(--paper); }

/* -- floating WhatsApp ------------------------------------------------------
   Built from the client sites: Victoria's inline SVG and focus ring, Intimo
   Bruna's safe-area insets, and a static anchor so it does not depend on JS
   to exist. z-index sits under the sticky header (5) is wrong: it must clear
   page content but never the nav, so 4 with the header at 5. */
.wa { position: fixed; z-index: 4;
  right: max(20px, env(safe-area-inset-right));
  bottom: max(20px, env(safe-area-inset-bottom));
  width: 56px; height: 56px; border-radius: 50%;
  background: #25D366; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 8px 24px rgba(19, 22, 28, 0.22);
  transition: transform 200ms ease, box-shadow 200ms ease;
  -webkit-tap-highlight-color: transparent; }
.wa svg { width: 30px; height: 30px; fill: #fff; }
.wa:hover { transform: translateY(-2px) scale(1.04);
  box-shadow: 0 12px 30px rgba(19, 22, 28, 0.28); }
.wa:focus-visible { outline: 3px solid var(--paper);
  box-shadow: 0 0 0 6px var(--ink); outline-offset: 3px; }

@media (max-width: 520px) {
  .wa { width: 50px; height: 50px; }
  .wa svg { width: 26px; height: 26px; }
}
/* landscape phones: none of the client sites handle this, and the button
   lands on top of form controls */
@media (max-height: 480px) {
  .wa { width: 44px; height: 44px; bottom: max(12px, env(safe-area-inset-bottom)); }
  .wa svg { width: 22px; height: 22px; }
}

@media (max-width: 999px) {
  .ask-row { grid-template-columns: 1fr; row-gap: var(--s-7); }
  .ask-go { justify-self: start; }
  .who-say, .who-more { grid-column: 1 / 13; }
  .who-more { margin-top: var(--s-6); }
  .marks { --mscale: 0.86; margin-top: var(--s-8); }
}
@media (max-width: 719px) {
  .check { padding-left: var(--s-4); }
  .marks { --mscale: 0.72; --mgap: var(--s-6); }
  .af-pair { grid-template-columns: 1fr; gap: 0; }

  /* 16px, not 18: iOS zooms the whole page when a focused input is smaller
     than 16px, and a zoomed page is a worse bug than a tall form. This is the
     only phone-specific rule the form needs; the rest of its rhythm belongs to
     being stacked, which starts 280px earlier. */
  .hero-af .field input { font-size: 1rem; }
  .hero { padding-bottom: var(--pad-section); }
  .af-done { padding-left: var(--s-4); }
}
/* the widest mark is 15.7:1, so a narrow phone needs it small or it overflows */
@media (max-width: 419px) {
  .marks { --mscale: 0.56; }
}


/* ==========================================================================
   PRINT
   This studio sends PDFs, so its own pages should survive being one. Last in
   the file, so it wins on source order without a single !important.
   ========================================================================== */
@media print {
  /* A green circle on every printed page is the worst thing here, and a nav
     that cannot be clicked is the second. */
  .wa, .site-head, .skip, .foot-index, .menu, .af, .side-toc { display: none; }

  /* The ink band loses its background. A full-bleed #13161C rectangle across
     a sheet of paper is toner nobody consented to. */
  .band { background: none; color: var(--ink); margin-top: var(--s-8);
    padding-block: var(--s-6) 0; }
  .band-note, .band-alt, .band-alt a, .foot-meta { color: var(--slate); }
  .band-cta { color: var(--ink); border: 0; padding: 0;
    border-bottom: 1px solid var(--ink); }

  /* The form goes but its alternatives stay: on paper the address and the
     number are the only part anybody can act on. */
  .af-h, .af-lead, .af-alt { display: block; }

  /* External only. Every internal href is root-relative, so this costs
     nothing to scope, and printing URLs after internal links would turn every
     side list into parentheses. */
  .prose a[href^="http"]::after, .band-alt a[href^="http"]::after {
    content: " (" attr(href) ")"; font-size: 0.85em; color: var(--slate); }

  .prose, .studio-prose, .side { grid-column: 1 / 13; }
  .gsc, .plate { break-inside: avoid; }
  h2, h3 { break-after: avoid; }
}

