/* El Sata Tattoo Estudi - site stylesheet.
   The only stylesheet written by hand for this site. ds.css, booking.css and
   media.css are shared and load AFTER this file, so anything that has to win
   against them is written at a higher specificity and says why in a comment.

   Design read: the purest cold-pitch first presence for a studio with no
   website and no social account at all, with a heavy, ornamental-lettering
   language, leaning toward a near-black broadsheet led in the shop's own
   Catalan.

   A dark broadsheet with free-standing plates. One centred measure carries all
   the text; the portfolio photographs are laid full width with no card, no
   border and no panel, so the ones shot against black bleed into the ground and
   appear to float. There is no grid of cards anywhere on this page.

   Nothing here is a tattoo-genre default: no grain, no torn paper, no
   blackletter, no smoke gradient, and no pure #000000. The ground is measured
   off the shop's own charcoal fascia in assets/photos/g2.jpg. */

/* ---------------------------------------------------------------- tokens --
   :root would lose to booking.css and ds.css, which load later and declare on
   :root themselves. html:root is one specificity step higher, so these values
   hold wherever they have to. */
html:root {
  /* Palette, sampled off the storefront in g2.jpg. Measured, not estimated. */
  --ground: #1C1B19;        /* the fascia charcoal. Not #000000. */
  --ink: #EDE9E1;           /* 14.21:1 on the ground */
  --muted: #A9A39A;         /* 6.88:1, so it is still body-legible */

  /* The red of the slab TATTOO on the fascia. 4.28:1 on the ground: that
     clears the 3:1 bar for large text, rules and button fills but NOT the
     4.5:1 body bar. So it may be a heading, a CTA fill or a rule, and it is
     never small running text, a caption, or a link inside a paragraph. Those
     are all --ink. Every use below is checked against that rule. */
  --accent: #E04A3D;
  --accent-ink: #16120F;    /* 4.63:1 on the accent */
  /* Hover LIFTS the fill rather than darkening it. Darkening to #C83E32 drops
     the label to 3.72:1, which is the wrong direction for a hover state. */
  --accent-lift: #E8594B;   /* label 5.29:1, fill 4.89:1 on the ground */

  --panel: #232120;         /* the form's inset frame */
  --bg-2: #232120;          /* read by media.css and by booking.css's result */
  --bg-3: #2E2B27;          /* read by booking.css for input fills */

  --line: #3D3934;          /* hairline rules. Decoration, not a boundary. */
  --line-strong: #7B7569;   /* input borders: 3.76:1 on the ground, 3.08:1 on
                               the input fill, so the control has a visible
                               boundary on both of its sides. */

  /* Error text. booking.css declares --bad on a bare :root and swaps it to the
     light salmon only under prefers-color-scheme: dark. This page is dark
     whatever the system is set to, so the light default #B3261E would land at
     2.63:1 here for every visitor whose system is in light mode. Pinned at a
     specificity that beats a bare :root inside a media query. 8.63:1. */
  --bad: #FF9E8D;

  /* One corner radius system, two steps, held everywhere. */
  --r-s: 4px;
  --r-m: 8px;

  /* Measured: the language switcher is the tallest thing in the header (44px
     links inside a 2px-padded, 1px-bordered shell, so 50px) and the row adds
     0.55rem top and bottom. 68px at every width. ds.css uses it to keep a
     focused control from landing under the sticky header. */
  --nav-h: 68px;
  --wrap: 74rem;
  /* The 62ch measure, expressed in rem. `ch` resolves against the element's own
     font, so a caption at 0.9rem got a 583px box against the plate's 728px one
     and sat 73px inside the page's text axis. Measured in the browser: 62ch of
     Commissioner at the 1.125rem body size is 728.27px, which is 45.5rem. One
     number, one axis, whatever size the element is set at. */
  --measure: 45.5rem;

  /* Interaction dials, read by ds.css. Hover barely moves: these are large,
     heavy, dark plates and anything quick makes them feel light, which
     contradicts the work. */
  --ds-lift: 1px;
  --ds-dur: 0.3s;
}

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

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

/* ------------------------------------------------------------------ type --
   Two steps and a canyon between them. The display step carries every heading
   on the page, which is why every heading is one to three words; the body step
   carries everything a person reads in sentences. Nothing sits between them.
   The one size below the body step is for captions, field labels and the demo
   footnotes, which are not part of the display scale.

   Familjen Grotesk is a contemporary European grotesque rather than a
   tattoo-genre face, on purpose: the shop signs itself ESTUDI, in Catalan, on
   its own fascia, and the type should read local-European before it reads
   American tattoo parlour. Google Fonts serves this family up to 700, so 700
   is the display weight and nothing asks the browser to fake an 800.
   Commissioner has sturdy enough stems to hold up as light-on-dark, which is
   where Karla and Barlow thin out. No script face anywhere: the shop's own
   lettering work lives in the photographs, where it belongs. */
body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: "Commissioner", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 1.125rem;
  line-height: 1.75;
  font-weight: 400;
  /* Nothing on this page may cause a sideways scroll at 360px. */
  overflow-x: hidden;
}

h1, h2, .display {
  font-family: "Familjen Grotesk", "Commissioner", system-ui, sans-serif;
  font-weight: 700;
  font-size: clamp(3rem, 2.2rem + 4vw, 5.75rem);
  line-height: 0.95;
  letter-spacing: -0.025em;
  margin: 0;
}

p { margin: 0; }
a { color: var(--ink); }
img { max-width: 100%; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 3rem);
}

/* The single centred measure that carries all of the text. */
.column {
  max-width: var(--measure);
  margin-inline: auto;
}
.column > * + * { margin-top: var(--s5, 1.5rem); }
/* The canyon between the two type steps is a spacing fact as well as a size
   one: a heading this large needs air under it before the body begins. */
.column h2 + * { margin-top: clamp(1.5rem, 3.5vw, 2.5rem); }

.sec { padding-block: clamp(3.5rem, 9vw, 7rem); }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Icons from assets/social.svg. Always beside a word, never instead of one. */
.si {
  flex: none;
  width: 1em;
  height: 1em;
  fill: currentColor;
}

/* An anchor whose href site.js removed because the shop has not confirmed that
   detail. It must still read as the fact it is, not as a broken link. */
a:not([href]) { color: inherit; text-decoration: none; cursor: default; }

/* ---------------------------------------------------------------- reveal --
   The floor under motion.js. If Motion never loads this transition is what
   reveals the page; ds.css switches it off when motion.js takes over. The
   slowest settle and the smallest rise of the four tattoo studios: state and
   weight, not decoration. Nothing on this page slides, scales or parallaxes. */
.rv {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.8s cubic-bezier(0.22, 0.75, 0.3, 1),
              transform 0.8s cubic-bezier(0.22, 0.75, 0.3, 1);
}
.rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .rv { opacity: 1; transform: none; transition: none; }
}

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  min-height: 48px;
  padding: 0.7rem 1.5rem;
  border: 1px solid transparent;
  border-radius: var(--r-s);
  font-family: inherit;
  font-weight: 500;
  font-size: 1rem;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  background: none;
  color: var(--ink);
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn-primary:hover { background: var(--accent-lift); border-color: var(--accent-lift); }

.btn-ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}
.btn-ghost:hover { background: var(--panel); }

/* aria-disabled rather than disabled: site.js leaves the control reachable so
   a reader can still hear why the route is not open yet. */
.btn[aria-disabled="true"] { color: var(--muted); cursor: default; }

/* ---------------------------------------------------------------- header -- */
.site-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--ground) 90%, transparent);
  backdrop-filter: saturate(130%) blur(10px);
  border-bottom: 1px solid var(--line);
}
@supports not (backdrop-filter: blur(2px)) {
  .site-head { background: var(--ground); }
}

.head-row {
  min-height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: clamp(0.6rem, 2vw, 1.75rem);
  padding-block: 0.55rem;
}

.brand {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 44px;
  line-height: 1.15;
  text-decoration: none;
  color: inherit;
  margin-inline-end: auto;
}
.brand b {
  font-family: "Familjen Grotesk", sans-serif;
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.015em;
}
/* The shop's own spelling. It is not translated on any of the three pages. */
.brand span {
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.head-nav { display: none; gap: 0.25rem; }
.head-nav a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: 0.7rem;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.95rem;
}
.head-nav a:hover { color: var(--accent); }

.lang {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--line);
  border-radius: var(--r-s);
}
.lang a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  border-radius: 2px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--muted);
}
.lang a:hover { color: var(--ink); }
.lang a[aria-current="true"] {
  background: var(--ink);
  color: var(--ground);
}

.nav-cta {
  display: none;
  align-items: center;
  min-height: 44px;
  padding: 0 1.1rem;
  border: 1px solid var(--accent);
  border-radius: var(--r-s);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
}
.nav-cta:hover { background: var(--accent-lift); border-color: var(--accent-lift); }

/* -------------------------------------------------------------- masthead --
   The broadsheet front page: the name, one line of orientation, the storefront
   plate, then the address and the telephone under it. Four text elements, no
   more. min-height uses dvh so the mobile browser chrome cannot push the
   booking button off the bottom of the first screen; the plate is the flexible
   part, so the type and the CTA always fit and the photograph takes whatever
   is left. */
.masthead {
  min-height: calc(100dvh - var(--nav-h));
  display: flex;
  flex-direction: column;
  gap: clamp(1.25rem, 3.5vw, 2.25rem);
  padding-block: clamp(1.75rem, 5vw, 3.25rem) clamp(1.75rem, 4vw, 2.75rem);
}

.mast-head h1 { max-width: 16ch; }
.mast-head h1 span {
  display: block;
  color: var(--accent);   /* display size, so 4.28:1 clears the 3:1 bar */
}
.mast-lede {
  margin-top: clamp(1rem, 2.5vw, 1.5rem);
  max-width: 46ch;
  color: var(--muted);
}

/* The plate bleeds to both edges: it is a direct child of the section and the
   section has no side padding of its own. overflow is clipped here because
   media.css only sets it for its own hero class names, which this page
   deliberately does not use (they carry a 9s scale drift, and nothing on this
   page scales). */
.mast-plate {
  margin: 0;
  /* flex-basis 0, not auto. With `auto` the basis is the photograph's own
     intrinsic height, which at 390px is 520px: the section then grew to
     1002px, min-height did nothing, and the booking button sat 138px below
     the fold. At 0 the plate asks for nothing and takes only the space the
     type and the CTA have not used, which is what "the hero fits the
     viewport" actually requires. Measured, not assumed. */
  flex: 1 1 0;
  min-height: 180px;
  overflow: hidden;
}
.mast-plate img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Holds the fascia in frame at every height the flex row can produce, so the
     signage this photograph is used for stays readable. */
  object-position: 50% 24%;
}

.mast-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.9rem, 2.5vw, 2rem);
}
.mast-where {
  font-size: 1rem;
  color: var(--muted);
  margin-inline-end: auto;
}
.mast-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem;
  /* Sized by its buttons, not by the space left over. With flex-basis auto and
     growing children the row collapsed to 299px at 1280 and stacked the two
     buttons into a narrow column against the right edge. */
  flex: 0 0 auto;
}
.mast-actions .btn { flex: 0 1 auto; }

/* ---------------------------------------------------------------- plates --
   Free-standing, full width, no card, no border, no panel. g5 and g3 were both
   shot against black, so against this ground they have no visible edge and
   read as floating.

   The crop is tuned per breakpoint rather than once, because object-position
   is a percentage of the overflow and the overflow changes with the box ratio.
   Two things it has to guarantee at every width: the subject of the tattoo
   stays in frame, and the partly visible face at the top of g5 does not. */
.plate { margin: 0; }
.plate-frame {
  overflow: hidden;
  aspect-ratio: 1 / 1;
  max-height: 82svh;
}
.plate-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The plate bleeds, so its caption is the one piece of text on the page with no
   .wrap around it, and at 390px it was sitting flush against the screen edge.
   content-box plus the wrap's own padding reproduces the wrap geometry exactly:
   the content box is capped at the measure and the padding is outside it, so the
   caption lands on the same left axis as every .column at every width. Checked
   at 320, 360, 390, 1024 and 1280. */
.plate figcaption {
  box-sizing: content-box;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: clamp(1.15rem, 4vw, 3rem);
  margin-top: 1.1rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted);
}

.is-g5 img { object-position: 50% 60%; }
.is-g3 img { object-position: 50% 15%; }

@media (min-width: 46em) {
  .plate-frame { aspect-ratio: 3 / 2; }
  .is-g5 img { object-position: 50% 48%; }
  .is-g3 img { object-position: 50% 20%; }
}
@media (min-width: 62em) {
  .plate-frame { aspect-ratio: 2 / 1; }
  .is-g5 img { object-position: 50% 40%; }
  .is-g3 img { object-position: 50% 25%; }
}

/* The two process photographs are not black-backed, so they do not float and
   are not pretended to. They sit at the text measure, still with no card, no
   border and no panel. Crisper, too: at the measure both are shown at or below
   their native pixel width. */
.run { display: grid; gap: clamp(2.5rem, 7vw, 4.5rem); }
.run figure { margin: 0; }
.run .plate-frame {
  max-width: var(--measure);
  margin-inline: auto;
  aspect-ratio: 5 / 4;
  max-height: none;
}
/* g1 is the one frame with a partly visible face at the top of the source. The
   box is wider than the source, so the crop lands below it at every width. */
.run .is-g1 img { object-position: 50% 55%; }
.run .is-g4 .plate-frame { aspect-ratio: 1 / 1; }   /* 720x720, shown native */
.run figcaption {
  max-width: var(--measure);
  margin-inline: auto;
  margin-top: 1.1rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted);
}

/* ----------------------------------------------------------------- hours --
   The simplest hours of the eight shops, and set as the one line it actually
   is rather than as a seven row grid that repeats the same range six times. */
.hours-line {
  font-size: 1.125rem;
  font-variant-numeric: tabular-nums;
}
.hours-line b { font-weight: 600; }
.hours-line .off { color: var(--muted); }

.facts {
  margin: 0;
  display: grid;
  gap: 0.9rem 1.5rem;
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
}
.facts dt {
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.facts dd { margin: 0; }
.facts a[href] { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.facts .tel-big {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  font-size: 1.125rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.facts .aside {
  display: block;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--muted);
}

/* ------------------------------------------------------------- the form --
   One inset frame, centred, and nothing else competing with it on the page. */
.book-lede { color: var(--muted); }

.form {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  margin-top: clamp(2rem, 5vw, 3rem);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  padding: clamp(1.25rem, 3.5vw, 2.25rem);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  /* booking.css gives .form textarea `border-radius: inherit`, so the radius
     the textarea uses is the one declared here. */
  border-radius: var(--r-s);
}
.field label {
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.5;
}
.field input,
.field select {
  font-family: inherit;
  font-size: 1rem;
  min-height: 48px;
  padding: 0.6rem 0.8rem;
  color: var(--ink);
  background: var(--bg-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-s);
  width: 100%;
  min-width: 0;
}
/* booking.css styles `.form textarea` at (0,1,1) and loads after this file, so
   the border it sets would win at equal specificity. One step higher, so the
   textarea gets the same visible boundary as every other control. */
.form .field textarea {
  border-color: var(--line-strong);
  color: var(--ink);
}
.field select {
  appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1.15rem) 55%, calc(100% - 0.8rem) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-inline-end: 2.4rem;
}
.field option { background: var(--panel); color: var(--ink); }
.field textarea { width: 100%; }
.field-msg { margin: 0.15rem 0 0; }

.form-error, .form-ok {
  grid-column: 1 / -1;
  display: none;
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.6;
}
.form-error {
  color: var(--bad);
  border-inline-start: 3px solid var(--bad);
  padding-inline-start: 0.8rem;
}
.form-error a { color: var(--bad); font-weight: 700; }
.form-ok {
  color: var(--ink);
  border-inline-start: 3px solid var(--accent);
  padding-inline-start: 0.8rem;
}
.form-error.show, .form-ok.show { display: block; }

.book-note {
  grid-column: 1 / -1;
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--muted);
}

#book-result {
  border-radius: var(--r-m);
  max-width: var(--measure);
  margin-inline: auto;
  margin-top: clamp(2rem, 5vw, 3rem);
}
/* booking.css sets this telephone in --accent at 1.15rem bold, which is a
   whisker under the 18.66px the large-text bar starts at, and the accent is
   4.28:1. The palette rule for this site says the red is never a link inside
   running text, so the number is set in ink and carries its own underline. */
#book-result .tel-row a { color: var(--ink); }

/* --------------------------------------------------------------- footer -- */
.site-foot {
  border-top: 1px solid var(--line);
  padding-block: clamp(2.5rem, 6vw, 4rem);
}
.foot-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem 2rem;
  align-items: baseline;
  justify-content: space-between;
}
.foot-brand {
  font-family: "Familjen Grotesk", sans-serif;
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: -0.015em;
}
.site-foot address {
  font-style: normal;
  color: var(--muted);
  font-size: 0.9rem;
  margin-top: 0.3rem;
}
.foot-links { display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; }
.foot-links a {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  padding-inline: 0.6rem;
  color: var(--ink);
  text-decoration: none;
  font-size: 0.95rem;
}
.foot-links a:hover { color: var(--accent); }

.demo-notes {
  margin-top: clamp(2rem, 5vw, 3rem);
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  max-width: var(--measure);
  font-size: 0.85rem;
  line-height: 1.7;
  color: var(--muted);
}
.demo-notes p + p { margin-top: 0.5rem; }

/* ------------------------------------------------------------ breakpoints --
   Every multi-column layout on this page declares its collapse here rather
   than being assumed to work out. Below 46em everything is one column. */
@media (min-width: 46em) {
  .form { grid-template-columns: 1fr 1fr; }
  .field.full { grid-column: 1 / -1; }
  .facts { grid-template-columns: auto 1fr; align-items: baseline; }
  .facts dt { padding-top: 0.2rem; }
}

@media (min-width: 62em) {
  .head-nav { display: flex; }
  .nav-cta { display: inline-flex; }
  .run { gap: clamp(3rem, 6vw, 5rem); }
}

/* The masthead stops trying to fill the screen once the screen is short and
   wide, where 100dvh is a few hundred pixels and the plate would be a sliver. */
@media (max-height: 34em) and (min-width: 46em) {
  .masthead { min-height: 0; }
  .mast-plate { min-height: 240px; }
}

@media (max-width: 30em) {
  .mast-actions .btn { flex: 1 1 100%; }
  .foot-grid { flex-direction: column; align-items: flex-start; }
}

/* 360px is the floor the brief sets and 320px still has to work. The h1 clamp
   bottoms out at 3rem, which is 21 characters of "El Sata" over two lines at
   that width, so nothing overflows. */
@media (max-width: 22em) {
  .mast-lede { font-size: 1.0625rem; }
  .facts dt { letter-spacing: 0.1em; }
}
