/* ===========================================================================
   Panya Consulting — production layer

   The Claude Design export carries its own layout, typography, colour and
   motion in inline style attributes and in each page's own <style> block.
   This file does NOT restyle that design. It supplies only what a design
   file has no way to express: the accessibility affordances, the real form
   controls, and the reduced-motion contract.

   Ground is warm ivory #F2EEE6 with graphite #1C1A16 type on every page; the
   footer is the one dark element. Radius 0, no shadow, no fill — anywhere.
   =========================================================================== */

/* ── Skip link ─────────────────────────────────────────────────────────────
   Off-screen until focused, then pinned top-left in the site's own palette. */
.pan-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
}

.pan-skip:focus {
  left: 0;
  padding: 14px 20px;
  background: #F2EEE6;
  color: #1C1A16;
  font-family: Archivo, sans-serif;
  font-size: 11px;
  letter-spacing: 0.24em;
}

/* ── Screen-reader-only ────────────────────────────────────────────────── */
.pan-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Honeypot — never shown to, or reachable by, a human ───────────────── */
.pan-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ── Images ────────────────────────────────────────────────────────────────
   The design places each <img> inside a sized, overflowing frame and expects
   it to fill that frame. Nothing here changes the frame or the crop. */
img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Visible keyboard focus ────────────────────────────────────────────────
   The design's own reset already sets `:focus-visible { outline: 1px solid
   currentColor; outline-offset: 4px; }`. These rules cover only what it
   cannot reach: the dark footer, where currentColor sits too close to the
   ground, and the form controls whose appearance is cleared below. Radius 0,
   so the ring is square like every other rule on the site. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 1px solid currentColor;
  outline-offset: 4px;
  border-radius: 0;
}

footer a:focus-visible,
footer button:focus-visible {
  outline-color: #F2EEE6;
}

/* ── Footer link hit areas ─────────────────────────────────────────────────
   The footer columns run at ~9.5px with a ~10px gap, giving ~13px tall tap
   targets. This grows the hit area into the gap that already exists, using
   the padding/negative-margin technique the design itself uses on the header
   nav, so the drawn layout is unchanged: the padding is cancelled by the
   margin and nothing moves. */
footer a {
  display: inline-block;
  padding: 6px 0;
  margin: -6px 0;
}

/* ── Contact form ──────────────────────────────────────────────────────────
   The form is line-based: a datum under each field and nothing else. Chrome
   and Safari still paint native affordances (an inner shadow on iOS, a spin
   box, the search decoration), so appearance is cleared explicitly. */
#pan-form input,
#pan-form textarea {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(28,26,22,0.2);
  border-radius: 0;
  box-shadow: none;
  outline-offset: 6px;
}

#pan-form input::placeholder,
#pan-form textarea::placeholder {
  color: #8E897F;
  opacity: 1;
}

#pan-form input:focus,
#pan-form textarea:focus {
  border-bottom-color: rgba(28,26,22,0.55);
}

.pan-status {
  margin: 2.6vh 0 0;
  min-height: 1.6em;
  max-width: 52ch;
  font-family: Archivo, sans-serif;
  font-weight: 300;
  font-size: 12px;
  line-height: 1.9;
  letter-spacing: 0.04em;
  color: #6B655B;
}

.pan-status[data-kind="ok"] { color: #1C1A16; }
.pan-status[data-kind="error"] { color: #8A4B34; }

/* ── Organisation type — a real radio group ────────────────────────────────
   The design draws six aria-pressed buttons driven by script. Each is now a
   <label> carrying the design's own inline styles with a visually-hidden
   radio inside, so the rendered result is identical and the semantics are
   native: arrow keys move within the group, the legend names it, and the
   browser reports the selection.

   A <fieldset> arrives with a 2px groove border, asymmetric padding, a 2px
   side margin and min-inline-size: min-content. All four are UA defaults and
   all four must be cleared, or the accessible markup draws a box the design
   does not have. */
fieldset.pan-fieldset {
  border: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  min-inline-size: 0 !important;
  min-width: 0 !important;
  background: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  display: block;
}

fieldset.pan-fieldset > legend {
  padding: 0 !important;
  margin-inline-start: 0 !important;
  float: none;
  width: auto;
}

label.pan-aud { cursor: pointer; }

label.pan-aud > input.pan-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* Selected and hover use the design's own graphite and its own 0.42 hairline,
   so nothing new is introduced. Drawn in CSS rather than by script. */
label.pan-aud:has(input:checked),
label.pan-aud[data-checked="true"] {
  border-bottom-color: rgba(28,26,22,0.42) !important;
  color: #1C1A16 !important;
}

@media (hover: hover) and (pointer: fine) {
  label.pan-aud:hover { color: #1C1A16 !important; }
}

label.pan-aud:focus-within {
  outline: 1px solid #1C1A16;
  outline-offset: 6px;
}

/* ── Touch targets ─────────────────────────────────────────────────────────
   The design's small-caps labels are 9–11px. Where a control is a link or a
   button, the hit area is grown into space that already exists rather than by
   moving anything: padding out, margin back. */
@media (max-width: 879px) {
  label.pan-aud {
    padding-top: 14px !important;
    margin-top: -14px !important;
  }
}

/* ── Reduced motion ────────────────────────────────────────────────────────
   The composition is preserved; only movement is removed. The design's own
   controller short-circuits to its settled state (`reduced()` → `settleAll()`),
   so every element authored at opacity 0 lands visible rather than staying
   invisible. These rules are the safety net for anything CSS-driven. */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto !important;
    scroll-snap-type: none !important;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0ms !important;
  }

  /* The opening overlay hides itself with a delayed animation. With animation
     neutralised it would sit over the page forever, so remove it outright. */
  #panIntro { display: none !important; }
}

/* ── Print ─────────────────────────────────────────────────────────────────
   /sergey is the page most likely to be printed or saved to PDF from a phone
   after the QR code is scanned. */
@media print {
  header, footer, #panMenu, #panMenuBtn, .pan-skip { display: none !important; }
  * { animation: none !important; transition: none !important;
      opacity: 1 !important; transform: none !important;
      clip-path: none !important; visibility: visible !important; }
}

/* ── Two targets the design draws smaller than 24px ─────────────────────────
   Both grow into the gap that already exists — padding out, margin back — so
   nothing moves on screen. Neither reaches 24px without changing the approved
   spacing, which is a design decision, not a build one:

     privacy TOC rows  16px tall, 4px apart  ->  20px, touching
     chapter rail bars 12px wide, 5px apart  ->  17px, touching

   Both are recorded as a known AA target-size gap rather than papered over. */
#panToc a {
  padding-top: 2px;
  padding-bottom: 2px;
  margin-top: -2px;
  margin-bottom: -2px;
}

[data-bar-s] {
  padding-left: 2.5px !important;
  padding-right: 2.5px !important;
  margin-left: -2.5px !important;
  margin-right: -2.5px !important;
}

/* ── The menu must not cover its own close control ──────────────────────────
   The design gives <header> z-index 90 and #panMenu z-index 100, both fixed
   in the root stacking context, so the open menu paints over the header and
   hides the trigger. Its own openMenu() relabels that trigger to CLOSE,
   collapses its three datum lines to one and renames it "Close navigation" —
   affordances that only mean anything if the header is visible, and the menu
   panel already reserves calc(9vh + 52px) of top padding for it. The header
   is raised above the panel so the close control is where the design says it
   is. #panIntro stays above both at 120, so the opening is unchanged. */
/* !important because the design sets z-index in the element's own style
   attribute, and an inline declaration beats a stylesheet rule. */
header {
  z-index: 110 !important;
}

/* ===========================================================================
   Three approved corrections — 11 Sep. Each is scoped to the element it
   fixes; none of them moves anything else.
   =========================================================================== */

/* ── 1. Brands: the two principal heroes on mobile ──────────────────────────
   Both frames are authored `width: min(72%, 1180px)` with a viewport height.
   On a phone that takes 72% of an already-narrow column and pairs it with
   64vh, so the picture renders as a vertical slit — 252px wide by 540px tall
   inside a 350px content width at 390. Desktop is untouched.

   Below 880px each hero takes the full content measure (the section already
   holds 20px margins) and drops the vh height for the photograph's own
   aspect, so each reads as the picture it is: Kaldewei's hero is 1600x900 and
   stays landscape, Zucchetti's is 1280x1600 and stays portrait. */
@media (max-width: 879px) {
  .pan-brand-hero {
    width: 100% !important;
    height: auto !important;
  }

  .pan-brand-hero--kal { aspect-ratio: 16 / 9; }
  .pan-brand-hero--zuc { aspect-ratio: 4 / 5; }
}

/* ── 2. Privacy: contents rows ──────────────────────────────────────────────
   Each row was one text node, "11 — FONTS AND EMBEDDED CONTENT", so a wrapped
   title fell back to the left edge under the number. The number and the title
   are now separate cells, and the title column holds its own left edge, so
   every continuation line sits under the title rather than under "11 —".
   Same type, same size, same measure — only the wrap changes. */
.pan-toc-row {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.5em;
  align-items: baseline;
}

.pan-toc-n { white-space: nowrap; }

/* ── 3. Projects: the italic J's tail ───────────────────────────────────────
   The headline's second line sits in an overflow:hidden reveal mask whose
   height is the line box — 100.17px against 119px of content at 1440. The
   italic J's descender falls in that missing 19px and is cut off.

   The allowance is added to the line as padding and taken back on the mask as
   an equal negative margin: the clip region grows downward, the baseline does
   not move, and the rule below does not move. Keeping the padding on the line
   also keeps the reveal honest — the animation starts at translateY(112%), a
   percentage of the line's own height, so a taller line starts further down
   and the text is still completely outside the mask at rest.

   Both values are the SAME expression rather than the same em value, because
   em resolves against each element's own font-size: the line is set at
   clamp(44px, 7.4vw, 150px) but the mask inherits 16px from the page, so
   0.2em meant 21.3px on one and 3.2px on the other, and the pair left +15px
   of real height behind. Written as one viewport expression they cancel
   exactly at every width.

   0.2 x the headline size, tracking the same clamp: the measured overflow is
   19px against a 106.56px line at 1440, and this gives 21.3px there, 8.8px at
   the 44px floor and 30px at the 150px cap. */
.pan-desc-line { padding-bottom: clamp(8.8px, 1.48vw, 30px); }
.pan-desc-mask { margin-bottom: calc(-1 * clamp(8.8px, 1.48vw, 30px)); }

/* ── The browser's default button chrome ────────────────────────────────────
   The approved design carries its own button reset in four of the six page
   helmets. Brands has no button rule at all and Contact groups it as
   `input, textarea, button`, so on Brands — the one page with a <button> and
   no reset — the mobile MENU trigger fell back to the user agent's own
   control styling: background #EFEFEF and a 2px outset border, drawn as a
   grey pill against the ivory header. Measured on the live site at 320, 360,
   390 and 430, at rest and mid-scroll; it was never a scroll, focus or
   compositing state.

   These are the design's own declarations, verbatim from the four helmets
   that already carry them, restated here so the reset no longer depends on
   which page happens to include it. Identical values, so the four pages that
   were already correct do not change. `outline` is deliberately not touched —
   Contact's helmet sets `outline: none` and the focus ring is restored by
   :focus-visible further down. */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ── Footer: the two principal names ────────────────────────────────────────
   Now links. The hover lift matches the footer's existing interaction
   language — a lift to the brighter ivory the SITE column already uses on the
   dark ground — with no underline, no new colour and no movement. Pointer
   only, so a tap never leaves a stuck state. */
@media (hover: hover) and (pointer: fine) {
  footer a:hover { color: #F2EEE6; }
}

/* ── [hidden] must actually hide ────────────────────────────────────────────
   The user agent hides [hidden] with `display: none`, but every element in
   this design carries its display in an inline style attribute, and an inline
   declaration beats a stylesheet rule. The Wallet action is authored
   `display: inline-flex` inline and `hidden`, so without this it stayed
   visible while `el.hidden` reported true — offering a pass that does not
   exist. !important is the only thing that outranks an inline declaration. */
[hidden] {
  display: none !important;
}

/* ===========================================================================
   Mobile pressure test, 12 Sep — three corrections. Each was measured on the
   live site at 320/360/390/430 and A/B tested before being applied. Nothing
   here changes desktop.
   =========================================================================== */

/* ── 1. The footer went two-up before its content fitted ────────────────────
   Each footer column is authored `flex: 1 1 170px` inline. Two columns plus
   the 26px gap floor fit from 416px, but the REGION line "DUBAI · UNITED ARAB
   EMIRATES" needs 220.1px and the PANYA strap needs 214.6px, so between about
   416 and 540 the two-up column is 172-198px and both break — EMIRATES hanging
   alone under its own line, the strap becoming three ragged lines. Every Pro
   Max and Plus iPhone lands in that band, so a wider phone rendered worse than
   a narrower one, on every page of the site.

   Held single-column to 540px, where the two-up column first exceeds the
   220.1px the longest line needs, so the rule and the natural layout agree at
   the seam. !important because the basis is an inline declaration.
   Measured: 320-414 byte-identical to before, 428/430/440/480/540 back to one
   line per row, 541 and every width above — including 768, 1024 and 1440 —
   completely unchanged. */
@media (max-width: 540px) {
  footer[role="contentinfo"] > div:first-child > div {
    flex-basis: 100% !important;
  }
}

/* ── 2. Anchored headings landed under the fixed header ─────────────────────
   The header is fixed and 98px tall at every mobile width. Tapping a contents
   link on /privacy scrolled the heading to top: 0, which is behind the header,
   so the reader arrived at a section whose title they could not see. Nothing
   in the design reserved that space. */
h2[id^="p"] {
  scroll-margin-top: 112px;
}

/* ── 3. The Projects index labels could not stack ───────────────────────────
   "03 FEATURED REFERENCES" and "04 FURTHER REFERENCES" sit in a flex row with
   no wrap, so instead of stacking they shrink: each needs ~190px, each is
   granted 134-184px, and both break to two lines on a 10px leading against
   9.5px type. Allowing the row to wrap puts each label back on one line at its
   natural width. A no-op at 480 and above, where the pair already fits. */
@media (max-width: 479px) {
  [data-rv="fade"][data-d="540"] {
    flex-wrap: wrap;
    row-gap: 6px;
  }
}

/* ===========================================================================
   Mobile polish, 12 Sep — two corrections. Both measured on the live site
   before and after, at every width named below.
   =========================================================================== */

/* ── 4. iOS Safari zoomed the page in on every form field ───────────────────
   The design sets the contact controls at 11px inline. Mobile Safari zooms
   the viewport whenever a focused control's text is under 16px, so tapping
   NAME threw the reader into a magnified, horizontally scrolling page they
   then had to pinch back out of — on every field, every time. The page has no
   maximum-scale and must not get one: capping zoom would fix the symptom by
   removing pinch-zoom, which is the accessibility affordance itself.

   So the control text is genuinely 16px on touch, and only the two properties
   that are authored in `em` are pinned back to the pixel values they resolve
   to at 11px — 0.24em tracking to 2.64px, 1.4em padding to 15.4px, the
   textarea's 2.0 leading to 22px. Without that pinning the em values would
   scale with the font and each field would grow by 12px instead of 5px.

   What moves: the three single-line fields are 5px taller, because 16px
   glyphs need a taller line box than 11px glyphs. Inside the form block that
   redistributes by at most 4px; the document grows 3px. The textarea keeps
   its exact height, the ORGANISATION TYPE legend does not move at all, and
   nothing above the form moves. Placeholders keep at least 53px of headroom
   at 320px, so none of them clip.

   Scoped to touch pointers and to the design's own 879px narrow breakpoint,
   so an iPhone in landscape is covered and desktop typography is not. */
@media (max-width: 879px), (pointer: coarse) {
  #pan-form input[type="text"],
  #pan-form input[type="email"],
  #pan-form textarea {
    font-size: 16px !important;
    letter-spacing: 2.64px !important;
    padding-bottom: 15.4px !important;
  }

  #pan-form textarea {
    line-height: 22px !important;
  }
}

/* ── 5. The same footer wrap, one breakpoint up ─────────────────────────────
   Correction 1 above held the footer single-column to 540px, which fixed the
   phones. It did not fix what the same `flex: 1 1 170px` basis does higher up:
   170px is far below the 220.1px "DUBAI · UNITED ARAB EMIRATES" needs and the
   214.6px the PANYA strap needs, so flexbox keeps packing columns that cannot
   hold their own copy. Measured on the live site, the column is 182px at 700,
   199.7px at 768, 213.2px at 820, 186.9px at 1024 and 215.3px at 1180 — every
   one of those breaks a line, and 700 to 899 also strands the fourth column
   alone on a second row at full width.

   Two columns are the widest arrangement that always fits between 541 and
   1279: the narrowest case, 541px, gives 224px a column, still clear of the
   220.1px floor. A 40% basis is what forces exactly two — two of them plus the
   gap is 85.7% of the row at every width in the band, and three can never fit
   under 120%. Above that the design's own basis already produces four columns
   that fit, so nothing here applies: 1280, 1440 and 1920 are untouched, as are
   541-640, which were already two-up and clean, and everything at 540 and
   below, which correction 1 holds at one column. */
@media (min-width: 541px) and (max-width: 1279px) {
  footer[role="contentinfo"] > div:first-child > div {
    flex-basis: 40% !important;
  }
}

/* ===========================================================================
   Sergey's contact actions, 13 Sep.
   =========================================================================== */

/* ── 6. Links that own only the space they occupy ───────────────────────────
   Sixteen anchors across the six pages were reported as resolving from
   apparently empty space. All sixteen are direct children of a
   `display: flex; flex-direction: column` container: a flex item is
   blockified, and the default `align-items: stretch` then hands it the whole
   column. Measured on the live site, boxes of 263-876px around 55-257px of
   ink, confirmed with document.elementFromPoint at 390 and 1440.

   `align-self: flex-start` gives the cross size back to the content. It moves
   nothing: these all sit at the start of the cross axis already, so the left
   edge and the baseline are exactly where they were — only the dead tail to
   the right goes away. build.py applies the class from the design's own
   inline styles, and only where the link is not itself drawn as a row. */
.pan-fit {
  align-self: flex-start;
}

/* ── 7. The Save Contact chooser ────────────────────────────────────────────
   One visible action on /sergey. Where more than one destination is really
   available, it opens this: the contact table's own language — thin rules,
   tracked numeral, tracked value — continued into a dialog, rather than a
   card or a platform badge. A <dialog> opened with showModal() because the
   background inertness, the focus trap and Escape are the browser's job and
   it does them properly. */
.pan-chooser {
  border: 0;
  padding: 0;
  background: none;
  color: inherit;
  width: min(430px, calc(100% - clamp(28px, 8vw, 80px)));
  max-width: none;
  max-height: none;
  overscroll-behavior: contain;
}

.pan-chooser::backdrop {
  background: rgba(28, 26, 22, 0.46);
}

.pan-chooser-inner {
  background: #F2EEE6;
  border: 1px solid rgba(28, 26, 22, 0.42);
  padding: clamp(22px, 4vw, 32px) clamp(20px, 4vw, 30px) 0;
}

.pan-chooser-title {
  margin: 0 0 clamp(14px, 2.2vh, 22px);
  font-family: Archivo, sans-serif;
  font-weight: 400;
  font-size: 9px;
  letter-spacing: 0.28em;
  color: #6B655B;
}

/* A chooser row is presented as a row — a rule above it, its value tracked
   across to the right edge — so the whole row reading as the target is the
   honest behaviour, and the opposite of the blank-space problem above. */
.pan-chooser-row {
  display: flex;
  width: 100%;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 20px;
  padding: clamp(14px, 2.2vh, 24px) 0;
  border-top: 1px solid rgba(28, 26, 22, 0.18);
  font-family: Archivo, sans-serif;
  text-align: left;
  color: #1C1A16;
}

.pan-chooser-n {
  font-size: 9px;
  letter-spacing: 0.28em;
  color: #6B655B;
}

.pan-chooser-v {
  font-size: 11px;
  letter-spacing: 0.22em;
  color: #1C1A16;
}

.pan-chooser-close {
  display: block;
  width: 100%;
  padding: clamp(14px, 2.2vh, 24px) 0;
  border-top: 1px solid rgba(28, 26, 22, 0.18);
  font-family: Archivo, sans-serif;
  font-size: 9px;
  letter-spacing: 0.28em;
  text-align: left;
  color: #6B655B;
}

@media (hover: hover) and (pointer: fine) {
  .pan-chooser-row:hover .pan-chooser-n,
  .pan-chooser-close:hover { color: #1C1A16; }
}

.pan-chooser-row:focus-visible,
.pan-chooser-close:focus-visible {
  outline: 1px solid #1C1A16;
  outline-offset: -3px;
}

/* On a phone the sheet meets the bottom edge, square, full width, clear of the
   home indicator. No drawer, no radius — the panel is the same rectangle. */
@media (max-width: 639px) {
  .pan-chooser {
    width: 100%;
    margin: auto 0 0;
  }

  .pan-chooser-inner {
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }
}

/* The reveal transition on /sergey is opacity and transform; the dialog has
   neither, so reduced motion needs nothing here. */
