/**
 * Shell-iT — Proof Mode base layer
 * Reset, document defaults, typography, focus, utilities.
 * Depends on 01-tokens.css.
 */

/* ============================================================ HIDDEN STATE */

/*
 * `hidden` must always win over a component's own `display`.
 *
 * The browser's own rule is `[hidden] { display: none }` — a single attribute
 * selector, which any class that sets `display` outranks. So every component
 * styled `display: flex` or `display: grid` silently ignores being hidden, and
 * the failure is invisible in code review: the JavaScript sets the property,
 * the property is set, and the element stays on screen.
 *
 * This theme had already hit it four times and written four separate guards —
 * nav panels, the drawer, accordions, the booking placeholder — and still
 * missed the fifth. On the booking page the result was a "calendar didn't
 * load" warning permanently visible beside a working calendar, and a "loading"
 * placeholder that never went away.
 *
 * A per-component guard is something to remember; this is something that
 * cannot be forgotten. `!important` is correct here in a way it rarely is:
 * `hidden` is a state, not a style, and no layout rule should ever be able to
 * override it.
 *
 * `until-found` is excluded because that value deliberately keeps the element
 * findable and expandable by the browser's find-in-page, which `display: none`
 * would break.
 */
[hidden]:not([hidden="until-found"]) { display: none !important; }

/* ---------------------------------------------------------------- RESET */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;

  /*
   * The scrollbar is part of the page, so it takes its colours from the palette
   * rather than from a hard-coded pair.
   *
   * Replaces a two-year-old snippet that styled `body::-webkit-scrollbar` with
   * literal hex values from the previous design. Two problems with that: the
   * standard `scrollbar-color` property has been supported for years and is
   * what non-WebKit browsers read, and colours written as literals drift the
   * moment the palette changes — which is exactly what had happened.
   */
  scrollbar-color: var(--color-panel) var(--color-surface-sunken);
  scrollbar-width: thin;
}

body {
  margin: 0;
  background-color: var(--color-page);
  color: var(--color-text-primary);
  font-family: var(--font-text);
  font-size: var(--type-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd {
  margin: 0;
}

/*
 * The list reset, wrapped in `:where()` so a component can actually override it.
 *
 * `ul[class], ol[class]` carries an attribute selector, so its specificity is
 * (0,1,1) — higher than any single-class rule. Two components had asked for a
 * top margin and silently never got one: the jump-list panel on the service
 * pages, where «Σε αυτή τη σελίδα» ended up sitting flush against «01», and
 * the mobile drawer's menu. Both declared `margin: var(--space-3) 0 0` and both
 * computed to 0.
 *
 * `:where()` contributes zero specificity, so the reset still applies to every
 * classed list, and a component that states a margin now wins by simply being
 * a class. Nothing that was already overriding this loses anything, because
 * everything already outranked (0,1,1).
 */
:where(ul[class], ol[class]) {
  margin: 0;
  padding: 0;
  list-style: none;
}

img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
  height: auto;
}

/*
 * `img { height: auto }` above already preserves the intrinsic ratio when
 * width/height attributes are present. A more specific `img[width][height]`
 * rule was removed deliberately: an attribute selector outranks a single
 * class, so it silently defeated every component that sizes an image (the
 * site logo in particular).
 */

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

table {
  border-collapse: collapse;
  border-spacing: 0;
}

/* ----------------------------------------------------------- TYPOGRAPHY */

.h1, .h2, .h3, .h4, .h5, .h6,
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  color: var(--color-text-primary);
  text-wrap: balance;
}

h1, .h1 {
  font-size: var(--type-h1);
  font-weight: 680;
  line-height: 1.02;
  letter-spacing: -0.025em;
  font-variation-settings: "wdth" 96, "opsz" 64;
}

h2, .h2 {
  font-size: var(--type-h2);
  font-weight: 650;
  line-height: 1.08;
  letter-spacing: -0.02em;
  font-variation-settings: "wdth" 98, "opsz" 48;
}

h3, .h3 {
  font-size: var(--type-h3);
  font-weight: 650;
  line-height: 1.12;
  letter-spacing: -0.015em;
  font-variation-settings: "wdth" 100, "opsz" 36;
}

h4, .h4 {
  font-size: var(--type-h4);
  font-weight: 650;
  line-height: 1.18;
  letter-spacing: -0.01em;
  font-variation-settings: "wdth" 100, "opsz" 28;
}

h5, .h5 {
  font-size: var(--type-h5);
  font-weight: 650;
  line-height: 1.25;
  font-variation-settings: "wdth" 100, "opsz" 22;
}

h6, .h6 {
  font-size: var(--type-h6);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0.005em;
  font-variation-settings: "wdth" 100, "opsz" 16;
}

.display-xl {
  font-family: var(--font-display);
  font-size: var(--type-display-xl);
  font-weight: 720;
  line-height: 0.96;
  letter-spacing: -0.035em;
  font-variation-settings: "wdth" 92, "opsz" 88;
  text-wrap: balance;
}

.display-lg {
  font-family: var(--font-display);
  font-size: var(--type-display-lg);
  font-weight: 700;
  line-height: 0.98;
  letter-spacing: -0.03em;
  font-variation-settings: "wdth" 94, "opsz" 72;
  text-wrap: balance;
}

/* Explicit breakpoint caps, applied after the fluid declarations. */
@media (max-width: 768px) {
  .display-xl { font-size: 4rem; }      /* 64 */
  .display-lg { font-size: 3.5rem; }    /* 56 */
  h1, .h1 { font-size: 2.75rem; }       /* 44 */
  h2, .h2 { font-size: 2.25rem; }       /* 36 */
  h3, .h3 { font-size: 1.875rem; }      /* 30 */
  h4, .h4 { font-size: 1.5rem; }        /* 24 */
  h5, .h5 { font-size: 1.1875rem; }     /* 19 */
}

@media (max-width: 390px) {
  .display-xl { font-size: 2.875rem; }  /* 46 */
  .display-lg { font-size: 2.5rem; }    /* 40 */
  h1, .h1 { font-size: 2.125rem; }      /* 34 */
  h2, .h2 { font-size: 1.875rem; }      /* 30 */
  h3, .h3 { font-size: 1.625rem; }      /* 26 */
  h4, .h4 { font-size: 1.375rem; }      /* 22 */
  h5, .h5 { font-size: 1.125rem; }      /* 18 */
}

/* Hero headline measure. Greek widens via the token in 01-tokens.css. */
.hero-title {
  max-inline-size: var(--hero-measure);
  text-wrap: balance;
  hyphens: none;
  overflow-wrap: normal;
}

/*
 * A hero title that should not be broken by the measure.
 *
 * `--hero-measure` exists to stack a long editorial headline into a strong
 * shape. "Κλείστε το ραντεβού σας" wants 748px and the Greek measure allows
 * 745 — three pixels, and the booking page's title split across two lines for
 * no compositional reason at all. This is an opt-out for a specific headline,
 * not a change to the measure: the column it sits in still bounds it.
 */
.hero-title--wide { max-inline-size: none; }

.lede {
  font-size: var(--type-body-lg);
  line-height: 1.7;
  max-inline-size: var(--measure-lead);
  color: var(--color-text-secondary);
}

.body-sm { font-size: var(--type-body-sm); line-height: 1.55; letter-spacing: 0.002em; }
.caption { font-size: var(--type-caption); line-height: 1.45; letter-spacing: 0.005em; color: var(--color-text-secondary); }

/* Micro labels are the ONLY place uppercase is allowed — never Greek headings. */
.micro-label {
  font-size: var(--type-micro);
  font-weight: 650;
  line-height: 1.35;
  letter-spacing: 0.035em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.prose > * + * { margin-block-start: var(--space-3); }
.prose p,
.prose li { max-inline-size: var(--measure-body); }

/*
 * Body lists keep their markers whether or not the editor added a class.
 *
 * The list reset above strips markers from any classed list, and the block
 * editor stamps `wp-block-list` on every list an editor inserts. So a list
 * typed into the editor lost its bullets and its indent and read as a run of
 * short paragraphs, while the older hand-written lists — which carry no class
 * — kept theirs. Same content, two appearances, decided by whether the markup
 * happened to come from the editor.
 *
 * Stated here for reading contexts only, so panels, navigation and card grids
 * keep the reset.
 */
:is(.prose, .reading) :is(ul, ol) {
  padding-inline-start: 1.35rem;
  list-style-position: outside;
}

:is(.prose, .reading) ul { list-style-type: disc; }
:is(.prose, .reading) ol { list-style-type: decimal; }
:is(.prose, .reading) li + li { margin-block-start: var(--space-1); }
:is(.prose, .reading) li::marker { color: var(--color-text-secondary); }
.prose h2 { margin-block-start: var(--space-6); }
.prose h3 { margin-block-start: var(--space-5); }

/*
 * Figures — images and video — get room and a frame.
 *
 * An article's video blocks were rendering as bare black rectangles with the
 * browser's own controls floating on them: three of them in a row on one post,
 * each taller than the viewport. A video without a poster frame will always
 * start dark, but it should at least read as a deliberate element of the page
 * rather than as something that failed to load. The radius and the sunken
 * surface are the same ones the project cards use.
 *
 * The height cap matters more than it looks: a portrait video was rendering
 * 950px tall inside a 760px column and pushed everything after it off the
 * screen.
 */
/*
 * An inline SVG with a viewBox and no width or height is 100% wide by default,
 * and the reset above gives it `height: auto` — so it grows to fill whatever
 * column it lands in. Six migrated service pages carried decorative check marks
 * written that way and every one of them rendered as a black circle 622px
 * across, one per list item.
 *
 * The markup is being cleaned, but the guard stays: this is a whole class of
 * bug that arrives with pasted content, and a stylesheet that lets one icon
 * take over the page is not doing its job.
 */
.prose svg:not([width]):not([height]) {
  display: inline-block;
  inline-size: 1em;
  block-size: 1em;
  vertical-align: -0.125em;
}

.prose figure { margin-block-start: var(--space-5); }
.prose figure + * { margin-block-start: var(--space-5); }

.prose video {
  inline-size: 100%;
  max-block-size: 70vh;
  border-radius: var(--radius-xs);
  background-color: var(--color-surface-sunken);
}

.prose figcaption {
  margin-block-start: var(--space-2);
  max-inline-size: var(--measure-body);
  color: var(--color-text-secondary);
  font-size: var(--type-body-sm);
}

/* ---------------------------------------------------------------- LINKS */

a {
  color: var(--color-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.16em;
  transition: color var(--motion-fast) var(--ease-standard),
              text-decoration-thickness var(--motion-fast) var(--ease-standard);
}

a:hover {
  color: var(--color-link-hover);
  text-decoration-thickness: 2px;
}

/*
 * There is deliberately NO `a:visited` rule here.
 *
 * There was one — `a:visited { color: var(--color-link) }` — added to keep
 * visited links on brand instead of turning purple. It was redundant and it
 * was breaking the site's main call to action.
 *
 * Redundant, because the `a` rule above already colours visited links: a
 * browser only falls back to its own purple when the author has set no colour
 * at all. Adding `:visited` changed nothing about how visited links looked.
 *
 * Breaking, because of what it did to specificity. `a:visited` is (0,1,1) — a
 * type selector plus a pseudo-class — while a component that colours an anchor
 * with a single class, like `.button--primary`, is only (0,1,0). So the
 * visited rule WON, and every visited primary button was painted
 * `--color-link` (#3c58a8) on `--control-bg` (#496bcb): brand blue text on a
 * brand blue background, at roughly 1.2:1.
 *
 * The effect was invisible to anyone testing on a fresh profile, and permanent
 * for every returning visitor — including the client, who reported it. It hit
 * `.skip-link`, `.proof-panel__link` and `.card__title a` the same way.
 *
 * If visited links ever need their own colour, it has to be declared at a
 * specificity that does not outrank components — or, better, on the specific
 * places it is wanted (`.prose a:visited`) rather than globally.
 */

/* ---------------------------------------------------------------- FOCUS */

/* Dual-layer ring: white separation inside keeps it visible on blue controls,
   the blue outer line stays visible on light surfaces. Verified ≥3:1 on every
   surface in the system. */
:focus-visible {
  outline: 3px solid var(--color-focus-ring);
  outline-offset: 3px;
  box-shadow: 0 0 0 2px var(--color-focus-gap);
}

/* Never remove focus because the pointer was used. :focus-visible handles it. */
:focus:not(:focus-visible) {
  outline: none;
}

/* ------------------------------------------------------------ UTILITIES */

.container {
  width: min(calc(100% - (2 * var(--page-gutter))), var(--container-default));
  margin-inline: auto;
}

.container--wide { max-width: var(--container-wide); }
.container--reading { width: min(calc(100% - (2 * var(--page-gutter))), var(--container-reading)); }

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  gap: var(--grid-gap);
}

.sr-only {
  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;
}

/* Skip link: first focusable element, visible on focus, never display:none. */
.skip-link {
  position: absolute;
  top: -100px;
  left: var(--space-1);
  z-index: var(--z-skip-link);
  padding: var(--space-1-5) var(--space-2);
  background: var(--color-ink);
  color: var(--color-text-inverse);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--radius-sm);
}

.skip-link:focus-visible {
  top: var(--space-1);
  color: var(--color-text-inverse);
}

/* --------------------------------------------------------- SECTION RHYTHM */

.section { padding-block: var(--space-12); }                       /* 96 */
.section--hero { padding-block: 4.5rem var(--space-12); }          /* 72 / 96 */
.section--feature { padding-block: var(--space-15); }              /* 120 */
.section--dense { padding-block: var(--space-8); }                 /* 64 */
.section--reading { padding-block: var(--space-10); }              /* 80 */
.section--cta { padding-block: var(--space-10); }                  /* 80 */

/*
 * The booking section is preceded by the three "what we will discuss" cards,
 * which exist to support the decision rather than to be the page's climax. A
 * full 96 on top of the cards' own 40 put 136px between the last card and the
 * calendar, so the widget arrived after the page appeared to have finished.
 * The page has one job, and the calendar should be the next thing you see.
 */
.section--booking { padding-block-start: var(--space-6); }         /* 48 */

/*
 * An INDEX hero (work, services, blog) is a compact introduction, not a
 * statement. A full `.section--hero` bottom (96) followed by a full `.section`
 * top (96) put 192px of empty canvas between the lede and the first filter
 * control, which read as an unfinished page. An archive's job is to reach the
 * evidence quickly, so the pair is tightened to 64 + 48.
 */
.section--index-hero { padding-block: 4.5rem var(--space-8); }     /* 72 / 64 */
.section--index-hero + .section { padding-block-start: var(--space-6); }  /* 48 */
/* A feature section changes surface and carries the page's main offer, so it
   keeps more air than a filter bar does — just not the full 120. */
.section--index-hero + .section--feature { padding-block-start: var(--space-10); }  /* 80 */

/*
 * A hero followed immediately by its own facts band is one composition, not two
 * sections. Full padding on both sides of that seam put 160px of nothing
 * between a project's image and its specification.
 */
.section--hero:has(+ .case-facts-band) { padding-block-end: var(--space-8); }        /* 64 */
.case-facts-band { padding-block: var(--space-6); }                                 /* 48 */

/*
 * A service hero and the copy under it are one argument, not two chapters.
 *
 * 96px of hero padding meeting 80px of section padding put 176px of nothing
 * between the proposition and the first thing that supports it — enough that
 * "Πότε αξίζει" arrived on the second screen on a laptop. The seam is halved;
 * the section keeps its full spacing everywhere else.
 */
.service .section--hero { padding-block-end: var(--space-6); }                       /* 48 */
.service .section--hero + .section--reading { padding-block-start: var(--space-6); } /* 48 */

/*
 * The same seam on an article, for the same reason but a worse symptom: the
 * hero ends with a full-width lead image, and 96 + 80 of padding put 176px of
 * empty page between the picture and the opening sentence. The image looked
 * detached from the article it belongs to.
 *
 * Only when there is an image. A text-only article hero keeps the full break,
 * because there the whitespace is the separation between title and body.
 */
.article .section--hero:has(.article-hero__media) { padding-block-end: var(--space-6); }
.article .section--hero:has(.article-hero__media) + .section--reading { padding-block-start: var(--space-6); }

@media (max-width: 1199px) {
  .section { padding-block: var(--space-12); }
  .section--hero { padding-block: 3.5rem 4.5rem; }                 /* 56 / 72 */
  .section--index-hero { padding-block: 3.5rem var(--space-6); }   /* 56 / 48 */
  .section--index-hero + .section { padding-block-start: var(--space-5); }
  .section--feature { padding-block: var(--space-12); }            /* 96 */
  .section--dense { padding-block: 3.5rem; }                       /* 56 */
  .section--booking { padding-block-start: var(--space-5); }       /* 40 */
  .section--reading,
  .section--cta { padding-block: var(--space-8); }                 /* 64 */
}

@media (max-width: 768px) {
  .section { padding-block: 3.5rem; }                              /* 56 */
  .section--hero { padding-block: var(--space-5) 3.5rem; }         /* 40 / 56 */
  .section--index-hero { padding-block: var(--space-5) var(--space-5); } /* 40 / 40 */
  .section--index-hero + .section { padding-block-start: var(--space-4); }
  .section--feature { padding-block: 4.5rem; }                     /* 72 */
  .section--dense { padding-block: var(--space-5); }               /* 40 */
  .section--booking { padding-block-start: var(--space-4); }       /* 32 */
  .section--reading,
  .section--cta { padding-block: var(--space-6); }                 /* 48 */
}

/* Two adjacent sections on the same surface must not both take full padding. */
/*
 * A section that changes surface colour keeps its top padding; one that
 * continues the same surface does not, so two sections in a row read as one
 * region rather than as two with a seam of dead space between them.
 *
 * The class exists alongside the attribute because block markup cannot carry
 * an arbitrary `data-` attribute: a Group block can be given a class and
 * nothing else. PHP templates keep the attribute, converted pages use the
 * class, and both mean the same thing.
 */
.section + .section:not([data-surface-change]):not(.section--surface-change) { padding-block-start: 0; }

/*
 * A section that exists only to introduce a dynamic list disappears with it.
 *
 * In a PHP template the conditional wrapped the whole section: no products, no
 * heading. Converted, only the GRID is a block — the heading and lede are
 * editable content — so an empty list left "Προϊόντα που χτίζουμε" and a lede
 * promising products sitting above 395px of nothing. Measured on the live
 * homepage after the conversion; it is the precise failure the products
 * section was originally written to avoid, reintroduced by moving the copy out
 * of PHP.
 *
 * Marking the section rather than the block is what makes this work: the block
 * cannot hide its own ancestor, but the ancestor can ask whether the block
 * produced anything. `display: none` is right here — there is genuinely
 * nothing to announce, so hiding it from assistive technology too is correct.
 *
 * The selector lists the wrappers these blocks emit when they DO have content.
 * A new conditional list adds its wrapper here, or it will vanish.
 */
.section--dynamic:not(:has(.grid-cards, .work-feature, .service-range)) { display: none; }

/* Surfaces */
.surface-page { background-color: var(--color-page); }
.surface-raised { background-color: var(--color-surface-raised); }
.surface-sunken { background-color: var(--color-surface-sunken); }

.surface-panel,
.surface-inverse {
  color: var(--color-text-inverse);
}

.surface-panel { background-color: var(--color-panel); }
.surface-inverse { background-color: var(--color-panel-inverse); }

.surface-panel :is(h1, h2, h3, h4, h5, h6),
.surface-inverse :is(h1, h2, h3, h4, h5, h6) { color: var(--color-text-inverse); }

.surface-panel .lede,
.surface-inverse .lede,
.surface-panel .caption,
.surface-inverse .caption,
.surface-panel .micro-label,
.surface-inverse .micro-label,
.surface-panel .facts__label,
.surface-inverse .facts__label,
/*
 * The offline-link note lives in the facts rail, which is a dark band on the
 * case study and a light one nowhere yet. `--color-text-muted` is a
 * light-surface token and gave 2.77:1 against the navy — a fail. It takes the
 * same colour as the labels around it, which is 9.6:1.
 */
.surface-panel .facts__note,
.surface-inverse .facts__note { color: var(--color-text-inverse-secondary); }

/* Links inside a dark band cannot use the light-surface link colour. */
.surface-panel a:not(.button),
.surface-inverse a:not(.button) { color: var(--color-text-inverse); text-decoration-color: rgb(255 255 255 / 45%); }

.surface-panel a:not(.button):hover,
.surface-inverse a:not(.button):hover { text-decoration-color: currentColor; }

/* --------------------------------------------------------- REDUCED MOTION */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------------------- TECHNICAL REGISTER */

/*
 * One typographic register for metadata, applied consistently.
 *
 * Micro-labels, counts, dates, years and project types are facts about the
 * work, not prose about it — so they are set apart in monospace with tabular
 * figures rather than being decorated. Headings and body copy are untouched:
 * the contrast between the two registers is what does the work.
 *
 * Slightly reduced size compensates for monospace's wider advance width, so
 * the labels occupy roughly the same space they did before.
 */
.micro-label,
.card__type,
.facts__label,
.proof-panel__label,
.award__meta,
.chip__count,
.post-date,
.post-date-updated,
.card__delivered,
.process__number,
.stat__label {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
}

/* Numbers that carry weight keep the display face but always align. */
.stat__value,
.award__name,
.proof-panel__value,
.metric__value { font-variant-numeric: tabular-nums; }
