/*
 * Touristas design system.
 *
 * Order: tokens, reset, layout, typography, buttons, forms, header, footer,
 * cards, utilities. Component classes are prefixed tt- and never nested more
 * than one level, so a component can be moved without dragging context along.
 *
 * Sources: docs/03-design-system.md for the tokens, mockups/ for the approved
 * composition. The mockups use gradient blocks where photography will go; this
 * theme uses a flat neutral placeholder instead, because gradients are not the
 * visual language of this project.
 */

/* Typeface ---------------------------------------------------------------- */

/*
 * INTER WAS NAMED IN THE TOKENS AND NEVER SHIPPED, so this site has been
 * rendering in whatever the visitor's operating system supplies — San
 * Francisco on a Mac, Segoe on Windows, Roboto on Android, and something else
 * again on a Samsung. Three visitors, three different sites, none of them the
 * one that was designed. It is also why the type looked unremarkable: not
 * because the scale is wrong, but because there was no typeface behind it.
 *
 * SELF-HOSTED, NOT LINKED. A `<link>` to fonts.googleapis.com would be a third
 * party learning the IP address of every visitor before they have answered the
 * consent banner — the exact transfer the map is now gated on, so linking the
 * font would make the banner a lie. It is also two extra connections on the
 * critical path. These four files live in the theme.
 *
 * FOUR SUBSETS AND NOT ONE FILE. `unicode-range` means the browser downloads
 * only what the page actually contains: a Greek page fetches greek.woff2 at
 * 19KB and latin.woff2 at 47KB for «Touristas Travel» and «Daily Tours», and
 * never touches greek-ext (polytonic, 11KB) or latin-ext (83KB) at all. The
 * whole family as one file would be 250KB, most of it alphabets this site does
 * not use.
 *
 * ONE VARIABLE FILE PER SUBSET, `font-weight: 400 700`. Every weight the design
 * uses is inside that range, so the browser interpolates rather than fetching a
 * second and third file — and nothing is left to synthesise a fake bold, which
 * is what was happening to every heading on the site.
 *
 * `swap`, so text is readable in the OS font from the first paint and reflows
 * to Inter when it arrives. The alternative — invisible text while the font
 * loads — is a blank page on the connection this most matters on.
 *
 * Inter is Rasmus Andersson's, under the SIL Open Font License 1.1;
 * assets/fonts/LICENSE.txt is the copy that licence requires to travel with it.
 */

@font-face {
	font-family: Inter;
	font-style: normal;
	font-weight: 400 700;
	font-display: swap;
	src: url("../fonts/inter-greek.woff2") format("woff2");
	unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}

@font-face {
	font-family: Inter;
	font-style: normal;
	font-weight: 400 700;
	font-display: swap;
	src: url("../fonts/inter-greek-ext.woff2") format("woff2");
	unicode-range: U+1F00-1FFF;
}

@font-face {
	font-family: Inter;
	font-style: normal;
	font-weight: 400 700;
	font-display: swap;
	src: url("../fonts/inter-latin.woff2") format("woff2");
	unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
	font-family: Inter;
	font-style: normal;
	font-weight: 400 700;
	font-display: swap;
	src: url("../fonts/inter-latin-ext.woff2") format("woff2");
	unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Tokens ------------------------------------------------------------------ */

:root {
	/* Colour. No green anywhere in this project. */
	--tt-navy: #122b4d;
	--tt-blue: #0868d7;
	--tt-blue-dark: #064c9c;
	--tt-blue-soft: #eaf4ff;
	--tt-gray-soft: #f4f7fa;
	--tt-border: #dce4ec;
	--tt-text: #12233c;

	/*
	 * docs/03 specifies #68778b for secondary text, which was chosen against
	 * white and reaches only 4.57:1 there. Milestone 3 introduced two tinted
	 * section backgrounds, on which it drops to 4.10 and 4.25 — both below the
	 * 4.5 the QA checklist requires. Darkened to the nearest tone that clears
	 * it on every surface this site actually uses: 5.67 on white, 5.10 on soft
	 * blue, 5.28 on soft gray.
	 */
	--tt-text-muted: #5a687b;
	--tt-white: #fff;

	/*
	 * The border of a form control is not decoration: it is the only thing that
	 * says where the control IS. WCAG 2.2 SC 1.4.11 asks for 3:1 on that
	 * boundary, and --tt-border reaches 1.28:1 against the white field and
	 * 1.19:1 against the soft-gray panel behind it — so /epikoinonia/ read as
	 * seven labels with nothing visible underneath them, and «Τηλέφωνο» looked
	 * like a heading rather than a field.
	 *
	 * A separate token rather than darkening --tt-border, because that one is
	 * used for card and section hairlines, which SC 1.4.11 does not cover and
	 * which would coarsen the whole page if they went this dark.
	 *
	 * 3.47:1 on white, 3.23:1 on --tt-gray-soft.
	 */
	--tt-border-control: #7d8b9c;

	/* Colour on dark surfaces. */
	--tt-on-navy: #dbe6f2;

	/*
	 * THE FOCUS RING IS A COLOUR THAT DEPENDS ON WHAT IS BEHIND IT, AND IT WAS
	 * WRITTEN AS IF IT WERE NOT.
	 *
	 * One blue ring was drawn everywhere. Tabbed through four pages and measured
	 * the ring against the surface actually painted behind it: #0868d7 on the
	 * navy footer is 2.69:1, under the 3:1 that WCAG 2.2 SC 1.4.11 asks of a
	 * focus indicator. That is every link in the footer — the navigation, the
	 * services, the social icons and the four legal pages a keyboard visitor has
	 * the most reason to reach — on every page of the site, plus the button in
	 * the navy «Χρειάζεσαι πούλμαν;» banner on the homepage.
	 *
	 * A custom property rather than a list of overrides, because it inherits: a
	 * dark surface declares once what a ring on it should be, and every control
	 * inside it — including ones added later by somebody who has not read this —
	 * is correct without knowing the rule exists. White on this navy is 14.2:1.
	 */
	--tt-focus: var(--tt-blue);

	/* 8px spacing system. */
	--tt-space-1: 8px;
	--tt-space-2: 16px;
	--tt-space-3: 24px;
	--tt-space-4: 32px;
	--tt-space-5: 48px;
	--tt-space-6: 64px;
	--tt-space-7: 96px;

	/* Section rhythm, mobile first. */
	--tt-section: var(--tt-space-5);

	/*
	 * RADII, AND THE RULE CHANGED ON 16/08/2026 AT THE CLIENT'S REQUEST.
	 *
	 * It used to read «pills are reserved for filters and tags», from
	 * docs/03. The client reviewed a reference site whose actions are all
	 * pills and said, in as many words, that it makes you want to go on
	 * holiday — so the shapes move and the palette does not. Every colour on
	 * this page is still blue, white and soft gray.
	 *
	 * What was measured on that reference, because «rounded» on its own is
	 * not a specification:
	 *
	 *   actions, navigation, segmented switches   pill (r ≥ half the height)
	 *   icon-only buttons                         50%
	 *   text inputs, selects, panels              10–12px
	 *   content cards and photographs             10–12px
	 *
	 * The third line is the one that stops this becoming a lozenge site. A
	 * pill says «press me»; a field the visitor types into is not pressed, and
	 * rounding it to a capsule makes a text cursor look like it is sitting in
	 * a button. So `--tt-radius-action` is new and narrow, `--tt-radius-button`
	 * keeps its 10px and keeps every input it already governs.
	 */
	--tt-radius-action: 999px;
	--tt-radius-button: 10px;
	--tt-radius-card: 12px;
	--tt-radius-pill: 999px;

	/* Borders before shadows. These stay deliberately faint. */
	--tt-shadow-card: 0 1px 2px rgb(18 43 77 / 4%);
	--tt-shadow-raised: 0 8px 24px rgb(18 43 77 / 8%);

	--tt-content: 1180px;
	--tt-font: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

	/* Minimum comfortable touch target. */
	--tt-tap: 44px;

	/*
	 * How much of the top of the screen the header occupies now that it stays
	 * there. Measured rather than derived: 58px of `min-height` plus its
	 * border and the container's padding come to 62 on a phone and 69 from
	 * 768px up, and design-system.spec.js asserts both so this token cannot
	 * drift away from the thing it describes.
	 *
	 * Everything that has to clear the header reads it from here — the sticky
	 * filter panel, the tour page's booking column, and the distance the browser
	 * leaves above anything it scrolls to.
	 */
	--tt-header: 62px;

	/*
	 * The height of the sticky booking bar. Three rules depend on it — the bar
	 * itself, the space the page reserves for it, and where the chat launcher
	 * sits — and they used to say «72px» separately.
	 */
	--tt-sticky: 72px;

	/*
	 * THE TYPE SCALE, WHICH DID NOT EXIST. Spacing, radii, shadows and colour
	 * all had tokens; every font-size was written at its use site, and it showed:
	 * twelve distinct sizes across 79 declarations, SEVEN of them crowded between
	 * 11px and 18px. Sizes one pixel apart cannot be told apart, so the page had
	 * no hierarchy to read — only a lot of nearly-equal text.
	 *
	 * Six steps, each far enough from its neighbours to mean something, with the
	 * ratio widening towards the top where the jumps have to carry further:
	 * 1.17, 1.14, 1.13, 1.17, 1.33.
	 *
	 * The two smallest sizes on the site went UP rather than down. 9px and 10px
	 * were being asked to carry a price label and a "Δημοφιλής" badge, which is
	 * not small type, it is unreadable type on a phone held at arm's length.
	 */
	--tt-text-micro: 0.75rem;     /* 12px — badges, price labels, meta */
	--tt-text-small: 0.875rem;    /* 14px — secondary interface text */
	--tt-text-body: 1rem;         /* 16px — running copy */
	--tt-text-strong: 1.125rem;   /* 18px — card titles */
	--tt-text-lead: 1.3125rem;    /* 21px — headings inside a page */
	--tt-text-title: 1.75rem;     /* 28px — headings that start a section */

	/*
	 * ELEVATION, WHICH THIS SITE HAD TWO OF AND NEEDED FIVE.
	 *
	 * `--tt-shadow-card` is 4% of navy at one pixel: on a white section it is
	 * literally invisible, and every card on the site read as an outlined
	 * rectangle. Cards drawn in outline only are why the whole thing looked
	 * like a printed form — there is no near and far, so there is no reading
	 * order beyond top-to-bottom.
	 *
	 * Each step is two shadows, not one: a tight dark one for the contact edge
	 * and a wide soft one for the cast. A single blurred shadow is the smudge
	 * every template has; the pair is what actually reads as a lifted object.
	 * Tinted navy rather than black, so the shadow belongs to this palette.
	 */
	--tt-elevation-1: 0 1px 2px rgb(18 43 77 / 6%), 0 1px 3px rgb(18 43 77 / 4%);
	--tt-elevation-2: 0 2px 4px rgb(18 43 77 / 6%), 0 6px 16px rgb(18 43 77 / 8%);
	--tt-elevation-3: 0 4px 8px rgb(18 43 77 / 7%), 0 14px 32px rgb(18 43 77 / 10%);
	--tt-elevation-4: 0 8px 16px rgb(18 43 77 / 8%), 0 28px 56px rgb(18 43 77 / 14%);

	/*
	 * MOTION TOKENS, SO EVERY ANIMATION ON THIS SITE SHARES ONE RHYTHM.
	 *
	 * Durations written at the use site drift: the same site ended up with
	 * 0.18s, 0.2s, 0.24s and 0.3s transitions on things a visitor sees in the
	 * same glance, which reads as several people's work rather than one
	 * product. Three speeds, each with a job:
	 *
	 *   fast   — a state that answers a press or a hover. Must feel immediate.
	 *   base   — something moving from one place to another in view.
	 *   slow   — something arriving from off-screen, which needs the extra
	 *            distance to not look like a flicker.
	 *
	 * The easing is a decelerating curve, not `ease`: things that arrive should
	 * arrive quickly and settle, which is what an object does. `ease` starts
	 * slowly, which is what nothing does.
	 */
	--tt-duration-fast: 140ms;
	--tt-duration-base: 220ms;
	--tt-duration-slow: 520ms;
	--tt-ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
	--tt-ease-spring: cubic-bezier(0.34, 1.32, 0.64, 1);

	/*
	 * THE ONE COLOUR IN THIS PALETTE THAT IS NOT BLUE, AND WHY IT EARNED THAT.
	 *
	 * CLAUDE.md fixes the palette at blue, white and soft gray, and the rating
	 * stars were drawn in brand blue to obey it. Blue stars do not read as a
	 * rating: they read as five blue shapes, and the section had to write out
	 * «4,5 από 5» beside them for anybody to know what they were. A star is one
	 * of the few marks on the web with a colour that IS its meaning.
	 *
	 * Amber, not gold-yellow: 4.63:1 on white as text, and it never appears
	 * except on a star. No other element in this stylesheet may use it.
	 */
	--tt-star: #b46a00;
	--tt-star-fill: #f0a92b;

	/*
	 * THE STAR TILES, AND THE ONE DELIBERATE BREACH OF THE PALETTE RULE.
	 *
	 * «Σε πράσινο Trustpilot τα αστέρια δεν νομίζω να πειράζει.» CLAUDE.md says
	 * «No green», and this is green — the client's decision about their own
	 * branding, taken after the cost was put to them in writing. The cost is
	 * that this is the mark people recognise as a Trustpilot score, and the
	 * agency has no Trustpilot account; see docs/08 §26.
	 *
	 * IT IS ONE LINE TO UNDO. `--tt-star-tile-fill: var(--tt-star-fill)` returns
	 * every tile on the site to the project's amber, and nothing else changes.
	 * Nothing but the tiles may use this token — the palette rule still holds
	 * everywhere else, and «no green» is not now «some green».
	 */
	--tt-star-tile-fill: #00b67a;
	--tt-star-tile-empty: #dcdce6;
}

@media (min-width: 768px) {
	:root {
		--tt-section: var(--tt-space-6);
		--tt-header: 69px;
	}
}

/* Reset ------------------------------------------------------------------- */

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

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

body {
	margin: 0;
	background: var(--tt-white);
	color: var(--tt-text);
	font-family: var(--tt-font);
	font-size: 1rem;
	line-height: 1.6;
	-webkit-font-smoothing: antialiased;
	overflow-x: hidden;
}

img {
	max-width: 100%;
	height: auto;
	display: block;
}

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

:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 2px;
}

/* Typography -------------------------------------------------------------- */

h1,
h2,
h3,
h4 {
	color: var(--tt-navy);
	line-height: 1.25;
	margin: 0 0 var(--tt-space-2);
	font-weight: 700;
	letter-spacing: -0.02em;
}

h1 {
	font-size: clamp(1.875rem, 1.3rem + 2.4vw, 2.5rem);
	letter-spacing: -0.03em;
}

h2 {
	font-size: clamp(1.375rem, 1.1rem + 1.2vw, 1.75rem);
}

/*
 * ONE VALUE FOR THE LEVEL. `h3` rendered at 15, 16, 17 and 18px across the site
 * depending on which component had last defined it, and 17px sits one pixel from
 * the body copy below it and one from the card titles beside it — three sizes
 * nobody can tell apart doing the work of a hierarchy.
 */
h3 {
	font-size: var(--tt-text-strong);
}

p {
	margin: 0 0 var(--tt-space-2);
}

a {
	color: var(--tt-blue);
	text-underline-offset: 0.2em;
}

a:hover,
a:focus {
	color: var(--tt-blue-dark);
}

.tt-lede {
	color: var(--tt-text-muted);
	font-size: 1rem;
	max-width: 62ch;
}

.tt-eyebrow {
	display: inline-block;
	padding: var(--tt-space-1) 12px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-blue-dark);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.01em;
}

/* Layout ------------------------------------------------------------------ */

.tt-container {
	width: 100%;
	max-width: var(--tt-content);
	margin-inline: auto;
	padding-inline: var(--tt-space-2);
}

@media (min-width: 768px) {
	.tt-container {
		padding-inline: var(--tt-space-3);
	}
}

.tt-main {
	display: block;
}

.tt-section {
	padding-block: var(--tt-section);
}

.tt-section--soft {
	background: var(--tt-gray-soft);
}

/*
 * For an intro that leads straight into the next section. Two full section
 * paddings stacked leave a gap that reads as a rendering fault.
 */
.tt-section--lead {
	padding-bottom: var(--tt-space-2);
}

.tt-section__head {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--tt-space-2);
	margin-bottom: var(--tt-space-3);
}

.tt-section__head h2 {
	margin-bottom: 0;
}

/*
 * `p:not([class])` — THE SECTION HEAD STYLES ITS OWN PROSE, NOT COMPONENTS
 * DROPPED INTO IT.
 *
 * As a bare descendant selector this claimed every paragraph inside the box. On
 * the homepage that includes the sample-data disclosure, so `.tt-sample-note`
 * rendered at 15px there and at its own 12px on /epikoinonia/ and
 * /enoikiasi-poulman/ — a 0,1,1 selector beating the component's 0,1,0. The one
 * label on this site whose job is to say «this text is invented» was loudest on
 * the page carrying the invented reviews.
 *
 * Not `> p`: the standfirst is nested one div down, beside the note, so the
 * child combinator would have styled neither. What actually separates them is
 * that one is anonymous prose and the other is a component with a name.
 */
.tt-section__head p:not([class]) {
	margin: var(--tt-space-1) 0 0;
	color: var(--tt-text-muted);
	font-size: 0.9375rem;
}

.tt-section__link {
	font-size: 0.875rem;
	font-weight: 700;
	color: var(--tt-blue-dark);
	text-decoration: none;
	white-space: nowrap;
}

.tt-section__link:hover,
.tt-section__link:focus {
	text-decoration: underline;
}

/*
 * A CLASS THAT WAS IN THE MARKUP AND IN NO STYLESHEET. `404.php` wraps the two
 * recovery buttons in `<p class="tt-404__actions">`, which computed
 * `display: block` with `gap: normal` — so the two 44px targets met at a 0.0px
 * edge and always stacked, even with 375px of room for both.
 */
.tt-404__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
}

/* Buttons ----------------------------------------------------------------- */

/*
 * `hidden` HAS TO WIN, and without this it does not.
 *
 * The browser's own `[hidden] { display: none }` is a user-agent rule, so any
 * author rule setting `display` beats it — and every class here that lays
 * something out sets one. Measured: the ordering form's submit button, given
 * `hidden` by the script that replaces it, stayed on screen and in the
 * accessibility tree because `.tt-button` says `display: inline-flex`. A
 * control that is announced, focusable and does nothing.
 *
 * `!important` because the whole point is to beat every author rule, including
 * ones written later by someone who has not read this.
 */
[hidden] {
	display: none !important;
}

.tt-button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--tt-space-1);
	min-height: var(--tt-tap);

	/*
	 * VERTICAL PADDING, BECAUSE LABELS WRAP. This was `0 var(--tt-space-3)`,
	 * which is fine for the one line it was designed against and wrong for
	 * every other case: «Ρώτησε για την εκδρομή» wraps in the 290px booking
	 * sidebar, and with no vertical padding the two lines ran from edge to edge
	 * of the box. min-height still governs the single-line case, so nothing
	 * that fitted before changes size.
	 */
	padding: 10px var(--tt-space-3);

	/*
	 * A centred flex item is not centred text. `justify-content` centres the
	 * anonymous box the label sits in; the lines INSIDE it still align to
	 * `start`, so a wrapped label came out ragged-left inside a button that was
	 * otherwise symmetrical. That is what «ασύμμετρα» was.
	 */
	text-align: center;

	/* 1.6 from the body leaves two lines looking like two paragraphs. */
	line-height: 1.25;

	border: 1px solid transparent;

	/*
	 * The one shape change that carries the rest. Measured on the reference:
	 * its primary action is 52px tall with a 48px radius, i.e. a capsule, and
	 * ours is 44px — so the same treatment reads slightly tighter here, which
	 * is right for a page whose accent is navy rather than coral.
	 */
	border-radius: var(--tt-radius-action);
	background: var(--tt-blue);
	color: var(--tt-white);
	font-weight: 700;
	text-decoration: none;
	cursor: pointer;

	/*
	 * `transform` is in the list for the press state at the end of this file.
	 * Without it the button snaps back the instant the finger leaves, which
	 * reads as a glitch rather than as a release.
	 */
	transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.tt-button:hover,
.tt-button:focus {
	background: var(--tt-blue-dark);
	color: var(--tt-white);
}

.tt-button--secondary {
	background: var(--tt-white);
	border-color: var(--tt-border);
	color: var(--tt-navy);
}

.tt-button--secondary:hover,
.tt-button--secondary:focus {
	background: var(--tt-gray-soft);
	border-color: var(--tt-text-muted);
	color: var(--tt-navy);
}

.tt-button--quiet {
	background: transparent;
	border-color: transparent;
	color: var(--tt-navy);
	padding-inline: var(--tt-space-2);
}

.tt-button--quiet:hover,
.tt-button--quiet:focus {
	background: var(--tt-gray-soft);
	color: var(--tt-navy);
}

.tt-button--block {
	width: 100%;
}

/* Forms ------------------------------------------------------------------- */

.tt-field {
	display: block;
	margin-bottom: var(--tt-space-2);
}

.tt-field__label {
	display: block;
	margin-bottom: 6px;
	font-size: 0.8125rem;
	font-weight: 600;
	color: var(--tt-navy);
}

.tt-field__hint {
	display: block;
	margin-top: 6px;
	font-size: 0.8125rem;
	color: var(--tt-text-muted);
}

.tt-input,
.tt-select,
.tt-textarea {
	width: 100%;
	min-height: var(--tt-tap);
	padding: 10px 12px;
	border: 1px solid var(--tt-border-control);
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
	color: var(--tt-text);
	font-size: 1rem;
}

.tt-textarea {
	min-height: 120px;
	resize: vertical;
}

.tt-select {
	appearance: none;
	padding-right: var(--tt-space-4);

	/*
	 * A select that cannot show its value says so. Every option here is either a
	 * destination the office named or a month with a count on it, so no width is
	 * ever safe forever; this is the difference between a word that stops and a
	 * word that stops with a mark. Chromium paints the mark, WebKit applies the
	 * same computed value and hard-clips anyway — worth having for the engine
	 * that does, and harmless in the one that does not.
	 */
	overflow: hidden;
	text-overflow: ellipsis;
	background-image: linear-gradient(45deg, transparent 50%, var(--tt-text-muted) 50%),
		linear-gradient(135deg, var(--tt-text-muted) 50%, transparent 50%);
	background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%;
	background-size: 5px 5px, 5px 5px;
	background-repeat: no-repeat;
}

/*
 * THE SOFT HALO WAS OVERRIDING THE FOCUS RING WITH SOMETHING THAT DOES NOT MEET
 * THE STANDARD THE RING WAS BUILT TO MEET.
 *
 * `outline: 3px solid rgb(8 104 215 / 18%)` on `:focus` outranks the site-wide
 * `:focus-visible` ring by being more specific, so every text field, select and
 * textarea on the site drew an 18%-alpha halo instead: measured 1.29:1 against
 * the form panel, where WCAG 2.2 SC 1.4.11 asks for 3. Tabbing through
 * /epikoinonia/ showed the difference in one column — the inputs at 1.29:1 and
 * the radios and checkboxes beside them, which never matched this selector, at
 * 4.92:1.
 *
 * On the fields the border also turned blue, which does clear 3:1, so those had
 * a conforming-but-thin indicator. THE HERO HAD NOTHING: `.tt-search--hero
 * .tt-select` sets `border: 0`, so the three controls the whole homepage is
 * built around were left with only the 1.30:1 halo — the site's designated
 * primary element was the one place a keyboard visitor could not see where they
 * were.
 *
 * `:focus-visible` and no outline of its own: the global ring at the top of this
 * file then applies, at full strength, in the colour each surface declares.
 * 4.92:1 on the form panels, 5.29:1 in the hero, and the same ring on the
 * checkbox as on the box above it.
 */
.tt-input:focus-visible,
.tt-select:focus-visible,
.tt-textarea:focus-visible {
	border-color: var(--tt-blue);
}

/*
 * The hero cells have no border to thicken and no room outside them — the bar
 * clips at its own rounded corner — so the ring is drawn inside the cell
 * instead. Same colour, same width, same token; it just grows inwards.
 */
.tt-search--hero .tt-select:focus-visible,
.tt-search--hero .tt-search__text:focus-visible {
	outline-offset: -3px;
}

.tt-input::placeholder,
.tt-textarea::placeholder {
	color: var(--tt-text-muted);
}

.tt-input[aria-invalid="true"],
.tt-select[aria-invalid="true"],
.tt-textarea[aria-invalid="true"] {
	border-color: #b3261e;
}

/*
 * AN ERROR IS READ, SO IT GETS THE FLOOR. It was 13px — the smallest type on the
 * form panel, set beside a 16px input value, in the one place where a visitor
 * has already failed once and has to understand a sentence to get past it.
 */
.tt-field__error {
	display: block;
	margin-top: 6px;
	font-size: var(--tt-text-body);
	font-weight: 600;
	color: #b3261e;
}

/* Skip link --------------------------------------------------------------- */

.tt-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100;
	padding: var(--tt-space-1) var(--tt-space-2);
	background: var(--tt-navy);
	color: var(--tt-white);
	border-radius: 0 0 var(--tt-radius-button) 0;
}

.tt-skip-link:focus {
	left: 0;
	color: var(--tt-white);
}

/* Header ------------------------------------------------------------------ */

/*
 * IT STAYS, BECAUSE IT WAS ALIVE FOR ABOUT A FIFTIETH OF THE PAGE.
 *
 * A tour page is 4,613px on a laptop and 6,891px on a phone, and the header —
 * the logo, the navigation and the one call to action — left the screen after
 * 70px of it. The visitor reads the itinerary, the inclusions, the price basis
 * and the cancellation terms, decides, and then has nothing in front of them.
 *
 * `sticky` and not `fixed`: a fixed header is out of flow, so the page beneath
 * it needs a padding equal to its own height, and that number then has to be
 * kept in sync by hand in a second place. Sticky keeps its space in the
 * document and there is nothing to synchronise.
 */
.tt-header {
	position: sticky;
	top: 0;
	z-index: 20;
	background: var(--tt-white);
	border-bottom: 1px solid var(--tt-border);
}

/*
 * EXCEPT WHILE THE DRAWER IS OPEN, AND THIS IS NOT A DETAIL.
 *
 * The mobile menu is INSIDE the header, which is what lets it push the page
 * down instead of floating over it — the reason the 70vh ceiling could be
 * removed at all. Pin the header to the viewport and that reasoning inverts:
 * an open drawer is 385px, the bar is 62, and 447 of an iPhone SE's 553 visible
 * pixels become permanently spoken for. The visitor would be scrolling a 106px
 * slot.
 *
 * So the header gives up its stickiness for exactly as long as the menu is
 * open, and the whole thing scrolls away like any other block. Nothing is
 * disabled and nothing is remembered: the rule stops matching the moment the
 * drawer closes.
 *
 * ITS OWN RULE, NOT A SELECTOR LIST — the reason is written out at the sticky
 * bar further down. A list is invalid as a whole if any selector in it is, and
 * `:has()` is the half that would take the plain `.tt-header` rule with it.
 */
.tt-header:has( .tt-nav.is-open ) {
	position: relative;
}

/*
 * ON A PHONE, HEADER AND HERO ARE ONE OBJECT — MEASURED AGAINST THE TWO SITES
 * THE CLIENT NAMED, AND NOT COPIED FROM EITHER.
 *
 * What was there: a 62px white bar, then a 589px navy hero, and inside it a
 * 338px white search panel. The client's own photograph — the largest image on
 * the page and the only thing on it that was actually photographed for them —
 * had about 90px of visible sky. The bar and the hero read as two stacked
 * bands, so the first screen spent 62 of its pixels saying «this is a website»
 * before saying anything about travel.
 *
 * GetYourGuide solves it by having no photograph at all: pale ground, centred
 * promise, one pill. flyhoper keeps a white bar and insets the photograph as a
 * rounded card. Neither is right here, and the reason is the same in both
 * directions — this agency HAS real photography and a navy wordmark. Dropping
 * the photograph throws away the one asset a stock-image competitor cannot
 * match; inverting the header to sit on the photograph needs a white logo that
 * does not exist, and would have to be commissioned before it could ship.
 *
 * So the bar stays white and stops being a band: 88% white over a blur, with
 * the photograph running underneath it to the top of the document. The navy
 * wordmark keeps a near-white ground to sit on, which is the whole reason the
 * translucency is 88 and not 60 — at 60 the logo was being read against sky.
 *
 * THE HERO IS PULLED UP BY EXACTLY THE HEADER'S OWN HEIGHT and given it back as
 * padding, so nothing inside the hero moves by a pixel; only the photograph
 * grows upward behind the glass. `--tt-header` is the same token the sticky
 * offset and `scroll-padding-top` already use, so the three cannot drift apart.
 *
 * `:has()` rather than a body class, for the reason the drawer rule above gives:
 * where it is not supported the whole block is skipped and a phone gets the two
 * stacked bands it got yesterday. There is nothing to fall back to because the
 * fallback is the previous design.
 *
 * PHONE ONLY. Above 768px the hero has room, the disc behind it is drawn, and a
 * translucent bar over a photograph solves a crowding problem the desktop does
 * not have.
 */
@media (max-width: 767px) {
	/*
	 * THE PHOTOGRAPH IS A CARD, NOT A BAND — WHICH IS THE SECOND ANSWER TO THIS
	 * QUESTION AND THE ONE THE REFERENCE ACTUALLY GIVES.
	 *
	 * The first attempt ran the photograph up under a translucent header so the
	 * two read as one object. It worked in the sense that the seam went away,
	 * and the client's verdict on the result was «πολύ πρόχειρο». Looking at
	 * flyhoper again on a phone shows why: it does not join its header to its
	 * hero at all. It keeps a plain white bar and then insets the photograph as
	 * a rounded card with margins on both sides — a deliberate object sitting on
	 * the page, rather than a strip of image that happens to reach the edges.
	 *
	 * A full-bleed band has no edges of its own, so it reads as a background
	 * that got cut off; the moment it has four corners and some white around it,
	 * it reads as a picture somebody chose. That is the whole difference between
	 * the two screenshots, and it costs one inset and one radius.
	 *
	 * THE HEADER GOES BACK TO SOLID WHITE, and the glass goes with the band. It
	 * was only ever there to hide a seam that no longer exists, and it was the
	 * riskier half: the wordmark is navy lettering and 88% white over a bright
	 * sky was the thinnest ground it could safely be read on. Solid white asks
	 * no such question.
	 *
	 * Phone only. The desktop hero is a pale panel with a disc behind it and has
	 * never been a band.
	 */
	body:has( .tt-hero--photo ) .tt-hero {
		padding: var(--tt-space-2);
		border-radius: 0;
		background: var(--tt-white);
	}

	/*
	 * THE NAVY MOVES ONTO THE CARD, AND FORGETTING IT MADE THE HEADING
	 * INVISIBLE.
	 *
	 * `.tt-hero--photo` sets `background: var(--tt-navy)` on the SECTION, and
	 * that navy is not decoration: it is what sits behind white hero text while
	 * the largest image on the page is still downloading, and what sits there
	 * for good if that image 404s. The rule further up this file records the day
	 * that was 1.11:1.
	 *
	 * Painting the section white for the card to sit on took the fallback with
	 * it — white text on white, 1.00:1 — and `homepage.spec.js` «the text is
	 * readable before the photograph arrives» said so on the first run. The
	 * surface the text is over is now the card, so the card is where the navy
	 * belongs.
	 */
	.tt-hero--photo .tt-hero__media {
		inset: var(--tt-space-2);
		z-index: 0;
		border-radius: 28px;
		background: var(--tt-navy);
	}

	/*
	 * The media stops being behind the whole section, so the text has to be told
	 * it is in front of it. `z-index: -1` put the picture behind the section's
	 * own background and everything painted over it for free; an inset card
	 * cannot do that, because a negative index would put it behind the white
	 * this section now paints.
	 */
	.tt-hero--photo .tt-hero__inner {
		z-index: 1;
		padding: var(--tt-space-3) var(--tt-space-2);
	}

	/*
	 * THE ARRIVAL IS TALLER THAN THE WORKING STATE, AND ONLY THE ARRIVAL.
	 *
	 * «Σκέψου την πρώτη εντύπωση με το που μπαίνει κάποιος στη σελίδα.» The card
	 * was 313px, which is enough to hold four lines of text and not enough to be
	 * a photograph — the picture was doing the job of a background when it is
	 * the one thing on this page that was taken for this client.
	 *
	 * THE HEIGHT IS SPENT ONLY WHILE THE PANEL IS SHUT, which is what makes it
	 * affordable. `:has(.is-collapsed)` stops matching the instant somebody taps
	 * «Πού θες να πας;», so the three fields and the submit button open into the
	 * hero's ordinary height rather than 66% of the screen plus a form. On a
	 * 390x664 profile the open panel already finishes 30px above the fold; there
	 * was no room to take, and this does not take any.
	 *
	 * `svh` and not `vh`: `vh` on iOS is the height WITHOUT Safari's bars, so a
	 * 66vh card is 66% of a screen the visitor cannot see all of. `svh` is the
	 * small viewport — bars showing, which is the state a page is arrived in.
	 * It is also self-limiting on a short screen: on a 553px iPhone SE 66svh is
	 * 365px, barely more than the content, so the smallest phones are left
	 * alone instead of being handed a card that pushes everything off.
	 *
	 * No fallback for `svh`. Where it is not supported the declaration is
	 * dropped and the card is the height of its contents, which is what it was
	 * yesterday.
	 */
	.tt-hero--photo:has( .tt-search--hero.is-collapsed ) {
		display: flex;
		flex-direction: column;
		min-block-size: 66svh;
	}

	/*
	 * And the stack centres in what it was given, so the extra height is
	 * photograph above AND below the words rather than a pile of text with a
	 * strip of picture under it.
	 */
	.tt-hero--photo:has( .tt-search--hero.is-collapsed ) .tt-hero__inner {
		flex: 1;
		justify-content: center;
	}
}

/*
 * AND WHILE THE ENQUIRY FORM IS BEING FILLED IN, for the reason written out at
 * `scroll-padding-top` below: walking BACKWARDS through a tour page with
 * Shift+Tab, WebKit parks focused fields at y=56 under a 62px header — 35 of
 * them in a 60-stop walk, and every one inside `#tt-form`. Neither scroll
 * property moves it, and there is no third one to reach for. WCAG 2.2 SC 2.4.11.
 *
 * `body.tt-form-focus` is not new: navigation.js already sets it on `focusin`
 * so the sticky booking bar can get off the bottom edge, for the identical
 * reason at the opposite end of the screen. This is that mechanism doing its
 * second job, and it costs no new script.
 *
 * The header is not needed while somebody is typing their telephone number
 * into a form, so nothing is lost by letting it scroll.
 */
body.tt-form-focus .tt-header {
	position: relative;
}

/*
 * THE SHADOW SAYS THERE IS SOMETHING ABOVE YOU, and it is the only thing that
 * does. A header pinned to the top edge looks identical whether the page is at
 * the beginning or a thousand pixels in; the hairline border is the same border
 * it has at rest. The shadow appears over the first 16px of scroll and is
 * therefore a readout of position rather than an effect.
 *
 * A scroll-driven animation and no JavaScript at all. `animation-timeline:
 * scroll()` is supported in BOTH engines here — checked, because the fallback
 * would have been a listener on every scroll frame:
 *
 *   CSS.supports( 'animation-timeline', 'scroll()' )   WebKit true, Chromium true
 *
 * Where it is NOT supported the animation simply never runs and the header
 * keeps its border, which is what it looks like today.
 *
 * Reduced motion needs no rule here and gets the right answer anyway: the
 * blanket block collapses `animation-duration` to 0.01ms, and on a scroll
 * timeline a duration that short resolves the whole range immediately — so the
 * shadow snaps on at the first pixel of scroll instead of easing in over
 * sixteen. That is the correct behaviour for somebody who asked for less
 * movement: the state still changes, it simply does not travel.
 */
@keyframes tt-header-lifts {
	to {
		box-shadow: 0 1px 12px rgb(18 43 77 / 10%);
	}
}

.tt-header {
	animation: tt-header-lifts linear both;
	animation-timeline: scroll();
	animation-range: 0 var(--tt-space-2);
}

/*
 * AND NOW EVERYTHING THE BROWSER SCROLLS TO HAS TO CLEAR IT. WCAG 2.2 SC 2.4.11.
 *
 * The site already carries the same compensation for the OTHER edge, where the
 * sticky booking bar sits: `scroll-padding-bottom` on the root and a matching
 * `scroll-margin-bottom` on every focusable thing, because scroll-padding alone
 * was measured as not enough in WebKit. A pinned header is that problem turned
 * upside down — tabbing backwards up a page, or following any link to a
 * fragment, stops with the target underneath the header instead of behind the
 * bar.
 *
 * Both properties again for the same reason as the bottom edge, and at every
 * width rather than only on a phone, because this header is sticky everywhere
 * while the bar is a phone-only element.
 *
 * The extra 8px is so a focused control is visibly clear of the border rather
 * than tucked against it — the same argument as the 88px below.
 */
html {
	scroll-padding-top: calc(var(--tt-header) + var(--tt-space-1));
}

/*
 * ONE PROPERTY AND NOT THE PAIR, WHICH IS THE OPPOSITE OF THE BOTTOM EDGE AND
 * IS MEASURED. Both were written first, by symmetry with the rule above. They
 * ADD: `scroll-padding` insets the scrollport and `scroll-margin` grows the
 * element's box, so a 70px pair leaves 140px of clearance for a 69px header —
 * 85px of blank screen above whatever the visitor was sent to. Two tests caught
 * it before it shipped.
 *
 *   both      the results land 154px down, 85 past the header
 *   either    77px, 8 past the header
 *   neither   0, which is 69px underneath it
 *
 * Same numbers in both engines, at 1280 and on a phone profile.
 *
 * AND NEITHER OF THEM FIXES BACKWARDS TABBING IN WEBKIT, which is the case that
 * can actually reach the top edge: walking forward scrolls DOWN and stops with
 * the target near the bottom, so the header is never in the way. Shift+Tab up a
 * tour page, 60 stops, iPhone 14 profile:
 *
 *   engine    scroll-padding only   both
 *   chromium  0 controls behind     2
 *   webkit    35                    35
 *
 * Every one of the 35 is inside `#tt-form`, and the fix for that is not a third
 * scroll property — see the header's own rule for `body.tt-form-focus`, which
 * is the same mechanism the sticky bar already uses at the other edge for
 * exactly the same reason.
 */

.tt-header__inner {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--tt-space-2);
	min-height: 58px;
}

@media (min-width: 768px) {
	.tt-header__inner {
		min-height: 68px;

		/*
		 * TWO AXES, BECAUSE ONE OF THEM WAS RESERVING SPACE FOR A DRAWER THAT IS
		 * SHUT.
		 *
		 * This is a wrapping flex row, and below 1200px the navigation takes
		 * `flex-basis: 100%` and drops to a second line — a line that is 1px tall
		 * while the drawer is closed. `gap` set both axes, so 32px of row gap sat
		 * above that invisible line on every tablet: the bar measured 78px
		 * against a `--tt-header` token of 69, the brand ended at y=39, and the
		 * remaining 34px was empty white. Everything that clears the header —
		 * the sticky filter panel, the booking column, `scroll-padding-top` —
		 * was reading a number 9px short of the thing it had to clear.
		 *
		 * The column gap is what separates the brand from the navigation, and it
		 * keeps its 32. The row gap is what separates the bar from the OPEN
		 * drawer, and 16 is what the phone has always used for the same job.
		 * Setting it there brings the measured header back to 69px — the value
		 * the token already claimed, so nothing downstream moves.
		 *
		 * Not `row-gap: 0`: that gap is the only separation between the bar and
		 * the expanded menu, and at zero the first link lands 1px under the
		 * button that opened it.
		 */
		column-gap: var(--tt-space-4);
		row-gap: var(--tt-space-2);
	}
}

/* Brand */

.tt-brand {
	display: inline-flex;
	align-items: center;
	gap: var(--tt-space-1);
	text-decoration: none;
	margin-right: auto;
}

.tt-brand__mark {
	display: grid;
	place-items: center;
	width: 35px;
	height: 35px;
	border-radius: 9px;
	background: var(--tt-blue);
	color: var(--tt-white);
	font-weight: 800;
	font-size: 0.875rem;
	letter-spacing: 0.02em;
}

.tt-brand__text {
	display: block;
	color: var(--tt-navy);
	font-weight: 800;
	line-height: 1;
	letter-spacing: 0.02em;
	font-size: 0.9375rem;
}

.tt-brand__sub {
	display: block;
	margin-top: 4px;
	color: var(--tt-blue);
	font-size: 0.5625rem;
	font-weight: 700;
	letter-spacing: 0.18em;
}

/* Navigation */

.tt-nav__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.tt-nav__list a {
	display: block;
	color: var(--tt-navy);
	text-decoration: none;
	font-weight: 600;
}

/*
 * `:focus-visible` AND NOT `:focus`, BECAUSE TWO ITEMS WERE CLAIMING TO BE THE
 * PAGE YOU ARE ON.
 *
 * The drawer moves focus to its first link when it opens, and blue is exactly
 * the colour `.current-menu-item` uses to mean «you are here». So opening the
 * menu from any inner page painted «Αρχική» in the current-page colour beside
 * the item that genuinely was current — two answers to one question, on a
 * control whose only job is telling somebody where they are.
 *
 * `:focus-visible` withholds the paint from a programmatic or pointer focus and
 * keeps it for the keyboard, which is who it was for.
 */
.tt-nav__list a:hover,
.tt-nav__list a:focus-visible {
	color: var(--tt-blue);
}

/*
 * A SECOND CUE BESIDES THE COLOUR. «You are here» carried by hue alone fails
 * WCAG 2.2 SC 1.4.1 for anybody who cannot separate #0868D7 from #122B4D, and
 * it is the one piece of state in the header. The underline sits below the
 * text rather than on it, so it reads as a marker instead of as a link that has
 * been visited.
 */
.tt-nav__list .current-menu-item > a,
.tt-nav__list .current-menu-ancestor > a {
	color: var(--tt-blue);
	text-decoration: underline;
	text-decoration-thickness: 2px;
	text-underline-offset: 6px;
}

.tt-header__actions {
	display: flex;
	align-items: center;
	gap: var(--tt-space-1);
}

/*
 * THE OFFICE TELEPHONE NEVER RENDERED, AT ANY WIDTH, AND BOTH HALVES OF THAT
 * WERE WRITTEN IN THIS FILE.
 *
 * These rules were authored for a phone number sitting in the top bar: hidden
 * below 1200px because the bar had no room, shown above it. Then the number was
 * measured against the bar — it needs 1,222px of a container that is capped at
 * 1,180 — and moved into the drawer instead, where a telephone number belongs
 * anyway. The visibility rules stayed pointing at the bar.
 *
 * The result was a link that is `display: none` below 1200 by the rule below,
 * and above 1200 sits inside `.tt-nav__cta`, which that breakpoint also hides.
 * Measured 0 x 0 on six pages at seventeen widths from 320 to 1920 in both
 * engines, drawer open and drawer closed. The markup was correct the whole time.
 *
 * So the pair is inverted: the drawer shows it, the wide bar — which still has
 * no room — does not.
 */
.tt-header__phone {
	display: flex;
	align-items: center;
	gap: 6px;

	/*
	 * A full-width row rather than an inline link, because in the drawer the row
	 * IS the tap target: it sits under the «Επικοινώνησε μαζί μας» button in a
	 * stretched flex column, and a 41px-wide hit area under a full-width button
	 * is the thing a thumb misses.
	 */
	min-height: var(--tt-tap);
	padding-inline: var(--tt-space-1);
	color: var(--tt-navy);
	font-size: 0.875rem;
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
}

.tt-header__phone:hover,
.tt-header__phone:focus-visible {
	color: var(--tt-blue);
}

/*
 * The glyph is muted at rest so the number reads first, and follows the link's
 * colour once the pointer is on it — one object, not two.
 */
.tt-header__phone-icon {
	display: inline-flex;
	color: var(--tt-text-muted);
}

.tt-header__phone:hover .tt-header__phone-icon,
.tt-header__phone:focus-visible .tt-header__phone-icon {
	color: inherit;
}

@media (min-width: 1200px) {
	.tt-header__phone {
		display: none;
	}
}

/*
 * WHATSAPP, AND THE ONLY GREEN ON THIS SITE.
 *
 * #25D366 is WhatsApp's own, and the palette rule it appears to break does not
 * cover it: that rule governs the colours this business speaks in, not somebody
 * else's registered mark. The site already reasons this way one section down,
 * about Tripadvisor — a wordmark «redrawn approximately is worse than a name
 * spelled correctly» — and the same sentence gives the opposite answer for a
 * mark with no name in it. A navy WhatsApp glyph is not a quieter WhatsApp
 * glyph; it is a speech bubble nobody recognises.
 *
 * It is 1.87:1 against white, which is why nothing is ever said in it. The
 * accessible name is on the link, the button beside it says «Επικοινωνία» in
 * words, and this is an icon rather than text — WCAG 1.4.11 asks 3:1 of a
 * graphic that carries meaning ON ITS OWN, and this one carries a duplicate of
 * a link the header already offers.
 *
 * 22px drawn, from the reference at 1440. The tap target is the full 44 —
 * padding rather than size, so the mark stays the size the reference sets it
 * while the thing a thumb aims at is the size this project insists on.
 */
/*
 * THE LANGUAGE SWITCHER — a segmented control, not a dropdown.
 *
 * Three items at most. A `<select>` for three items costs a tap to open, a
 * scan and a tap to choose, on a bar whose whole job is to be one tap; this is
 * two links and a marker, so it also works with JavaScript off.
 *
 * IT USED TO BE TWO BARE LETTER PAIRS AND THE CLIENT WAS RIGHT ABOUT IT.
 * Muted navy on white, no fill, no border, one soft-blue chip on the current
 * language — quiet to the point of not reading as a control at all. «ΕΛ EN»
 * floating in a header row is two letter pairs; nothing said they were
 * connected, that one could be pressed, or that any of it was about language.
 *
 * So the three parts a segmented control actually needs are drawn now: a track
 * that groups them, a chip that lifts the current one off it, and a translate
 * mark that names the whole thing without spending the words the row has no
 * room for. Still no colour that is not already in this palette, and still
 * nothing louder than the navigation beside it.
 *
 * THE SHAPE IS SPECIFIED, NOT CHOSEN. `--tt-radius-action` carries the
 * measurement taken off the reference the client picked — «actions, navigation,
 * segmented switches → pill» — and this is the only segmented switch on the
 * site, so it is the one control that line was written for.
 *
 * SIZED AGAINST THE ROW THAT ALREADY EXISTS. The header was measured at
 * 1,200px with 943px needed and 1,068px available. The track is the marks plus
 * the glyph plus 2px of padding either side: 106px for two languages, 144px for
 * three, both inside that headroom. It renders only where a translation
 * exists, so on a page with none it takes nothing at all.
 */
/*
 * THE ONE-MARK SWITCHER, which is what the bar draws on every page of this
 * site: the translate glyph, and a list of the languages under it when somebody
 * asks. A segmented pill spent a third of the header row saying «ΕΛ EN» at a
 * visitor who was reading one of them already.
 *
 * 44px of target under an 18px drawing. The mark is deliberately quiet — muted,
 * no border, no fill — because it sits in a header row beside a telephone and a
 * WhatsApp button and is not competing with either. It earns its contrast on
 * hover, on focus and while the list is open, which is where a quiet control is
 * allowed to be found.
 */

.tt-langs__menu {
	position: relative;
}

.tt-langs__toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: var(--tt-tap);
	block-size: var(--tt-tap);
	border-radius: var(--tt-radius-action);
	color: var(--tt-text-muted);
	text-decoration: none;
	cursor: pointer;

	/*
	 * The disclosure triangle, removed twice: `list-style` is what the standard
	 * says and `::-webkit-details-marker` is what Safari still reads. A marker
	 * beside the glyph would be a second thing to look at in a control whose
	 * whole point is being one.
	 */
	list-style: none;
	transition:
		background-color var(--tt-duration-fast) var(--tt-ease-out),
		color var(--tt-duration-fast) var(--tt-ease-out);
}

.tt-langs__toggle::-webkit-details-marker {
	display: none;
}

@media (hover: hover) {
	.tt-langs__toggle:hover {
		background: var(--tt-gray-soft);
		color: var(--tt-blue-dark);
	}
}

/* Open is a state worth drawing: the mark stays lit while its list is on screen,
 * so the thing that opened it is obvious once the eye has moved down. */

.tt-langs__menu[open] .tt-langs__toggle {
	background: var(--tt-gray-soft);
	color: var(--tt-blue-dark);
}

/*
 * The list. Right-aligned under the mark because the mark sits at the right end
 * of the header row and a panel hanging off the left edge would reach across the
 * navigation it belongs beside.
 */

.tt-langs__panel {
	position: absolute;
	inset-block-start: calc(100% + 6px);
	inset-inline-end: 0;
	z-index: 30;
	min-inline-size: 11rem;
	margin: 0;
	padding: 4px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-2);
	list-style: none;
}

.tt-langs__option {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-space-2);
	min-block-size: 36px;
	padding-block: 6px;
	padding-inline: var(--tt-space-2);
	border-radius: var(--tt-radius-button);
	color: var(--tt-text);
	font-size: 0.9375rem;
	text-decoration: none;
	white-space: nowrap;
	transition: background-color var(--tt-duration-fast) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-langs__option:hover {
		background: var(--tt-gray-soft);
	}
}

/*
 * The name, and under it where the link goes when that is not this page. The
 * homepage has no English counterpart and never will — it is assembled from
 * settings rather than written as a post — so «English» there leads to the
 * catalogue, and a row that said only «English» would have somebody click it
 * and land in a tour listing wondering what they broke.
 */

.tt-langs__name {
	display: flex;
	flex-direction: column;
	gap: 1px;
}


/*
 * The language being read. The tick is the part that matters: colour alone
 * would be a WCAG 1.4.1 failure, and a list is where that is easiest to get
 * wrong because every row looks like every other one.
 */

.tt-langs__option.is-current {
	color: var(--tt-blue-dark);
	font-weight: 600;
}

.tt-langs__tick {
	flex: 0 0 auto;
}

.tt-langs__track {
	display: inline-flex;
	gap: 2px;
	align-items: center;
	padding: 2px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-action);
	background: var(--tt-gray-soft);
}

/*
 * The translate mark. Decorative in the strict sense — `the_icon()` hides it
 * from assistive technology and the links beside it carry «Ελληνικά» and
 * «English» as their accessible names — but it is the reason a sighted visitor
 * knows what the two letter pairs are before reading them.
 *
 * Muted, and it stays muted on hover: it is a label, not a target.
 */
.tt-langs__mark {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding-inline-start: 6px;
	padding-inline-end: 2px;
	color: var(--tt-text-muted);
}

.tt-langs__list {
	display: flex;
	gap: 2px;
	align-items: center;
	margin: 0;
	padding: 0;
	list-style: none;
}

.tt-langs__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: 34px;

	/*
	 * 28px AND NOT WHATEVER THE LINE-HEIGHT GIVES. Measured at 1440 before this
	 * line existed, each mark was 34x21 — and WCAG 2.2 SC 2.5.8 asks for 24x24
	 * CSS pixels, so the height failed while the width passed. A control this
	 * quiet is exactly the one where a missing floor goes unnoticed.
	 *
	 * The drawer's rows override it to the full 44px tap target below, because
	 * there the row IS the target and a finger is doing the pointing.
	 */
	min-block-size: 28px;
	padding-inline: var(--tt-space-1);
	border-radius: var(--tt-radius-action);
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	text-decoration: none;

	/*
	 * The chip moves between two languages a visitor is switching in front of,
	 * so it transitions rather than jumps — `fast`, which is the token for a
	 * state that answers a press.
	 */
	transition:
		background-color var(--tt-duration-fast) var(--tt-ease-out),
		color var(--tt-duration-fast) var(--tt-ease-out),
		box-shadow var(--tt-duration-fast) var(--tt-ease-out);
}

/*
 * The language being read. `is-current` and not `[aria-current]` alone,
 * because the attribute is what a screen reader announces and this is what an
 * eye reads — keeping them separate means neither has to carry the other's job.
 *
 * A white chip on the gray track, lifted by the smallest elevation the site
 * has. This is the whole trick a segmented control turns: the selected item is
 * NEARER than the track, and everything else is flush with it. Colour alone
 * would have been a WCAG 1.4.1 problem too — the shadow and the surface change
 * say «selected» without asking anybody to compare two blues.
 */
.tt-langs__link.is-current {
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-1);
	color: var(--tt-blue-dark);
}

@media (hover: hover) {
	.tt-langs__link:hover {
		color: var(--tt-text);
	}

	/*
	 * The current one is already as dark as it gets, so hover moves the surface
	 * instead of the text — otherwise pointing at it does nothing at all, which
	 * reads as a dead control.
	 */
	.tt-langs__link.is-current:hover {
		box-shadow: var(--tt-elevation-2);
		color: var(--tt-blue-dark);
	}
}

/*
 * THE BAR SHAPE SITS OUT ABOVE 1,199px ONLY, in the same pair as the WhatsApp
 * mark below: exactly one of the two shapes is reachable at any width, or the
 * same three links appear twice in one tab order.
 */
.tt-langs--bar {
	display: none;
}

.tt-langs--row {
	inline-size: 100%;
	margin-block-start: var(--tt-space-2);
	padding-block-start: var(--tt-space-2);
	border-block-start: 1px solid var(--tt-border);
}

/*
 * «Γλώσσα», written out, because the drawer has the room the bar does not and
 * a heading is a better answer than a glyph wherever there is space for one.
 *
 * No `text-transform`, deliberately: uppercasing «Γλώσσα» in CSS gives
 * «ΓΛΏΣΣΑ», and Greek capitals do not take accents. The rule holds for every
 * accented word on this site.
 */
.tt-langs__label {
	margin: 0 0 var(--tt-space-1);
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	font-weight: 600;
	letter-spacing: 0.04em;
}

/*
 * In the drawer the row IS the tap target, so the track goes full width and the
 * marks grow into full-height rows carrying the endonym rather than the
 * two-letter code.
 */
.tt-langs--row .tt-langs__track {
	display: flex;
	inline-size: 100%;
}

.tt-langs--row .tt-langs__list {
	flex: 1;
	gap: 2px;
}

.tt-langs--row .tt-langs__item {
	flex: 1;
}

.tt-langs--row .tt-langs__link {
	inline-size: 100%;

	/*
	 * THE FULL 44 ON THE LINK ITSELF, and it briefly was not. Deducting the
	 * track's 2px padding and 1px border from the target looked like arithmetic
	 * and was not: the thing a thumb aims at is the <a>, not the box around it,
	 * so the row measured 175x38 against the 44 this project holds itself to.
	 * The track is what grows instead.
	 */
	min-block-size: var(--tt-tap);
	min-inline-size: 0;
	padding-inline: var(--tt-space-2);
	font-size: 0.9375rem;
}

@media (min-width: 1200px) {
	.tt-langs--bar {
		display: block;
	}

	.tt-langs--row {
		display: none;
	}
}

.tt-header__whatsapp {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: #25d366;
	text-decoration: none;
}

.tt-header__whatsapp--bar {
	inline-size: var(--tt-tap);
	block-size: var(--tt-tap);

	/*
	 * Optical rather than metric. The mark is a circle and the button beside it
	 * is a rounded rectangle, so equal gaps measured edge to edge read as too
	 * much space on the circle's side. Pulled back by 4px, checked at 1280,
	 * 1440 and 1920.
	 */
	margin-inline-end: -4px;
}

/*
 * `filter` and not a second colour. The mark is one flat green by definition,
 * and picking a darker green for the hover would be inventing a shade of
 * somebody else's brand; darkening the one they publish is the same colour,
 * dimmer.
 */
.tt-header__whatsapp:hover {
	filter: brightness(0.88);
}

/*
 * The drawer shape: the telephone row directly above it, with a word instead of
 * a number. Same height, same padding, same weight — two ways to reach the same
 * office should not look like two different kinds of thing.
 */
.tt-header__whatsapp--row {
	gap: 6px;
	justify-content: flex-start;
	min-height: var(--tt-tap);
	padding-inline: var(--tt-space-1);
}

.tt-header__whatsapp--row svg {
	inline-size: 18px;
	block-size: 18px;
}

/*
 * The word is the site's, not WhatsApp's: navy at the weight of the telephone
 * beside it. Green text at 1.87:1 would be the accessibility failure the mark
 * itself is exempt from.
 */
.tt-header__whatsapp-label {
	color: var(--tt-navy);
	font-size: 0.875rem;
	font-weight: 600;
}

.tt-header__whatsapp--row:hover .tt-header__whatsapp-label,
.tt-header__whatsapp--row:focus-visible .tt-header__whatsapp-label {
	color: var(--tt-blue);
}

/*
 * ONE OF THE TWO, NEVER BOTH. The pair mirrors `.tt-header__cta` and
 * `.tt-nav__cta` exactly: the bar is empty of actions below 1200 and the drawer
 * does not exist above it. Two links to the same conversation inside one open
 * menu is a duplicate in the tab order, and it is the sort of thing that only
 * shows up on a keyboard.
 */
@media (max-width: 1199px) {
	.tt-header__whatsapp--bar {
		display: none;
	}
}

@media (min-width: 1200px) {
	.tt-header__whatsapp--row {
		display: none;
	}
}

/*
 * Declared before the breakpoints below, so the desktop rule that hides it
 * wins on specificity ties. Moving this after them silently brings the
 * hamburger back on desktop.
 */
.tt-nav-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--tt-space-1);
	min-height: var(--tt-tap);
	min-width: var(--tt-tap);
	padding: 0 14px;
	border: 1px solid var(--tt-border);

	/* The other exception the audit caught. It is a control, so it is a capsule. */
	border-radius: var(--tt-radius-action);
	background: var(--tt-white);
	color: var(--tt-navy);
	font-weight: 600;
	cursor: pointer;
}

.tt-nav-toggle:hover {
	background: var(--tt-gray-soft);
}

/*
 * Mobile: the drawer wraps onto its own row below the bar. flex-basis 100%
 * on a wrapping flex container is what pushes it there; without it the menu
 * would try to sit beside the brand.
 */
/*
 * THE DRAWER'S BREAKPOINT IS 1200px, NOT 767px, AND IT IS MEASURED.
 *
 * The header holds a logo, the menu and the call-to-action on one line, and the
 * line has a width budget: at 1440px the content box is 1132px and the three
 * came to 1146px, so the button wrapped onto a row of its own. That is what six
 * menu items cost once the service pages joined them and the text lockup became
 * a wordmark.
 *
 * The rest of the theme still breaks at 768px. This is the one component whose
 * content decides its own breakpoint, which is the honest way round: a tablet
 * gets the drawer because the bar does not fit on a tablet, not because 768 is
 * a number people write down.
 */
@media (max-width: 1199px) {
	/*
	 * OPENS AND CLOSES OVER 0.2s INSTEAD OF APPEARING.
	 *
	 * This was `display: none` to `display: block`, which cannot be transitioned
	 * at all, so the menu materialised in one frame and the rest of the page
	 * jumped down under it. The purpose is not decoration: the movement is what
	 * tells the visitor WHERE the panel came from, on the one control that
	 * rearranges the page under their thumb.
	 *
	 * `visibility`, not `display`, is what hides it — and it is load-bearing.
	 * `max-height: 0` alone leaves the links focusable, so a keyboard user tabs
	 * into a menu nobody can see. `visibility: hidden` removes them from the tab
	 * order AND is transitionable, which `display` is not.
	 *
	 * The delay on the closing `visibility` is why it is written twice: going out,
	 * visibility must wait for the height to finish or the links vanish before the
	 * panel does; coming in, it must apply immediately or the panel opens empty.
	 *
	 * IT USED TO ANIMATE `max-height`, AND A CEILING IS NOT A MEASUREMENT.
	 *
	 * The old rule went from 0 to 70vh, which is 465px on an iPhone 14 and 587px
	 * on a Pixel 7 — against a menu that is 340px tall. The browser eased the
	 * CEILING over the declared time and the panel simply stopped growing when it
	 * ran out of content, so the two halves of the animation were both wrong and
	 * in opposite directions. Sampled every frame in both engines:
	 *
	 *   opening   declared 0.22s   full size at 100ms (WebKit), 74ms (Chromium)
	 *   closing   declared 0.20s   FIRST MOVEMENT at 61ms, in both
	 *
	 * So it snapped open in a third of the time it claimed, and closing sat
	 * perfectly still for 61ms after the tap before anything moved — which is
	 * exactly the moment the visitor is watching for a response, and the reason
	 * this animation exists at all. The taller the screen, the longer the stall,
	 * so it was worst on the tablets that get this drawer because the bar does
	 * not fit rather than because the screen is small.
	 *
	 * `grid-template-rows: 0fr → 1fr` measures the content instead of guessing at
	 * it: the row resolves to whatever the menu actually needs, so the easing
	 * describes the whole distance and nothing is left over at either end. It
	 * needs one wrapper element, because a grid row can only collapse a single
	 * item and this <nav> has two children; that is the only reason header.php
	 * carries `.tt-nav__inner`.
	 *
	 * `interpolate-size: allow-keywords` with `height: auto` would read better
	 * and was not used: WebKit has no `interpolate-size` — measured on this
	 * project when the FAQ was built — so Safari, which is most of the Greek
	 * mobile market, would have got no animation at all.
	 *
	 * THE 70vh CEILING IS GONE AND NOTHING REPLACES IT. It was guarding against a
	 * menu taller than the screen, and the drawer is in normal flow rather than
	 * fixed to the viewport: a long menu makes the header tall and the page
	 * scrolls, which is what a visitor expects anyway. A nested scroll area
	 * inside a header is the harder thing to use, not the safer one.
	 */
	/*
	 * The drawer sits between the brand and the actions in the source, which is
	 * the correct reading order, and `flex-basis: 100%` made it break the line —
	 * so the closed menu pushed the «Μενού» button onto a second row and the
	 * header was two rows tall on every phone, for nothing. Reordering visually
	 * puts the button back beside the brand and leaves the source order alone.
	 */
	.tt-header__actions {
		order: 2;
	}

	.tt-nav {
		order: 3;
		display: grid;
		grid-template-rows: 0fr;
		flex-basis: 100%;
		visibility: hidden;
		border-top: 1px solid transparent;
		background: var(--tt-white);
		transition: grid-template-rows 0.2s ease, visibility 0s linear 0.2s, border-color 0.2s ease;
	}

	/*
	 * The clip is here rather than on the <nav>, because the row is what shrinks
	 * and this is the thing sitting in it. `min-height: 0` is not optional: a
	 * grid item's automatic minimum size is its content, so without it the item
	 * refuses to go below 340px and the row animates against a floor it can
	 * never reach — the panel would stay open at full height and only the
	 * visibility delay would hide it.
	 */
	.tt-nav__inner {
		min-height: 0;
		overflow: hidden;
	}

	.tt-nav.is-open {
		grid-template-rows: 1fr;
		visibility: visible;
		border-top-color: var(--tt-border);
		transition: grid-template-rows 0.22s ease, visibility 0s, border-color 0.2s ease;
	}

	.tt-nav__list a {
		min-height: var(--tt-tap);
		display: flex;
		align-items: center;
		border-bottom: 1px solid var(--tt-border);
	}

	.tt-nav__cta {
		padding-block: var(--tt-space-2);
	}

	.tt-header__cta {
		display: none;
	}
}

@media (min-width: 1200px) {
	.tt-nav-toggle,
	.tt-nav__cta {
		display: none;
	}

	.tt-nav {
		margin-left: auto;
	}

	.tt-nav__list {
		display: flex;
		align-items: center;
		gap: var(--tt-space-3);
	}

	.tt-nav__list a {
		font-size: 0.875rem;
	}
}

/* Search hero ------------------------------------------------------------- */

/*
 * A flat panel, not a banner. No background image, no gradient, no min-height:
 * the brief says the search is the primary element and the hero must not become
 * a large decorative block, which in practice means the form has to be reachable
 * without scrolling at 375px.
 */
/* The hero with a photograph behind it -------------------------------------- */

/*
 * SAME HEIGHT, SAME LOCKUP, SAME EVERYTHING. The only change is what is behind
 * the text. docs/03 says the search is the primary element and the hero must
 * not become a decorative banner, and a Playwright test measures that at 375px
 * — so nothing here adds a min-height, a padding or an aspect ratio.
 */
/*
 * `.tt-hero.tt-hero--photo` AND NOT `.tt-hero--photo`, because the modifier has
 * to beat the base and one class cannot beat another class: `.tt-hero` sets the
 * pale blue further down this file, and being LATER it won every time. Measured
 * in the browser, not read off the page — the element carried both classes and
 * computed to rgb(234, 244, 255) while the heading computed to white. 1.11:1.
 *
 * Which is invisible text, not a theoretical one. This background is what shows
 * while the photograph is still loading, and it is what shows for good if the
 * photograph 404s — and the photograph is the largest image on the page.
 */
.tt-hero.tt-hero--photo {
	position: relative;
	isolation: isolate;
	background: var(--tt-navy);

	/*
	 * A DARK SURFACE THAT HAD NEVER HELD A CONTROL, AND THE DAY IT DID THE RING
	 * WAS 2.69:1.
	 *
	 * Blue on this navy is the same 2.69:1 the footer and the navy banner
	 * already declare their way out of, and this hero simply had not needed to:
	 * until the trust strip arrived, every focusable thing in it lived inside
	 * the white search panel, so the ring was always drawn on white and the
	 * walk-up in design-system.spec.js never reached the navy.
	 *
	 * Caught by that test on the first run after the strip shipped, which is the
	 * whole reason it walks with the Tab key instead of listing selectors.
	 */
	--tt-focus: var(--tt-white);
}

.tt-hero__media {
	position: absolute;
	inset: 0;
	z-index: -1;
	overflow: hidden;
}

.tt-hero__media img {
	/*
	 * ABSOLUTE SO THAT SIX OF THEM ARE STILL ONE PICTURE.
	 *
	 * The container is already `position: absolute; inset: 0` with a fixed box,
	 * so for the single photograph this is exactly what `width/height: 100%` in
	 * flow was doing — it changes nothing about the hero every install has
	 * today. What it buys is that a second photograph lands on top of the first
	 * rather than under it, which is the whole of the crossfade.
	 */
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;

	/*
	 * CROPPED FROM THE TOP, BECAUSE THE BOTTOM OF A TRAVEL PHOTOGRAPH IS WHERE
	 * THE CLUTTER LIVES.
	 *
	 * This band is 1440 x 437 — a 3.3:1 slot for a 3:2 picture, so 55% of the
	 * frame is thrown away whatever we do. Centred, it kept the middle: the
	 * horizon and the sky went, and what stayed was a folded parasol, a café
	 * table, two stacking chairs and somebody's jacket over the back of one.
	 * The first thing on the site was a corner of a terrace.
	 *
	 * The clutter in this picture starts at row 534 of 959, which is any offset
	 * above 18%; the sky is only the first 150 rows, which is any offset near 0.
	 * Both conditions are met at the top and nowhere else.
	 *
	 * It is a default and not a decision about this photograph. The office picks
	 * the picture and has nowhere to say where its subject is, so the rule has
	 * to hold for a picture nobody has seen — and for a wide crop of a landscape
	 * the horizon is the safer thing to keep than the foreground. A per-image
	 * focal point is the honest fix once photographs start being swapped.
	 *
	 * Below 768px it changes nothing: the band is taller than the picture is
	 * wide, so the crop is horizontal and this axis has no slack to distribute.
	 */
	object-position: 50% 0;
}

/*
 * WITHOUT JAVASCRIPT THE HERO IS THE FIRST PHOTOGRAPH AND NOTHING ELSE.
 *
 * Stacked images with no rule would leave the LAST one on top, so a visitor
 * with no script would see the sixth picture — the one loaded lazily, the one
 * not marked as the priority, the one nobody chose to be first. This is the
 * whole of the fallback, and `polish.spec.js` loads this page with JavaScript
 * disabled on every run.
 */
.tt-hero__media img:not(:first-child) {
	opacity: 0;
}

/*
 * The attribute is written by the script at the same moment it marks the first
 * image current, in one synchronous block before a frame is painted. Split
 * across two frames instead and the page would flash: the moment the attribute
 * lands, `:not(.is-current)` hides every image including the one on screen.
 *
 * A transition and not an animation: an animation runs on a schedule, and this
 * has to be interruptible — the pause button has to stop the picture where it
 * is, mid-fade, rather than let the current step finish.
 */
.tt-hero__media[data-tt-hero-slides] img {
	opacity: 0;
	transition: opacity 1200ms var(--tt-ease-out);
}

.tt-hero__media[data-tt-hero-slides] img.is-current {
	opacity: 1;
}

/*
 * THE PAUSE CONTROL, OFF THE CANVAS UNTIL A KEYBOARD ASKS FOR IT.
 *
 * «το play button να φυγει απο το hero δεν σου ειπα να το βαλεις» — and it has
 * gone: nothing is drawn over the photograph any more.
 *
 * IT CANNOT GO FURTHER THAN THAT, and this is the same compromise the client
 * already accepted for the reviews row rather than a refusal to follow the
 * instruction. WCAG 2.2 SC 2.2.2 asks for a mechanism to pause anything that
 * starts by itself, runs past five seconds and sits beside other content, and a
 * hero that crossfades every 6.5 seconds behind the page's heading and its
 * search form is exactly that. «It stops if your pointer happens to rest on it»
 * is not a mechanism a keyboard or a screen reader can find.
 *
 * So the button survives, positioned out of the viewport, and returns the
 * instant a keyboard reaches it — the identical treatment and the identical
 * reasoning as `.tt-carousel__nav--quiet` further down this file, so the site
 * answers this question the same way in both places.
 *
 * `:focus` AND NOT `:focus-visible`, for the reason recorded on that rule: the
 * only way to reach a control positioned off the canvas is the keyboard, so the
 * two are identical here except on engines where `:focus-visible` is patchy,
 * and there the difference is a focused control nobody can see.
 *
 * The rotation still has four other brakes with no control at all: a pointer
 * resting on the hero, focus moving inside it, a hidden tab, and the hero
 * scrolling out of view.
 */
.tt-hero__pause {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.tt-hero__pause:focus {
	z-index: 2;
	inset-block-end: var(--tt-space-2);
	inset-inline-end: var(--tt-space-2);
	display: grid;
	place-items: center;
	inline-size: 44px;
	block-size: 44px;
	margin: 0;
	overflow: visible;
	clip-path: none;
	border: 1px solid rgb(255 255 255 / 28%);
	border-radius: 50%;
	background: rgb(18 43 77 / 78%);
	color: var(--tt-white);
	cursor: pointer;
}

.tt-hero__pause svg {
	inline-size: 18px;
	block-size: 18px;
	fill: none;
	stroke: currentcolor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/*
 * The scrim, and it is not a mood. White text over a photograph is unreadable
 * across its bright parts, and the OFFICE picks the photograph — so the
 * contrast has to hold for a picture nobody has seen yet.
 *
 * The value is measured, not chosen. Sampling every pixel the heading and the
 * subtitle actually cover, compositing the scrim over it and taking the
 * LIGHTEST result — the worst case, not the average:
 *
 *   alpha  0.52   0.58   0.62   0.66   0.70
 *   white   3.23   3.83   4.31   4.86   5.50
 *
 * The subtitle is 16px, which is normal text, which is 4.5:1 under WCAG 2.2 SC
 * 1.4.3 — so 0.52 failed it and 0.66 is the first value with room to spare. A
 * first guess of 0.52 was written up as «4.9:1» before anything was measured;
 * it was 3.23:1.
 *
 * A Playwright test re-measures this against whatever photograph is set, so
 * swapping the picture for a brighter one fails the suite rather than the
 * reader.
 */
/*
 * THE 66% IS NOW A FALLBACK RATHER THAN THE ANSWER. The office sets this from
 * the homepage settings screen, where the slider prints the contrast its own
 * photograph would actually produce at each step — see `HeroScrim` in the
 * plugin, which reproduces the table above to within 0.04 by sampling the
 * image instead of a person doing it by hand.
 *
 * The literal stays because it has to: with the plugin absent, or the setting
 * never touched, this renders exactly what it rendered before the control
 * existed. A custom property with no fallback would have made a missing plugin
 * into a transparent scrim and white text on a bare photograph.
 */
.tt-hero__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: rgb(18 43 77 / var(--tt-hero-scrim, 66%));
}

.tt-hero--photo .tt-hero__heading,
.tt-hero--photo .tt-hero__sub {
	color: var(--tt-white);
}

.tt-hero--photo .tt-hero__eyebrow {
	background: rgb(255 255 255 / 92%);

	/*
	 * The hairline earns its place on the flat hero and loses it here. Against a
	 * scrimmed photograph the fill is already the brightest thing in the frame,
	 * so the ring adds no separation — it only puts a grey outline around a white
	 * shape, which reads as the border of a disabled button rather than the edge
	 * of a badge. Photographed at 3x before deciding.
	 */
	border-color: transparent;
}

.tt-hero {
	position: relative;
	overflow: hidden;
	background: var(--tt-blue-soft);

	/*
	 * Tighter at the top than at the bottom is deliberate on a phone: the space
	 * under the search panel is dead, and every pixel of it pushes the submit
	 * button closer to the fold. The guard in homepage.spec.js measures exactly
	 * this and caught the eyebrow and the larger title costing 48px here.
	 */
	padding-block: var(--tt-space-3) var(--tt-space-2);
}

/*
 * The one decorative element on the page: the soft disc the approved mockup
 * puts behind the top-right of the hero. Flat tint, not a gradient, and drawn
 * on a pseudo-element so it costs no markup and no request. Hidden below 768px,
 * where the hero has no room to spare and the disc would only push the search
 * further down.
 */
@media (min-width: 768px) {
	.tt-hero {
		padding-block: var(--tt-space-5);
	}

	.tt-hero::before {
		content: "";
		position: absolute;
		z-index: 0;
		inset-block-start: -34%;
		inset-inline-end: -8%;
		width: 420px;
		height: 420px;
		border-radius: 50%;
		background: rgb(8 104 215 / 7%);
	}
}

/* The photograph hero on a desktop -----------------------------------------
 *
 * «ΤΟ HERO ΠΡΕΠΕΙ ΚΑΙ ΣΕ ΥΠΟΛΟΓΙΣΤΗ ΝΑ ΤΟ ΚΑΝΟΥΜΕ ΓΑΜΑΤΟ ΟΠΩΣ ΣΤΟ MOBILE», and
 * then «παρόμοιο μέγεθος με το flyhoper». Both are one change, because the phone
 * card and the reference are the same idea at different scales and the desktop
 * had neither.
 *
 * WHAT WAS HERE: a full-bleed band with square corners, 439px tall in a 900px
 * window, running edge to edge under the header, with the search panel parked in
 * its lower third and 48px of photograph doing nothing beneath it. The phone had
 * been given an inset rounded card months earlier. Two ideas of what this element
 * is, on one page.
 *
 * MEASURED ON flyhoper.com IN CHROMIUM AT FOUR WINDOW SIZES, because «similar
 * size» is a number and not a feeling:
 *
 *   window      their card     % of window   their pill
 *   1280x800    1256 x 715     89%           1204 x 70 at x38 y37
 *   1440x900    1416 x 795     88%           1364 x 70 at x38 y37
 *   1920x1080   1872 x 795     74%           1820 x 70 at x50 y37
 *   1440x700    1416 x 715     102%          1364 x 70 at x38 y37
 *
 * So it is NOT a viewport percentage — it is a clamp between 715 and 795px that
 * overflows a short window rather than collapsing. The card is inset 12px, the
 * radius is 20px, and the pill sits 26px inside the card horizontally and 25px
 * below its top edge.
 *
 * Reproduced with our own tokens at the same proportions: card inset 16px, radius
 * 20px, pill 26px inside it, 24px below its top. The three heights below are the
 * same clamp arithmetic — the hero box is the card plus its 16px inset top and
 * bottom, so a 712-792px card is a 744-824px box.
 */
@media (min-width: 768px) {
	/*
	 * THE HEADER STOPS BEING A BAND AND BECOMES A FLOATING PILL, on this page
	 * only, because that is also flyhoper's own rule. Measured on their /about/:
	 * `header-wrapper` is `position: sticky`, 1440x71, radius 0 — which is, to the
	 * pixel, the header this site already has. The pill is a homepage modifier
	 * (`--floating`) and nothing else.
	 *
	 * WHERE WE DEPART FROM THEM, DELIBERATELY: theirs is `position: absolute` and
	 * scrolls away. Ours stays. The note beside `.tt-header`'s `position: sticky`
	 * records why — a tour page is 4,613px on a laptop and the visitor was losing
	 * the logo, the navigation and the one call to action after 70px of it. So the
	 * pill is what the header looks like ON the hero, and `tt-past-hero` puts the
	 * band back the moment the photograph is behind us. Same first impression,
	 * without giving up persistent navigation.
	 *
	 * Without JavaScript the class never arrives and the pill simply stays: a
	 * white rounded bar over white content, legible and clickable. A worse look
	 * than the band, and not a broken page, which is the trade this site makes
	 * everywhere else too.
	 */
	body:has( .tt-hero--photo ) .tt-hero {
		padding-block: var(--tt-space-2);
		background: var(--tt-white);
	}

	.tt-hero--photo .tt-hero__media {
		inset: var(--tt-space-2);
		z-index: 0;
		border-radius: 20px;
		background: var(--tt-navy);
	}

	/*
	 * THE REFERENCE'S CEILING, UNDER THIS PROJECT'S OWN 90% RULE.
	 *
	 * Their card is 715-795px and overflows a short window — 102% of a 700px
	 * one. We cannot copy that half: docs/03 rules out the hero becoming a
	 * decorative banner and `reviews.spec.js › it costs the hero no height`
	 * enforces it at 90% of the viewport. The first version of this rule was
	 * `clamp(744px, calc(100svh - 76px), 824px)`, which is 824 of 900 — **91.6%**
	 * — and the suite caught it.
	 *
	 * `min()` of the two constraints says exactly that: never more than 86% of
	 * the window, and never more than the reference's own ceiling once the window
	 * is tall enough for it to bite. 774px at 900, 688 at 800, 792 at 1080, and
	 * 86% at every height in between — with the 4% of headroom left deliberately,
	 * because the rule is a ceiling and not a target.
	 */
	.tt-hero--photo {
		display: flex;
		flex-direction: column;
		min-block-size: min(792px, 86svh);
	}

	/*
	 * In front of the card, centred in it — and the top padding is the header,
	 * so «centred» means centred in the part of the photograph the pill is not
	 * covering rather than in the box.
	 */
	.tt-hero--photo .tt-hero__inner {
		z-index: 1;
		display: flex;
		flex: 1;
		flex-direction: column;
		justify-content: center;
		padding-block: var(--tt-space-5);
	}

	/*
	 * A FLEX COLUMN STRETCHES ITS CHILDREN, AND THE EYEBROW IS A PILL.
	 *
	 * The rule above turns `.tt-hero__inner` into a flex column so the stack can
	 * centre in the taller card, and the default `align-items: stretch` then took
	 * «Εκδρομές από την Αθήνα» from a 208px badge to a 1,132px white bar across
	 * the whole photograph. The phone card learned this the same way and carries
	 * the same declaration.
	 */
	.tt-hero--photo .tt-hero__eyebrow {
		align-self: center;
	}

	.tt-hero--photo.tt-hero::before {
		content: none;
	}
}


/* The floating pill, and why it starts where the inline menu does ----------
 *
 * The reference puts a white rounded pill on the hero and a plain sticky bar on
 * every other page; that split is reproduced here. The width it starts at is
 * ours rather than theirs, and it has now moved once — which is worth keeping,
 * because the number was never a preference.
 *
 * IT WAS 1280, AND MEASURED. The row needed about 1,130px — 179 for the brand,
 * 635 for six links, 244 for the call to action and 72 of gaps. Insetting the
 * pill by 42px a side and padding it by 24 takes 132px out of a row that had 22
 * of slack, so at a 1200px window the navigation wrapped and the header measured
 * 135px against a token saying 109 — a bar a third taller than the thing every
 * sticky offset on the site is calculated from.
 *
 * IT IS 1200, AND MEASURED AGAIN. The client took «Αρχική» out of the menu and
 * shortened the header's button to «Επικοινωνία» at 40px tall, which between
 * them handed 86px back. Re-measured with the pill's geometry forced on at four
 * widths: at 1200 the row needs 943px and the pill's content box offers 1,068 —
 * one line, 125px of slack, header 69px in bar mode and 109 in pill mode at
 * every width from here up.
 *
 * 1200 AND NOT SOME OTHER NUMBER, because it is the breakpoint the navigation
 * itself already uses: below it the menu is a drawer and there is no row to fit.
 * A separate gate at 1280 would now be a workaround kept past the problem it was
 * for, and one more number in a file that has enough of them.
 */
@media (min-width: 1200px) {
	:root:has( body.home .tt-hero--photo ) {
		/*
		 * The token is not decoration: `design-system.spec.js` measures it against
		 * the real header ON THIS PAGE, and `scroll-padding-top` is calculated
		 * from it. 109 and not 108 — 40px of inset, the 68px bar, and the 1px
		 * bottom border, which is kept at `transparent` rather than removed so
		 * that swapping to the bar does not move everything below it by a pixel.
		 */
		--tt-header: 109px;
	}

	:root.tt-past-hero:has( body.home .tt-hero--photo ) {
		--tt-header: 69px;
	}

	body.home:has( .tt-hero--photo ) .tt-header {
		padding-block-start: 40px;
		background: transparent;
		border-bottom-color: transparent;
	}

	body.home:has( .tt-hero--photo ) .tt-header__inner {
		/*
		 * `width: auto` AND NOT ONLY `max-width: none`. `.tt-container` carries
		 * `width: 100%`, which resolves against the header's content box — so the
		 * pill came out 1440px wide starting at x=42 and hung 42px off the right
		 * edge of the window. Auto lets the margins do the sizing.
		 */
		width: auto;
		max-width: none;

		/* 16px of card inset plus the 26px the reference leaves inside it. */
		margin-inline: 42px;
		padding-inline: var(--tt-space-3);
		border-radius: 17px;
		background: var(--tt-white);
	}

	.tt-past-hero body.home .tt-header {
		padding-block-start: 0;
		background: var(--tt-white);
		border-bottom-color: var(--tt-border);
	}

	.tt-past-hero body.home .tt-header__inner {
		max-width: var(--tt-content);
		margin-inline: auto;
		border-radius: 0;
		background: none;
	}

	/*
	 * THE HERO SLIDES UP UNDER THE HEADER, which is what makes the pill float on
	 * the photograph instead of sitting above it. A negative margin equal to the
	 * header's own height and nothing else: the header keeps its place in the
	 * document — see the note refusing `position: fixed` — and the two overlap,
	 * with the header's z-index already at 20.
	 */
	body.home:has( .tt-hero--photo ) .tt-hero {
		margin-block-start: calc(var(--tt-header) * -1);
		padding-block: 0;
	}

	/*
	 * And the words centre in the part of the photograph the pill is not
	 * covering, rather than in the box.
	 */
	body.home:has( .tt-hero--photo ) .tt-hero__inner {
		padding-block-start: calc(var(--tt-header) + var(--tt-space-3));
	}
}

/* The hero arriving --------------------------------------------------------
 *
 * «ΔΕΝ ΠΡΕΠΕΙ ΝΑ ΣΚΑΕΙ ΜΕ ΩΡΑΙΟ ANIMATION ΤΟ HERO;»
 *
 * NOT `data-tt-reveal`, AND THAT IS THE FIRST DECISION. The scroll-reveal system
 * deliberately REFUSES to animate anything above the fold: navigation.js marks
 * every target already on screen as revealed and settled BEFORE it adds
 * `.tt-motion`, precisely so a deferred script cannot hide something the visitor
 * has already seen and fade it back in. `polish.spec.js` guards it. The hero is
 * the most above-the-fold thing on the site, so it can never arrive that way and
 * should not.
 *
 * A CSS animation is a different mechanism with a different failure mode. It
 * begins at the element's first painted frame rather than after a download, so
 * there is no state where the visitor sees content, then nothing, then content.
 * If the stylesheet never arrives, nothing is styled at all and the question is
 * moot; if the visitor has asked for less motion, the whole block is skipped by
 * the query around it and the hero is simply there.
 *
 * THE PHOTOGRAPH NEVER FADES. It scales from 1.06 to 1 and holds full opacity
 * throughout, so the largest image on the page is legible from its first frame —
 * the settle is a lens coming to rest, not a reveal. Only the words and the
 * search panel rise, 460ms each, staggered 70ms apart in the order they are read.
 *
 * The stagger is the UX purpose and the reason this is not decoration: it walks
 * the eye from the promise down to the control the page exists to offer. 70ms is
 * the same order as the reveal system's own 60ms, so the two read as one site.
 */
@media (prefers-reduced-motion: no-preference) {
	@keyframes tt-hero-settle {
		from {
			scale: 1.06;
		}

		to {
			scale: 1;
		}
	}

	@keyframes tt-hero-rise {
		from {
			opacity: 0;
			translate: 0 18px;
		}

		to {
			opacity: 1;
			translate: none;
		}
	}

	/*
	 * `scale` rather than `transform: scale()`, for the reason written out beside
	 * the reveal: this project keeps `transform` free so the press feedback —
	 * `transform: translateY(1px)` on every pressable thing — has nothing to
	 * argue with. Nothing inside the hero is pressable through this element, but
	 * the rule is the rule and the property is independent for free.
	 */
	/*
	 * `:first-child`, so a rotation does not promote six images to six
	 * compositing layers for one animation that only one of them is visible
	 * for. The layer count is not theoretical on this page — see the note below
	 * about what a compositing layer did to the search panel's `:active`.
	 */
	.tt-hero--photo .tt-hero__media img:first-child {
		animation: tt-hero-settle 1100ms var(--tt-ease-out) backwards;
	}

	/*
	 * THE SEARCH PANEL IS NOT IN THIS LIST, AND IT WAS. It cost half an hour and
	 * it is the most useful thing on this page to have written down.
	 *
	 * Animating `.tt-search--hero` alongside the words made
	 * `design-system.spec.js › the acknowledgement is immediate going in and
	 * eased coming out` intermittent on WebKit: 5/5 passing before the change,
	 * 2/3 after. The failure is that pressing the hero's submit button reads a
	 * 0.15s transition instead of 0s — the test's way of saying the `:active`
	 * state never took. Verified as mine and not pre-existing by stashing the
	 * change and running it five more times, all green.
	 *
	 * The animation is what does it. An animated element is promoted to its own
	 * compositing layer, and while the panel held one, the press on its own
	 * submit button intermittently landed on the panel instead. Nothing about
	 * timing: instrumented, the animation was long finished and `getAnimations()`
	 * empty at the moment of the press. Switching `both` to `backwards` made it
	 * WORSE (4 failures in 6), which is what ruled out the fill and pointed at
	 * the declaration itself. Taking the panel out: 6/6.
	 *
	 * It is also the better design. The panel is the control this page exists to
	 * offer, and fading in the one thing a visitor may have come to use is a
	 * delay dressed as a flourish. The words arrive; the search is simply there.
	 *
	 * `backwards` stays for the three that remain — it applies the `from` state
	 * during the delay and then hands the element back to its ordinary computed
	 * style, which IS the `to` state here. Nothing is left under animation
	 * control, which is the same rule `is-settled` follows for the scroll reveal.
	 */
	.tt-hero--photo .tt-hero__eyebrow,
	.tt-hero--photo .tt-hero__heading,
	.tt-hero--photo .tt-hero__sub {
		animation: tt-hero-rise 460ms var(--tt-ease-out) backwards;
	}

	.tt-hero--photo .tt-hero__heading {
		animation-delay: 70ms;
	}

	.tt-hero--photo .tt-hero__sub {
		animation-delay: 140ms;
	}


}

.tt-hero__inner {
	position: relative;
	z-index: 1;
	text-align: center;
}

/*
 * ON A PHONE THE SEARCH GOES ABOVE THE PROMISE, AND THAT IS THE BRIEF RATHER
 * THAN A PREFERENCE.
 *
 * docs/03 says the search is the primary element and the hero must not become a
 * large decorative banner. It had become one. Measured at the real visible
 * height of an iPhone SE with Safari's bars up — 375 x 553, not the 375 x 667 of
 * the screen — the hero is 587px, and:
 *
 *   two of the three controls fit
 *   «Αναζήτηση» sits 63px BELOW the fold
 *
 * The suite did not catch it because Playwright's phone projects are 664px and
 * 839px tall and model no browser chrome at all, so the guard in
 * homepage.spec.js has been measuring a device nobody owns.
 *
 * 233px of that is spent before the panel even starts: 24 of padding, 41 of
 * eyebrow, 75 of title and 93 of subtitle. The subtitle is the only one of the
 * four that the search does not need in order to be understood — «Μονοήμερες,
 * πολυήμερες και ταξίδια εξωτερικού» is a list the destination and type controls
 * are about to offer anyway.
 *
 * So it moves BELOW the panel rather than away: nothing is hidden, nothing goes
 * behind a tap, no script is involved, and the copy still reads as reassurance
 * under the thing it is reassuring you about. `order` and not a second markup
 * branch, because the subtitle is not focusable — the panel's controls are the
 * only tab stops in this hero, so visual order and focus order cannot disagree.
 *
 * CENTRED, WHICH REVERSES WHAT THIS BLOCK USED TO SAY AND IS THE CLIENT'S CALL.
 *
 * It read «left alignment comes from the approved mobile mockup, which centres
 * nothing», and warned that centred Greek at this size wraps into a diamond the
 * eye has to hunt the start of every line in. That objection was raised and the
 * client asked for centred anyway, so centred it is — and the collapse above is
 * what makes it survivable: the hero is now three short blocks over a
 * photograph rather than four blocks over a 338px form, and three centred lines
 * with a pill above them read as a poster instead of a diamond.
 *
 * It inherits from `.tt-hero__inner` rather than being restated: the desktop
 * hero has always been centred, so below 768px there is nothing left to say.
 */
@media (max-width: 767px) {
	.tt-hero__inner {
		display: flex;
		flex-direction: column;
	}

	/*
	 * The pill is `display: inline-block` and would stretch the full width of a
	 * flex column, which turns a label into a bar. `center` rather than
	 * `flex-start` for the same reason it was `flex-start` before — the property
	 * exists to stop the stretch, and it may as well agree with the text.
	 */
	.tt-hero__eyebrow {
		align-self: center;
	}

	/*
	 * THE SUBTITLE COMES BACK ABOVE THE PANEL, AND THE REASON IT LEFT IS GONE.
	 *
	 * It was pushed below with `order: 1` to buy 93px, because four blocks over
	 * a 338px form put «Αναζήτηση» 63px under the fold. The form is 52px now, so
	 * the height is not needed — and below the panel is where it looked worst:
	 * three lines of white text stranded at the bottom of a photograph with
	 * nothing under them, which is most of what «πρόχειρο» was pointing at.
	 *
	 * Read in order, eyebrow / title / promise / search is a sentence. The panel
	 * last is also the honest arrangement now that it expands: the fields grow
	 * downward into the space below the hero instead of shoving the promise
	 * around.
	 */

}

/*
 * Small, quiet, and above the title: it names the one thing the title cannot,
 * which is where these trips leave from.
 */
.tt-hero__eyebrow {
	display: inline-block;
	margin: 0 0 var(--tt-space-1);
	padding: 6px 14px;

	/*
	 * The prototype draws this pill with a hairline and the theme dropped it,
	 * which costs nothing on a photograph and everything on the flat hero: a
	 * white pill on #eaf4ff is a 1.06:1 edge, so the label read as three floating
	 * words rather than as a badge. The photo variant paints over it below.
	 */
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-blue-dark);
	font-size: 0.8125rem;
	font-weight: 600;
}

/*
 * Bigger than a section heading, because it is the first thing on the site and
 * the mockup gives it the weight. It still tops out well short of the display
 * sizes a brochure template would use — the search below it is the primary
 * element, and this must not push it off a 375px screen.
 */
.tt-hero__heading {
	/*
	 * A LOCKUP, NOT A BANNER LINE. Uncapped, «Βρες την επόμενη απόδρασή σου»
	 * set as one 1,132px line across the full container, with a 187px pill
	 * above it and a 557px subtitle below — three elements of three different
	 * widths, centred, which is a stack rather than a composition.
	 *
	 * 760px is the approved prototype's own cap (`.hero h1 { max-width: 760px }`)
	 * and it puts the title on two lines, which is exactly what the mockup PNG
	 * shows. `text-wrap: balance` then makes those two lines even.
	 *
	 * A pixel cap and not a `ch` one, because the office writes this line: `ch`
	 * is the width of a zero at the current size, and the current size is a
	 * clamp, so the cap would move with the viewport for no reason.
	 */
	max-width: 760px;
	margin-inline: auto;
	margin-bottom: var(--tt-space-1);
	font-size: clamp(1.875rem, 1rem + 2.8vw, 3.25rem);
	letter-spacing: -0.035em;
	line-height: 1.12;
	text-wrap: balance;
}

.tt-hero__sub {
	max-width: 52ch;
	margin: 0 auto var(--tt-space-2);
	color: var(--tt-text-muted);

	/*
	 * `balance` rather than `pretty` here, and only here. `pretty` fixes the
	 * last line — it prevents a one-word orphan and nothing else — so this line
	 * broke as 63 characters and then 22, a long line with a short tail under a
	 * centred title. Two even lines are the mockup. Both engines cap `balance`
	 * at a handful of lines, which is why the rest of the site's running copy
	 * keeps `pretty`: this is the one paragraph short enough to qualify.
	 */
	text-wrap: balance;
}

/*
 * THE TRUST STRIP AT THE FOOT OF THE HERO.
 *
 * Measured on the reference the client asked to be copied: 300 × 24, sitting at
 * the bottom of the hero's booking card, three parts in one line — stars, the
 * rating in words, the platform. Same place and same order here, under the
 * search rather than under a booking card, because that is where our hero ends.
 *
 * A WHITE PILL IN BOTH HEROES, and that is a decision reused rather than made.
 * `.tt-hero__eyebrow` already solved this exact problem — a small element that
 * has to stay legible over a flat blue band AND over a scrimmed photograph — and
 * the answer was photographed at 3x before it was settled. Two elements at
 * opposite ends of the same hero solving it differently would look like an
 * accident.
 *
 * The capsule radius comes from `--tt-radius-action`: this is a control, it goes
 * somewhere, and the shape says so.
 */
/*
 * A BAND OF ITS OWN RATHER THAN A ROW INSIDE THE HERO.
 *
 * `homepage.spec.js` caps the hero at 90% of the viewport so it cannot become
 * the decorative banner docs/03 rules out, and on the iPhone profile the strip
 * inside it took the hero from 589px to 663px against a ceiling of 598. Nine
 * pixels of headroom, spent. Directly underneath is the same place on the page
 * and costs the hero nothing.
 */
.tt-trustbar-band {
	display: flex;
	justify-content: center;
	padding-block: var(--tt-space-2);
}

.tt-trustbar-band .tt-container {
	display: flex;
	justify-content: center;
}

/*
 * A STATEMENT, NOT A CONTROL — and the shape had to be re-argued, not just
 * inherited, when the link came off.
 *
 * The pill was justified here as «this is a control, it goes somewhere, and the
 * shape says so». It no longer goes anywhere, so that argument is spent. What
 * replaces it is the reference's own: flyhoper's bar is a bordered capsule too,
 * and it reads as a badge rather than a button because nothing on it offers to
 * be pressed.
 *
 * So the capsule stays and the affordances go. `--tt-elevation-1` is gone —
 * raised means pressable — leaving a flat hairline card. No `:hover` border, no
 * `:focus-visible` ring, no `cursor: pointer`, and no tab stop, because a
 * paragraph is not in the tab order in the first place.
 *
 * `margin: 0` because this is now a `<p>` and would otherwise inherit the
 * document's paragraph spacing into a flex-centred band.
 */
.tt-trustbar {
	display: inline-flex;
	align-items: center;
	gap: var(--tt-space-1);
	margin: 0;
	padding: 7px 16px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-action);
	background: var(--tt-white);
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
	line-height: 1.2;
}

/*
 * The platform's name, set in the site's own type. Navy and bold rather than
 * Tripadvisor's green — the palette has no green in it, and a wordmark redrawn
 * approximately is worse than a name spelled correctly.
 */
.tt-trustbar__brand {
	color: var(--tt-navy);
	font-weight: 700;
}

/*
 * THE WORD THAT GOES WITH THE SCORE, first in the row exactly as the reference
 * sets it: «Εξαιρετικό ★★★★½ Tripadvisor», and then nothing.
 *
 * A size up from the rest of the strip, because it is the part that survives
 * being read at a glance — measured on the reference at 18px/500 against 12px
 * body, and the same relationship holds here at a smaller overall scale.
 *
 * It carries more weight here than it does there. The client asked for no
 * figures on this bar, so this word is the only text describing the stars: it is
 * the row's label for a screen reader and its summary for a hurried eye, not a
 * flourish in front of a sentence that repeats it.
 */
.tt-trustbar__word {
	flex: none;
	color: var(--tt-navy);
	font-size: var(--tt-text-body);
	font-weight: 700;
	letter-spacing: -0.01em;
}

/*
 * 17px tiles rather than the cards' 20px. The reference's own trust widget sets
 * its star row at 100 × 18 against 106 × 20 on its review cards — a strip that
 * has to sit inside a 35px pill wants a smaller tile than a card with 20px of
 * padding around it.
 */
.tt-trustbar__stars {
	--tt-star-tile: 17px;

	display: inline-flex;
	flex: none;
	gap: 2px;
	line-height: 1;
}

/*
 * THE STAR GRAPHIC, WHICH IS A TILE AND NOT A GLYPH.
 *
 * «Βάλε απλά το γραφιστικό με τα αστέρια ακριβώς όπως είναι στο άλλο.» Measured
 * in the screenshot the client sent: five 20px rounded squares, ~1.5px apart,
 * each carrying a white star knocked out of a solid fill — 106 × 20 for the row.
 * A bare «★» in amber is a different object: it reads as punctuation, and at a
 * glance from a scrolling phone it does not read as a rating at all.
 *
 * THE GREEN IS THE CLIENT'S EXPLICIT CALL, OVER TWO WRITTEN RULES.
 *
 * This was built in amber first, because CLAUDE.md fixes the palette at blue,
 * white and soft gray and says «No green», and because the green in that
 * screenshot is not a generic rating colour — it is Trustpilot's, on a genuine
 * Trustpilot widget, beside reviews this site does not have from Trustpilot.
 *
 * «Σε πράσινο Trustpilot τα αστέρια δεν νομίζω να πειράζει.» That is the owner
 * of the site deciding about their own branding, and it is theirs to decide, so
 * it is built. What it costs is written down in DEVELOPMENT.md and docs/08 in
 * their language: a visitor who recognises the mark will read these stars as a
 * Trustpilot score, and there is no Trustpilot account behind them.
 *
 * ONE TOKEN, ONE PLACE. `--tt-star-tile-fill` is the whole override. Setting it
 * to `var(--tt-star-fill)` puts every star on the site back to the project's
 * amber in one line, and nothing else has to be touched.
 *
 * HALF A TILE, NOT HALF A STAR. A five-slot row that only knows full and empty
 * has to lie in one direction about a 4,5. The old half was a clipped glyph
 * stacked on another glyph; a tile makes it simpler and truer to the reference,
 * which fills half the SQUARE and leaves the white star whole on top of it. So
 * the fill is an absolutely positioned element behind the glyph, and the glyph
 * is lifted above it — a positioned child would otherwise paint over the text
 * node it is meant to sit behind.
 *
 * SCOPED TO THE TRUST STRIP AND NOWHERE ELSE, WHICH WAS A CORRECTION.
 *
 * The tiles went onto the review cards too, and the cards are signed **Google**.
 * «Τα Google reviews τα έχεις γαμήσει, είναι ξεχωριστό το ένα με το άλλο.» A
 * Google review carrying Trustpilot's green tiles is the exact false association
 * this file has been avoiding all along, drawn onto the one part of the page
 * that already had a correct, honest rating of its own.
 *
 * So the two rating displays stay two things. The strip is the Trustpilot-shaped
 * badge the client asked for, tiles and all. A review card shows its platform's
 * own rating in the project's amber — which is also what Google's own listing
 * draws, and what `--tt-star` was created for. The footer's row keeps its glyphs
 * too: it sits on navy, where a tile would be a block of colour in the one part
 * of the page that is deliberately quiet.
 */
.tt-trustbar__stars .tt-rating__star {
	position: relative;
	display: inline-grid;
	place-items: center;
	width: var(--tt-star-tile, 20px);
	height: var(--tt-star-tile, 20px);
	border-radius: 3px;
	background: var(--tt-star-tile-empty);
	color: var(--tt-white);
	font-size: calc(var(--tt-star-tile, 20px) * 0.62);
	line-height: 1;
}

.tt-trustbar__stars .tt-rating__star.is-filled {
	background: var(--tt-star-tile-fill);
}

/*
 * `z-index` and `position` together, because a positioned sibling with `z-index:
 * auto` paints after an in-flow text node. Without this the half-fill covers the
 * star it is supposed to sit behind.
 *
 * The stroke is switched off here: it exists to give an amber star an edge on
 * white, and a white star on amber already has one.
 */
/*
 * The colour lives HERE and not on the tile, because `.tt-rating__star.is-filled`
 * further down this file is also two classes and also sets `color` — equal
 * specificity, later in the source, so it wins on the tile and painted an amber
 * star on a green square. This selector is one the older rule does not reach.
 *
 * The stroke goes off for the same reason it existed: it gave an amber star an
 * edge on white, and a white star on a filled tile already has one.
 */
.tt-trustbar__stars .tt-rating__glyph {
	position: relative;
	z-index: 1;
	color: var(--tt-white);
	-webkit-text-stroke: 0;
}

.tt-trustbar__stars .tt-rating__half {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 50%;
	border-radius: 3px 0 0 3px;
	background: var(--tt-star-tile-fill);
}

/*
 * The forward arrow, the hover border and the focus ring were all deleted with
 * the link. `--tt-focus` on `.tt-search` and `.tt-hero--photo` stays: it was
 * added for this element's ring and is now carrying the search field's, which is
 * why the contrast test that produced it still passes.
 */

/*
 * Narrow phones wrap the line onto a second row, and a wrapped word beside a
 * left-pinned brand name reads as two unrelated things. Centring the whole pill
 * keeps it one object at any width.
 */
@media (max-width: 479px) {
	.tt-trustbar {
		flex-wrap: wrap;
		justify-content: center;
		text-align: center;
	}
}

@media (min-width: 768px) {
	.tt-hero__sub {
		margin-bottom: var(--tt-space-3);
		font-size: 1.0625rem;
	}
}

/* Search form ------------------------------------------------------------- */

.tt-search {
	display: grid;

	/*
	 * THE PANEL DECLARES ITS OWN RING RATHER THAN INHERITING THE SURFACE'S.
	 *
	 * This is a white card that gets dropped onto whatever is underneath it —
	 * the pale hero, the navy photo hero, the archive. The navy one now sets
	 * `--tt-focus: white` for the trust strip sitting directly on it, and that
	 * token inherits: without this line every control inside this form would
	 * draw a white ring on a white form the moment the office chose a hero
	 * photograph. One invisible ring traded for another.
	 *
	 * Blue on white is 4.6:1 and the panel is white wherever it lands, so it is
	 * the panel's fact to state and not the page's.
	 */
	--tt-focus: var(--tt-blue);

	/*
	 * Explicit, because a <form> that is also a grid container shrink-wraps to
	 * its content in WebKit rather than filling its parent: the archive panel
	 * rendered 267px wide inside a 358px column, centred, looking like a
	 * deliberate but wrong indent.
	 */
	width: 100%;
	gap: var(--tt-space-2);
	margin: 0 auto;
	padding: var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-shadow-card);
	text-align: left;
}

.tt-search--hero {
	max-width: 900px;
}

.tt-search__fields {
	display: grid;
	gap: var(--tt-space-2);
	margin: 0;
	padding: 0;
	border: 0;
	min-width: 0;
}

.tt-search__field {
	margin: 0;
	min-width: 0;
}

.tt-search__label {
	display: block;
	margin-bottom: 4px;
	color: var(--tt-text-muted);
	font-size: 0.75rem;
	font-weight: 600;
}

.tt-search__submit {
	width: 100%;
}

/*
 * THE COLLAPSED HERO SEARCH. Phone only, and only once the script has taken
 * `hidden` off the trigger and put `is-collapsed` on the form — see the note
 * beside the button in tour-search.php for why both halves are needed before
 * anything here can match.
 *
 * The panel gives up its own chrome while collapsed, so what is left on the
 * photograph is a single floating pill rather than a white card with a pill
 * inside it. The chrome comes straight back when the fields open, because then
 * there IS a panel and the three controls need a ground to sit on.
 *
 * 52px, not 44. The minimum target is 44 and this is the primary action of the
 * whole site on the device most of its visitors are holding; the extra eight
 * pixels are the difference between clearing the floor and being designed for.
 */
.tt-search__trigger {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	min-height: 52px;
	padding: 0 var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-2);
	color: var(--tt-text-muted);
	font: inherit;
	font-size: 1rem;
	text-align: left;
	cursor: pointer;
}

.tt-search__trigger svg {
	flex: 0 0 auto;
	color: var(--tt-blue);
}

/*
 * THE WHOLE COLLAPSE LIVES INSIDE THE PHONE QUERY, AND THAT IS THE POINT.
 *
 * The first draft hid the fields unconditionally and put them back at 768px,
 * which meant writing `display` values for a fieldset, a button and a heading
 * that three other rules already own — get one of them wrong and the desktop
 * submit button silently becomes a grid container. Bounding the query instead
 * means the desktop never sees any of this: resize past 768 with the class
 * still on the form and the hero is simply the hero, whether or not the script
 * got round to removing it.
 */
@media (max-width: 767px) {
	.tt-search--hero.is-collapsed {
		padding: 0;
		border-color: transparent;
		background: none;
		box-shadow: none;
	}

	.tt-search--hero.is-collapsed .tt-search__fields,
	.tt-search--hero.is-collapsed .tt-search__submit,
	.tt-search--hero.is-collapsed .tt-search__heading {
		display: none;
	}
}

@media (min-width: 768px) {
	.tt-search__trigger {
		display: none;
	}
}

/*
 * The control count is a class rather than auto-fit, because a facet that has
 * no terms is not rendered at all: with auto-fit a two-control form would
 * silently leave a hole where the third used to be.
 */
@media (min-width: 768px) {
	.tt-search {
		grid-template-columns: 1fr auto;
		align-items: end;
	}

	.tt-search--2 .tt-search__fields {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.tt-search--3 .tt-search__fields {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}

	.tt-search__submit {
		width: auto;
	}
}

/*
 * The hero search, on a screen wide enough to hold it in one line, reads as a
 * single control rather than as three boxes and a button. The cells lose their
 * own borders and are separated by hairlines instead, which is the mockup and
 * is also what every travel site the brief cites does — the visitor is filling
 * in one question, not three fields.
 *
 * Below 768px it stays exactly as it was: stacked, each control bordered. Hairline
 * dividers between full-width rows would read as a table, and the borders are
 * what make a stacked control look tappable.
 */
@media (min-width: 768px) {
	.tt-search--hero {
		align-items: stretch;
		gap: 0;
		padding: var(--tt-space-1);

		/*
		 * 18px, WHICH IS THE APPROVED NUMBER AND NOT A PILL. This shipped as
		 * `--tt-radius-pill`, against two written rules and the mockup: the
		 * token block says «Pills are reserved for filters and tags», CLAUDE.md
		 * says «No excessive rounded cards», and `.searchbox` in the approved
		 * prototype is `border-radius: 18px`. A 97px-tall lozenge with a
		 * lozenge inside it is the house style of every booking widget on the
		 * internet, which is the one thing the brief asks this not to look like.
		 *
		 * Not `--tt-radius-card` (12px) either: this box is eight times the area
		 * of a card, and a radius reads by its ratio to the box, not in pixels.
		 */
		border-radius: 18px;
		box-shadow: var(--tt-shadow-raised);
	}

	/*
	 * THE CELLS ARE NOT THIRDS, BECAUSE THE THINGS INSIDE THEM ARE NOT EQUAL.
	 *
	 * Three equal columns gave each select 171px of room; «Οποιαδήποτε
	 * ημερομηνία» needs 197, so 26px of it was cut off at the cell edge. The
	 * first thing on the site read «Οποιαδήποτε ημερομη ▾» — a word stopped
	 * halfway through, on the control the whole page is built around.
	 *
	 * The ratios come from measuring the WIDEST option each control can show,
	 * not from taste — the placeholder in two cases, a counted month in the
	 * third:
	 *
	 *   destination   «Όλοι οι προορισμοί»     147px   needs 203   gets 261
	 *   month         «Σεπτέμβριος 2026 (6)»   170px   needs 226   gets 251
	 *   type          «Όλες οι εκδρομές»       135px   needs 191   gets 218
	 *
	 * (needs = the option + 24px for the chevron + 32px of cell padding.)
	 *
	 * The slack is not spread evenly: the type vocabulary is closed and its
	 * placeholder is already its longest string, months grow by one digit when a
	 * count passes nine, and the destination list is the one the office adds to.
	 *
	 * A first pass at these ratios was written from arithmetic and shipped a
	 * three-pixel shortfall on «Όλες οι εκδρομές» — the same defect, moved one
	 * cell to the right. The numbers above are read out of the browser.
	 *
	 * Scoped to `--3`: the count is in a class precisely because a facet with no
	 * terms is not rendered at all, and forcing three columns onto a two-control
	 * form would leave a hole where the third used to be.
	 */
	.tt-search--hero.tt-search--3 .tt-search__fields {
		grid-template-columns: 1.2fr 1.15fr 1fr;
	}

	/*
	 * A 4th column, added when Θεματική joined the bar alongside Διάρκεια.
	 * Equal widths rather than the hand-measured 3-column ratios above: with a
	 * fourth cell there is less slack to redistribute per control, and equal
	 * columns did not clip any of the four labels at 1440 (checked in the
	 * browser, same way the 3-column ratios were).
	 */
	.tt-search--hero.tt-search--4 .tt-search__fields {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}

	/*
	 * 900px was sized for 3 cells plus the button; a 4th cell in that same
	 * width clipped every placeholder to «…». 1100 gives the 4 cells back
	 * roughly the per-cell room the 3-column bar had (checked in the browser,
	 * no clipping left at 1440).
	 */
	.tt-search--hero.tt-search--4 {
		max-width: 1100px;
	}

	.tt-search--hero .tt-search__fields {
		gap: 0;
	}

	/*
	 * 16px, down from 24. The approved prototype puts 13px here; 24 was spending
	 * 48px per cell on air while the text inside it was being cut off, which is
	 * the wrong end to be generous at. 16 keeps 32px of clear space between one
	 * cell's text and the next one's, which is more than the hairline needs to
	 * read as a divider.
	 */
	.tt-search--hero .tt-search__field {
		padding: 6px var(--tt-space-2);
	}

	.tt-search--hero .tt-search__field + .tt-search__field {
		border-inline-start: 1px solid var(--tt-border);
	}

	.tt-search--hero .tt-select,
	.tt-search--hero .tt-search__text {
		height: auto;
		padding: 0;
		border: 0;
		background-color: transparent;
		font-weight: 600;

	}

	/*
	 * The arrow is TWO triangles, offset from each other by 5px, and the pair of
	 * positions is what makes them a chevron. A single-value override collapses
	 * them onto each other and draws a small solid lozenge instead — which is what
	 * shipped for one screenshot. Both positions are restated, shifted flush to
	 * the cell's inline end now that the control has no padding of its own.
	 */
	.tt-search--hero .tt-select {
		padding-inline-end: var(--tt-space-3);
		background-position: calc(100% - 5px) 50%, 100% 50%;
	}

	/*
	 * The button follows the box it sits in: `--tt-radius-button` is what every
	 * other button on the site uses, and the prototype draws this one at 13px —
	 * a rounded rectangle either way, not the 79px-tall lozenge that shipped.
	 *
	 * It stops stretching to the full height of the bar as well. Stretched, it
	 * was 79px against a 44px control — the loudest thing in the hero was its
	 * least interesting element, a button that says «Αναζήτηση» on a form whose
	 * three fields all say «everything».
	 */
	.tt-search--hero .tt-search__submit {
		align-self: center;
		min-height: 52px;

		/*
		 * A CAPSULE HERE TOO, and this rule is the reason the change needed a
		 * second look: the hero submit sets its own radius, so making
		 * `.tt-button` a pill left the one button a visitor meets first as the
		 * only square action on the page. Measured before the fix: 5 pills out
		 * of 7 actions on the homepage, and the two exceptions were the search
		 * and the menu toggle — the two most prominent controls on the site.
		 */
		border-radius: var(--tt-radius-action);
		padding-inline: var(--tt-space-4);
	}
}

/*
 * AND ON A TABLET THE BUTTON GOES UNDERNEATH, BECAUSE THE ROW IS 168px SHORT.
 *
 * The ratios above were measured once, at 1440, and shipped as if they were a
 * property of the bar. They are a property of the bar AT ITS FULL WIDTH: the
 * form is capped at 900px, so from 948px of viewport up the numbers hold, and
 * below that everything in the row shrinks except the button, which is set by
 * its own text. Photographed at 834 in WebKit, the third control read «Όλες οι
 * εκδρομέ» — the identical defect this whole block exists to fix, one breakpoint
 * to the left, found only because the screenshot was taken.
 *
 * Moving the button to its own row returns its 152px and the 16px beside it to
 * the fields, which is the whole shortfall and then some. At the narrowest width
 * this applies to:
 *
 *   fields get 704px       destination 252   month 241   type 210
 *   fields need 620px      destination 203   month 226   type 191
 *
 * The alternative was to stack the three controls the way a phone does, and it
 * is worse: it spends four full-width rows on a form whose controls need 250px
 * each, in a hero whose whole argument is that the search must be visible
 * without scrolling.
 */
@media (min-width: 768px) and (max-width: 959px) {
	.tt-search--hero {
		grid-template-columns: 1fr;
	}

	.tt-search--hero .tt-search__submit {
		width: 100%;

		/* The bar sets `gap: 0` so the hairline cells can touch. This row is not one of them. */
		margin-top: var(--tt-space-1);
	}
}

/* Banner ------------------------------------------------------------------ */

.tt-banner {
	display: grid;
	overflow: hidden;
	border-radius: var(--tt-radius-card);
	background: var(--tt-navy);
	color: var(--tt-on-navy);

	/* Blue on this navy is 2.69:1; see the token. */
	--tt-focus: var(--tt-white);
}

@media (min-width: 768px) {
	.tt-banner {
		grid-template-columns: 3fr 2fr;
		align-items: stretch;
	}
}

.tt-banner__body {
	padding: var(--tt-space-4);
}

/*
 * AND IT CENTRES IN WHAT THE PHOTOGRAPH GIVES IT.
 *
 * The banner is a two-column grid whose height is set by the picture: at 1440
 * the media column is about 640px wide and a 3:2 frame makes the row 427px
 * tall. The body holds a heading, two lines and a button — roughly 250px — and
 * with the default `start` alignment the remaining 175px piled up underneath as
 * one block of empty navy occupying the bottom 40% of the banner.
 *
 * Empty navy at the foot of a dark panel does not read as breathing room; it
 * reads as content that failed to load. Centred, the same 175px becomes a
 * margin above and below and the two columns balance.
 *
 * Inside the desktop query only. On a phone the columns stack, the body is the
 * height of its own contents, and there is nothing to centre in.
 */
@media (min-width: 768px) {
	/*
	 * `align-items: start` IS NOT OPTIONAL HERE, AND THE FIRST DRAFT LEFT IT
	 * OUT.
	 *
	 * A flex column stretches its children across the cross axis by default, so
	 * turning this box into one turned «Ζήτησε προσφορά» into an 850px bar
	 * spanning the whole navy column — which is worse than the dead space it was
	 * meant to fix, because a button that wide stops reading as a button.
	 *
	 * Every child of this box wants its own width: the heading wraps to the
	 * column, the paragraph is capped at 46ch, and the button is the width of
	 * its label. `start` gives all three what they had before the box became a
	 * flex container.
	 */
	.tt-banner__body {
		display: flex;
		flex-direction: column;
		align-items: start;
		justify-content: center;
	}
}

.tt-banner__heading {
	color: var(--tt-white);
}

.tt-banner__text {
	max-width: 46ch;
	color: var(--tt-on-navy);
}

/*
 * The same hatch the cards use for missing photography, so an empty half of the
 * banner reads as "a photograph goes here" rather than as a rendering fault.
 * It disappears below 768px: on a phone it would be a tall empty block carrying
 * no information at all.
 */
.tt-banner__media {
	display: none;
	background: var(--tt-blue-soft) repeating-linear-gradient(
		135deg,
		transparent 0 10px,
		rgb(18 43 77 / 4%) 10px 20px
	);
}

@media (min-width: 768px) {
	.tt-banner__media {
		display: block;
		min-height: 0;
	}
}

/* Panels ------------------------------------------------------------------ */

.tt-panel {
	padding: var(--tt-space-4);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

.tt-panel--accent {
	background: var(--tt-blue-soft);
	border-color: transparent;
}

.tt-split__body {
	max-width: 62ch;
}

/*
 * THE LAST PANEL ON THE PAGE PUTS ITS BUTTON OPPOSITE ITS SENTENCE.
 *
 * The panel is 1180px wide and the copy is capped at a reading measure, so
 * everything sat in the left 45% and the right 55% was empty white inside a
 * bordered box — the same shape of hole the coach banner had, and worse here
 * because this is the page's final call to action and the button was the
 * smallest thing in it.
 *
 * Two columns, the second sized to the button, the button spanning every row
 * and centred against them. Nothing in the markup moved: the kicker, heading
 * and paragraph stay in source order in column one, so the reading order and
 * the tab order are what they were.
 *
 * 900px rather than the usual 768: below that the panel is narrow enough that a
 * button beside a three-line paragraph leaves neither of them room, and the
 * stacked version is already right.
 */
@media (min-width: 900px) {
	.tt-home-contact {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--tt-space-4);
	}

	/*
	 * `min-width: 0` because a flex item refuses to shrink below its content's
	 * minimum by default, and this one holds a heading that would rather not
	 * wrap. Without it the text box wins the whole row and pushes the button
	 * off the panel's padding.
	 */
	.tt-home-contact__text {
		min-width: 0;
	}

	.tt-home-contact > .tt-button {
		flex: 0 0 auto;
	}
}

/*
 * The last child of the text box carries the panel's bottom margin on a phone,
 * where the button sits under it. Above 900px they are side by side and that
 * margin becomes a limp under the paragraph.
 */
@media (min-width: 900px) {
	.tt-home-contact__text > :last-child {
		margin-bottom: 0;
	}
}

/* Trust cards ------------------------------------------------------------- */

/*
 * auto-fit, because the section ships with two of three cards until the client
 * confirms the third claim, and a fixed three-column grid would leave a gap.
 */
.tt-trust {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--tt-space-2);
}

@media (min-width: 768px) {
	.tt-trust {
		grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
		gap: var(--tt-space-3);
	}
}

.tt-trust__card {
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

.tt-trust__title {
	margin-bottom: 6px;
}

/*
 * RUNNING COPY SITS ON THE BODY TOKEN. docs/03 asks for at least 16px of body
 * text on a phone; this was 14, and it is three real sentences about why the
 * office is worth trusting, not a caption.
 */
.tt-trust__text {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-body);
}

/* Reviews ----------------------------------------------------------------- */

/*
 * A SIDEWAYS TRACK, BECAUSE FIVE REAL REVIEWS ARE LONGER THAN FIVE INVENTED ONES.
 *
 * This was `grid` with `auto-fit`, written when the section held three typed
 * sentences of about twenty words. The Google reviews that replaced them run
 * 150 to 400 characters each, and there are five, so on a 390px screen the
 * section measured 4,238px — longer than everything above it put together, and
 * the thing directly between the tours and the contact form.
 *
 * The mechanism is entirely CSS. `overflow-x` plus `scroll-snap` gives swipe on
 * a phone, trackpad and wheel on a desktop, and arrow keys once the track has
 * focus, with no script involved; the buttons the script adds are a shortcut,
 * not the interface.
 */
.tt-carousel {
	position: relative;
}

/*
 * `position: relative` IS LOAD-BEARING, AND THE BUG IT FIXES LOOKS LIKE NOTHING.
 *
 * An `overflow` box only clips descendants whose containing block is inside it.
 * Every review card carries `.screen-reader-text` spans — «Βαθμολογία 5 από 5»,
 * «(ανοίγει σε νέα καρτέλα)» — and those are `position: absolute !important`
 * with no offsets. With the wrapper positioned and the track not, their
 * containing block was the WRAPPER, which sits outside the scroller, so the
 * track could not clip them: a 1px span belonging to the fifth card stayed in
 * the document's overflow at x = 1700 and the whole page scrolled 261px
 * sideways at 1440.
 *
 * It is invisible in a screenshot — the span is one pixel and clipped to
 * nothing — and it broke three of the homepage's «does not scroll sideways»
 * tests, which is the only reason it was caught. Any future scroller in this
 * file needs this line for the same reason.
 */
.tt-carousel__track {
	position: relative;
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	gap: var(--tt-space-2);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;

	/*
	 * Room for the focus ring, exactly as .tt-destinations needs it: without the
	 * inline padding the outline of a focused card is clipped by the scroll
	 * container, and without scroll-padding the browser parks the card flush
	 * against the edge so half the ring is off-screen anyway.
	 */
	padding: 3px 3px var(--tt-space-1);
	margin-inline: -3px;
	scroll-padding-inline: 3px;
	-webkit-overflow-scrolling: touch;
}

/*
 * The track itself is a tab stop — see the note in the template — so it needs a
 * ring of its own. Without this the keyboard lands on a region with no visible
 * indication that the arrow keys now do something.
 */
.tt-carousel__track:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 2px;
}

/*
 * One card and a slice of the next. The slice is the affordance: a track that
 * ends exactly at the fold looks like a list that happens to be short.
 */
.tt-reviews__item {
	flex: 0 0 84%;
	display: flex;
	scroll-snap-align: start;
}

.tt-reviews__item > .tt-review {
	width: 100%;
}

/*
 * EQUAL HEIGHTS ARE FOR CARDS YOU CAN SEE AT THE SAME TIME.
 *
 * A flex row stretches every child to the tallest one, which is right on a
 * desktop where three cards sit side by side and a ragged bottom edge would
 * look like a rendering fault. On a phone one card is visible, the tallest
 * quote runs 391 characters, and the shortest was drawn as a 155-character
 * review followed by roughly 400px of empty white card — the attribution
 * stranded at the bottom of a box with nothing in the middle of it.
 *
 * Nothing is truncated to achieve this. The quotes are the customers' own
 * words, printed in full at every width; only the box stops pretending it has
 * more in it than it does.
 */
.tt-carousel__track {
	align-items: start;
}

@media (min-width: 768px) {
	.tt-carousel__track {
		gap: var(--tt-space-3);
		align-items: stretch;
	}

	/*
	 * Until somebody opens one. See the note beside the «Περισσότερα» handler in
	 * navigation.js: a stretched row makes every visible card as tall as the
	 * longest, so expanding one review left the two beside it holding a short
	 * quote above a pool of white. Set by the script, on the track, for good.
	 */
	.tt-carousel__track.is-ragged {
		align-items: start;
	}

	.tt-reviews__item {
		flex-basis: 46%;
	}
}

/*
 * FOUR ACROSS, NOT THREE, AND THE ARITHMETIC IS THE GAP'S.
 *
 * The reference puts four 276px cards in a 1200px row with 32px between them:
 * 4 × 276 + 3 × 32 = 1200 exactly. This is the same sum with this project's
 * numbers — `calc` rather than a percentage, so the cards stay flush with the
 * container edges at any width instead of drifting as the gap is divided
 * between three seams instead of two.
 */
@media (min-width: 1024px) {
	.tt-reviews__item {
		flex-basis: calc((100% - 3 * var(--tt-space-3)) / 4);
	}
}

/* Carousel controls, added by script and therefore styled for a real button. */
.tt-carousel__nav {
	display: flex;
	justify-content: flex-end;
	gap: var(--tt-space-1);
	margin-top: var(--tt-space-1);
}

.tt-carousel__button {
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	padding: 0;
	border: 1px solid var(--tt-border-control);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-navy);
	cursor: pointer;
}

.tt-carousel__button:hover:not([disabled]) {
	border-color: var(--tt-blue);
	color: var(--tt-blue);
}

/*
 * Disabled at the ends rather than hidden, so the pair does not shift sideways
 * every time the visitor reaches an edge.
 */
.tt-carousel__button[disabled] {
	opacity: 0.4;
	cursor: default;
}

.tt-carousel__button svg {
	width: 20px;
	height: 20px;
	fill: none;
	stroke: currentcolor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/*
 * Smooth scrolling is motion the visitor asked for, but «asked for» is not the
 * same as «can tolerate»: a vestibular trigger is a vestibular trigger even when
 * it follows a button press. The jump still happens, instantly.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-carousel__track {
		scroll-behavior: auto;
	}
}

.tt-review {
	margin: 0;
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

.tt-review__quote {
	margin: 0;
}

/*
 * TWO LINES, WHICH IS THE REFERENCE'S OWN NUMBER.
 *
 * The complaint was «υπερμακρόστενα», and it was arithmetic: at 357px wide the
 * cards were 685px tall on a desktop — a ratio of 1.92, against 0.96 on the
 * reference site the client pointed at, where the card is very nearly square.
 * Four lines got that to 0.78 and the client then sent a screenshot of the
 * reference's own card, measured at 276 × 265 with its quote clamped to two.
 *
 * The cause was always that a review sets its own height and Greek reviews are
 * long. What makes two lines safe here and unsafe there is the button: the
 * reference ends a 233-character review at 70 characters and that is where it
 * ends for the visitor, while here the same two lines have «Περισσότερα» under
 * them. A shorter card, not a shorter review. See the note in reviews.php.
 *
 * Clamped on the paragraph and not on the `<blockquote>`: the clamp counts line
 * boxes, and a blockquote whose only child is a block-level paragraph has none
 * of its own.
 */
.tt-review__quote p {
	display: -webkit-box;
	overflow: hidden;
	margin: 0;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
}

/*
 * FOUR LINES WHERE THERE IS NO HEADLINE, so the two lines the headline would
 * have occupied are spent on the review instead of on white space.
 *
 * Google's reviews arrive without a title and the office's typed ones have one.
 * In a row stretched to equal heights that is 52px of empty card in every
 * untitled one, and at a two-line clamp it was the first thing the eye found.
 *
 * The rule is written after `is-open` deliberately: `is-open` has to win, and
 * both selectors are one class on the same element.
 */
.tt-review--untitled .tt-review__quote p {
	-webkit-line-clamp: 4;
}

.tt-review__quote.is-open p {
	display: block;
	overflow: visible;
	-webkit-line-clamp: none;
}

/*
 * Where the keyboard lands after «Περισσότερα» removes itself. `:focus-visible`
 * and not `:focus`, so a mouse click — which also moves focus here — draws
 * nothing, while somebody who pressed Enter can see the review they just opened.
 */
.tt-review__quote:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 4px;
	border-radius: 4px;
}

.tt-review__author {
	margin-top: var(--tt-space-2);
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
	font-weight: 600;
}

/*
 * THE BUTTON THAT OPENS THE REST, built by navigation.js and only on the cards
 * that need it — a card whose quote already fits gets no control, because a
 * «Διαβάστε όλη την κριτική» that opens nothing is a lie about the card.
 *
 * A real `<button>` with `aria-expanded`, not a styled link: it changes what is
 * on this page rather than going anywhere, so a screen reader must not offer it
 * as a destination and the space bar must work.
 */
/*
 * 44px OF HIT AREA WITHOUT MOVING THE LABEL BY A PIXEL.
 *
 * «Περισσότερα» is the only control in the whole reviews section and it was a
 * 90×22 target — half the minimum, on the one thing in that block a thumb is
 * meant to find. Padding grows the box and a negative margin of the same size
 * takes the growth back out of the layout, so the underline stays exactly where
 * it was drawn and the tappable area reaches the floor.
 *
 * ONE DECLARATION DID NOTHING AND SAID SOMETHING, WHICH IS WORSE THAN EITHER.
 *
 * This block declared `margin-top: var(--tt-space-1)` and then, two lines later,
 * `margin-block: -11px var(--tt-space-1)`. A shorthand sets both of its
 * longhands whether or not you write them, so the top value of the shorthand
 * replaced the longhand outright and the first declaration never reached a
 * pixel. It stayed there reading like an 8px gap that anyone measuring the card
 * would fail to find.
 *
 * THE VALUE IS NOT THE BUG. The -11px is the compensation the paragraph above
 * asks for in capitals, and it is doing exactly what that paragraph describes:
 * cancelling the 11px of padding so the underline sits where it was drawn. The
 * card it produces is the 276x265 one measured against the client's screenshot
 * — see the note at the top of this section about dropping the quote clamp to
 * two lines to reach it — and turning the top margin into
 * `calc(var(--tt-space-1) - 11px)` moves that approved measurement 8px, on
 * every card with a toggle, and through the equal-height carousel row onto the
 * homepage. No test asserts the card's height, so it would have shipped
 * unmeasured.
 *
 * So the dead declaration goes and nothing moves. The bottom is deliberately
 * NOT compensated: `--tt-space-1` plus the 11px of padding is the 19px above
 * the attribution this card already ships.
 */
.tt-review__more {
	align-self: flex-start;
	padding-block: 11px;
	margin-block: -11px var(--tt-space-1);
	padding-inline: 0;
	border: 0;
	background: none;
	color: var(--tt-blue);
	font: inherit;
	font-size: var(--tt-text-small);
	font-weight: 600;
	text-decoration: underline;
	text-underline-offset: 3px;
	cursor: pointer;
}

.tt-review__more:hover {
	color: var(--tt-blue-dark);
}

.tt-review__more:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 3px;
	border-radius: 4px;
}

/*
 * WHAT PUSHES THE ATTRIBUTION TO THE BOTTOM CHANGED, AND IT HAD TO.
 *
 * This used to be `flex: 1 1 auto` on the quote: the words grew, the caption
 * was pushed down, and a row of cards ended on the same line however long the
 * sentences were. A clamped quote does not grow — it is four lines whatever is
 * in it — so a growing blockquote now means four lines of text followed by a
 * pool of empty white, with the «Διαβάστε όλη την κριτική» button floating at
 * the bottom of it instead of under the sentence it opens.
 *
 * The quote hugs its text and `.tt-review__author` takes `margin-top: auto`
 * instead. Same result at the bottom edge, and the button stays where the eye
 * leaves off reading.
 */
.tt-review {
	display: flex;
	flex-direction: column;
}

/*
 * ONE METADATA ROW ABOVE THE HEADLINE: stars on the left, date on the right.
 *
 * Both facts used to be somewhere else — the stars on their own line with 14px
 * under them, the date stacked as a third row inside the attribution block —
 * which spent about 50px of card height on two short strings that belong beside
 * each other. The reference site puts them on a single 26px line and it is the
 * right call: they are both «about the review», where the name underneath is
 * «about the person».
 *
 * `space-between` rather than a gap, so the date sits on the card's right edge
 * and the row reads as a header even when the rating is missing and the date is
 * the only thing in it.
 */
.tt-review__meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-space-1);
	margin: 0 0 var(--tt-space-1);
	min-height: 20px;
}

/*
 * `margin-left: auto` AND NOT ONLY THE ROW'S `space-between`, because half of
 * these cards have no stars.
 *
 * With two children `space-between` puts the date on the right edge; with one it
 * puts it on the left. Six of the nine cards on the homepage are rated and three
 * are not — the office has not been given the Tripadvisor scores yet — so in a
 * row that scrolls sideways the date jumped from one corner to the other every
 * third card. The auto margin pushes it right whether or not there is anything
 * beside it, and the dates line up down the row.
 */
.tt-review__when {
	margin-left: auto;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	font-weight: 400;
}

/*
 * THE HEADLINE THE CUSTOMER GAVE THEIR OWN REVIEW.
 *
 * Borrowed from the reference the client pointed at, and it is the one part of
 * that card worth borrowing: a row of quotations all set at the same size gives
 * a moving carousel nothing to catch the eye, while one bold line per card can
 * be read at a glance from a phone. The words are still the customer's — the
 * platform asked them for a title and this prints it.
 *
 * Sized ABOVE the quote it introduces, not below a heading that does not exist:
 * there is no `<h3>` here on purpose. Six headings inside a scroller would put
 * six false entries into a screen reader's outline of the page.
 */
/*
 * Two lines at most, for the same reason the quote is two: «Η πενθήμερη μας
 * έμεινε αξέχαστη» is two lines at this width and a title nobody capped ran to
 * three, which is a third of a card spent before the review starts.
 *
 * No ellipsis is added by hand. The clamp draws its own, and a title cut here
 * is still readable in full — it is the first line of the quotation underneath.
 *
 * 18px/26 AND 600, WHICH IS THE REFERENCE'S SIZE AT THIS PROJECT'S WEIGHT. The
 * screenshot's headline measures 18px/26px at weight 500; 500 in that typeface
 * is this one's 600, and going up from 17px is what stops the headline and the
 * 15px quote under it reading as one block of text at two slightly different
 * sizes. `text-wrap: balance` stays — at 262px a two-line Greek headline splits
 * badly often enough to be worth the browser's second pass.
 */
.tt-review__title {
	display: -webkit-box;
	overflow: hidden;
	margin: 0 0 var(--tt-space-1);
	color: var(--tt-text);
	font-size: 1.125rem;
	font-weight: 600;
	line-height: 1.44;
	text-wrap: balance;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
}

/*
 * The platform the review was left on, on the card rather than in a footnote.
 * Two sources share this row now, so a single line underneath saying «from
 * Google» would be wrong about half of them — and `source` exists precisely so
 * a visitor can go and check.
 *
 * IT LOST ITS RULE AND ITS OWN LINE. The 28px hairline above it forced the
 * element to `display: block` and put a third row inside a caption that sits
 * beside a 38px circle, so the attribution alone was taller than the avatar
 * next to it. It now shares one line with «τύπος ταξιδιού», separated by a
 * middle dot, and the caption is two rows that fit the circle exactly.
 */
.tt-review__source {
	color: var(--tt-blue);
	font-size: var(--tt-text-micro);
	font-weight: 600;
}

/*
 * STARS IN THE BRAND BLUE, AND NEVER ALONE. CLAUDE.md fixes the palette at
 * blue, white and soft gray, so the gold every other site uses is not
 * available — which is fine, because colour was never allowed to be the only
 * carrier of meaning anyway. Every rating on this page is also written out:
 * «4,5 από 5» beside the summary, and a screen-reader sentence on each card.
 */
.tt-rating {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-space-1);
	margin: var(--tt-space-1) 0 0;
}

.tt-rating__stars,
.tt-review__rating {
	display: inline-flex;
	gap: 2px;
	margin: 0;
	line-height: 1;
}

.tt-rating__star {
	color: var(--tt-border);
	font-size: 1rem;
}

.tt-rating__star.is-filled {
	color: var(--tt-blue);
}

.tt-rating__text {
	color: var(--tt-text-muted);
	font-size: 0.875rem;
}

/* Sample-content marker --------------------------------------------------- */

/*
 * Shown to everyone, not only editors. Unlike .tt-editor-hint this marks
 * content that is visible and invented, so hiding it from the public would be
 * the whole problem.
 */
.tt-sample-note {
	display: inline-block;
	margin: var(--tt-space-1) 0 0;
	padding: 6px 12px;
	border: 1px dashed var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-text-muted);
	font-size: 0.75rem;
	font-weight: 600;
}

/*
 * THE SECTIONS VARIANT LABELS WHAT COMES AFTER IT, AND WAS SITTING ON WHAT CAME
 * BEFORE.
 *
 * `--sections` has been in the markup since sections.php was written and has
 * never had a rule, so it inherited the base note's `margin: 8px 0 0` — 8px of
 * air above and none below. Measured on /sxolikes-metafores/ at 1440px: the
 * office's own last paragraph ends at 640, the note runs 655 to 685, and «Πώς
 * δουλεύει» starts at 690. Five pixels under the note and fifteen over it, so
 * the disclosure read as a footnote to the paragraph it is not about.
 *
 * It says «τα παρακάτω είναι ενδεικτικά» — everything about it is a claim over
 * the sections below — so the space goes above it and the heading it introduces
 * stays close. The following section's own top margin is zero, so there is
 * nothing to collapse against.
 */
.tt-sample-note--sections {
	margin-top: var(--tt-space-5);
	margin-bottom: var(--tt-space-1);
}

/* Editor hint ------------------------------------------------------------- */

/*
 * Never styled like a control. This is an internal note shown only to users who
 * can act on it, and it must not be mistaken for a live button in a screenshot.
 * The gating is server side; hiding it with CSS would leave it in the HTML, in
 * any page cache and in a screen reader.
 */
.tt-editor-hint {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-space-1);
	margin: var(--tt-space-2) 0 0;
	padding: 10px 12px;
	border: 1px dashed var(--tt-border);
	border-radius: var(--tt-radius-button);
	background: var(--tt-gray-soft);
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

.tt-editor-hint__badge {
	padding: 2px 8px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	border: 1px solid var(--tt-border);
	font-size: 0.6875rem;
	font-weight: 700;
	text-transform: none;
}

.tt-banner .tt-editor-hint {
	background: rgb(255 255 255 / 10%);
	border-color: rgb(255 255 255 / 24%);
	color: var(--tt-on-navy);
}

.tt-banner .tt-editor-hint__badge {
	background: transparent;
	border-color: rgb(255 255 255 / 30%);
	color: var(--tt-on-navy);
}

/* Cards, shared ----------------------------------------------------------- */

/*
 * Neutral stand-in for photography that has not been delivered yet. Flat, not
 * gradient, so nobody mistakes it for a finished visual treatment.
 */
.tt-media {
	position: relative;
	display: block;
	overflow: hidden;
	background: var(--tt-blue-soft);
	border-radius: var(--tt-radius-card);
}

.tt-media::after {
	content: "";
	position: absolute;
	inset: 0;
	background: repeating-linear-gradient(
		135deg,
		transparent 0 10px,
		rgb(18 43 77 / 3%) 10px 20px
	);
}

.tt-media img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Once a real image is present the hatch must not sit on top of it. */
.tt-media:has(img)::after {
	display: none;
}

/* Destination card -------------------------------------------------------- */

.tt-destinations {
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * Mobile shows one full card and part of the next, per the design brief, so
 * the row reads as scrollable without a visible affordance. 68% is what makes
 * the second card clearly cut rather than merely narrow.
 */
@media (max-width: 767px) {
	.tt-destinations {
		display: grid;
		grid-auto-flow: column;
		grid-auto-columns: 68%;
		gap: var(--tt-space-2);
		overflow-x: auto;
		scroll-snap-type: x mandatory;

		/*
		 * Room for the focus ring. Without the inline padding the outline of a
		 * keyboard-focused card is clipped by the scroll container, and without
		 * scroll-padding the browser scrolls the card flush against the edge so
		 * half the ring is off-screen anyway.
		 */
		padding: 3px 3px var(--tt-space-1);
		margin-inline: -3px;
		scroll-padding-inline: 3px;
		-webkit-overflow-scrolling: touch;
	}

	.tt-destination {
		scroll-snap-align: start;
	}
}

@media (min-width: 768px) {
	/*
	 * FOUR ACROSS, NOT SIX. The prototype specified six because it was drawn
	 * with six placeholder tiles; the catalogue has eight destinations, and
	 * eight in a six-column grid is a full row followed by two tiles and 749px
	 * of empty line — under a heading that says «Δημοφιλείς προορισμοί», which
	 * makes the gap read as missing content rather than as spacing.
	 *
	 * Four gives two complete rows, and the tiles are large enough that the
	 * photograph does some work instead of being a swatch.
	 */
	.tt-destinations {
		display: grid;
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--tt-space-2);
	}
}

@media (min-width: 1024px) {
	.tt-destinations {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

.tt-destination__link {
	display: block;
	text-decoration: none;
}

.tt-destination .tt-media {
	aspect-ratio: 4 / 3;
}

/*
 * THE CAPTION SITS AT THE FOOT OF THE PHOTOGRAPH — see the note in
 * cards/destination.php for why the white capsule and the loose line under the
 * tile both went.
 *
 * `justify-content: end` on a box pinned to all four corners, rather than a
 * box pinned to the bottom edge with a height of its own. The scrim has to
 * cover more than the words do — about the bottom half of the tile — or its top
 * edge is a visible horizontal seam across the picture, and one box doing both
 * jobs cannot drift out of alignment with itself.
 *
 * `pointer-events: none` because the whole tile is already one link. Without it
 * the caption is a second hit target stacked on the first, which changes
 * nothing for a mouse and breaks `elementFromPoint` for anything measuring the
 * card.
 */
.tt-destination__caption {
	position: absolute;
	z-index: 1;
	inset: 0;
	display: flex;
	flex-direction: column;
	justify-content: end;
	gap: 2px;
	padding: 12px;
	background: linear-gradient(to top, rgb(9 20 36 / 82%) 0%, rgb(9 20 36 / 48%) 34%, rgb(9 20 36 / 0%) 68%);
	pointer-events: none;
}

.tt-destination__name {
	display: block;
	color: var(--tt-white);
	font-weight: 700;
	font-size: 1rem;
	line-height: 1.25;

	/*
	 * The scrim carries the contrast and this carries the edges. A place name
	 * over a photograph is set on whatever the photographer happened to leave in
	 * the bottom-left corner — foam, a whitewashed wall, snow — and the shadow is
	 * what keeps the letterforms readable against detail rather than against an
	 * average. Tinted navy, so it reads as depth and not as a drop shadow.
	 */
	text-shadow: 0 1px 3px rgb(9 20 36 / 55%);
}

.tt-destination__count {
	display: block;
	color: rgb(255 255 255 / 82%);
	font-size: var(--tt-text-micro);
	font-weight: 600;
	line-height: 1.3;
	text-shadow: 0 1px 3px rgb(9 20 36 / 55%);
}

/*
 * The hover used to recolour the name from navy to blue, which was the only
 * signal the tile was a link. On a photograph that is not available — blue on a
 * scrim is the 2.69:1 this file already refuses elsewhere — and it is no longer
 * needed: `.tt-lift` on the tile and the image's own zoom already answer the
 * pointer. White stays white.
 */

/* Tour card --------------------------------------------------------------- */

.tt-tours {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	gap: var(--tt-space-2);
	grid-template-columns: 1fr;
}

/*
 * ONE COLUMN OF ROWS ON A PHONE, AND THREE COLUMNS ONLY WHEN THERE IS ROOM.
 *
 * This was two columns from 320px and three from 768px, which put the grid at
 * its densest exactly where the screen was narrowest: 171px tiles on a phone
 * and 212px cards at 1024px, with the meta line wrapping to three rows to fit.
 * The approved prototype breaks to two columns at 900px, not 768.
 *
 * The phone gets a row instead — photograph left, text right — because a 171px
 * tile cannot hold a tour name, a duration, a departure point, the languages
 * and a price without turning each into a fragment. It also cuts twelve tours
 * from roughly 2,470px of scrolling to 1,700px.
 */
@media (min-width: 768px) {
	.tt-tours {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--tt-space-3);
	}
}

@media (min-width: 900px) {
	.tt-tours {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

/*
 * EXCEPT WHERE THE FILTER SIDEBAR IS ALREADY TAKING 260px OF IT. Between 1024px
 * and 1199px the archive has a sidebar and a results column, and three cards in
 * what is left measured 212px each — narrower than the same grid on a 900px
 * screen with no sidebar at all, which is the wrong way round. Two there, three
 * once the window is wide enough to pay for both.
 *
 * Scoped to the results column rather than to the viewport, because the same
 * grid runs full width on the homepage at exactly these sizes and is fine there.
 */
@media (min-width: 1024px) and (max-width: 1199px) {
	.tt-archive__results .tt-tours {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/*
 * The modifier carries the real number of cards, so a shelf holding three
 * tours does not leave a card-shaped hole in a four-column grid.
 */
@media (min-width: 768px) {
	.tt-tours--2 {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/*
 * And one card is one column, capped so it does not become a 1,180px banner.
 * A destination with a single tour used to draw one card beside 69% of empty
 * results column, which reads as cards that failed to load.
 */
@media (min-width: 768px) {
	.tt-tours--1 {
		grid-template-columns: minmax(0, 420px);
	}
}

@media (min-width: 1024px) {
	.tt-tours--4 {
		grid-template-columns: repeat(4, minmax(0, 1fr));
	}
}

.tt-tour-card {
	position: relative;
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-shadow-card);
	transition: border-color 0.15s ease;
}

/*
 * A hover affordance, not an effect. The whole card is one link and nothing else
 * on it says so — the earlier border-colour change was too quiet to read as
 * «this is clickable». Two pixels and a shadow are enough to lift it off the
 * page; anything more on a grid of twelve is a page that shivers.
 *
 * `:focus-within` matters more than `:hover` here: it gives a keyboard user the
 * same signal a mouse user gets, on a card whose focus ring is on a link inside
 * it rather than on the card.
 */
.tt-tour-card {
	transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.tt-tour-card:hover,
.tt-tour-card:focus-within {
	transform: translateY(-2px);
	border-color: var(--tt-text-muted);
	box-shadow: var(--tt-shadow-raised);
}

.tt-tour-card .tt-media {
	border-radius: 0;
	aspect-ratio: 3 / 2;
}

.tt-tour-card__tag {
	position: absolute;
	left: 10px;
	top: 10px;
	z-index: 2;
	padding: 5px 8px;
	border-radius: 7px;
	background: var(--tt-white);
	color: var(--tt-blue-dark);
	font-size: var(--tt-text-micro);
	font-weight: 800;
}

.tt-tour-card__body {
	display: flex;
	flex-direction: column;
	flex: 1;
	gap: 6px;
	padding: var(--tt-space-2);
}

.tt-tour-card__type {
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
}

/*
 * Two lines are reserved so the meta and price rows line up across a row of
 * cards. Titles longer than two lines still grow; this is a floor, not a clamp.
 */
/*
 * ONE SIZE, AT EVERY WIDTH, AND IT IS THE ONE THE CARD'S OWN RULE ASKS FOR.
 *
 * «THE TITLE LEADS, NOT THE NUMBER» is written in this file a few rules below,
 * and on a phone the file did the opposite: the title held at 15px here while
 * the price took `--tt-text-strong` unconditionally, so the first card on the
 * archive set «One day trip to Meteora» at 15 and «€900» at 18. The intended
 * relationship existed only from 768px up.
 *
 * The title was also a pixel BELOW the site's body copy, which is a strange
 * thing for the name of the product to be.
 *
 * The two-line floor above is unaffected — it is expressed in `em`, so it grows
 * with the type and the grid stays aligned.
 */
.tt-tour-card__title {
	margin: 0;
	min-height: calc(2 * 1.35em);
	font-size: var(--tt-text-strong);
	line-height: 1.35;
}

.tt-tour-card__title a {
	color: var(--tt-navy);
	text-decoration: none;
}

/* The whole card is clickable, without nesting interactive elements. */
.tt-tour-card__title a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.tt-tour-card__title a:hover,
.tt-tour-card__title a:focus {
	color: var(--tt-blue);
}

.tt-tour-card__meta {
	display: flex;
	flex-wrap: wrap;
	gap: 4px 10px;
	margin: 0;
	color: var(--tt-text-muted);
	font-size: 0.75rem;
}

/*
 * THE ICON KEEPS ITS OWN COLUMN, SO THE SECOND LINE DOES NOT START UNDERNEATH
 * IT.
 *
 * Each item is an icon and a phrase in one inline box, and on a two-across
 * phone card «Αναχώρηση από Αθήνα» does not fit on one line. Left inline, the
 * wrap put «Αθήνα» hard against the card's left edge, directly below the pin —
 * a stray word that reads as a separate fact rather than as the end of the one
 * above it. Three cards out of four did it.
 *
 * `inline-flex` with the icon at `flex: 0 0 auto` gives the phrase a column of
 * its own, so it wraps under itself. `align-items: start` keeps the icon on the
 * FIRST line rather than centred against a two-line block.
 */
.tt-tour-card__meta > span {
	display: inline-flex;
	align-items: start;
	gap: 5px;
}

.tt-tour-card__meta .tt-icon {
	flex: 0 0 auto;
	margin-top: 2px;
}

/*
 * A HAIRLINE, BECAUSE THE GAP ABOVE THE PRICE IS STRUCTURE AND WAS READING AS A
 * HOLE.
 *
 * `margin-top: auto` pushes this to the bottom so that prices line up across a
 * row whatever each card's title and meta cost — which is the point, since the
 * row exists to be compared. The side effect is that a card with a short title
 * carries 90px of blank white between its last fact and its price, and blank
 * white with nothing marking it is indistinguishable from a mistake.
 *
 * One 1px rule turns the same 90px into a card with a foot.
 */
.tt-tour-card__foot {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--tt-space-1);
	margin-top: auto;
	padding-top: var(--tt-space-2);
	border-top: 1px solid var(--tt-border);
}

/*
 * `margin-inline-start: auto` AND NOT `space-between`, WHICH HAD NEVER PUT THE
 * PRICE WHERE ITS OWN NOTE SAYS IT IS.
 *
 * The rule below reads «the price is the only dark bold thing in the
 * bottom-right corner». It was not in the bottom-right corner. The foot is a
 * `space-between` row and the price is its ONLY child — the development note
 * beside it is an `elseif`, so the two are never rendered together — and
 * `space-between` with one item leaves that item at the start. What shipped was
 * a right-aligned block sitting against the LEFT edge, which is why «από» hung
 * over «€210» looking centred and slightly wrong without being obviously
 * anything.
 *
 * Pushed by its own margin it goes where it was always described as being, and
 * the placeholder note keeps the left edge that suits it.
 */
.tt-tour-card__price {
	margin-inline-start: auto;
	text-align: right;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	line-height: 1.3;
}

/*
 * THE TITLE LEADS, NOT THE NUMBER. This was 20px against a 17px title, so on a
 * grid of twelve the eye was pulled to the prices and the names of the places
 * came second — on a catalogue whose whole job is choosing WHERE to go. Both
 * are now the same step and the same weight; the title leads by sitting first,
 * and the price is still unmissable because it is the only dark bold thing in
 * the bottom-right corner.
 */
.tt-tour-card__price strong {
	display: block;
	color: var(--tt-navy);
	font-size: var(--tt-text-strong);
	letter-spacing: -0.02em;
}

.tt-tour-card__note {
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	font-style: italic;
}

/*
 * ONE HAIRLINE PER ROW, AND THERE WERE THREE.
 *
 * The rule is drawn at the TOP of the foot and the foot is bottom-anchored by
 * `margin-top: auto`, so its top edge moves with whatever it is holding. The
 * three things it holds are not the same height: measured on /ekdromes/ at
 * 1440px, a price is 39px, a price carrying «ανά γκρουπ» is 53px and a
 * development note is 20px. The first row of cards therefore drew its rules
 * 343, 329 and 343px down, and the second row 367, 387 and 387px — five of the
 * six cards above the fold had a hairline at a different height from the card
 * beside them, on a grid whose entire job is side-by-side comparison.
 *
 * THE FLOOR IS THE COMMON CASE, NOT THE TALLEST: 16px of padding, a two-line
 * price, the border. Reserving the tallest instead would leave 50px of white
 * between the rule and a one-line note on every unpriced card, which trades a
 * ragged edge for a hole. This way the note cards rise to meet the priced ones,
 * the content stays bottom-aligned inside the taller box, and only a price with
 * a basis note still sits 14px low — one card in thirteen instead of five in six.
 *
 * Below 768px there is one card per row and nothing to line up with, so the
 * reservation would only push the price down. Same reasoning as the two-line
 * title floor a few rules above.
 */
@media (min-width: 768px) {
	.tt-tour-card__foot {
		min-block-size: 56px;
	}
}

@media (max-width: 767px) {
	/*
	 * The card is already exactly two flex children, so the whole change is the
	 * direction and a width. 38% leaves about 220px of text column on a 390px
	 * screen, which is what the title and the meta line need in order to read
	 * as sentences rather than as fragments.
	 */
	.tt-tour-card {
		flex-direction: row;
	}

	/*
	 * THE PHOTOGRAPH FILLS ITS SIDE OF THE CARD, AND THE SQUARE WAS THE BUG.
	 *
	 * The history is worth keeping because both previous versions were wrong in
	 * opposite directions. First the media had `aspect-ratio: 3 / 2` and default
	 * `stretch`, and the ratio lost to the layout — photographs came out as
	 * vertical strips near 0.51, one height per card, because the text column
	 * decides the row height. `getComputedStyle` still answered «3 / 2», so the
	 * declaration looked applied while the pixels disagreed.
	 *
	 * The fix for that was `align-self: start` with a square, which made every
	 * photograph the same honest shape — and left a band of white card under
	 * each one, exactly as tall as the text column overran the square. That is
	 * what «δεν εφάπτονται» describes: on a four-card shelf the photographs
	 * ended at four different heights and none of them reached the bottom
	 * corner they were supposed to be filling.
	 *
	 * A list row does not want a fixed ratio at all. It wants a fixed WIDTH and
	 * the full height, which is what every catalogue that uses this shape does,
	 * and it is what the default `stretch` gives once the ratio stops fighting
	 * it. `.tt-media img` is already absolutely positioned with `object-fit:
	 * cover`, so the crop follows the height instead of the height following
	 * the crop, and the card's own `overflow: hidden` keeps the photograph
	 * inside the rounded corner.
	 *
	 * The floor is for the shortest card on the site — a tour with no price and
	 * a one-line title — where the text column alone would leave a letterbox.
	 */
	.tt-tour-card .tt-media {
		flex: 0 0 38%;
		align-self: stretch;
		aspect-ratio: auto;
		min-height: 132px;
	}

	/*
	 * The two-line floor exists so meta and price line up ACROSS a row of
	 * cards. In a single column there is no row to line up with, and the
	 * reservation only pushes the price down.
	 */
	.tt-tour-card__title {
		min-height: 0;
	}

	.tt-tour-card__foot {
		flex-direction: column;
		align-items: flex-start;
	}

	.tt-tour-card__price {
		text-align: left;
	}

	/*
	 * A SHELF IS NOT A LISTING, AND ON A PHONE THEY WANT OPPOSITE CARDS.
	 *
	 * `--shelf` marks the three places that show a short, chosen set: the
	 * homepage's «Εκδρομές που ξεχωρίζουν», the related tours under a tour, and
	 * the daily-tours page. The archive has no modifier because it is the
	 * listing, and the two have different jobs.
	 *
	 * A listing is scanned — twelve or more rows, where the compact row above
	 * earns its keep by cutting roughly 2,470px of scrolling to 1,700px and
	 * keeping the next result on screen.
	 *
	 * A shelf is browsed, four cards at most, and the photograph is the point.
	 * The approved prototype draws it as two columns of upright cards with the
	 * image across the top, and that is also the shape the reference catalogues
	 * use for a homepage row. The row treatment turned it into a stack of
	 * database records with thumbnails: correct, and nothing anybody wants to
	 * look at on the page that has to sell the trip.
	 *
	 * Restoring the ratio here needs all three declarations back, because the
	 * rule above is a sibling and not a parent.
	 */
	.tt-tours--shelf {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	.tt-tours--shelf .tt-tour-card {
		flex-direction: column;
	}

	.tt-tours--shelf .tt-tour-card .tt-media {
		flex: 0 0 auto;
		align-self: auto;
		aspect-ratio: 3 / 2;
		min-height: 0;
	}

	/*
	 * Half the width means half the room, so the body loses the padding it can
	 * spare rather than the words. The meta line is the one that suffers at
	 * 171px — it holds up to three facts — so it stacks instead of wrapping
	 * mid-phrase, which is what turned «Αναχώρηση από Αθήνα» into two fragments
	 * on separate lines.
	 */
	.tt-tours--shelf .tt-tour-card__body {
		padding: var(--tt-space-2) 12px;
	}

	.tt-tours--shelf .tt-tour-card__meta {
		flex-direction: column;
		gap: 2px;
	}
}

/* Notices ----------------------------------------------------------------- */

.tt-notice {
	margin: 0 0 var(--tt-space-3);
	padding: var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-left: 4px solid var(--tt-blue);
	border-radius: var(--tt-radius-button);
	background: var(--tt-blue-soft);
	color: var(--tt-navy);
	font-size: 0.9375rem;
}

.tt-empty {
	padding: var(--tt-space-5) var(--tt-space-3);
	border: 1px dashed var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-gray-soft);
	color: var(--tt-text-muted);
	text-align: center;
}

.tt-empty p:last-of-type {
	margin-bottom: var(--tt-space-3);
}

/* Active filters ---------------------------------------------------------- */

/*
 * `display: block`, deliberately, and it must not become flex again. This
 * element is now a <details>, and a flex container breaks the disclosure: the
 * summary becomes a flex item, the marker moves, and in some engines the panel
 * paints while closed. The declaration below was a leftover from an earlier
 * layout in which this wrapper held chips.
 */
/*
 * No margin-bottom. `.tt-archive__layout` is a grid with `gap: space-3`, so the
 * margin was a second helping of the same 24px — measured on a phone as 53px
 * of nothing between the filter button and «13 εκδρομές», on the screen where
 * the tours are the point.
 */
.tt-archive__filters {
	display: block;
}

/* The filter disclosure --------------------------------------------------- */

.tt-filters__toggle {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-space-1);
	padding: 12px 16px;
	border: 1px solid var(--tt-border-control);
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
	color: var(--tt-navy);
	font-weight: 600;
	cursor: pointer;

	/* The default triangle is replaced by the chevron drawn below, which can
	   be rotated to say open or closed in the same place on every engine. */
	list-style: none;
}

.tt-filters__toggle::-webkit-details-marker {
	display: none;
}

.tt-filters__toggle::after {
	content: "";
	width: 10px;
	height: 10px;
	border-right: 2px solid currentcolor;
	border-bottom: 2px solid currentcolor;
	transform: translateY(-2px) rotate(45deg);
}

.tt-filters[open] .tt-filters__toggle::after {
	transform: translateY(2px) rotate(-135deg);
}

/*
 * A <summary> IS A BUTTON AND HAS TO LOOK LIKE ONE UNDER THE POINTER. Measured
 * across every clickable thing on the site: buttons darken, links and
 * breadcrumbs turn blue, cards lift two pixels. These two disclosures — this
 * one and the FAQ question — were the only controls that answered a hover with
 * nothing at all, having `cursor: pointer` and no other sign they could be
 * pressed. Same blue as the links, so the site says «pressable» one way.
 */
.tt-filters__toggle:hover {
	border-color: var(--tt-blue);
	color: var(--tt-blue-dark);
}

/*
 * 3px and the token, like the rest of the site. This was the one control drawing
 * its own ring at 2px, for no reason anybody wrote down — the clipping worry in
 * the note further down is about the controls INSIDE the panel, and a <summary>
 * is outside `::details-content` and cannot be clipped by it.
 */
.tt-filters__toggle:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 2px;
}

/* The number of applied filters, so a closed panel still says it is filtering. */
.tt-filters__count {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 22px;
	height: 22px;
	margin-inline-start: auto;
	margin-inline-end: var(--tt-space-1);
	padding: 0 6px;
	border-radius: 11px;
	background: var(--tt-blue);
	color: var(--tt-white);
	font-size: 0.8125rem;
	line-height: 1;
}

@media (min-width: 1024px) {
	/*
	 * No toggle on desktop: the sidebar has room for the panel and a
	 * disclosure there is a click between the visitor and a control they can
	 * already see. The script leaves `open` alone above this width.
	 */
	.tt-filters__toggle {
		display: none;
	}

	/*
	 * AND IT COMES WITH THE VISITOR. The tour page's booking card is already
	 * sticky, because a control beside a long scrolling column that scrolls
	 * away with it is a control the visitor has to go back for. The filter
	 * panel is the same kind of thing and was not: measured static, gone from
	 * the screen by 800px of a 2590px page — with thirteen tours, and the
	 * client has more to add.
	 *
	 * `align-items: start` on the grid is what makes this possible and it was
	 * already there; a stretched grid item fills the row and has no room to
	 * stick. Same 24px offset as the booking card, so the two sidebars on this
	 * site settle in the same place.
	 *
	 * THE HEIGHT GUARD IS NOT DECORATION. Sticky positioning pins the top, so a
	 * panel taller than the viewport would hang its submit button below the
	 * fold with no way to reach it — the failure arrives the day somebody adds
	 * a fourth filter, not today. Scrolling the panel itself is what keeps the
	 * button reachable, and `overscroll-behavior` stops that scroll from
	 * running on into the page behind it.
	 */
	.tt-archive__filters {
		position: sticky;

		/* Below the header, which now stays where it is. */
		top: calc(var(--tt-header) + var(--tt-space-3));
		max-height: calc(100vh - var(--tt-header) - (2 * var(--tt-space-3)));
		overflow-y: auto;
		overscroll-behavior: contain;
	}
}

/*
 * THE PANEL GROWS, for the same reason the FAQ answer does and with more at
 * stake. On a phone this opens 390px of form directly above the results, in one
 * frame — every card the visitor was looking at leaves the screen at once, and
 * nothing says the form is what pushed them off it.
 *
 * DELIBERATELY BELOW 1024px ONLY. Above that the toggle is `display: none` and
 * the panel is permanently open, so there is nothing to animate; scoping it
 * says that, and keeps `block-size: 0` away from a panel that has no way to be
 * reopened if anything ever closed it.
 *
 * `overflow: hidden` is what clips the growth, and it was the reason to measure
 * before writing this: a clipped focus ring on a filter control would be a real
 * accessibility regression traded for a nicety. Measured on a Pixel 7 with the
 * panel open — every input, select and button clears all four edges by 17px,
 * against outlines drawn at 2px.
 *
 * WebKit keeps the instant open here too, missing `interpolate-size` rather
 * than the pseudo-element; see the FAQ block for the measurement.
 */
@media (max-width: 1023px) {
	.tt-archive__filters {
		interpolate-size: allow-keywords;
	}

	.tt-archive__filters::details-content {
		block-size: 0;
		overflow: hidden;
		transition: block-size 0.22s ease, content-visibility 0.22s;
		transition-behavior: allow-discrete;
	}

	.tt-archive__filters[open]::details-content {
		block-size: auto;
	}

	/* `*, *::before, *::after` does not reach this pseudo-element either. */
	@media (prefers-reduced-motion: reduce) {
		.tt-archive__filters::details-content {
			transition: none;
		}
	}
}

/*
 * THE FIRST OF THREE UNSCOPED `.tt-chip` RULES USED TO BE HERE, AND EVERY
 * DECLARATION IN IT WAS DEAD.
 *
 * It set `align-items: baseline`, a 6px gap, 6px/12px padding and a
 * `--tt-blue-soft` fill. Two later rules with the identical selector — the base
 * in the Chips section and, at the time, a second base in Chips-with-icons —
 * overwrote all five, so nothing on any page has ever been drawn by it. What it
 * did do is make the pale-blue fill look like a thing `.tt-chip` already had,
 * which is how the applied-filter chip below ended up asking for the same fill
 * and losing it to source order without anybody noticing.
 *
 * There is exactly one `.tt-chip` rule now and it is in the Chips section.
 * `.tt-chip__label` stays here, where it is the only rule for that class.
 */
.tt-chip__label {
	color: var(--tt-text-muted);
	font-weight: 400;
	font-size: 0.75rem;
}

/* Footer ------------------------------------------------------------------ */

/*
 * TOP PADDING BACK, TOP MARGIN GONE, AND THE TWO ARE NOT INTERCHANGEABLE.
 *
 * `.tt-seam` is no longer the first child — it is the sibling directly above,
 * outside this element, which is what lets the page show between the crests
 * instead of this background painting navy behind them.
 *
 * A MARGIN here would open white water between the wave and the footer, which
 * is the straight edge back in a worse form. The section gap belongs on
 * `.tt-seam`, above the water; exactly one of the two may have it.
 *
 * PADDING here is navy and is required. The old seam lived inside this element
 * and carried `margin-bottom: var(--tt-space-4)`; moving it out took that with
 * it, and the footer's white logo plate ended up flush against the wave. It was
 * `motion-asked-for.spec.js` that caught it, and by accident: the colour test
 * samples four pixels below the water at 8%, 50% and 92% of the width, and at
 * 390px two of those three landed on the logo. The failure read as «the wave
 * does not reach the footer» and was «the footer starts too soon».
 */
.tt-footer {
	padding-block: var(--tt-space-4) var(--tt-space-5);
	background: var(--tt-navy);
	color: var(--tt-on-navy);

	/* Blue on this navy is 2.69:1; see the token. */
	--tt-focus: var(--tt-white);
	font-size: 0.875rem;
}

.tt-footer__grid {
	display: grid;
	gap: var(--tt-space-4);
}

/*
 * TWO COLUMNS ON A PHONE, FOR THE TWO SHORT LISTS ONLY.
 *
 * «Εκδρομές» holds five links and «Υπηρεσίες» four, all of them one or two
 * words, and stacked they were 400px of a footer that already ran to 1,990px.
 * Side by side they are one screenful and read as two categories rather than as
 * nine unrelated items.
 *
 * The brand block and the contact block stay full width: one has a logo and a
 * sentence, the other has an address that must not wrap after every second
 * word.
 */
.tt-footer__links {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: var(--tt-space-3);
}

@media (min-width: 768px) {
	.tt-footer__grid {
		grid-template-columns: 1.4fr repeat(3, 1fr);
		gap: var(--tt-space-5);
	}

	/*
	 * The wrapper stops existing so its two navs become items of the outer grid
	 * again — which is the whole reason it may be a plain <div> and nothing
	 * else: `display: contents` removes an element from the accessibility tree
	 * as well as from layout, and doing that to a <nav> would take its landmark
	 * with it.
	 */
	.tt-footer__links {
		display: contents;
	}
}

/*
 * The measured rating, linked to the listing it came from. Not a button: it is
 * a fact with a source, and dressing it as a call to action would make the one
 * claim on this page the agency did not write look like marketing.
 */
.tt-footer__rating {
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
	margin-top: var(--tt-space-2);
	padding-block: 6px;
	color: var(--tt-on-navy);
	font-size: 0.8125rem;
	text-decoration: none;
}

.tt-footer__rating:hover,
.tt-footer__rating:focus-visible {
	color: var(--tt-white);
	text-decoration: underline;
}

.tt-footer__rating-stars {
	display: inline-flex;
	gap: 2px;
	line-height: 1;
}

/*
 * The stars are the one place on the navy where the brand blue would land at
 * 2.69:1, so the filled ones take the light tone instead. They are decorative
 * anyway — the sentence beside them says «4,5 από 5» in words — but a shape
 * nobody can make out beside a sentence that can is just noise.
 */
.tt-footer__rating-stars .tt-rating__star {
	color: rgb(255 255 255 / 35%);
	font-size: 0.875rem;
}

.tt-footer__rating-stars .tt-rating__star.is-filled {
	color: var(--tt-white);
}

/*
 * Opening hours, one line each. `display: block` on the spans rather than <br>,
 * because these are separate statements — a closing time and a note about
 * appointments — and not one sentence that happens to wrap.
 */
.tt-footer__hours {
	margin: var(--tt-space-2) 0 0;
	color: var(--tt-on-navy);
	font-size: 0.8125rem;
}

.tt-footer__hours span {
	display: block;
	padding-block: 2px;
}

/*
 * The registry line. Deliberately the quietest thing in the footer: it is there
 * because a visitor is entitled to know which business is taking their money,
 * not because anybody is meant to read it on the way past.
 */
.tt-footer__registry {
	margin: 0 0 var(--tt-space-1);
	color: rgb(219 230 242 / 72%);
}

.tt-footer .tt-brand__text,
.tt-footer h2 {
	color: var(--tt-white);
}

/* Brand blue on navy is 2.69:1. On this surface the light tone is the readable one. */
.tt-footer .tt-brand__sub {
	color: var(--tt-on-navy);
}

.tt-footer__about {
	margin: var(--tt-space-2) 0 0;
	max-width: 40ch;
	color: var(--tt-on-navy);
	font-size: 0.8125rem;
}

.tt-footer__heading {
	margin: 0 0 var(--tt-space-1);
	color: var(--tt-white);
	font-size: 0.8125rem;
	font-weight: 700;
}

/*
 * The brand column is the only one with something ABOVE its heading — the
 * lockup, the sentence and the rating all come first, and «Ακολούθησέ μας» was
 * landing 4px under the stars as though it labelled them.
 */
.tt-footer__brand .tt-footer__heading {
	margin-top: var(--tt-space-3);
}

.tt-footer__list {
	list-style: none;
	margin: 0;
	padding: 0;
}

.tt-footer__list a {
	display: block;
	padding-block: 6px;
	color: var(--tt-on-navy);
	font-size: 0.8125rem;
	text-decoration: none;
}

/* 13px was the smallest navigation on the site; see the footer's own note. */
@media (max-width: 767px) {
	.tt-footer__list a {
		font-size: 1rem;
	}
}

.tt-footer__list a:hover,
.tt-footer__list a:focus {
	color: var(--tt-white);
	text-decoration: underline;
}

.tt-footer__bottom {
	margin-top: var(--tt-space-4);
	padding-top: var(--tt-space-3);
	border-top: 1px solid rgb(255 255 255 / 14%);
	font-size: 0.75rem;
}

/*
 * THE FOOTER IS ALSO A PAGE, AND ON A PHONE IT WAS ALL BELOW THE FLOOR.
 *
 * 14px for the body, 13px for the address, the telephone and the email, 12px
 * for the legal row — on the one surface that carries the office's street, its
 * number and the four legal documents, set in the smallest type on the site
 * over the lowest-contrast ground on the site. This project's own rule is 16px
 * minimum for body text on a phone, and the footer had simply never been
 * measured against it.
 *
 * Phone only: at desktop reading distance the smaller step is the right
 * hierarchy for a footer. Tap targets do not move — the coarse-pointer block
 * already forces every link in here to 44px.
 */
@media (max-width: 767px) {
	.tt-footer {
		font-size: 1rem;
	}

	.tt-footer__bottom {
		font-size: 0.875rem;
	}
}

.tt-footer__bottom p {
	margin: 0;
}

/* Utilities --------------------------------------------------------------- */

.screen-reader-text {
	position: absolute !important;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.screen-reader-text:focus {
	position: static !important;
	width: auto;
	height: auto;
	clip-path: none;
}

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

	*,
	*::before,
	*::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;

		/*
		 * The DELAY has to go too, and it was missing. The mobile menu hides
		 * itself with `transition: visibility 0s linear 0.2s` — the delay is what
		 * keeps the links on screen until the panel has finished collapsing. With
		 * only the duration zeroed, that 0.2s delay survived, so closing the menu
		 * under reduced motion left it collapsed but still nominally visible for a
		 * fifth of a second. Zeroing both is what makes the state change immediate
		 * rather than merely fast.
		 */
		transition-delay: 0s !important;
	}
}

/* Moving between pages ----------------------------------------------------- */

/*
 * THE WHOLE SITE NAVIGATES WITH FULL PAGE LOADS, ON PURPOSE. Every filter,
 * every ordering, every page of results and every tour is a real request with a
 * real URL — which is why the catalogue works with JavaScript switched off, why
 * the back button is never wrong, and why nothing here needs a framework.
 *
 * The price of that decision was the white flash between every one of them, and
 * a visitor comparing four tours pays it eight times. This is what buys it back:
 * the browser holds the old page until the new one is ready and animates
 * between them. Not decoration — it is the continuity a single-page app spends
 * a megabyte of JavaScript to fake, and here it is a stylesheet.
 *
 * It also means the ordering select and the filter panel no longer blank the
 * screen to answer, which is the interaction this page exists for.
 */
@view-transition {
	navigation: auto;
}

/*
 * The shell does not travel. Naming the header and the footer takes them out of
 * the cross-fade, so they stay exactly where they are while the content beneath
 * them changes — which is what tells the reader they are still in the same
 * place rather than somewhere new.
 */
.tt-header {
	view-transition-name: tt-header;
}

.tt-footer {
	view-transition-name: tt-footer;
}

/*
 * The controls hold still while the results change, which is the whole point on
 * this page: choosing a destination reloads the document, and without these the
 * panel the visitor is still reading cross-fades along with the list they asked
 * to change. Naming them says «these two are the same object as before» — so
 * only the answer moves, and the question stays put under their hand.
 */
.tt-archive__filters {
	view-transition-name: tt-filters;
}

.tt-archive__toolbar {
	view-transition-name: tt-toolbar;
}

/*
 * The form becomes the receipt in place. Sending an enquiry is a real POST and
 * a real reload, and the panel that comes back carries the same id as the form
 * that left — so naming it here animates one into the other rather than
 * repainting the page around a message that appears out of nowhere. It is the
 * moment the visitor most needs to be told something happened.
 *
 * An ID SELECTOR ON PURPOSE. `.tt-form-section` would match a second form on
 * the same page the day one exists, and two elements sharing a name do not
 * degrade the transition — they abandon it, silently, for every element on the
 * page. An id cannot be there twice without the document already being invalid.
 */
#tt-form {
	view-transition-name: tt-form;
}

/*
 * GOING BACK LOOKS LIKE GOING BACK.
 *
 * Everything above cross-fades, which is directionless — the same animation
 * plays whether the visitor opened a tour or returned from one. On a catalogue
 * that is the wrong silence: comparing tours means going back more often than
 * going forward, and the browser has just been told to hold the old page still
 * and dissolve it, which is precisely what forward looks like too.
 *
 * ONLY HISTORY TRAVERSALS ARE MARKED — the back and forward buttons, and the
 * gestures that stand in for them. Clicking a card is a push, and it keeps the
 * cross-fade on purpose: on that navigation the photograph is already morphing
 * from the card into the tour's lead image, and sliding the page underneath it
 * would give the eye two things to follow and a reason to trust neither.
 *
 * The header, the footer and the filter panel are named, so they are not in the
 * `root` snapshot and do not move. What slides is the content between them,
 * which is the only part that actually changed.
 *
 * AND THE OBVIOUS OBJECTION, ANSWERED. Going back from a tour carries the same
 * photograph morph as going to one — so why does one slide and the other not?
 * Because on the way in the morph is the ANSWER: the visitor asked for that
 * photograph and it is growing into the page they asked for, with nothing else
 * to say. On the way out it is a LANDMARK: the photograph shrinking to its card
 * is what tells them where in a long list they are being returned to, and the
 * slide is what tells them they are being returned at all. Checked frame by
 * frame at 1440px rather than argued: the shell holds, the results arrive from
 * the left, the photograph lands in its card, and the three read as one
 * movement because they all mean «back».
 *
 * 6% AND NOT A FULL SCREEN. A page-width slide reads as an app pretending to be
 * a native one; a short shift under a fade reads as the page stepping aside. The
 * distance is doing one job — saying which way — and it does not need to travel
 * to say it.
 *
 * The reduced-motion block below removes these too: `animation: none !important`
 * beats every custom animation here, whatever its specificity.
 */
@keyframes tt-page-leaves-forward {
	to {
		transform: translateX(-6%);
		opacity: 0;
	}
}

@keyframes tt-page-arrives-forward {
	from {
		transform: translateX(6%);
		opacity: 0;
	}
}

@keyframes tt-page-leaves-back {
	to {
		transform: translateX(6%);
		opacity: 0;
	}
}

@keyframes tt-page-arrives-back {
	from {
		transform: translateX(-6%);
		opacity: 0;
	}
}

:root:active-view-transition-type(tt-back)::view-transition-old(root) {
	animation: tt-page-leaves-back 0.25s ease both;
}

:root:active-view-transition-type(tt-back)::view-transition-new(root) {
	animation: tt-page-arrives-back 0.25s ease both;
}

:root:active-view-transition-type(tt-forward)::view-transition-old(root) {
	animation: tt-page-leaves-forward 0.25s ease both;
}

:root:active-view-transition-type(tt-forward)::view-transition-new(root) {
	animation: tt-page-arrives-forward 0.25s ease both;
}

/*
 * REDUCED MOTION HAS TO BE SAID AGAIN HERE, and this is the trap. The blanket
 * rule above is `*, *::before, *::after` — and `*` does not match a
 * pseudo-element like `::view-transition-group`, which is generated on the root
 * and is not in the document tree at all. So every transition on this page
 * would have run at full length for somebody who had asked the operating system
 * for less movement, and nothing in the existing block would have stopped it.
 *
 * `animation: none` rather than a short duration: with the animation removed
 * the browser still performs the transition, it simply arrives finished.
 */
@media (prefers-reduced-motion: reduce) {
	::view-transition-group(*),
	::view-transition-old(*),
	::view-transition-new(*) {
		animation: none !important;
	}
}

/* Tour detail ------------------------------------------------------------- */

.tt-crumbs {
	padding-block: var(--tt-space-2);
}

.tt-crumbs__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	list-style: none;
	margin: 0;
	padding: 0;
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

/*
 * THE SEPARATOR TRAILS ITS CRUMB, IT DOES NOT LEAD THE NEXT ONE.
 *
 * It was `::before` on every item after the first, which is correct on one line
 * and wrong the moment the list wraps — and on a phone it always wraps. Measured
 * on `2-day-santorini` at 390px: «Αρχική › Εκδρομές › Σαντορίνη» filled the
 * first line and the second began with a stray «›» hard against the left margin,
 * pointing at nothing, 79px of a 664px screen spent looking like a bullet
 * somebody forgot to style.
 *
 * Trailing, the same wrap reads «… › Σαντορίνη ›» and then the tour name, which
 * is how a continued line is supposed to end. The margin swaps sides so the
 * spacing either side of the mark is unchanged.
 */
.tt-crumbs__item:not(:last-child)::after {
	content: "›";
	margin-left: 6px;
	color: var(--tt-border);
}

.tt-crumbs__item a {
	color: var(--tt-text-muted);
	text-decoration: none;
}

.tt-crumbs__item a:hover,
.tt-crumbs__item a:focus {
	color: var(--tt-blue-dark);
	text-decoration: underline;
}

.tt-crumbs__item[aria-current="page"] {
	color: var(--tt-navy);
	font-weight: 600;
}

.tt-tour__head {
	margin-bottom: var(--tt-space-3);
}

.tt-tour__type {
	margin: 0 0 6px;
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

.tt-tour__title {
	margin-bottom: var(--tt-space-1);
}

/* Gallery */

.tt-gallery {
	display: grid;
	gap: var(--tt-space-2);
	margin-bottom: var(--tt-space-3);
}

.tt-gallery__main {
	aspect-ratio: 16 / 10;
	margin: 0;
}

@media (min-width: 768px) {
	.tt-gallery {
		grid-template-columns: 2fr 1fr;
	}

	.tt-gallery--single {
		grid-template-columns: 1fr;
	}

	/*
	 * Capped, not just proportional. At the full content width a 3:2 hero is
	 * nearly 800px tall, which pushes the price and the programme off the first
	 * screen — and while photography is still missing it is 800px of nothing.
	 */
	.tt-gallery__main {
		aspect-ratio: 3 / 2;
		max-height: 440px;
	}

	.tt-gallery__rest {
		display: grid;
		grid-template-rows: repeat(2, 1fr);
		gap: var(--tt-space-2);
	}

	.tt-gallery__rest .tt-media {
		margin: 0;
		height: 100%;
	}
}

/*
 * Below the breakpoint the extra shots stay on one row rather than stacking:
 * four full-width photographs would push the price and the programme a screen
 * and a half down the page.
 *
 * A ROW, NOT A SCROLLER, AND THE SCROLLER COULD NEVER SCROLL.
 *
 * It was `grid-auto-flow: column` at `grid-auto-columns: 45%` with
 * `overflow-x: auto` — a carousel built for an unknown number of shots. The
 * number is not unknown: gallery.php slices the list to exactly two. Two
 * columns at 45% and one gap came to 27.8px short of the hero above them, so
 * the pair sat visibly inset with a strip of empty page to their right and an
 * overflow container that had nothing to overflow.
 *
 * Two equal columns. They finish flush with the hero, and if the slice is ever
 * widened this rule is where to look.
 */
@media (max-width: 767px) {
	.tt-gallery__rest {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--tt-space-1);
		padding: 3px;
		margin-inline: -3px;
	}

	.tt-gallery__rest .tt-media {
		margin: 0;
		aspect-ratio: 4 / 3;
	}
}

/* Essential facts */

/*
 * `align-items: start` SO ONE SENTENCE DOES NOT INFLATE THE WHOLE STRIP.
 *
 * Grid stretches every tile to its row's height. One of these values is not a
 * fact but a sentence — «Επιστροφή στην Αθήνα μετά την άφιξη των 20:20 στη
 * Ραφήνα τη δεύτερη ημέρα», 72 characters — which took its tile to 178px and
 * dragged «Αγγλικά, Ισπανικά» up to 178px beside it with 127px of empty box
 * under 51px of text. At 1440 the strip is one row of four, so the long one
 * inflated three neighbours at once.
 *
 * Nothing is clipped to achieve this and nothing is hidden behind a tooltip:
 * the sentence is a real answer to «πότε γυρνάω» and a visitor deciding on a
 * two-day tour needs all of it. Each tile is simply the height of what is in
 * it. Ragged bottoms are the honest shape of facts that are not the same
 * length — see docs/08 §31 for the content-side note, because this value would
 * be better placed under «Χρήσιμες πληροφορίες» and that is the office's call.
 */
.tt-facts {
	display: flex;
	flex-wrap: wrap;
	align-items: start;
	gap: var(--tt-space-1);
	list-style: none;
	margin: 0 0 var(--tt-space-4);
	padding: 0;
}

/*
 * ON THE PHONE TOO, AND THAT WAS MEASURED LATE.
 *
 * The desktop block below was rewritten from grid to flex the day a fifth fact
 * appeared, and it was measured at 1440 only. On a phone this stayed a
 * two-column grid, so five facts came out 2 + 2 + 1 and the last one held 175px
 * of a 358px line — the same orphan, on the narrower screen, where the empty
 * half is a larger share of what a visitor can see.
 *
 * `calc(50% - …)` and not `160px`: half the gap is what each of two items has
 * to give up for the gap between them, so this is exactly two per line at every
 * width the phone has, and the leftover then grows across the whole line.
 */
.tt-facts__item {
	flex: 1 1 calc(50% - var(--tt-space-1) / 2);
}

@media (min-width: 768px) {
	/*
	 * FLEX AND NOT GRID, BECAUSE THE NUMBER OF FACTS IS NOT FIXED.
	 *
	 * This was `repeat(auto-fit, minmax(160px, 1fr))`, chosen when every tour
	 * had four facts: 4 x 160 + 3 x 16 = 688 inside a 744px column, so all four
	 * sat on one line. Tours that carry departure dates now show a fifth,
	 * «Επόμενη αναχώρηση», and four-plus-one is the same orphan the note above
	 * was written to prevent — with two thirds of the line empty beside it.
	 *
	 * No fixed track count fixes both: three columns turns 5 into a tidy 3+2 and
	 * turns 4 into 3+1, and a tour without dates still has four. The count
	 * varies per tour and always will.
	 *
	 * Flex items on the last line GROW to fill it, so whatever is left over
	 * spans the width instead of hugging the left edge. One fact alone then
	 * reads as a full-width row, which is a shape, not a mistake. `1 1 160px`
	 * keeps the same minimum the grid had, so the wrap points do not move.
	 */
	.tt-facts {
		display: flex;
		flex-wrap: wrap;
		gap: var(--tt-space-2);
	}

	.tt-facts__item {
		flex: 1 1 160px;
	}
}

@media (min-width: 1024px) and (max-width: 1279px) {
	/*
	 * Two by two, on purpose. This is the band where the two-column layout has
	 * started but the container has not caught up, so the left column is about
	 * 596px — enough for three tracks, which leaves a fourth fact alone on a
	 * line. Three-and-one reads as a mistake; two-and-two reads as a decision.
	 *
	 * Written as a basis rather than a track count now that the row is a flex
	 * line: `grid-template-columns` here stopped doing anything the moment the
	 * display changed, and a rule that silently does nothing is worse than one
	 * that was never written.
	 */
	.tt-facts__item {
		flex: 1 1 calc(50% - var(--tt-space-2) / 2);
	}
}

.tt-facts__item {
	padding: 12px 14px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-button);
	background: var(--tt-gray-soft);
}

/*
 * 14 AND 16, NOT 12 AND 14. THIS STRIP IS READ, NOT SCANNED.
 *
 * «Διάρκεια 2 ημέρες», «Αναχώρηση από Αθήνα», «Γλώσσες Αγγλικά, Ισπανικά» — a
 * visitor deciding on a tour reads every one of these, and they were set at
 * 12px and 14px on a phone, under this project's own 16px floor for body text.
 * The label is genuinely a label and keeps a step down; the value is the answer
 * and gets the floor. Neither changes any width.
 */
.tt-facts__label {
	display: block;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
}

.tt-facts__value {
	display: block;
	margin-top: 2px;
	color: var(--tt-navy);
	font-weight: 700;
	font-size: var(--tt-text-body);
}

/* Layout */

.tt-tour__layout {
	display: grid;
	gap: var(--tt-space-4);
	padding-bottom: var(--tt-space-5);
}

/*
 * ON A PHONE THE PRICE COMES BEFORE THE READING, AND IT USED TO COME AFTER
 * EVERYTHING.
 *
 * The aside is emitted after the main column, which is the right source order
 * for a desktop where it is a sticky rail beside the article. Stacked, it put
 * the booking card — the price, the reassurance, and the only tappable phone
 * number on the page — at document y 5496 of a 7563px page. Nobody scrolls 73%
 * of a tour page to find out what it costs.
 *
 * It also made the card's own button lie: it says «Ρώτησέ μας» with a
 * downward arrow and links to `#tt-form`, and from y 5496 that anchor is 936px
 * UPWARDS. The arrow becomes true again as soon as the card is above the form.
 *
 * `order` on a flex column, which is the one place this project allows visual
 * order to disagree with source order: the aside is a self-contained landmark
 * rather than a step in the reading, and moving it in the markup would make the
 * desktop rail come before the article it belongs to.
 */
@media (max-width: 1023px) {
	.tt-tour__layout {
		display: flex;
		flex-direction: column;
	}

	.tt-tour__aside {
		order: -1;
	}

	/*
	 * AND THE PHOTOGRAPH COMES BEFORE THE PRICE.
	 *
	 * The rule above solved reachability and created a first impression: with
	 * the booking card pulled to the top and the gallery still inside the
	 * article behind it, the opening screen of a tour page was a breadcrumb, a
	 * title and a white box with a number in it. Measured on `2-day-santorini`
	 * at 390x664 — the gallery began at 586, and the sticky bar cuts the usable
	 * fold to 591, so the amount of photography a visitor saw on arrival was
	 * five pixels.
	 *
	 * -2 rather than 0, because `order` sorts before source position and the
	 * card is already at -1. The card keeps everything the note above won: it
	 * is still the second thing on the page, still ahead of the entire article,
	 * still 380px up from where it was.
	 */
	.tt-tour__layout > .tt-gallery {
		order: -2;
		margin-bottom: 0;
	}
}

@media (min-width: 1024px) {
	.tt-tour__layout {
		grid-template-columns: minmax(0, 1fr) 340px;

		/*
		 * TWO EXPLICIT ROWS, AND THEY HAVE TO BE EXPLICIT.
		 *
		 * The aside spans both with `grid-row: 1 / -1`, and `-1` resolves
		 * against the EXPLICIT grid — with only implicit rows it names nothing,
		 * the span silently does not happen, and the article auto-places into
		 * whatever cell that leaves. The same trap cost this project a homepage
		 * contact panel; it is written down here so it costs nothing next time.
		 */
		grid-template-rows: auto minmax(0, 1fr);
		gap: var(--tt-space-5);
		align-items: start;
	}

	/*
	 * The gallery is a layout child now rather than the article's first block,
	 * so the row gap has to reproduce what its own margin used to give it: 24px
	 * between the photographs and the fact strip, not the 48px that separates
	 * the article from the rail beside it.
	 */
	.tt-tour__layout {
		row-gap: var(--tt-space-3);
	}

	.tt-tour__layout > .tt-gallery {
		grid-column: 1;
		grid-row: 1;
		margin-bottom: 0;
	}

	.tt-tour__main {
		grid-column: 1;
		grid-row: 2;
	}

	.tt-tour__aside {
		grid-column: 2;
		grid-row: 1 / -1;
		position: sticky;

		/*
		 * Clear of the header, which STAYS now — the previous note here said it
		 * "is not fixed but can be scrolled past", and that stopped being true
		 * the day it was made sticky. Without the offset the price and the
		 * enquiry button park underneath it.
		 */
		top: calc(var(--tt-header) + var(--tt-space-3));
	}
}

/*
 * A TOUR PAGE HAD ONE HEADING LEVEL. Eleven H2s, all at 28px, down 4,682px of
 * page — «Πολιτική ακύρωσης» shouting exactly as loudly as «Πρόγραμμα», and
 * nothing to tell a reader scanning for the itinerary where it starts.
 *
 * These are headings INSIDE a page, not headings that start one, so they take
 * the step below. «Παρόμοιες εκδρομές» keeps the section size deliberately: it
 * is the boundary where this tour ends and other tours begin, which is exactly
 * what a section heading is for.
 */
.tt-tour__block h2 {
	font-size: var(--tt-text-lead);
}

.tt-tour__block + .tt-tour__block {
	margin-top: var(--tt-space-4);
	padding-top: var(--tt-space-4);
	border-top: 1px solid var(--tt-border);
}

/*
 * THE ONE READING SURFACE ON THE SITE WITH NO MEASURE.
 *
 * `.tt-prose` held a single `> :last-child` margin rule, so the tour
 * description and the itinerary ran the full width of their column: measured
 * first-line counts of 93, 97, 102, 102, 89 characters at 1440, against 60-75
 * for comfortable reading. Nine other surfaces here are capped — at 40, 46, 52,
 * 60, 62 and 70ch — so the longest text on the site was the only unbounded one,
 * sitting on a page beside a 40ch footer blurb.
 *
 * 56ch AND NOT 65ch, because `ch` is the width of a zero and Greek is narrower
 * than that: 1ch measures 9.77px here against a mean Greek lowercase glyph of
 * 8.80px, about 11% out. 56ch reproduces roughly 62 Greek characters per line.
 *
 * The nine existing caps are left where they are. They disagree with each other
 * and that is worth tidying, but every one of them is a deliberate width on a
 * surface somebody looked at, and unifying them would move text on eight pages
 * to fix a defect on one.
 */
/*
 * THE DECLARATION MOVED RATHER THAN BEING REPEATED. It used to sit here and was
 * dead: `.tt-prose { max-width: 68ch }` further down the file has the same
 * specificity and wins on source order, so every word of the argument above was
 * being overruled and the tour description ran 80 characters a line. The
 * reasoning is kept here, next to the surfaces it was measured on; the value now
 * lives once, in the running-text block that was overriding it.
 */

.tt-prose > :last-child {
	margin-bottom: 0;
}

.tt-columns {
	display: grid;
	gap: var(--tt-space-3);
}

@media (min-width: 600px) {
	.tt-columns {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

.tt-list {
	margin: 0;
	padding-left: 1.15rem;
}

.tt-list li {
	margin-bottom: 6px;
}

.tt-itinerary {
	margin: 0;
	padding-left: 1.15rem;
}

.tt-itinerary__day + .tt-itinerary__day {
	margin-top: var(--tt-space-3);
}

.tt-itinerary__title {
	margin-bottom: 6px;
}

.tt-dates {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	list-style: none;
	margin: 0;
	padding: 0;
}

.tt-dates__item {
	padding: 6px 12px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-blue-soft);
	color: var(--tt-navy);
	font-size: 0.8125rem;
	font-weight: 600;
}

/* Booking card ------------------------------------------------------------ */

/*
 * The one raised surface on the page, and it carried the same shadow as the
 * twelve tour cards — byte for byte. `--tt-shadow-raised` already existed and
 * was used once, on card hover. A booking panel that sits at the same depth as
 * a list item is a panel the eye files as list furniture.
 */
.tt-booking {
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-shadow-raised);
}

.tt-booking__from {
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

.tt-booking__amount {
	display: block;
	color: var(--tt-navy);
	font-size: 1.75rem;
	line-height: 1.2;
}

.tt-booking__sample {
	display: inline-block;
	padding: 4px 10px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
	font-size: 0.75rem;
	font-weight: 700;
}

.tt-booking__next {
	margin: var(--tt-space-2) 0 0;
	color: var(--tt-navy);
	font-size: 0.875rem;
	font-weight: 600;
}

.tt-booking__note {
	margin: var(--tt-space-1) 0 0;
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

.tt-booking__actions {
	display: grid;

	/*
	 * Every action the same height, whether or not its label wrapped. Without
	 * this the primary came out 53px against the secondary's 44px — two
	 * full-width buttons, stacked, visibly different sizes, at every width from
	 * 1024px up. `1fr` on the implicit rows makes them all as tall as the
	 * tallest, so the stack stays a stack.
	 */
	grid-auto-rows: 1fr;
	gap: var(--tt-space-1);
	margin-top: var(--tt-space-3);
}

/*
 * A TELEPHONE NUMBER MUST NOT BREAK, AND THIS ONE WAS BREAKING MID-NUMBER.
 *
 * «Τηλεφώνησε, 210 5234992» does not fit one line inside a 340px rail once the
 * handset icon has taken its place, so it wraps — and left to itself the
 * browser fills the first line greedily and puts as much as it can there.
 * Measured in Chromium at 1440px: two line boxes of 168px and 46px, reading
 * «Τηλεφώνησε, 210» and then «5234992». The office's number was split across
 * two lines at a point that is not a boundary in it, on the one control that
 * exists for a visitor who would rather ring than type.
 *
 * `balance` evens the line boxes instead of filling greedily: 110px and 104px,
 * which is «Τηλεφώνησε,» and then «210 5234992» whole. The button's height is
 * 62px either way, so nothing around it moves. Where the property is not
 * supported the label wraps exactly as it does today.
 *
 * On the label rather than on the number because the label is one string —
 * `sprintf( 'Τηλεφώνησε, %s', $phone )` in the plugin — and splitting it into
 * elements to hang a `nowrap` on the digits would put markup in a translated
 * sentence for a line break.
 */
.tt-booking__actions .tt-button {
	text-wrap: balance;
}

.tt-booking__reassure {
	margin: var(--tt-space-2) 0 0;
	color: var(--tt-text-muted);
	font-size: 0.75rem;
	font-style: italic;
}

/* Mobile sticky CTA -------------------------------------------------------- */

.tt-sticky-cta {
	display: none;
}

@media (max-width: 1023px) {
	/*
	 * WHERE THE BROWSER PARKS A FOCUSED ELEMENT. Tabbing scrolls the next
	 * control flush to the bottom edge of the viewport — which is exactly the
	 * band the fixed bar occupies, so the focused control lands BEHIND it.
	 * Measured on an iPhone 14 viewport: the submit button «Στείλε το ερώτημα»,
	 * the people field, the reply-channel radio and three footer links were all
	 * 100% covered at the moment they took focus. WCAG 2.2 SC 2.4.11.
	 *
	 * `body:has(.tt-sticky-cta) { padding-bottom }` further down does NOT solve
	 * this. It reserves room at the end of the document so the bar does not
	 * cover the footer at rest; it has no effect on where a scroll stops.
	 *
	 * 88px = the bar's 74px plus room to see the control has focus rather than
	 * merely to have it be technically unobscured.
	 */
	html {
		scroll-padding-bottom: 88px;
	}

	/*
	 * And the same distance as a margin on the targets themselves, because
	 * scroll-padding on the container is not enough in WebKit. Measured on an
	 * iPhone 14 profile, tabbing 60 stops through a tour page:
	 *
	 *   engine    no fix            scroll-padding only
	 *   chromium  7-8 controls hid  0
	 *   webkit    1 control hidden  2 (worse under reduced motion)
	 *
	 * Safari is most of the Greek mobile market, so the Chromium result on its
	 * own would have been a fix that helped the minority of visitors and was
	 * reported as done.
	 */
	main :is(a[href], button, input, select, textarea),
	footer a[href] {
		scroll-margin-bottom: 88px;
	}

	/*
	 * And the bar gets out of the way entirely while the form is being filled
	 * in. Even with both scroll properties WebKit still parked one field behind
	 * it, and there is no third scroll property to reach for.
	 *
	 * This is not a workaround dressed as a feature. The bar's whole job is to
	 * carry the visitor TO this form; once the caret is inside it the bar
	 * offers nothing and occupies the bottom of a screen that is already
	 * shortened by the keyboard. It comes back the moment focus leaves.
	 *
	 * No layout shift: the element stays in the DOM, so the
	 * `body:has(.tt-sticky-cta)` padding below is unaffected.
	 *
	 * TWO RULES, NOT ONE SELECTOR LIST. A selector list is invalid as a whole if
	 * any selector in it is, so pairing the `:has()` form with the class form
	 * took the class form down with it wherever `:has()` was not honoured — and
	 * the class is the half that has to work everywhere.
	 */
	/*
	 * It steps aside rather than blinking out. The bar is the thing the reader
	 * was just looking at, and a fixed element that disappears between two
	 * frames reads as a fault — the eye cannot tell «it moved for the keyboard»
	 * from «it broke». A fifth of a second says which.
	 *
	 * The visibility delay is what keeps it present until the fade has finished;
	 * without it the element is gone on the first frame and there is nothing
	 * left to fade. Zeroed under reduced motion by the blanket rule, which does
	 * reach this one — it is an element, not a pseudo-element.
	 */
	body.tt-form-focus .tt-sticky-cta {
		opacity: 0;
		visibility: hidden;
		transition: opacity 0.18s ease, visibility 0s linear 0.18s;
	}

	/*
	 * The no-JavaScript path. Chromium applies this; WebKit matches the
	 * selector but does not apply the declaration when the change came from
	 * `:focus`, which is why navigation.js sets the class above.
	 */
	body:has( #tt-form :focus ) .tt-sticky-cta {
		opacity: 0;
		visibility: hidden;
		transition: opacity 0.18s ease, visibility 0s linear 0.18s;
	}

	.tt-sticky-cta {
		/* Coming back is immediate in visibility and gradual in opacity. */
		transition: opacity 0.18s ease, visibility 0s;
		display: block;
		position: fixed;
		inset: auto 0 0;
		z-index: 30;
		border-top: 1px solid var(--tt-border);
		background: var(--tt-white);

		/* Clear of the iOS home indicator. */
		padding-bottom: env(safe-area-inset-bottom, 0);
		box-shadow: 0 -2px 12px rgb(18 43 77 / 8%);
	}

	.tt-sticky-cta__inner {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: var(--tt-space-2);

		/* What --tt-sticky promises the rest of the page. */
		min-height: var(--tt-sticky);
		max-width: var(--tt-content);
		margin-inline: auto;
		padding: 10px var(--tt-space-2);
	}

	.tt-sticky-cta__price {
		color: var(--tt-text-muted);
		font-size: 0.75rem;
		line-height: 1.2;
	}

	.tt-sticky-cta__price strong {
		display: block;
		color: var(--tt-navy);
		font-size: 1.125rem;
	}

	.tt-sticky-cta__inner .tt-button {
		/*
		 * `max-width: 60%` tied the button's width to whatever the price text
		 * happened to be, so the same button was a different size on two
		 * different tours. It now takes the room it needs and no more, and the
		 * price keeps the rest.
		 */
		flex: 0 1 auto;
		padding-inline: var(--tt-space-2);
	}

	/*
	 * The bar is fixed, so it sits on top of whatever is at the bottom of the
	 * page. Reserving the space is what actually keeps it from covering the
	 * footer and the last section, which docs/06 requires.
	 *
	 * TWO THINGS THIS USED TO GET WRONG, both measured at 360/390/414px on both
	 * engines:
	 *
	 * The number was 72px while the bar measured 74px, because the CTA label
	 * wrapped to two lines on every common phone width. The label is shorter
	 * now and `.tt-sticky-cta__inner` carries a min-height, so the bar is a
	 * known 73px — 72px of content plus its own top border — rather than
	 * whatever the longest translation happens to produce.
	 *
	 * And `env(safe-area-inset-bottom)` was padding the bar without being added
	 * here, so on any iPhone with a home indicator the bar was ~34px taller
	 * than the space set aside for it and the footer sat underneath.
	 *
	 * WHY 73px AND NOT var(--tt-sticky). Written with the custom property, this
	 * one declaration computed to 0px in WebKit — no padding at all, which is
	 * worse than the bug it was fixing. The same value resolved correctly on a
	 * child element, and an injected copy of this very rule resolved correctly
	 * too, so the cause is narrower than «WebKit and var()» and is not
	 * something to guess at. What is certain is the measurement, so the number
	 * is written out. The token still drives the bar's own height above.
	 */
	body:has(.tt-sticky-cta) {
		padding-bottom: calc( 73px + env(safe-area-inset-bottom, 0px) );
	}
}

/* Tour archive ------------------------------------------------------------ */

.tt-archive__head {
	margin-bottom: var(--tt-space-3);
}

@media (max-width: 767px) {
	/*
	 * A listing page earns nothing from a generous top margin: the visitor
	 * arrived to see the catalogue and the first card was starting 376px down a
	 * 664px screen. The heading still has air, it just has less of it than a
	 * page whose job is to be read.
	 */
	.tt-archive__head {
		margin-top: calc(var(--tt-space-3) * -1);
		margin-bottom: var(--tt-space-2);
	}
}

/*
 * minmax(0, 1fr) and not 1fr: a grid item defaults to min-width auto, which is
 * what lets a long tour title widen the whole grid past the viewport. body
 * carries overflow-x: hidden, which HIDES that rather than preventing it.
 */
.tt-archive__layout {
	display: grid;

	/*
	 * `minmax(0, 1fr)` AND NOT THE IMPLICIT `auto`.
	 *
	 * A single-column grid with no template gets an `auto` track, which sizes
	 * to the MAX-CONTENT of its items — and a grid item defaults to
	 * `min-width: auto`, so nothing inside can shrink below its own minimum
	 * either. The desktop rule below already says `minmax(0, 1fr)`, which is
	 * why only phones were exposed.
	 *
	 * It surfaced when the results column gained a toolbar holding a label and
	 * a select side by side. Measured on a 390px screen: the document scrolled
	 * to 494px and the filter panel — which shares this grid and had not
	 * changed at all — rendered 478px wide. One flex row in one item widened
	 * the whole page.
	 */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--tt-space-3);
}

@media (min-width: 1024px) {
	.tt-archive__layout {
		grid-template-columns: 260px minmax(0, 1fr);
		gap: var(--tt-space-4);
		align-items: start;
	}
}

.tt-archive__results:focus {
	outline: none;
}

.tt-archive__count {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: 0.875rem;
}

/* The results toolbar: how many, and in what order --------------------------
 *
 * The count and the ordering are one row on a desktop and two on a phone, with
 * the select going full width rather than being squeezed beside the count —
 * «Διάρκεια: από τις πιο σύντομες» is a long label and a truncated one is a
 * control the visitor has to open to read.
 */

/*
 * `min-width: 0` ON BOTH, and it is not decoration.
 *
 * The results column is a grid track, and a grid track sizes to the MIN-CONTENT
 * of what is in it unless something says otherwise. A flex row holding a
 * `white-space: nowrap` label and a select has a min-content width of both of
 * them added together — so this toolbar widened its own track, which widened
 * the filter panel sharing the grid, which pushed the page past the viewport.
 * Measured on a 390px screen: the document scrolled to 494px and the filter
 * panel rendered 478px wide inside it, from adding a select.
 */

.tt-archive__toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--tt-space-2);
	min-width: 0;
	margin-bottom: var(--tt-space-2);
}

.tt-sort {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-space-1);
	min-width: 0;
	margin-left: auto;
}

.tt-sort__label {
	color: var(--tt-text-muted);
	font-size: 0.875rem;
	white-space: nowrap;
}

.tt-sort__select {
	/*
	 * `width: auto`, overriding the `width: 100%` every .tt-select carries.
	 *
	 * That rule is right inside a form, where a select is a full-width field.
	 * Here the select is a flex item beside a label, and 100% resolves against
	 * the whole flex line — measured at 478px on a 1440px screen, which pushed
	 * the control onto its own row at every width including the widest. The
	 * toolbar was 130px tall to hold what should be one line.
	 *
	 * It sizes to its longest option instead, with a floor so a short label
	 * does not make the control jump width when the visitor changes it.
	 */
	width: auto;
	min-width: 15rem;
	max-width: 100%;
}

@media (max-width: 559px) {
	/*
	 * LABEL ABOVE, SELECT FULL WIDTH, and the extra row is bought deliberately.
	 *
	 * Keeping them side by side fits, and the select then shrinks to whatever
	 * is left: measured at 152px on a 390px screen and 82px on a 320px one.
	 * At those widths «Διάρκεια: από τις πιο σύντομες» is cut to a few
	 * characters — and this control's value IS the current state of the list,
	 * so a truncated one hides the answer to «why is this list in this order».
	 *
	 * 52px of height against a legible control is not a close call on a phone.
	 */
	.tt-sort {
		flex-basis: 100%;
		margin-left: 0;
	}

	.tt-sort__label {
		flex-basis: 100%;
	}

	.tt-sort__select {
		flex: 1 1 auto;
		min-width: 0;
	}
}

.tt-archive__active {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-space-2);
	margin-bottom: var(--tt-space-2);
}

.tt-archive__reset {
	font-size: 0.8125rem;
	font-weight: 700;
	color: var(--tt-blue-dark);
}

/* Chips ------------------------------------------------------------------- */

.tt-chips {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * The chip row is its own scroll container below the breakpoint, so the
 * document never scrolls sideways. Same recipe as the destination scroller,
 * including the 3px padding that keeps a focus ring from being clipped by the
 * container and the scroll-padding that stops the browser parking a focused
 * chip flush against the edge.
 */
.tt-chips-row {
	margin-bottom: var(--tt-space-3);
}

@media (max-width: 767px) {
	.tt-chips-row .tt-chips {
		flex-wrap: nowrap;
		overflow-x: auto;
		scroll-snap-type: x mandatory;
		padding: 3px;
		margin-inline: -3px;
		scroll-padding-inline: 3px;
		-webkit-overflow-scrolling: touch;
	}

	.tt-chips-row .tt-chips > li {
		scroll-snap-align: start;
		flex: 0 0 auto;
	}
}

/*
 * THE ONE `.tt-chip` RULE. There were three, in three sections, all unscoped and
 * all setting the same properties — so the pill every visitor actually saw was
 * whichever of them the file happened to end with, and two of the three were
 * writing declarations that reached nothing.
 *
 * The values here are what the browser was already computing, gathered into one
 * place: the gap, the padding, the type size, the tap height and the shadow came
 * from the Chips-with-icons copy at the far end of the file, which was last and
 * therefore won; `text-decoration` and `white-space` came from this one, which
 * was the only rule that set them. Nothing about any chip on any page changes,
 * which is the point — the cascade was untangled, not redesigned.
 *
 * `--tt-tap` UNCONDITIONALLY, AND THE `pointer: coarse` BLOCK THAT USED TO
 * FOLLOW IS GONE. It raised `min-height` from 36px to 44px for a coarse pointer,
 * and the rule underneath it set 44px for everybody — so «36px under a mouse»
 * was a sentence about a stylesheet that had not been true since the second base
 * rule was written. A phone measurement in design-system.spec.js kept passing
 * throughout, because it only ever asked for at least 44.
 *
 * WHY THE ORDER MATTERS BELOW: everything after this rule modifies it —
 * `is-current`, the hover response, the applied-filter chip — and every one of
 * those is a single class, exactly as specific as `.tt-chip` itself. A fourth
 * base rule added anywhere later in this file would silently take the fill back
 * off all of them. That is not hypothetical: it is what happened to
 * `.tt-chip--remove`.
 */
.tt-chip {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	min-height: var(--tt-tap);
	padding: 10px 18px 10px 14px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-navy);
	font-size: var(--tt-text-small);
	font-weight: 600;
	text-decoration: none;
	white-space: nowrap;
	box-shadow: var(--tt-elevation-1);
}

a.tt-chip:hover,
a.tt-chip:focus {
	border-color: var(--tt-blue);
	color: var(--tt-blue-dark);
}

.tt-chip.is-current {
	background: var(--tt-blue);
	border-color: var(--tt-blue);
	color: var(--tt-white);
}

.tt-chip.is-current:hover,
.tt-chip.is-current:focus {
	background: var(--tt-blue-dark);
	border-color: var(--tt-blue-dark);
	color: var(--tt-white);
}

/*
 * «ΠΡΟΟΡΙΣΜΟΣ ΑΙΓΙΝΑ ×» IS NOT «ΑΙΓΙΝΑ», AND FOR MONTHS THEY WERE DRAWN THE
 * SAME.
 *
 * These two rows sit within about 200px of each other on /ekdromes/ and they do
 * opposite things: the category row ADDS a filter, the row above the results
 * TAKES ONE OFF. The remove chip asked for a pale-blue fill and a borderless
 * edge to say so — and then `.tt-chip` was declared a third time, 2,700 lines
 * later, with `background: var(--tt-white)` and `border: 1px solid
 * var(--tt-border)`. Same specificity, later in the file, so it won both
 * declarations back and the applied filter was pixel-for-pixel an unapplied one.
 * The only thing left saying «this removes something» was a grey × the size of
 * the label, in `--tt-text-muted`.
 *
 * `a.tt-chip.tt-chip--remove` AND NOT `.tt-chip--remove`, which is the whole
 * lesson of the paragraph above. Adding a fourth base-weight rule would have
 * fixed nothing — it would have been one more 0-1-0 selector in a queue decided
 * by line number. This is 0-2-1, which beats the base rule at 0-1-0 AND the
 * `a.tt-chip:hover` and `a.tt-chip:active` rules at 0-2-1 wherever in the file
 * they sit, including the hover rule 2,700 lines below. Naming the `<a>` is also
 * honest: every one of these is a link — `archive.php` builds them from
 * `remove_url` — and the site already writes `a.tt-chip:hover` for the same
 * reason, that a promise belongs to the element which can keep it.
 *
 * THE FILL IS THE FILTER AND THE HOVER EMPTIES IT. Pale blue with a blue edge
 * reads as «on»; hovering drains it back to white, which is a preview of what
 * pressing does. `--tt-text-muted` on `--tt-blue-soft` measures 5.10:1, so the
 * facet label keeps its contrast on the new fill.
 */
a.tt-chip.tt-chip--remove {
	background: var(--tt-blue-soft);
	border-color: var(--tt-blue);
	box-shadow: none;
}

/*
 * The × is the difference between a label and a control, so it stops being the
 * quietest thing in the chip. Same colour as the text it sits beside, one step
 * heavier, and nudged off the value it removes.
 */
.tt-chip--remove .tt-chip__x {
	margin-inline-start: 2px;
	color: var(--tt-blue-dark);
	font-weight: 700;
}

@media (hover: hover) {
	a.tt-chip.tt-chip--remove:hover {
		background: var(--tt-white);
		border-color: var(--tt-blue-dark);
		color: var(--tt-blue-dark);
		box-shadow: none;
	}
}

/*
 * And the press still says something. `a.tt-chip:active` further down the file
 * presses a chip INTO pale blue, which is where this one already rests, so
 * without its own rule the one control on the page whose whole job is to be
 * pressed would acknowledge a press with a single pixel of travel.
 */
a.tt-chip.tt-chip--remove:active {
	background: var(--tt-blue-soft);
	border-color: var(--tt-blue-dark);
	color: var(--tt-blue-dark);
}

.tt-chip__x {
	font-size: 1rem;
	line-height: 1;
	color: var(--tt-text-muted);
}

/* Filter panel ------------------------------------------------------------ */

.tt-search__heading {
	margin: 0 0 var(--tt-space-2);
	font-size: 1rem;
}

/*
 * Two up only when there is room. Below 480px a select holding «Οποιαδήποτε
 * ημερομηνία» clips its own value at half width, and on a destination archive
 * the panel has ONE control, which a fixed two-column grid would squeeze into
 * half the width for no reason.
 */
@media (min-width: 480px) and (max-width: 1023px) {
	.tt-search--panel.tt-search--2 .tt-search__fields {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 1024px) {
	.tt-search--panel {
		grid-template-columns: 1fr;
	}

	.tt-search--panel .tt-search__fields {
		grid-template-columns: 1fr;
	}

	.tt-search--panel .tt-search__submit {
		width: 100%;
	}
}

/* Pagination -------------------------------------------------------------- */

.tt-archive__results .pagination {
	margin-top: var(--tt-space-4);
}

.tt-archive__results .nav-links {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	align-items: center;
}

.tt-archive__results .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: var(--tt-tap);
	min-height: var(--tt-tap);
	padding: 0 12px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-button);
	color: var(--tt-navy);
	text-decoration: none;
	font-weight: 600;
}

.tt-archive__results .page-numbers.current {
	background: var(--tt-blue);
	border-color: var(--tt-blue);
	color: var(--tt-white);
}

.tt-archive__results a.page-numbers:hover,
.tt-archive__results a.page-numbers:focus {
	border-color: var(--tt-blue);
	color: var(--tt-blue-dark);
}

/* Enquiry forms ----------------------------------------------------------- */

.tt-form-section {
	margin-top: var(--tt-space-5);
	padding: var(--tt-space-4);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-gray-soft);

	/*
	 * The form is the target of every «Ζήτησε προσφορά» on the site, and on a
	 * tour page the sticky bar sits over the bottom of the viewport. Without
	 * this the heading lands underneath the header after a jump.
	 */
	/*
	 * Was a bare 32px, which is less than the 69px the header now occupies, so
	 * the heading landed underneath it. Reads the token like everything else.
	 */
	scroll-margin-top: calc(var(--tt-header) + var(--tt-space-2));
}

.tt-form-section__heading {
	margin-top: 0;
}

/*
 * THE ONE IN-PAGE H2 THAT WAS NOT BROUGHT IN LINE.
 *
 * `.tt-channels-block__heading`, `.tt-office__heading`, `.tt-fleet__heading` and
 * every `h2` inside `.tt-tour__block` already take `--tt-text-lead`, which is
 * the rule this file states further up: an H2 that starts a section is
 * `--tt-text-title`, an H2 inside a page is `--tt-text-lead`.
 *
 * This one kept the bare-element 28px, so /epikoinonia/ rendered three sibling
 * H2s as «Βρες μας κατευθείαν» 21px, «Στείλε μας μήνυμα» 28px, «Το γραφείο»
 * 21px — one level, two sizes, in one column.
 *
 * Scoped to `h2` because the template's `level` argument can make this an `h3`,
 * and an `h3` at 21px would break the level below in exactly the same way.
 */
h2.tt-form-section__heading {
	font-size: var(--tt-text-lead);
}

.tt-form-section__intro {
	max-width: 60ch;
	color: var(--tt-text-muted);
}

.tt-form__fields {
	display: grid;
	gap: var(--tt-space-2);

	/*
	 * One column up to the point where two fit without the labels wrapping.
	 * Auto-fit rather than a fixed count so the coach form's seventeen fields
	 * and the contact form's eight both look deliberate.
	 */
	grid-template-columns: 1fr;
}

@media (min-width: 640px) {
	.tt-form__fields {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}

	/* Anything a person writes a sentence into takes the full width. */
	.tt-form__fields .tt-field--textarea,
	.tt-form__fields .tt-field--checkbox,
	.tt-form__fields .tt-field--checkboxes,
	.tt-form__fields .tt-field--radio {
		grid-column: 1 / -1;
	}
}

.tt-form .tt-field {
	margin-bottom: 0;
}

.tt-form__submit {
	margin-top: var(--tt-space-3);
}

.tt-field__optional {
	margin-left: 6px;
	font-weight: 400;
	font-size: 0.75rem;
	color: var(--tt-text-muted);
}

.tt-field__group {
	margin: 0;
	padding: 0;
	border: 0;
}

/*
 * And so is a consent sentence. 15px on a checkbox whose label is a legal
 * statement — «συμφωνώ με την επεξεργασία των στοιχείων μου» — was under this
 * project's own 16px floor, on text somebody is expected to read before
 * agreeing to it.
 */
.tt-field__check {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	margin-top: 8px;
	font-size: var(--tt-text-body);
	line-height: 1.45;
	color: var(--tt-text);

	/* The whole label is the target, and 44px is the minimum on a phone. */
	min-height: var(--tt-tap);
	padding: 4px 0;
	cursor: pointer;
}

.tt-field__check input {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	margin-top: 2px;
	accent-color: var(--tt-blue);
}

/*
 * The honeypot. Offscreen rather than display:none, which some form fillers
 * read and skip — and the skip is the whole point of the field. aria-hidden and
 * tabindex="-1" in the markup keep it away from assistive technology and the
 * tab order, so nobody who can perceive the page can reach it.
 */
.tt-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* Outcomes ---------------------------------------------------------------- */

.tt-form__outcome {
	margin-bottom: var(--tt-space-3);
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

/*
 * THE LAST OF THE SOFT HALOS, ON THE ONE ELEMENT FOCUS IS MOVED TO ON PURPOSE.
 *
 * The inputs were corrected earlier in this file; this one survived, and it is
 * the worst place for it to have. `.tt-form__outcome` is `tabindex="-1"` and
 * receives focus from navigation.js after a submission, so a keyboard visitor
 * who submits a form with an error is PUT here by the site and then shown an
 * 18%-alpha ring: computed 1.29:1 on the error box and 1.28:1 on the success
 * box, where WCAG 2.2 SC 1.4.11 asks for 3.
 *
 * The token gives 5.29:1 and 4.75:1 on those two surfaces, and it is the same
 * ring every other focus target on the site draws.
 *
 * `:focus` rather than `:focus-visible`, deliberately: focus arrives here by
 * script, and the heuristics for whether a programmatically focused element
 * counts as focus-visible differ between engines. This element is only ever
 * focused because the visitor did something, so it should always show it.
 */
.tt-form__outcome:focus {
	outline: 3px solid var(--tt-focus);
	outline-offset: 2px;
}

.tt-form__outcome--error {
	border-left: 4px solid #b3261e;
}

.tt-form__outcome--success {
	border-left: 4px solid var(--tt-blue);
	background: var(--tt-blue-soft);
}

.tt-form__outcome-title {
	margin-top: 0;
	font-size: 1.0625rem;
}

.tt-form__error-list {
	margin: 0;
	padding-left: 1.1rem;
}

.tt-form__error-list a {
	color: #b3261e;
}

/*
 * THE REQUEST NUMBER DOES NOT BREAK. It is the one string on the receipt a
 * visitor may have to read back down a telephone, and at 390px it was wrapping
 * mid-token across two lines — a 14-character reference printed as two
 * fragments, which is how a digit gets lost. It fits on one line at 255px.
 */
.tt-form__reference strong {
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	letter-spacing: 0.02em;
	white-space: nowrap;
}

/*
 * THE «SEE THEM ALL» LINK NEEDED AIR, AND HAD EXACTLY NONE.
 *
 * Measured on /daily-tours-athina/ at 1440px: the card grid's bottom edge and
 * the link's top edge are both at document y 1482 — a **0px** gap. The button
 * sat welded to the last row of cards, reading as part of the grid rather than
 * as the thing that follows it, and its own shadow overlapped theirs.
 *
 * On the paragraph rather than on the grid, because `.tt-tours` is the shared
 * card grid and appears on six other surfaces that do not want a trailing gap.
 */
.tt-service__tours .tt-tours + p {
	margin-top: var(--tt-space-4);
}

/* The other legal documents ------------------------------------------------
 *
 * A quiet footer to a document, separated by a rule rather than by a panel: the
 * four legal pages are running text and a filled box under one of them would
 * read as a call to action, which is the last thing a privacy policy should
 * end on. It only ever draws on those four — see the note in page.php.
 */

.tt-legal-more {
	margin-top: var(--tt-space-5);
	padding-top: var(--tt-space-3);
	border-top: 1px solid var(--tt-border);
}

.tt-legal-more__heading {
	margin-bottom: var(--tt-space-2);
	font-size: var(--tt-text-lead);
}

/* Channels ---------------------------------------------------------------- */

.tt-channels {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	margin: var(--tt-space-2) 0 0;
	padding: 0;
	list-style: none;
}

.tt-channels-block {
	margin-top: var(--tt-space-4);
}

/*
 * An in-page H2 takes `--tt-text-lead`, which is the step the tour page's own
 * blocks already use. /enoikiasi-poulman/ was rendering H2 at 18, 17 and 28px on
 * one screen, and /epikoinonia/ added a fourth at 20 — one level, four sizes,
 * a 10px spread, so the level said nothing about the level.
 */
.tt-channels-block__heading {
	font-size: var(--tt-text-lead);
}

/*
 * ONE ROW WHEN THERE IS ONLY ONE CHANNEL, WHICH IS THE CASE ON EVERY PAGE.
 *
 * The block is built for a handful of ways to reach the office — telephone,
 * Viber, WhatsApp, email — and the office has configured exactly one. So what
 * it actually draws, on /epikoinonia/, /daily-tours-athina/,
 * /thelo-na-gino-touristas/ and both service pages, is a heading on one line and
 * a single 290px button on the next, inside a 1,132px container: two rows and
 * 74% of the width empty, for one telephone number.
 *
 * Beside its heading it is a sentence with its answer attached, and it takes
 * 44px of page instead of 106px. It still wraps to two rows the moment the
 * office adds a second channel or the screen is narrow enough to need it, which
 * is the same behaviour by a different route rather than a special case.
 *
 * Below 768px it stays stacked: at 390px the heading alone is most of the line.
 */
@media (min-width: 768px) {
	.tt-channels-block {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: var(--tt-space-1) var(--tt-space-3);
	}

	.tt-channels-block__heading {
		margin: 0;
	}

	.tt-channels {
		margin: 0;
	}
}

/* Service pages ----------------------------------------------------------- */

.tt-service__title {
	margin-top: 0;
}

.tt-service__body {
	max-width: 70ch;
}

/*
 * ONE BEAT FOR THE WHOLE TEMPLATE, AND IT HAD SIX.
 *
 * The four service pages share this template, and its top-level joins measured
 * 0, 8, 24, 32, 48 and 56px — six different distances between sections of the
 * same weight, with the TIGHTEST of them above the enquiry panel, which is the
 * one thing on the page the visitor is meant to arrive at. Different gaps say
 * «these two belong together» and «these two do not»; six gaps say nothing at
 * all.
 *
 * 48px between every top-level section, which is the value the majority of them
 * already used.
 */
.tt-service__cases {
	margin-top: var(--tt-space-4);
	margin-bottom: var(--tt-space-5);
}

.tt-service__case-list {
	display: grid;
	gap: 8px;
	margin: 0;
	padding: 0;
	list-style: none;
	grid-template-columns: 1fr;
}

@media (min-width: 640px) {
	.tt-service__case-list {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

.tt-service__case-list li {
	padding: 12px var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
	font-size: 0.9375rem;
}

.tt-service__tours {
	margin-top: var(--tt-space-5);
}

/*
 * The reply-channel radios sit on one line where there is room. Stacked, four
 * 44px targets take more vertical space than the six fields above them, which
 * makes a minor preference look like the most important question on the form.
 */
@media (min-width: 640px) {
	.tt-field--radio .tt-field__group {
		display: flex;
		flex-wrap: wrap;
		gap: var(--tt-space-3);
	}

	.tt-field--radio .tt-field__group legend {
		width: 100%;
	}

	.tt-field--radio .tt-field__check {
		margin-top: 0;
	}
}

/*
 * Photo credit. A licence condition, not a caption: small, quiet, and always
 * present when it is owed. See Core\Media\LicensedPhotos::credit_for().
 */
.tt-gallery__credit {
	margin: var(--tt-space-1) 0 0;
	font-size: 0.75rem;
	color: var(--tt-text-muted);
}

/* The banner photograph fills its half; the hatch shows through when absent. */
.tt-banner__media img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Social profiles --------------------------------------------------------- */

.tt-social {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * 44px, which is the tap target this project uses everywhere. An icon drawn at
 * 20px is a 20px target unless the box around it says otherwise, and a row of
 * small icons in a footer is the hardest thing on a phone to hit.
 */
.tt-social__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: var(--tt-tap);
	height: var(--tt-tap);
	border: 1px solid rgb(255 255 255 / 24%);
	border-radius: var(--tt-radius-pill);
	color: var(--tt-on-navy);
	text-decoration: none;
	transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.tt-social__link:hover,
.tt-social__link:focus-visible {
	border-color: var(--tt-white);
	background: rgb(255 255 255 / 12%);
	color: var(--tt-white);
}

.tt-social__icon {
	display: block;
}

/* The office ---------------------------------------------------------------
 *
 * The address block on the contact page, and the map that does not load until
 * the visitor asks for it.
 */

.tt-office {
	margin-top: var(--tt-space-6);
	padding-top: var(--tt-space-5);
	border-top: 1px solid var(--tt-border);
}

.tt-office__heading {
	margin: 0 0 var(--tt-space-3);
	font-size: var(--tt-text-lead);
}

.tt-office__grid {
	display: grid;
	gap: var(--tt-space-4);
	grid-template-columns: 1fr;
}

/* A grid track is minmax(auto, …) by default, so one wide child pushes the
 * whole column past its container instead of being scrolled or shrunk. */
.tt-office__grid > * {
	min-width: 0;
}

@media (min-width: 768px) {
	.tt-office__grid {
		grid-template-columns: 1fr 1fr;
		align-items: start;
	}
}

/*
 * <address> is the right element and browsers italicise it by default, which
 * on a street name reads as an aside rather than as the address it is.
 */
.tt-office__address,
.tt-footer__office {
	font-style: normal;
}

.tt-office__address,
.tt-office__row {
	margin: 0 0 var(--tt-space-3);
}

.tt-office__label {
	display: block;
	margin-bottom: 2px;
	color: var(--tt-text-muted);
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.02em;
}

.tt-office__value {
	display: block;
	font-size: 1rem;
}

.tt-office__hours {
	list-style: none;
	margin: 0;
	padding: 0;
	font-size: 1rem;
}

.tt-office__hours li {
	padding-block: 2px;
}

/*
 * A fixed shape, so the placeholder and the map that replaces it occupy the
 * same space. Without it, pressing the button shoves everything below the map
 * down the page — the layout shift the visitor caused but did not ask for.
 */
/*
 * `min-height` HERE WAS A HORIZONTAL OVERFLOW, and an instructive one.
 *
 * With `aspect-ratio: 3 / 2` the box is free to derive EITHER dimension from
 * the other, so `min-height: 260px` did not raise a short box — it widened it to
 * 390px inside a 358px column, and the whole page scrolled sideways on a phone.
 * The ratio alone gives the box its shape, which is all that was wanted, and it
 * also reserves the space before the lazy iframe arrives so the map does not
 * shove the footer down as it loads.
 */
.tt-office__map {
	position: relative;
	overflow: hidden;
	aspect-ratio: 3 / 2;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-gray-soft);
}

.tt-office__frame {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

/* Footer office and legal --------------------------------------------------- */

.tt-footer__office {
	margin: var(--tt-space-2) 0 0;
	color: var(--tt-on-navy);
	font-size: 0.8125rem;
}

.tt-footer__office-line {
	display: block;
	padding-block: 2px;
}

.tt-footer__office-line a {
	color: var(--tt-on-navy);
	text-decoration: none;
}

.tt-footer__office-line a:hover,
.tt-footer__office-line a:focus-visible {
	color: var(--tt-white);
	text-decoration: underline;
}

.tt-footer__legal {
	margin-bottom: var(--tt-space-2);
}

.tt-footer__legal-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1) var(--tt-space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

/*
 * The tap target is the padding, not the text. These are the smallest links on
 * the site and they sit at the very bottom of a phone screen, where a thumb is
 * least accurate.
 */
.tt-footer__legal-list a {
	display: inline-block;
	padding-block: 8px;
	color: var(--tt-on-navy);
	text-decoration: none;
}

.tt-footer__legal-list a:hover,
.tt-footer__legal-list a:focus-visible {
	color: var(--tt-white);
	text-decoration: underline;
}


/* Busy controls -------------------------------------------------------------
 *
 * A submit that has been accepted and is waiting. Both the enquiry forms and the
 * filter panel already set aria-busy and disable the button — that told a screen
 * reader something was happening and told everybody else nothing, so a slow
 * connection looked like a button that had not worked and got pressed again.
 *
 * Driven by the ARIA attribute rather than by a class of its own, so the visible
 * state and the announced state cannot drift apart: there is one thing to set.
 */

.tt-button[aria-busy="true"] {
	position: relative;
	color: transparent;
	cursor: progress;
}

.tt-button[aria-busy="true"]::after {
	content: "";
	position: absolute;
	inset: 50% auto auto 50%;
	width: 18px;
	height: 18px;
	margin: -9px 0 0 -9px;
	border: 2px solid currentcolor;
	border-radius: 50%;
	border-top-color: transparent;
	color: var(--tt-white);
	animation: tt-spin 0.7s linear infinite;
}

.tt-button--secondary[aria-busy="true"]::after {
	color: var(--tt-blue);
}

@keyframes tt-spin {
	to {
		transform: rotate(360deg);
	}
}

/*
 * Under reduced motion the global rule above collapses the animation to nothing,
 * which leaves a static ring — still a visible «working» indicator, and still
 * better than a button whose label has vanished. The ring is drawn with a gap so
 * it reads as a spinner rather than as a full circle that might be a radio.
 */

/* Lazy image reveal ---------------------------------------------------------
 *
 * Scoped to `.tt-img-fade`, which assets/js/navigation.js adds to <html>. That
 * scoping is the entire safety argument: with no script there is no class, no
 * rule matches, and every photograph renders exactly as it does today. The
 * alternative — opacity 0 in the stylesheet, raised from script — turns one
 * broken script into a site with no images at all.
 *
 * Only `loading="lazy"`. The one eager image is the tour hero, which is the
 * largest contentful paint: fading it in would delay the measurement of the very
 * thing the page is waiting for.
 *
 * AND NOT THE HOMEPAGE HERO'S SLIDES, WHICH RUN THEIR OWN FADE.
 *
 * Every photograph after the first in a rotating hero is lazy, so this rule
 * caught all of them — and `[data-tt-loaded]` is a class plus two attributes,
 * which outweighs anything the hero can reasonably say about its own images.
 * The result was a hero whose slides were all opaque at once: the last one
 * painted, the crossfade invisible, the rotation apparently dead. Measured in
 * Chromium with `CSS.getMatchedStylesForNode`, which named the winning rule
 * rather than leaving it to be guessed at.
 *
 * The fix is not more specificity — that is an argument this rule wins again
 * the next time somebody adds an attribute — it is saying that these images are
 * not this system's business. They fade over 1.2 seconds into each other, not
 * over 0.25 out of nothing, and the two fades cannot both be in charge.
 */

.tt-img-fade img[loading="lazy"]:not(.tt-hero__slide) {
	opacity: 0;
	transition: opacity 0.25s ease;
}

.tt-img-fade img[loading="lazy"][data-tt-loaded]:not(.tt-hero__slide) {
	opacity: 1;
}

/*
 * Printing happens after everything has loaded, but a print stylesheet cannot
 * count on the script having run at all — and an invisible photograph on paper
 * cannot be scrolled into view to fix itself.
 */
@media print {
	.tt-img-fade img[loading="lazy"]:not(.tt-hero__slide) {
		opacity: 1;
	}
}

/* Service page header -------------------------------------------------------
 *
 * Replaces an <h1> alone on white. Text first in the source on every viewport,
 * so the page still reads correctly with stylesheets off and a screen reader
 * meets the title before the photograph.
 */

.tt-page-header {
	margin-bottom: var(--tt-space-5);
}

.tt-page-header__title {
	margin-bottom: var(--tt-space-2);
}

.tt-page-header__lede {
	margin: 0;
	max-width: 62ch;
	color: var(--tt-text-muted);
	font-size: 1.0625rem;
	line-height: 1.55;
}

.tt-page-header__media {
	margin-top: var(--tt-space-3);
}

.tt-page-header__media img {
	width: 100%;
	border-radius: var(--tt-radius-card);
}

/*
 * `--plain` FINALLY DOES SOMETHING. The class has been emitted by
 * template-parts/service/header.php since the header was written, with a note
 * saying «a filled band instead of a title floating on white — the modifier
 * needs its rule in main.css before it does anything». It never got one, and it
 * is on SIX pages: /to-grafeio/, /epikoinonia/, /enoikiasi-poulman/,
 * /daily-tours-athina/, /sxolikes-metafores/ and /thelo-na-gino-touristas/ —
 * every page on the site that is neither the homepage nor a tour.
 *
 * What that looked like, measured on /enoikiasi-poulman/ at 1440px: the title
 * and the lede are capped at a reading measure and occupy the left 646px of a
 * 1132px container, so the opening screen of the page carried a 486 x 560px
 * rectangle of nothing to the right of the heading. Not white space around a
 * shape — a hole where a shape was supposed to be.
 *
 * A ground fixes it without inventing content. Text that stops at 62ch inside a
 * filled band reads as room; the same text stopping at 62ch on the page reads
 * as something missing beside it.
 *
 * GREY AND NOT BLUE, THOUGH `.tt-panel--accent` WAS RIGHT THERE. Three of these
 * six pages put a blue-soft band — the coach scene, the fleet figures — within
 * 400px of the header, and a blue header above a blue band is one field with a
 * seam in it. `--tt-gray-soft` is the site's neutral ground and keeps the blue
 * for the things that are actually accents.
 */
.tt-page-header--plain {
	padding: var(--tt-space-3);
	border-radius: var(--tt-radius-card);
	background: var(--tt-gray-soft);
}

@media (min-width: 768px) {
	.tt-page-header--plain {
		padding: var(--tt-space-4) var(--tt-space-4) var(--tt-space-5);
	}
}

@media (min-width: 768px) {
	.tt-page-header--illustrated {
		display: grid;
		grid-template-columns: 3fr 2fr;
		align-items: center;
		gap: var(--tt-space-5);
	}

	/* A grid track is minmax(auto, …), so a wide photograph would otherwise
	 * push the text column past the container. */
	.tt-page-header--illustrated > * {
		min-width: 0;
	}

	.tt-page-header__media {
		margin-top: 0;
	}
}

/* Steps ---------------------------------------------------------------------
 *
 * The numbers come from the ordered list itself, through a counter, rather than
 * being typed into the titles — so they cannot end up reading 1, 2, 2 after an
 * edit, and there is nothing to renumber when the office writes a third step.
 */

.tt-steps,
.tt-points,
.tt-faq {
	margin-bottom: var(--tt-space-5);
}

/*
 * The FAQ is the last thing on the page, under the form and the contact
 * channels, so it needs the separation the sections above get from what follows
 * them. Without it «Συχνές ερωτήσεις» sits directly on the telephone button.
 */
.tt-faq {
	margin-top: var(--tt-space-5);
}

.tt-steps__list {
	counter-reset: tt-step;
	display: grid;
	gap: var(--tt-space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

@media (min-width: 768px) {
	.tt-steps__list {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--tt-space-4);
	}
}

.tt-steps__item {
	counter-increment: tt-step;
	position: relative;
	padding-left: 52px;
}

.tt-steps__item::before {
	content: counter(tt-step);
	position: absolute;
	inset: 0 auto auto 0;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

/*
 * A card title sat at 16px above a 15px body — a one-pixel step across a level
 * boundary, which is not a step. Now that the bodies are on `--tt-text-body`,
 * the titles take the one above it.
 */
.tt-steps__title,
.tt-points__title {
	margin-bottom: 4px;
	font-size: var(--tt-text-strong);
}

/*
 * 16px, because this is prose. `docs/03-design-system.md` asks for at least
 * 16px of body text on a phone and these three components carry eleven
 * paragraphs of it between them — the fleet description, how the service works,
 * why the office, and every cancellation answer. They were 15.
 */
.tt-steps__body,
.tt-points__body {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-body);
}

/* Points -------------------------------------------------------------------- */

.tt-points__list {
	display: grid;
	gap: var(--tt-space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

@media (min-width: 768px) {
	.tt-points__list {
		grid-template-columns: repeat(3, minmax(0, 1fr));
		gap: var(--tt-space-4);
	}
}

.tt-points__item {
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

/* Fleet ---------------------------------------------------------------------
 *
 * DELIBERATELY NOT .tt-points. Both sit on the coach page, both are three short
 * items, and drawn the same way they read as six interchangeable cards that the
 * eye skips together. This one is a filled band with rules between the items
 * rather than bordered cards on white: one block that says «here is the
 * company», against three that say «here is a reason».
 *
 * The band is --tt-blue-soft, which the design system already uses for the
 * numbered step markers, so nothing new enters the palette.
 */

.tt-fleet {
	margin-bottom: var(--tt-space-5);
	padding: var(--tt-space-4);
	border-radius: var(--tt-radius-card);
	background: var(--tt-blue-soft);
}

.tt-fleet__heading {
	margin-bottom: var(--tt-space-3);
	color: var(--tt-blue-dark);
	font-size: var(--tt-text-lead);
}

.tt-fleet__list {
	display: grid;
	gap: var(--tt-space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

@media (min-width: 768px) {
	.tt-fleet__list {
		grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
		gap: var(--tt-space-4);
	}
}

/*
 * NO VERTICAL RULES BETWEEN THE ITEMS, and they were written first.
 *
 * `.tt-fleet__item + .tt-fleet__item { border-left }` is adjacency in the DOM,
 * not in the row, so the moment the grid wraps the first item of the second row
 * keeps the rule. Measured at 768px, where auto-fit gives two and then one: the
 * third card drew a 1px rule against the band edge with nothing to its left and
 * sat 32px right of the card above it.
 *
 * Deleted rather than propped up. The filled band already separates this block
 * from the bordered white cards of .tt-points below it, which is the whole job
 * the rules were doing, and a decoration with a layout bug is worth less than
 * the gap it was drawn in.
 */

.tt-fleet__title {
	margin-bottom: 4px;
	color: var(--tt-navy);
	font-size: var(--tt-text-strong);

	/*
	 * The numbers are the content of these headings — «περίπου 15», «3
	 * λεωφορεία». Lining figures stop 15 and 3 from sitting at different
	 * heights across three adjacent cards.
	 */
	font-variant-numeric: tabular-nums lining-nums;
}

.tt-fleet__body {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-body);
}

/* FAQ ----------------------------------------------------------------------
 *
 * Native <details>. The default marker is replaced with one that has a 44px
 * target and rotates, and `list-style: none` plus the WebKit pseudo-element are
 * both needed — Safari ignores the first.
 */

.tt-faq__list {
	border-top: 1px solid var(--tt-border);
}

.tt-faq__item {
	border-bottom: 1px solid var(--tt-border);
}

.tt-faq__question {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--tt-space-2);
	min-height: var(--tt-tap);
	padding-block: var(--tt-space-2);
	color: var(--tt-navy);
	font-weight: 700;
	cursor: pointer;
	list-style: none;
}

.tt-faq__question:hover {
	color: var(--tt-blue-dark);
}

.tt-faq__question:hover::after {
	border-color: var(--tt-blue-dark);
}

.tt-faq__question::-webkit-details-marker {
	display: none;
}

.tt-faq__question::after {
	content: "";
	flex: 0 0 auto;
	width: 10px;
	height: 10px;
	border-right: 2px solid var(--tt-text-muted);
	border-bottom: 2px solid var(--tt-text-muted);
	transform: rotate(45deg) translate(-2px, -2px);
	transition: transform 0.15s ease;
}

.tt-faq__item[open] .tt-faq__question::after {
	transform: rotate(-135deg) translate(-2px, -2px);
}

.tt-faq__answer {
	padding-bottom: var(--tt-space-2);
	color: var(--tt-text-muted);
	font-size: var(--tt-text-body);
	max-width: 70ch;
}

.tt-faq__answer p {
	margin: 0;
}

/*
 * THE ANSWER GROWS INTO PLACE INSTEAD OF ARRIVING WHOLE.
 *
 * A native <details> opens in one frame, so every question below the one that
 * was tapped jumps down the screen by the height of an answer nobody has read
 * yet. On a phone, with five questions in the list, the thing the visitor was
 * looking at is somewhere else by the time their thumb has left the glass. The
 * movement is not decoration — it is what says «this pushed that down» instead
 * of leaving the reader to work out what changed.
 *
 * `::details-content` is the browser's own wrapper around everything after the
 * <summary>. It exists because the content is hidden through a shadow slot that
 * no author rule reaches — the same reason the archive's panel has to be
 * collapsed from script rather than from here.
 *
 * `interpolate-size` is what makes `0` to `auto` an animation rather than a
 * jump, and it is set on the LIST rather than on :root deliberately. It is
 * inherited, and switching keyword interpolation on for the whole document
 * would change how every future height transition on this site behaves,
 * silently, from a line nobody would think to look at.
 *
 * `content-visibility` has to be in the transition too, with `allow-discrete`:
 * it is what the browser flips to hide the content, and without it the answer
 * disappears on the first frame of the closing animation and the box collapses
 * empty.
 *
 * WEBKIT GETS THE OLD BEHAVIOUR, and it is worth naming which half it is
 * missing. It has `::details-content` — the box is addressable — but not
 * `interpolate-size`, so `0` to `auto` is not an animation there and the answer
 * arrives in one frame, exactly as it does today. Measured in both engines:
 * 59px to 82px to 123px in Chromium, 59px straight to 123px in WebKit. Nothing
 * is broken, nothing is clipped, and find-in-page, keyboard operation and
 * JavaScript-off behave identically either way.
 */
.tt-faq__list {
	interpolate-size: allow-keywords;
}

.tt-faq__item::details-content {
	block-size: 0;
	overflow: hidden;
	transition: block-size 0.22s ease, content-visibility 0.22s;
	transition-behavior: allow-discrete;
}

.tt-faq__item[open]::details-content {
	block-size: auto;
}

/*
 * AND THE BLANKET RULE DOES NOT REACH THIS EITHER.
 *
 * The reduced-motion block near the top of this file is written `*, *::before,
 * *::after`, which is every element and exactly two pseudo-elements. It does
 * not match `::details-content`, for the same reason it does not match
 * `::view-transition-group`. Without the rule below, somebody who has asked
 * their system for less movement gets the full 0.22s here — on the one control
 * on the page they are most likely to operate repeatedly.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-faq__item::details-content {
		transition: none;
	}
}

/* Brand logo -------------------------------------------------------------- */

/*
 * The height is capped here, not in the crop, so the client can upload a wide
 * wordmark or a square mark and neither breaks the bar. `width: auto` is what
 * keeps the aspect ratio while the height leads — without it a square logo
 * would be stretched to the wordmark's width.
 */
.tt-brand--logo {
	display: inline-flex;
	align-items: center;
	margin-right: auto;
}

.tt-brand--logo img {
	width: auto;
	max-height: 48px;
}

/*
 * The 34px/48px split below was measured for a WIDE wordmark, where extra
 * height also means a lot of extra width (48px tall was 254px wide — enough
 * to push the CTA onto a second row at 1440). The real logo turned out to be
 * a near-square circular mark, so the same height buys almost no extra width
 * this time; checked in the browser at 1440 and 375 with the service pages
 * in the menu, no wrap.
 */
@media (min-width: 768px) {
	.tt-brand--logo img {
		max-height: 56px;
	}
}

/*
 * The footer is navy and most logos are drawn for white. A logo with its own
 * background stays legible; one on transparency would otherwise disappear into
 * the surface, so it sits on a white plate rather than being tinted — tinting
 * somebody's logo is not a decision a stylesheet gets to make.
 */
.tt-footer .tt-brand--logo img {
	max-height: 44px;
	padding: 6px 10px;
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
}

/* Price basis ------------------------------------------------------------- */

/*
 * Drawn only for a per-group price, so it must read as part of the price rather
 * than as a footnote beside it — a label this rare is easy to skim past, and
 * skimming past it is how «από €900» gets read as per person.
 */
.tt-tour-card__basis,
.tt-booking__basis,
.tt-sticky-cta__basis {
	display: block;
	color: var(--tt-text-muted);
	font-size: 0.75rem;
	font-weight: 600;
	line-height: 1.2;
}

.tt-sticky-cta__basis {
	font-size: 0.6875rem;
}

/* Being pressed ------------------------------------------------------------ */

/*
 * NOTHING ON THIS SITE ANSWERED A PRESS, AND ON A PHONE THAT IS THE ONLY ANSWER
 * THERE IS.
 *
 * Measured rather than assumed, on an iPhone 14 profile in WebKit and a Pixel 7
 * profile in Chromium: `(hover: none)` is true on both. So every `:hover` rule
 * in this stylesheet — the darkening buttons, the blueing links, the lifting
 * cards, the two disclosures fixed earlier for exactly this reason — is dead
 * code on a touch screen. Then held the pointer down on the hero's «Αναζήτηση»,
 * on a filter chip and on a card title, and read background, colour, border,
 * transform, opacity and shadow while held: identical to the resting style, on
 * every control, in both engines.
 *
 * Which means that between touching something and the next page painting, a
 * visitor on a phone was shown nothing whatsoever. On a fast connection that
 * gap is short enough to forgive. On a train it is long enough to tap again —
 * and a second tap is a second navigation on a GET form, or a duplicate the
 * office has to ring twice about on the enquiry form.
 *
 * These are STATES, NOT EFFECTS, which is what keeps them on the right side of
 * «no decorative animation without a UX purpose». One step darker, one pixel
 * down, and on a card the hover lift taken back so it settles onto the page
 * instead of rising off it. One pixel because it is the smallest distance that
 * reads as movement: a control that jumps under the thumb feels broken, and a
 * grid of twelve of them would shiver.
 *
 * THE WHOLE SECTION IS AT THE END OF THE FILE ON PURPOSE. Every rule here has
 * the same specificity as the `:hover` rule it has to beat — `.tt-button:active`
 * against `.tt-button:hover` is 0-2-0 against 0-2-0 — so the cascade decides it
 * by source order and nothing else. Moved earlier, the chips at line 2797 and
 * the pagination at 2893 would silently stop answering a press, and the only
 * symptom would be on a phone.
 */

/*
 * `transition-duration: 0s` INSIDE the pressed rule, which buys both halves with
 * one declaration. A transition is governed by the style being transitioned TO:
 * arriving at `:active` reads 0s, so the acknowledgement is on screen in the
 * frame the finger lands rather than 150ms after it has gone; leaving `:active`
 * reads the base rule again and eases back over its own 0.15s, which is what
 * stops the control snapping.
 *
 * Under reduced motion the blanket rule's `transition-duration: 0.01ms
 * !important` beats this, and that is the correct outcome: the state still
 * changes, it simply arrives finished. A press is an acknowledgement, and
 * suppressing it would leave that visitor with nothing again.
 */
.tt-button:active,
a.tt-chip:active,
.tt-header__phone:active,
.tt-nav-toggle:active,
.tt-filters__toggle:active,
.tt-faq__question:active,
.tt-social__link:active,
.tt-archive__results a.page-numbers:active {
	transform: translateY(1px);
	transition-duration: 0s;
}

/*
 * The background is the half that matters on a touch screen, because the
 * translate alone is one pixel and the visitor's own finger is over it.
 */
.tt-button:active {
	background: var(--tt-blue-dark);
}

.tt-button--secondary:active,
.tt-button--quiet:active,
.tt-nav-toggle:active {
	background: var(--tt-gray-soft);
	border-color: var(--tt-text-muted);
}

.tt-button--quiet:active {
	border-color: transparent;
}

a.tt-chip:active,
.tt-archive__results a.page-numbers:active,
.tt-filters__toggle:active,
.tt-header__phone:active {
	background: var(--tt-blue-soft);
	border-color: var(--tt-blue);
	color: var(--tt-blue-dark);
}

/* An applied filter is already blue, so it presses darker rather than lighter. */
.tt-chip.is-current:active {
	background: var(--tt-blue-dark);
	border-color: var(--tt-blue-dark);
	color: var(--tt-white);
}

/*
 * THE CARD GOES DOWN LIKE EVERYTHING ELSE, AND THE FIRST ATTEMPT DID NOT.
 *
 * It was written to SETTLE — `translateY(0)` against the `-2px` of its own hover
 * lift, so a press undid the rise instead of adding to it. That reads beautifully
 * under a mouse and it is worth nothing at all under a thumb, which is where it
 * was needed: `(hover: none)` means the card is never lifted in the first place,
 * so pressing it moved the computed transform from `none` to `matrix(1,0,0,1,0,0)`
 * — a different string and an identical screen. Measured on both touch profiles
 * before it was believed.
 *
 * So it presses down one pixel, exactly like the buttons and the chips. From a
 * resting card that is a one-pixel dip; from a lifted one it is a three-pixel
 * drop, which is more emphatic on the pointer that already had a signal and
 * fine, because the same rule now says the same thing on both. The shadow goes
 * back to the flat one in the same frame, so the card reads as pressed into the
 * page rather than hovering above it.
 *
 * TWO RULES AND NOT A SELECTOR LIST, and the reason is written out at the sticky
 * bar above: a list is invalid as a whole if any selector in it is, so pairing
 * the `:has()` form with the bare form would take the bare form down with it
 * wherever `:has()` is not honoured. `:has()` is the one that carries this —
 * the pressed element is the title's link, and whether `:active` reaches its
 * ancestors is a browser habit rather than a guarantee.
 */
.tt-tour-card:active {
	transform: translateY(1px);
	box-shadow: var(--tt-shadow-card);
	transition-duration: 0s;
}

.tt-tour-card:has( a:active ) {
	transform: translateY(1px);
	box-shadow: var(--tt-shadow-card);
	transition-duration: 0s;
}

/*
 * THE DESTINATION TILE HAD NOTHING TO SETTLE FROM, WHICH THE FIRST VERSION OF
 * THIS SECTION MISSED. It was written into the rule above and measured: the
 * computed transform went from `none` to `matrix(1, 0, 0, 1, 0, 0)`, which is a
 * change in the string and no change at all on the screen.
 *
 * The underlying gap is older than the press state. This site has exactly two
 * photograph-led things that are entirely one link — the tour card and this —
 * and they answered a pointer in two different languages: the card lifted two
 * pixels, and the tile changed the colour of a 14px word underneath a
 * photograph 250px tall. On a rail whose whole job is «tap a place», the part
 * being tapped was the part that said nothing.
 *
 * So it borrows the card's vocabulary rather than inventing a third one. No
 * shadow with it: a card is a surface and can cast one, and a bare photograph
 * on the page background would be casting a shadow from nothing.
 */
.tt-destination__link {
	transition: transform 0.16s ease;
}

.tt-destination__link:hover,
.tt-destination__link:focus-visible {
	transform: translateY(-2px);
}

.tt-destination__link:active {
	transform: translateY(1px);
	transition-duration: 0s;
}

/* Touch comfort ----------------------------------------------------------- */

/*
 * THE THEME SETS `--tt-tap: 44px` AND THEN LEAVES SIX CONTROLS UNDER IT.
 *
 * Measured at 375x553 in both engines: «Καθαρισμός φίλτρων» — the recovery link
 * on an archive that found nothing — is 135.7 x 20.8 with no padding at all, 47%
 * of the token. «Όλες οι εκδρομές», the homepage's way into the catalogue, is
 * 122 x 22.4. The six footer links are 32.8px tall with edge-to-edge gaps of
 * 0.0px between them, while the social icons in the same footer are 44 x 44 with
 * 8px gaps — the standard was already set locally and these were missed.
 *
 * All of them clear the 24x24 that WCAG 2.2 SC 2.5.8 requires at AA, so this is
 * a comfort miss against the project's own number rather than a failure. The
 * 0px footer gap is the one that is genuinely a mis-tap.
 *
 * `pointer: coarse` and not a width query: a 1024px tablet is a finger and a
 * 1024px laptop is a mouse, and the thing that matters is which one is pointing.
 * The same treatment is already applied to `.tt-chip` further up this file.
 *
 * Padding grows around the text rather than moving it, so no line shifts.
 */
/*
 * A LATER PASS FOUND THE TWO THIS ONE MISSED, AND ONE OF THEM IS THE PHONE.
 *
 * Measured on the iPhone profile after the rule above was in place: the footer
 * navigation was 44px, the social icons 44x44, and «210 5234992» — the link
 * that dials the shop — was 82 x 15. It was the smallest target in the footer
 * and the most consequential one on the whole page for a travel agency, sitting
 * directly beneath a list of links that had already been corrected.
 *
 * The legal row was 35px for the same reason: it was not in the selector list.
 *
 * AND THE RATING, ADDED THE DAY IT WAS WRITTEN. It went into the footer at 33px
 * and the test above caught it before anything shipped — which is the argument
 * for that test asserting over every link in the footer rather than over a
 * named few. A selector list is a thing you forget to add to; `.tt-footer a` is
 * not.
 */
@media (pointer: coarse) {
	.tt-archive__reset,
	.tt-section__link,
	.tt-crumbs a,
	.tt-footer__list a,
	.tt-footer__office-line a,
	.tt-footer__rating,

	/*
	 * AND THE CREDIT, ADDED THE DAY IT WAS WRITTEN, CAUGHT AT 15px. Exactly the
	 * pattern the note above predicts: a new link went into the footer, the
	 * selector list did not know about it, and `.tt-footer a` in
	 * navigation-reach.spec.js did. Third time this list has been extended by a
	 * failing test rather than by somebody remembering.
	 */
	.tt-footer__credit a,
	.tt-footer__legal-list a {
		display: inline-flex;
		align-items: center;
		min-height: var(--tt-tap);
	}

	.tt-footer__list li + li {
		margin-top: var(--tt-space-1);
	}
}

/* Chat launcher ----------------------------------------------------------- */

/*
 * THE BUTTON IS OURS; THE WIDGET IT FETCHES IS NOT.
 *
 * `Integrations\WebChat` prints this instead of the vendor's script tag, so
 * nothing third-party is requested until it is pressed. That makes it a real
 * control on this site rather than a bubble drawn over it — which means it has
 * to obey the same rules as every other control here: a 44px target, the
 * project's own colours, a visible focus ring, and a press that answers.
 *
 * IT IS A DOCK AND NOT A LONE BUTTON, because the component has three things to
 * put in that corner: the launcher, a polite live region, and a panel carrying
 * either the disclosure a refuser sees before anything loads or the office
 * telephone when the loader never arrives. The dock is what is fixed; every
 * child is in normal flow, stacked upwards from the corner.
 */
.tt-chat-dock {
	position: fixed;

	/*
	 * SAFE AREAS ON BOTH AXES, AND THE SECOND ONE WAS MISSING. The bottom was
	 * handled with `margin-bottom: env(safe-area-inset-bottom)` on the button;
	 * the same phone turned on its side puts the notch on a vertical edge, and
	 * the launcher went underneath it. Written as insets rather than as a
	 * margin so the sticky-bar rule further down can restate one axis without
	 * disturbing the other.
	 */
	inset: auto calc( var(--tt-space-2) + env(safe-area-inset-right, 0px) )
		calc( var(--tt-space-2) + env(safe-area-inset-bottom, 0px) ) auto;
	z-index: 40;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: var(--tt-space-1);

	/* A long translated label never pushes the page sideways. */
	max-width: calc( 100vw - 2 * var(--tt-space-2) );

	/* A container, not a surface: only the controls inside it take a tap. */
	pointer-events: none;
	transition: opacity 0.16s ease;
}

.tt-chat-dock > * {
	pointer-events: auto;
}

/*
 * IT GETS OUT OF THE WAY OF A FORM SOMEBODY IS FILLING IN — WITHOUT LEAVING THE
 * TAB ORDER, WHICH IS WHAT THE FIRST VERSION DID.
 *
 * The problem was real. On /epikoinonia/ at 390px the fixed launcher overlapped
 * the right third of «Στείλε το αίτημα», `elementFromPoint` over that third
 * returned `.tt-chat`, and a tap there opened the chat instead of sending the
 * enquiry the visitor had just spent two minutes writing. The fix was
 * `display: none`, which also removes the element from the tab order — so a
 * keyboard visitor working through that form could never Tab forward to the
 * launcher at all. A pointer collision was paid for by the people who do not
 * use a pointer.
 *
 * `pointer-events` is the property that was wanted: the tap falls through to
 * the submit button underneath and the launcher stays focusable. `opacity` and
 * not `visibility`, for the same reason — `visibility: hidden` would take it
 * out of the tab order again.
 *
 * `:not(:focus-within)` is the other half, and it is WCAG 2.2 SC 2.4.11. The
 * moment Tab lands on the launcher or inside its panel, the dock is fully
 * visible and fully clickable again, so focus is never sitting on something
 * nobody can see.
 *
 * `body.tt-form-focus` is not a new mechanism: navigation.js already sets it on
 * `focusin` inside a form and removes it on `focusout`, and the header and the
 * sticky booking bar already both step aside for it.
 */
body.tt-form-focus .tt-chat-dock:not(:focus-within) {
	opacity: 0;
	pointer-events: none;
}

.tt-chat {
	display: inline-flex;
	align-items: center;
	gap: var(--tt-space-1);
	min-height: var(--tt-tap);
	max-width: 100%;
	padding: var(--tt-space-1) var(--tt-space-3);
	border: 0;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-blue);
	color: var(--tt-white);
	font-family: inherit;
	font-size: 0.9375rem;
	font-weight: 600;
	line-height: 1.25;
	text-align: left;
	cursor: pointer;
	box-shadow: var(--tt-shadow-raised);
	transition: background-color 0.16s ease, transform 0.16s ease;
}

.tt-chat:hover {
	background: var(--tt-blue-dark);
}

/*
 * THE ATTRIBUTE THE SCRIPT ACTUALLY SETS, which is `aria-disabled` and no
 * longer `disabled`. Disabling a focused button blurs it in every browser, so a
 * keyboard visitor who pressed Enter lost their place in the document at the
 * exact moment the component started working. The script refuses the second
 * press instead, and this is what «working on it» looks like meanwhile.
 */
.tt-chat[aria-disabled="true"] {
	cursor: progress;
	opacity: 0.72;
}

.tt-chat[aria-disabled="true"]:hover {
	background: var(--tt-blue);
}

.tt-chat__icon {
	flex: 0 0 auto;
}

/*
 * ABOVE THE BOOKING BAR, NOT ON TOP OF IT. The sticky call to action is 72px of
 * fixed white at the bottom edge of every tour page below 1024px, and a chat
 * button at the same offset would sit on «Ζήτησε κράτηση» — covering the one
 * control docs/06 requires to stay reachable.
 */
@media (max-width: 1023px) {
	body:has( .tt-sticky-cta ) .tt-chat-dock {
		/* Literal, for the reason recorded on the reservation rule above. */
		inset-block-end: calc( 73px + env(safe-area-inset-bottom, 0px) + 16px );
	}
}

/*
 * AND THE VENDOR'S OWN BUBBLE, WHICH IS THE ONE THAT ACTUALLY STAYS.
 *
 * Everything above moves OUR launcher out of the booking bar's way — and our
 * launcher is hidden the instant the chat opens. What remains on screen from
 * then on is LeadConnector's `<chat-widget>`, which paints its own floating
 * bubble in the same bottom-right corner and has never been told about the
 * 72px of fixed white beneath it. So the collision this block exists to
 * prevent came back the moment the visitor succeeded.
 *
 * It is styled by element rather than by class because the class names inside
 * a vendor bundle are theirs to change; the custom element name is the
 * contract. `!important` because the bundle sets the offset inline.
 */
@media (max-width: 1023px) {
	body:has( .tt-sticky-cta ) chat-widget {
		--tt-chat-vendor-lift: calc( 73px + env(safe-area-inset-bottom, 0px) + 16px );

		position: fixed !important;
		inset-block-end: var( --tt-chat-vendor-lift ) !important;
	}
}

/*
 * A RING THAT CARRIES ITS OWN CONTRAST, BECAUSE THIS BUTTON DOES NOT KNOW WHAT
 * IT IS SITTING ON.
 *
 * Every other focus ring on the site can name its backdrop: a form panel, the
 * navy footer, the white bar. This one is `position: fixed`, so the backdrop is
 * whatever happens to be scrolled underneath it — white for most of the page,
 * and the navy footer once you reach the bottom. A single colour fails at one
 * end or the other: white-on-white was the first attempt and the focus audit
 * caught it on four pages; navy would have failed on the footer instead, where
 * nothing would have caught it because the test reads the DOM ancestors, and
 * this button's ancestor is `body`.
 *
 * So the ring is two rings. The white halo is painted first and is 6px wide;
 * the navy outline lands inside it at 2-5px and on top of it. Against the page
 * the navy reads; against the footer the white halo separates the navy from the
 * navy. The existing raised shadow is restated because `box-shadow` replaces
 * rather than adds.
 */
.tt-chat:focus-visible {
	outline-color: var(--tt-navy);
	box-shadow: 0 0 0 6px var(--tt-white), var(--tt-shadow-raised);
}

/*
 * The press, on the one control most likely to be tapped by a thumb. Same rule
 * as everything else at the end of this file: instant down, eased up.
 */
.tt-chat:active {
	transform: translateY(1px);
	transition-duration: 0s;
}

/*
 * AND THE PRESS IS THE ONE PIECE OF MOTION HERE THAT THE GLOBAL RULE DOES NOT
 * COVER. The `prefers-reduced-motion` block near the top of this file zeroes
 * every duration and delay, which takes care of the fade and the colour — it
 * cannot take care of a `transform`, which is applied instantly either way. It
 * is 1px, and it is still a control moving under a finger for decoration.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-chat:active {
		transform: none;
	}
}

/* The panel, on the two occasions the launcher has to speak in sentences --- */

/*
 * ONE ELEMENT FOR TWO STATES THAT ARE NEVER BOTH TRUE: the disclosure shown to
 * somebody who answered «Απόρριψη όλων» before anything is fetched, and the
 * failure message with the office telephone in it when the bundle never
 * arrives. They share every rule of layout, focus and dismissal, so they share
 * an element and the script swaps the text.
 */
.tt-chat__panel {
	width: min( 320px, 100% );
	padding: var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	color: var(--tt-text);
	box-shadow: var(--tt-shadow-raised);
	text-align: left;
}

/*
 * It takes focus when it opens, so it needs a ring: a container that is focused
 * and shows nothing is the same defect as a control that is.
 */
.tt-chat__panel:focus-visible {
	outline-color: var(--tt-navy);
}

.tt-chat__panel-title {
	margin: 0 0 var(--tt-space-1);
	font-size: 1rem;
	font-weight: 600;
	line-height: 1.3;
}

.tt-chat__panel-text {
	margin: 0;
	font-size: 0.875rem;
	line-height: 1.5;
	color: var(--tt-text-muted);
}

.tt-chat__panel-links {
	margin: var(--tt-space-1) 0 0;
	padding: 0;
	list-style: none;
}

/*
 * Full-height rows and not a run of inline links. This panel opens on a phone,
 * beside a thumb, and «Πολιτική απορρήτου» at 14px is a 20px target.
 */
.tt-chat__panel-link {
	display: inline-flex;
	align-items: center;
	min-height: var(--tt-tap);
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--tt-blue-dark);
}

.tt-chat__panel-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	margin-top: var(--tt-space-1);
}

/*
 * ONE SHAPE FOR BOTH ANSWERS — the rule the consent banner's own actions are
 * written under, and it applies here for the same reason. The moment
 * «Κλείσιμο» is drawn quieter than «Άνοιξε τη συνομιλία», a disclosure stops
 * being one. The quiet variant differs in fill only: same size, same weight,
 * same border, and 7.0:1 on white.
 */
.tt-chat__panel-button {
	flex: 1 1 auto;
	min-height: var(--tt-tap);
	padding: 0 var(--tt-space-2);
	border: 1px solid var(--tt-blue);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-blue);
	color: var(--tt-white);
	font-family: inherit;
	font-size: 0.875rem;
	font-weight: 600;
	cursor: pointer;
}

.tt-chat__panel-button:hover {
	background: var(--tt-blue-dark);
	border-color: var(--tt-blue-dark);
}

.tt-chat__panel-button--quiet {
	background: var(--tt-white);
	color: var(--tt-blue-dark);
}

.tt-chat__panel-button--quiet:hover {
	background: var(--tt-gray-soft);
}

/*
 * The general schedule, above the specific dates. It is prose where the list
 * below it is data, so it reads as a sentence rather than as a first row.
 */
.tt-dates__schedule {
	margin: 0 0 var(--tt-space-2);
	color: var(--tt-text-muted);
}

/* Consent -----------------------------------------------------------------
 *
 * `Consent\Banner` prints the markup and the plugin owns the policy; the look
 * lives here for the same reason the chat launcher's does — one stylesheet,
 * one set of tokens, and a theme that can restyle it without touching business
 * logic.
 *
 * ALONG THE BOTTOM EDGE, NOT ACROSS THE PAGE. There is no overlay and no
 * scroll lock, deliberately: a banner that holds the page hostage is why
 * people press «accept» without reading, and a consent given to get rid of
 * something is not worth the row it is stored in. `position: fixed` also means
 * it cannot shift the layout, so it costs nothing in CLS.
 */
/*
 * THE PAGE HAS TO BE ABLE TO SCROLL PAST IT. Fixed to the bottom edge means
 * that at the end of a document the banner covers whatever is there, and what
 * is there is the footer — measured on the homepage with «Πολιτική Απορρήτου»
 * underneath it and no further to scroll. The script measures the banner and
 * writes its height here; the default of zero is what every visitor who has
 * already answered gets, so nobody carries dead space around for ever.
 */
body {
	padding-bottom: var(--tt-consent-height, 0px);
}

/*
 * A SHEET ON A PHONE, A CARD ON A DESKTOP, AND IT WAS NEITHER.
 *
 * It used to be a 1,180px-wide band of body text with two outlined buttons
 * floating in the middle of it — the widest, plainest thing on the site,
 * arriving in front of the design it was sitting on top of. The client's word
 * for it was «άχαρο», and the reason is structural rather than cosmetic: a
 * consent notice is a message from the site to one person, and a full-bleed
 * band is the shape of an announcement to a room.
 *
 * So it is a card. On a phone it is the width of the screen because there is no
 * other width, but it keeps the rounded top edge of a sheet rather than the
 * squared-off edge of a bar, so it reads as something that came UP rather than
 * something that was always there. From 768px it detaches from the corner
 * entirely.
 */
.tt-consent {
	position: fixed;
	inset: auto 0 0;
	z-index: 60;
	background: var(--tt-white);
	border: 1px solid var(--tt-border);
	border-bottom: 0;
	border-radius: 18px 18px 0 0;
	box-shadow: var(--tt-elevation-4);
	padding-bottom: env(safe-area-inset-bottom, 0);
}

/*
 * IT ARRIVES, RATHER THAN APPEARING. The banner is served `hidden` and the
 * script unhides it once it has read the cookie, so there is a definite moment
 * to animate — and something sliding up from the bottom edge is the one piece
 * of motion here that carries information: it says «this came from outside the
 * page», which is exactly what it is.
 *
 * `both` so the starting frame applies before the first paint; without it the
 * banner flashes at full opacity for one frame and then animates.
 */
@keyframes tt-consent-arrives {
	from {
		opacity: 0;
		transform: translateY(24px);
	}
}

.tt-consent:not([hidden]) {
	animation: tt-consent-arrives 0.32s var(--tt-ease-out) both;
}

@media (prefers-reduced-motion: reduce) {
	.tt-consent:not([hidden]) {
		animation: none;
	}
}

/*
 * THE ICON IS THE ONE THING HERE THAT IS READ BEFORE THE WORDS. A cookie
 * notice that opens with a heading gets skimmed as legal furniture; a small
 * marked object at the top of a card gets looked at.
 */
.tt-consent__mark {
	display: grid;
	place-items: center;
	width: 40px;
	height: 40px;
	margin: 0;
	border-radius: 13px;
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
	box-shadow: inset 0 0 0 1px rgb(8 104 215 / 10%);
}

.tt-consent__inner {
	width: min(100% - 2 * var(--tt-space-2), 1180px);
	margin-inline: auto;
	padding-block: var(--tt-space-3);
	display: grid;
	gap: 10px;

	/*
	 * The collapsed banner is four lines. Expanded, with every category and
	 * every service written out, it filled a 390px screen — which turns the
	 * thing that was deliberately not an overlay into one, at exactly the
	 * moment somebody is trying to read what they are agreeing to. It scrolls
	 * inside itself instead, and the page stays visible behind it.
	 */
	max-height: 76vh;
	overflow-y: auto;
	overscroll-behavior: contain;
}

.tt-consent__title {
	margin: 0;
	font-size: var(--tt-text-strong);
}

.tt-consent__text {
	margin: 0;
	max-width: 70ch;
	color: var(--tt-text-muted);
	font-size: 0.875rem;
}

.tt-consent__detail summary {
	display: inline-flex;
	align-items: center;
	min-height: var(--tt-tap);
	color: var(--tt-blue);
	font-size: 0.875rem;
	font-weight: 600;
	cursor: pointer;
}

.tt-consent__list {
	list-style: none;
	margin: 0 0 var(--tt-space-2);
	padding: 0;
	display: grid;
	gap: var(--tt-space-2);
}

.tt-consent__item-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--tt-space-1);
	margin: 0;
	font-size: 0.9375rem;
}

.tt-consent__item-head label {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: var(--tt-tap);
	cursor: pointer;
}

.tt-consent__always {
	padding: 2px 8px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-gray-soft);
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
}

.tt-consent__item-text {
	margin: 0;
	max-width: 70ch;
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

.tt-consent__services {
	margin: 6px 0 0;
	padding-left: 1.1em;
	color: var(--tt-text-muted);
	font-size: 0.8125rem;
}

/*
 * TWO IDENTICAL BUTTONS, AND THIS IS THE RULE THAT MUST NOT BE "IMPROVED".
 *
 * Accept and reject share one class and differ in nothing but their words. The
 * Greek supervisory authority has fined for drawing refusal quieter than
 * acceptance — a lighter weight, a smaller box, a link instead of a button —
 * and identical is the only version of "equally easy" that leaves no room for
 * an argument. If a later pass makes one of these the primary colour, it has
 * turned a consent mechanism into a dark pattern.
 */
.tt-consent__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
}

/*
 * BOTH SOLID, AND THAT IS THE POINT RATHER THAN A COMPROMISE.
 *
 * The pair used to be two white outlined boxes, which is equal and also looks
 * like two cancel buttons — the visitor has to read both to find out that one
 * of them is the ordinary answer. Filling both keeps them exactly as equal as
 * they were and makes them read as decisions instead of as an escape.
 *
 * Navy rather than the brand blue, so neither can be mistaken for the site's
 * primary action, which is «Επικοινώνησε μαζί μας» in the header and is blue.
 */
.tt-consent__button {
	flex: 1 1 150px;
	min-height: var(--tt-tap);
	padding: 0 var(--tt-space-3);
	border: 1px solid var(--tt-navy);
	border-radius: var(--tt-radius-button);
	background: var(--tt-navy);
	color: var(--tt-white);
	font-size: 0.9375rem;
	font-weight: 600;
	cursor: pointer;
	box-shadow: var(--tt-elevation-1);
	transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.tt-consent__button:hover {
	background: #0b1e38;
	border-color: #0b1e38;
}

.tt-consent__button:active {
	transform: translateY(1px);
	transition-duration: 0s;
}

.tt-consent__save {
	min-height: var(--tt-tap);
	padding: 0 var(--tt-space-2);
	border: 1px solid var(--tt-border-control);
	border-radius: var(--tt-radius-button);
	background: var(--tt-white);
	color: var(--tt-navy);
	font-size: 0.875rem;
	cursor: pointer;
}

.tt-consent__policy {
	margin: 0;
	font-size: 0.8125rem;
}

/*
 * FROM 768px IT LEAVES THE EDGE. Anchored bottom-left rather than centred: the
 * bottom-right corner is where the chat launcher and the booking bar live, and
 * a card that lands on top of them is a card the visitor has to dismiss before
 * they can do the thing they came for.
 *
 * 440px is about sixty characters of the body text, so the paragraph breaks
 * where a paragraph should rather than running the width of a laptop.
 */
@media (min-width: 768px) {
	.tt-consent {
		inset: auto auto var(--tt-space-3) var(--tt-space-3);
		width: min(440px, calc(100vw - 2 * var(--tt-space-3)));
		border-bottom: 1px solid var(--tt-border);
		border-radius: 18px;
	}

	.tt-consent__inner {
		width: 100%;
		padding-inline: var(--tt-space-3);
	}

	.tt-consent__actions {
		justify-content: stretch;
	}
}

/*
 * The panel sits on the bottom edge, which is where the chat launcher and the
 * booking bar already are. It is the newest and the most urgent of the three,
 * so the other two step up out of its way while it is open — `:has` on the
 * document, because the banner is a sibling of neither.
 */
body:has( .tt-consent:not([hidden]) ) .tt-chat-dock,
body:has( .tt-consent:not([hidden]) ) .tt-sticky-cta {
	/*
	 * `display: none` here and `pointer-events` on the form rule above, and the
	 * difference is deliberate. A form is something the visitor is working on
	 * and the launcher only has to stop stealing taps; the banner is a question
	 * they have to answer, and a chat button that is still tabbable behind it
	 * offers a way to load the third party the banner is asking about.
	 */
	display: none;
}

/* The map, before it has permission ---------------------------------------- */

.tt-office__map[data-tt-consent-state="parked"] {
	display: grid;
	place-items: center;
}

.tt-office__map-ask {
	display: grid;
	justify-items: center;
	gap: var(--tt-space-2);
	padding: var(--tt-space-3);
	text-align: center;
}

.tt-office__map-text {
	margin: 0;
	max-width: 40ch;
	color: var(--tt-text-muted);
	font-size: 0.875rem;
}

/* Once it has loaded there is nothing left to ask. */
.tt-office__map[data-tt-consent-state="loaded"] .tt-office__map-ask {
	display: none;
}

/*
 * The footer's withdrawal control. A <button> that has to look like the legal
 * links beside it, because it belongs to that row — the browser's own button
 * styling would make it the loudest thing in the footer.
 */
.tt-footer__consent {
	display: inline-block;
	padding: 8px 0;
	border: 0;
	background: none;
	color: var(--tt-on-navy);
	font: inherit;
	cursor: pointer;
}

.tt-footer__consent:hover,
.tt-footer__consent:focus-visible {
	color: var(--tt-white);
	text-decoration: underline;
}

@media (pointer: coarse) {
	.tt-footer__consent {
		display: inline-flex;
		align-items: center;
		min-height: var(--tt-tap);
	}
}

/* Stepped forms ------------------------------------------------------------
 *
 * The grouping is server-rendered `<fieldset>`s; navigation.js folds them. With
 * no script the rules below still apply and the result is a long form with
 * headings on it, which is the fallback this is designed around rather than an
 * accident of it.
 */
.tt-step {
	margin: 0;
	padding: 0;
	border: 0;
}

.tt-step__legend {
	padding: 0;
	margin-bottom: var(--tt-space-2);
	color: var(--tt-navy);
	font-size: var(--tt-text-lead);
	font-weight: 700;
}

/*
 * The legend is a focus target — see the script — so it needs a ring, and one
 * that does not look like a mistake around a heading.
 */
.tt-step__legend:focus-visible {
	outline: 3px solid var(--tt-focus);
	outline-offset: 4px;
	border-radius: 4px;
}

/* Without the script every step is visible, so they need separating. */
.tt-step + .tt-step {
	margin-top: var(--tt-space-4);
}

.tt-form__steps:has( [data-tt-step][hidden] ) .tt-step + .tt-step {
	margin-top: 0;
}

.tt-steps__progress {
	display: grid;
	gap: 6px;
	margin-bottom: var(--tt-space-3);
}

.tt-steps__count {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	font-weight: 600;
}

.tt-steps__bar {
	height: 4px;
	border-radius: var(--tt-radius-pill);
	background: var(--tt-border);
	overflow: hidden;
}

/*
 * IT MEASURES SOMETHING, WHICH IS THE WHOLE JUSTIFICATION FOR IT MOVING.
 * CLAUDE.md forbids decorative animation; a bar that grows from a third to two
 * thirds is showing the visitor how much of the form is left, and the movement
 * is what connects the width they just had to the width they have now. A jump
 * would be the same information with the connection removed.
 */
.tt-steps__fill {
	display: block;
	height: 100%;
	width: 0;
	border-radius: inherit;
	background: var(--tt-blue);
	transition: width 0.24s ease;
}

.tt-steps__nav {
	display: flex;
	flex-wrap: wrap;
	gap: var(--tt-space-1);
	margin-top: var(--tt-space-3);
}

.tt-steps__nav button[hidden] {
	display: none;
}

/*
 * DIRECTION IS THE INFORMATION. A step arriving from the right means «new», one
 * arriving from the left means «you have been here before» — which is the one
 * thing a cross-fade cannot say, and the reason «Πίσω» does not feel like a
 * fourth question. 180ms, transform and opacity only, so it never touches
 * layout.
 */
@keyframes tt-step-forward {
	from { opacity: 0; transform: translateX(16px); }
	to   { opacity: 1; transform: none; }
}

@keyframes tt-step-back {
	from { opacity: 0; transform: translateX(-16px); }
	to   { opacity: 1; transform: none; }
}

[data-tt-step-enter="forward"] {
	animation: tt-step-forward 0.18s ease-out;
}

[data-tt-step-enter="back"] {
	animation: tt-step-back 0.18s ease-out;
}

/*
 * The site-wide reduced-motion block zeroes `animation-duration` on everything,
 * but the progress bar is a `transition` on a width and needs saying
 * separately — and a width transition is exactly the kind a vestibular
 * disorder reacts to.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-steps__fill {
		transition: none;
	}

	[data-tt-step-enter] {
		animation: none;
	}
}

/* ==========================================================================
 * POLISH LAYER
 *
 * Everything from here down was added in one pass, after the client looked at
 * the finished site and said it read as poor and did not take anybody
 * anywhere. They were right, and the diagnosis is worth writing down because it
 * was not a matter of taste:
 *
 * The site was drawn entirely in outline. Every card, every panel, every list
 * row was a 1px #dce4ec rectangle on white, and the only shadow token in the
 * file was 4% of navy at one pixel — invisible in practice. With nothing near
 * and nothing far, a page has no depth for the eye to sort, so a visitor reads
 * it strictly top to bottom like a printed form. That is why a page about
 * islands felt like a tax return.
 *
 * Second: nothing had an icon. «Στόλος περίπου 15 οχημάτων» is a fact about
 * fifteen coaches rendered as fifteen characters of body text at 15px. The
 * number was in there, but nothing made it a number.
 *
 * Third: nothing moved, so nothing acknowledged the visitor. CLAUDE.md forbids
 * decorative animation and that rule stays — nothing below moves on a timer or
 * loops. What was missing is the other kind: motion that answers an action or
 * marks an arrival. Every animation here is tied to something the visitor did
 * (a hover, a press, a scroll that brought a section into view), it runs once,
 * and it is off entirely under `prefers-reduced-motion`.
 * ========================================================================== */

/* Icons -------------------------------------------------------------------- */

/*
 * `currentcolor` is set on the element in inc/icons.php, so an icon is the
 * colour of the sentence it sits in and needs no per-surface override. The only
 * thing CSS has to fix is the baseline: an inline SVG sits ON the baseline
 * while text sits above it, which drops every icon a few pixels below the words
 * beside it.
 */
.tt-icon {
	display: inline-block;
	flex: none;
	vertical-align: -0.15em;
}

/*
 * THE TINTED BADGE, WHICH IS THE WHOLE REASON THE ICON SET EXISTS.
 *
 * A stroke icon dropped into body copy is a small grey drawing. Put it in a
 * tinted square and it becomes an object with weight, which is what lets a
 * three-item row read as three things rather than three paragraphs.
 *
 * Squircle rather than circle: circles read as avatars and this site has no
 * people in it. The 1px inset ring is doing real work — on the soft-blue
 * section background the badge fill and the section are close enough in tone
 * that the badge would otherwise dissolve.
 */
.tt-icon-badge {
	display: grid;
	place-items: center;
	flex: none;
	width: 44px;
	height: 44px;
	border-radius: 14px;
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
	box-shadow: inset 0 0 0 1px rgb(8 104 215 / 10%);
}

.tt-icon-badge--sm {
	width: 34px;
	height: 34px;
	border-radius: 11px;
}

.tt-icon-badge--lg {
	width: 56px;
	height: 56px;
	border-radius: 18px;
}

/*
 * On navy the soft-blue fill has nothing to sit on, so the badge inverts:
 * a translucent white wash instead of a tint. Written as a descendant of the
 * dark surfaces rather than a modifier class, so a badge dropped into the
 * footer by somebody later is correct without them knowing this rule exists.
 */
.tt-footer .tt-icon-badge,
.tt-banner .tt-icon-badge,
.tt-hero .tt-icon-badge {
	background: rgb(255 255 255 / 12%);
	color: var(--tt-white);
	box-shadow: inset 0 0 0 1px rgb(255 255 255 / 16%);
}

/* Arriving on screen ------------------------------------------------------- */

/*
 * REVEAL ON SCROLL, WITH THE FAILURE MODE DESIGNED FIRST.
 *
 * The obvious way to write this — `opacity: 0` in the stylesheet, a class added
 * by script — has one catastrophic state: if the script does not run, the page
 * is blank. Not degraded, blank. That is a real outcome on a flaky mobile
 * connection, and it is not worth an entrance animation.
 *
 * So the hidden state is scoped to `.tt-motion`, a class the script puts on
 * <html> as its first act. No script, no class, no hiding: every section is
 * simply visible, which is exactly the page this site was before today.
 *
 * `--tt-reveal-delay` staggers a group. 60ms per item is enough to read as a
 * sequence and short enough that the fourth card is not still arriving after
 * the visitor has started reading the first.
 */
/*
 * `translate`, NOT `transform`, AND THIS IS NOT A STYLE PREFERENCE.
 *
 * The first version moved these with `transform: translateY(16px)`, and it
 * silently disabled the press feedback on everything it touched. This site
 * acknowledges a press with `transform: translateY(1px)` — on buttons, chips,
 * pagination, tour cards and destination tiles — and every one of those rules
 * is one class plus a pseudo-class, 0-2-0. `.tt-motion [data-tt-reveal]
 * .is-revealed` is 0-3-0 and sets `transform: none`, so it beat all of them:
 * the tile a visitor pressed on a phone did nothing at all, which is exactly
 * the defect the press states were written to fix. Caught by the existing «a
 * destination tile acknowledges being pressed» test, which is the only reason
 * it did not ship.
 *
 * `translate` is a separate, independent property. The two COMPOSE — the
 * element is translated, then transformed — so the reveal and the press have
 * nothing left to argue about, at any specificity.
 *
 * It also disposes of the containing-block problem rather than working around
 * it. `translate: none` is the resting value and creates no containing block,
 * so a `position: fixed` descendant — the tour page's sticky booking bar sits
 * inside a revealed section — anchors to the viewport as it should.
 */
.tt-motion [data-tt-reveal] {
	opacity: 0;
	translate: 0 16px;
}

.tt-motion [data-tt-reveal].is-revealed {
	opacity: 1;
	translate: none;
	transition:
		opacity var(--tt-duration-slow) var(--tt-ease-out),
		translate var(--tt-duration-slow) var(--tt-ease-out);
	transition-delay: var(--tt-reveal-delay, 0ms);
}

/*
 * Nothing is left transitioning once it has arrived, so a later change to one
 * of these elements — a hover, a class a script toggles — does not inherit a
 * 520ms ease it was never meant to have.
 */
.tt-motion [data-tt-reveal].is-revealed.is-settled {
	transition: none;
}

/* Hover, press, lift ------------------------------------------------------- */

/*
 * ONE LIFT, USED EVERYWHERE, SO A CARD FEELS THE SAME WHATEVER IT CONTAINS.
 *
 * `will-change` is deliberately absent: it promotes a layer permanently, and a
 * page with twenty promoted cards costs more memory than the animation saves.
 * A 4px translate and a shadow swap are cheap enough without it.
 *
 * The hover is gated on `hover: hover` — on a touch screen `:hover` sticks
 * after a tap, so without the gate a card a visitor tapped stays lifted behind
 * them for the rest of the session.
 */
.tt-lift {
	transition:
		transform var(--tt-duration-base) var(--tt-ease-out),
		box-shadow var(--tt-duration-base) var(--tt-ease-out),
		border-color var(--tt-duration-base) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-lift:hover {
		transform: translateY(-4px);
		box-shadow: var(--tt-elevation-3);
		border-color: rgb(8 104 215 / 28%);
	}
}

/*
 * The keyboard gets the same lift as the mouse. `:focus-within` rather than
 * `:focus-visible` because on the tour and destination cards the focusable
 * element is the link INSIDE the card, not the card.
 */
.tt-lift:focus-within {
	box-shadow: var(--tt-elevation-3);
	border-color: rgb(8 104 215 / 28%);
}

/*
 * PRESS IS ALREADY SOLVED IN THIS FILE, AND THIS BLOCK USED TO BREAK IT.
 *
 * The first draft of the polish layer gave every button a `scale(0.97)` press
 * and a 140ms transition. Both were wrong, and the section at «Everything
 * pressable answers the finger» further up says why in detail:
 *
 * - The press language on this site is `translateY(1px)` — one pixel down, on
 *   buttons, chips, pagination, cards and tiles alike. A scale on the buttons
 *   only would have been a second vocabulary for the same gesture.
 * - Those rules win purely on source order, being the same specificity as the
 *   `:hover` rules they beat. Anything appended after them and matching the
 *   same elements silently takes the press away, and only on a phone.
 * - The 0.15s resting transition is a contract, not a number: entering
 *   `:active` reads 0s so the acknowledgement lands in the same frame as the
 *   finger, and leaving it eases back over 0.15s. Retuning it to 140ms breaks
 *   the pair for nothing.
 *
 * So the polish layer adds no transform and no transition to any control that
 * already has one. What it adds is depth, which nothing else was claiming.
 *
 * A coloured cast rather than a grey one: a solid blue button casting a grey
 * shadow looks like a sticker; one casting its own colour looks like an object.
 * `box-shadow` only, so the press rules above stay untouched.
 */
.tt-button--primary {
	box-shadow: 0 1px 2px rgb(6 76 156 / 24%), 0 6px 16px rgb(8 104 215 / 18%);
}

@media (hover: hover) {
	.tt-button--primary:hover {
		box-shadow: 0 2px 4px rgb(6 76 156 / 26%), 0 10px 24px rgb(8 104 215 / 26%);
	}
}

/*
 * An arrow that moves on hover is the cheapest possible «this goes somewhere»,
 * and unlike an underline it does not compete with the label for attention.
 * Scoped to icons that are actually arrows so it cannot catch a bus.
 */
.tt-button .tt-icon,
.tt-section__link .tt-icon {
	transition: transform var(--tt-duration-base) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-button:hover .tt-icon--forward,
	.tt-section__link:hover .tt-icon--forward {
		transform: translateX(3px);
	}
}

/* Section headers ---------------------------------------------------------- */

/*
 * THE HEADINGS WERE THE SAME SIZE AS EVERYTHING ELSE, so no section announced
 * itself. `.tt-section__head h2` inherits the global h2 clamp, which tops out
 * at 28px — on a 1440px screen that is a heading barely larger than a card
 * title, floating over 1,180px of width.
 *
 * The eyebrow above it is not decoration: it is the one line that says what
 * KIND of thing follows, which is the question a visitor scanning a long page
 * is actually asking. Uppercase Latin only — Greek in caps loses its accents,
 * so the eyebrow is letter-spaced at its natural case instead.
 */
.tt-kicker {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0 0 10px;
	color: var(--tt-blue-dark);
	font-size: var(--tt-text-small);
	font-weight: 700;
	letter-spacing: 0.02em;
}

.tt-kicker::before {
	content: "";
	width: 22px;
	height: 2px;
	border-radius: 2px;
	background: currentcolor;
	flex: none;
}

.tt-section--dark .tt-kicker,
.tt-hero .tt-kicker {
	color: var(--tt-blue-soft);
}

@media (min-width: 768px) {
	.tt-section__head h2 {
		font-size: clamp(1.75rem, 1.2rem + 1.6vw, 2.25rem);
	}
}

/*
 * EXCEPT INSIDE A PAGE THAT ALREADY HAS AN H1.
 *
 * The rule above tops out at 36px, which is right on the homepage where a
 * section head is the largest thing in its band. /daily-tours-athina/ reuses the
 * component under a 40px page title, so its «Μονοήμερες» heading rendered 4px
 * under the H1 — and 8px LARGER than the same-role heading on its three sibling
 * service pages, which are all 28px. Three pages built from one template do not
 * get to disagree about what a section heading is.
 */
.tt-service__tours .tt-section__head h2 {
	font-size: var(--tt-text-title);
}

/*
 * The head was a `space-between` row with `align-items: baseline`, which on a
 * wide screen strands the «Όλες οι εκδρομές» link 900px from the heading it
 * belongs to. Capping the text column keeps the pair readable as a pair.
 */
.tt-section__head > div:first-child {
	max-width: 62ch;
}

/* Stat band ---------------------------------------------------------------- */

/*
 * «Στόλος περίπου 15 οχημάτων» — the number was the point of the sentence and
 * it was set in the same 15px grey as the words around it. A fleet is a
 * quantity; quantities are read as figures, not as prose.
 *
 * `tabular-nums` so a row of counters does not jitter sideways while it counts,
 * which is the standard failure of every animated statistic on the web.
 */
.tt-stats {
	display: grid;
	gap: var(--tt-space-2);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * `align-content: start`, AND WITHOUT IT NOTHING IN THIS ROW LINED UP.
 *
 * The four cards are stretched to a common height by the outer grid, which is
 * right — a ragged row of fact cards looks like a fault. Each card is itself a
 * grid of auto rows, and a grid container's default `align-content` resolves to
 * `stretch`, so every one of those auto rows grew to absorb its share of the
 * leftover height. The share depends on how many rows the card happens to have:
 * «Έτος ίδρυσης» has three (icon, number, label) and «Αριθμός οχημάτων» has
 * four, so the same element sat at a different height in each card.
 *
 * Measured at 1440: the four numbers landed at 460, 430, 445 and 430, and the
 * four labels at 597, 521, 551 and 492. Four boxes of facts with no shared
 * baseline read as four unrelated things rather than as one comparison.
 *
 * `start` sizes the rows to their content and puts the leftover at the bottom
 * of the card, where it costs nothing: the icons align, the numbers align, the
 * labels align, and only the notes end at different depths — which is honest,
 * because they are different lengths.
 */
.tt-stat {
	position: relative;
	display: grid;
	align-content: start;
	gap: 6px;
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-1);
}

.tt-stat__icon {
	margin-bottom: var(--tt-space-1);
}

.tt-stat__value {
	display: flex;
	align-items: baseline;
	gap: 4px;
	color: var(--tt-navy);
	font-size: clamp(2rem, 1.4rem + 2.4vw, 2.75rem);
	font-weight: 700;
	line-height: 1;
	letter-spacing: -0.03em;
	font-variant-numeric: tabular-nums;
}

/*
 * THE HEDGE IS A QUALIFIER, NOT A SECOND HEADLINE.
 *
 * «περίπου» is set bold, brand blue and one step below a 44px number, which put
 * it at roughly the weight of the figure it is qualifying — «περίπου 15» read as
 * two competing words, and the blue made the first one look like a link in the
 * middle of a statistic.
 *
 * THE WORD ITSELF STAYS, AND THAT IS DELIBERATE. The client's own document says
 * «περίπου 15 οχήματα»; `Fleet::approximate()` exists so the hedge survives
 * rather than being rounded into a precise claim nobody made. It is demoted
 * here, never dropped.
 *
 * Muted and at body size it reads as what it is: a small word attached to a big
 * number. `baseline` alignment on the parent already keeps the two sitting on
 * the same line, so the pairing is not weakened by the change of size.
 */
.tt-stat__unit {
	font-size: var(--tt-text-body);
	font-weight: 600;
	color: var(--tt-text-muted);
	letter-spacing: 0;
}

.tt-stat__label {
	margin: 0;
	color: var(--tt-navy);
	font-size: var(--tt-text-body);
	font-weight: 700;
}

.tt-stat__note {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
	line-height: 1.5;
}

/* Feature cards ------------------------------------------------------------ */

/*
 * The «Γιατί το γραφείο μας» pattern: a promise, a sentence explaining it, and
 * an icon that lets the three be told apart before any of them is read.
 */
.tt-features {
	display: grid;
	gap: var(--tt-space-2);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
	margin: 0;
	padding: 0;
	list-style: none;
}

.tt-feature {
	display: grid;
	gap: 12px;
	align-content: start;
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-1);
}

.tt-feature__title {
	margin: 0;
	color: var(--tt-navy);
	font-size: var(--tt-text-strong);
	font-weight: 700;
	line-height: 1.35;
}

.tt-feature__text {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
	line-height: 1.6;
}

/* Numbered flow ------------------------------------------------------------ */

/*
 * «Πώς δουλεύει» was three outlined boxes in a row, which says «three things»
 * and does not say «in this order» — the one fact the section exists to
 * communicate. A process is a line with stops on it.
 *
 * The connector is drawn with a pseudo-element on the item rather than a border
 * on the list, so the last stop can simply not draw one. `:last-child` on a
 * border would still leave the line running past the final number.
 */
.tt-flow {
	display: grid;
	gap: var(--tt-space-3);
	margin: 0;
	padding: 0;
	list-style: none;
	counter-reset: tt-flow;
}

.tt-flow__item {
	position: relative;
	display: grid;
	grid-template-columns: 44px 1fr;
	gap: var(--tt-space-2);
	counter-increment: tt-flow;
}

.tt-flow__marker {
	position: relative;
	display: grid;
	place-items: center;
	width: 44px;
	height: 44px;
	border-radius: 50%;
	background: var(--tt-blue);
	color: var(--tt-white);
	font-size: var(--tt-text-body);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
	box-shadow: 0 0 0 4px var(--tt-white), 0 0 0 5px var(--tt-border);
}

.tt-flow__marker::after {
	content: counter(tt-flow);
}

/* The line between one stop and the next. */
/*
 * THE CONNECTOR HANGS OFF THE ITEM, BECAUSE THE ITEM IS THE GAP.
 *
 * It was `height: calc(100% + var(--tt-space-3))` on a pseudo-element inside
 * `.tt-flow__marker` — a 44px box — so «100%» was 44px and the line ran 68px
 * towards a day that starts several hundred pixels lower. It reached about a
 * sixth of the way and stopped, which is not a fade, it is a tick. The
 * expression reads as «the marker plus the gap»; the containing block was
 * simply the wrong box for it to be measured against.
 *
 * On the item it is `top` under the marker to `bottom` past the row gap, so it
 * always spans the real distance whatever a day's text costs. The gradient
 * still fades out, which is the intended look — it is now a fade of the whole
 * span rather than the top sixth of one.
 */
.tt-flow__item:not(:last-child)::before {
	content: "";
	position: absolute;
	top: 49px;
	left: 21px;
	bottom: calc(var(--tt-space-3) * -1);
	width: 2px;
	background: linear-gradient(to bottom, var(--tt-border), rgb(220 228 236 / 0%));
}

.tt-flow__body {
	padding-top: 8px;
}

.tt-flow__title {
	margin: 0 0 6px;
	color: var(--tt-navy);
	font-size: var(--tt-text-strong);
	font-weight: 700;
	line-height: 1.35;
}

.tt-flow__text {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
	line-height: 1.6;
}

/*
 * Horizontal from 768px, where three stops fit side by side and the line
 * becomes what it is on a map: the distance between them.
 */
@media (min-width: 768px) {
	.tt-flow {
		grid-auto-flow: column;
		grid-auto-columns: 1fr;
		gap: var(--tt-space-3);
	}

	.tt-flow__item {
		grid-template-columns: 1fr;
		gap: var(--tt-space-2);
	}

	/*
	 * Sideways, and on the item for the same reason: «100%» inside the marker
	 * was 44px there too, so the horizontal connector between two stops was a
	 * 68px stub rather than a line reaching the next one.
	 */
	.tt-flow__item:not(:last-child)::before {
		top: 21px;
		left: 49px;
		right: calc(var(--tt-space-3) * -1);
		bottom: auto;
		width: auto;
		height: 2px;
		background: linear-gradient(to right, var(--tt-border), rgb(220 228 236 / 0%));
	}

	.tt-flow__body {
		padding-top: 0;
	}
}

/* Chips with icons --------------------------------------------------------- */

/*
 * «Για τι αναλαμβάνουμε μετακινήσεις» was six full-width outlined rows, each
 * holding two or three words — six rectangles of mostly empty space stacked
 * down a 1,180px page. They are labels, so they should be the size of their
 * labels.
 */
.tt-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * THE THIRD `.tt-chip` RULE WAS HERE AND IT IS THE ONE THAT DID THE DAMAGE.
 *
 * It repeated the whole base — gap, tap height, padding, type size, fill,
 * border, shadow — because this section wanted a slightly roomier pill for the
 * service pages' labels, and repeating an unscoped selector is how it got one:
 * last rule in the file, so it won everywhere, on the catalogue's filter chips
 * as much as on the labels it was written for. Two consequences, both invisible
 * from here: the Chips section's «36px under a mouse» stopped being true, and
 * `.tt-chip--remove` had its fill and its borderless edge taken back off it.
 *
 * Its values are now the base rule in the Chips section, unchanged, so every
 * chip on the site is drawn exactly as it was — by one rule instead of three.
 * What stays here is what is genuinely about THIS section: the icon colour, the
 * hover response that only links may have, and the 44px floor that only
 * controls may have.
 */
.tt-chip .tt-icon {
	color: var(--tt-blue);
}

/*
 * `a.tt-chip:hover`, NOT `.tt-chip:hover` — TEN OF THESE ARE NOT LINKS.
 *
 * `.tt-service__cases` on the coach-hire and school pages uses the same pill to
 * LABEL a case («Σχολικές εκδρομές», «Γάμοι»): six on one page, four on the
 * other, all inert `<span>`s. They inherited the catalogue chip's whole
 * clickable costume — 44px of tap height and a border-and-shadow response to
 * the pointer — so a visitor pressed them and nothing happened, ten times.
 *
 * A hover response is a promise. It belongs to the element that can keep it.
 */
@media (hover: hover) {
	a.tt-chip:hover {
		border-color: rgb(8 104 215 / 30%);
		box-shadow: var(--tt-elevation-2);
	}
}

/*
 * And the 44px floor goes with it. `--tt-tap` exists so a finger can hit a
 * control; a label is not a control, and forcing ten of them to 44px tall built
 * a row of things that look pressable out of things that are not.
 */
.tt-chip:not( a ):not( button ) {
	min-height: 0;
}

/* Ticks and crosses -------------------------------------------------------- */

/*
 * «Περιλαμβάνονται» and «Δεν περιλαμβάνονται» were two bulleted lists with
 * identical bullets, side by side, distinguished only by their headings. A
 * visitor scanning for what they get has to read the heading, hold it, and
 * scan — every time their eye moves between the columns.
 *
 * The cross is not red. Nothing in «Δεν περιλαμβάνονται» went wrong; those are
 * ordinary facts about the price, and colouring them as errors would be the
 * page shouting at its own content.
 */
.tt-marklist {
	display: grid;
	gap: 12px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * AND SO IS THIS ONE. `.tt-marklist` is «Τι περιλαμβάνει» and «Τι δεν
 * περιλαμβάνει» — the block that answers what the money buys, which is the most
 * consequential prose on a tour page and was set at 14px on a phone.
 */
.tt-marklist__item {
	display: grid;
	grid-template-columns: 22px 1fr;
	gap: 12px;
	align-items: start;
	font-size: var(--tt-text-body);
	line-height: 1.6;
}

.tt-marklist__mark {
	display: grid;
	place-items: center;
	width: 22px;
	height: 22px;
	margin-top: 1px;
	border-radius: 50%;
}

.tt-marklist__mark--yes {
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
}

.tt-marklist__mark--no {
	background: var(--tt-gray-soft);
	color: var(--tt-text-muted);
	box-shadow: inset 0 0 0 1px var(--tt-border);
}

/* Scrollers ---------------------------------------------------------------- */

/*
 * THE SCROLLBAR HAD TO GO, AND THE SCROLLING HAD TO STAY.
 *
 * A permanent grey bar under a row of testimonials is desktop-browser
 * furniture sitting inside the design. Hiding it is safe here and nowhere else
 * on the page, because these tracks are the one case where the scrollbar is not
 * the only affordance: the cards are cut off mid-slice at the fold, there are
 * arrow buttons, the track is a focusable region with arrow-key support, and on
 * touch — where no scrollbar is drawn anyway — it is a swipe.
 *
 * Both properties are needed. `scrollbar-width` covers Firefox and every
 * current engine; the WebKit pseudo-element covers Safari and older Chrome.
 */
.tt-carousel__track,
.tt-destinations {
	scrollbar-width: none;
}

.tt-carousel__track::-webkit-scrollbar,
.tt-destinations::-webkit-scrollbar {
	display: none;
	width: 0;
	height: 0;
}

/*
 * A FADE AT THE EDGE, WHICH IS THE AFFORDANCE THE SCROLLBAR USED TO BE.
 *
 * `mask-image` rather than a gradient overlay: an overlay is an element that
 * covers the cards, so it eats the pointer on the very card it is telling you
 * about, and it has to be painted in the section's background colour — which
 * means it breaks on any section whose background differs.
 *
 * The two custom properties are toggled by the script as the track reaches each
 * end, so the fade is only ever drawn on the side that has more to show. A
 * permanent fade on both sides would dim a card at the start of a list nobody
 * has scrolled.
 */
.tt-carousel {
	--tt-fade-start: 0px;
	--tt-fade-end: 0px;
}

.tt-carousel__track {
	mask-image: linear-gradient(
		to right,
		transparent 0,
		#000 var(--tt-fade-start),
		#000 calc(100% - var(--tt-fade-end)),
		transparent 100%
	);
	transition: mask-image var(--tt-duration-base) linear;
}

.tt-carousel[data-tt-overflow="start"],
.tt-carousel[data-tt-overflow="both"] {
	--tt-fade-start: 48px;
}

.tt-carousel[data-tt-overflow="end"],
.tt-carousel[data-tt-overflow="both"] {
	--tt-fade-end: 48px;
}

/*
 * The buttons move to sit beside the heading rather than under the track,
 * where they were a stray pair of circles in the whitespace between two
 * sections.
 */
.tt-carousel__button {
	box-shadow: var(--tt-elevation-1);
}

@media (hover: hover) {
	.tt-carousel__button:hover:not([disabled]) {
		box-shadow: var(--tt-elevation-2);
		background: var(--tt-blue);
		border-color: var(--tt-blue);
		color: var(--tt-white);
	}
}

/* Reduced motion ----------------------------------------------------------- */

/*
 * THE ONE BLOCK THAT HAS TO BE RIGHT. Everything above is an enhancement;
 * this is the promise that none of it is imposed on somebody whose vestibular
 * system reacts to it.
 *
 * Reveals resolve to visible rather than to «no transition» — a `[data-tt-reveal]`
 * left at `opacity: 0` because its transition was cancelled is a blank section.
 * The script also skips the observer entirely when this query matches, so this
 * is a second line of defence, not the only one.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-motion [data-tt-reveal],
	.tt-motion [data-tt-reveal].is-revealed {
		opacity: 1;
		translate: none;
		transition: none;
	}

	.tt-lift,
	.tt-carousel__track {
		transition: none;
	}

	.tt-lift:hover {
		transform: none;
	}

	.tt-button:hover .tt-icon--forward,
	.tt-section__link:hover .tt-icon--forward {
		transform: none;
	}
}

/* Reviews, redrawn -------------------------------------------------------- */

/*
 * The controls slot in the section head. It is empty in the markup and stays
 * empty when no script arrives, so it must collapse to nothing rather than
 * reserve a gap beside the heading.
 */
.tt-section__controls:empty {
	display: none;
}

.tt-section__controls .tt-carousel__nav {
	margin-top: 0;
}

/*
 * THE ATTRIBUTION IS NOW THE PROOF, so it is built like a control rather than
 * printed like a footnote. It replaces two things that used to sit apart: the
 * grey «4,5 από 5» line under the heading, and a ghost button on the far right
 * of the row saying «Δες τες στο Google». One target, one sentence, one fact.
 */
.tt-google {
	display: inline-flex;
	align-items: center;
	gap: 10px;
	min-height: var(--tt-tap);
	margin-top: 14px;
	padding: 8px 16px 8px 10px;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-pill);
	background: var(--tt-white);
	color: var(--tt-navy);
	font-size: var(--tt-text-small);
	text-decoration: none;
	box-shadow: var(--tt-elevation-1);
	transition:
		box-shadow var(--tt-duration-base) var(--tt-ease-out),
		border-color var(--tt-duration-base) var(--tt-ease-out);
}

.tt-google__mark {
	display: grid;
	place-items: center;
	width: 28px;
	height: 28px;
	border-radius: 50%;
	background: var(--tt-white);
	box-shadow: inset 0 0 0 1px var(--tt-border);
}

.tt-google__text strong {
	font-variant-numeric: tabular-nums;
}

.tt-google .tt-icon {
	color: var(--tt-blue);
}

@media (hover: hover) {
	.tt-google:hover {
		border-color: rgb(8 104 215 / 34%);
		box-shadow: var(--tt-elevation-2);
		color: var(--tt-navy);
	}

	.tt-google:hover .tt-icon--forward {
		transform: translateX(3px);
	}
}

/*
 * THE CARD. It was a 1px rectangle holding four blue stars and a paragraph, and
 * five of them in a row read as five paragraphs.
 *
 * The large quotation mark that used to sit in the top-right corner has gone —
 * see the note in reviews.php. What it was for is now done by the stars and the
 * customer's own headline, and the corner it occupied belongs to the date.
 *
 * 20px of padding rather than 24. On a 357px card that is 8px more room per
 * line, which is most of a word in Greek, and it matches the reference site the
 * client asked this block to be measured against.
 */
/*
 * THE CARDS SIT UP OFF THE PAGE INSTEAD OF BEING DRAWN ON IT.
 *
 * `--tt-elevation-1` is the hairline step: 1px of contact shadow and 3px of
 * cast, which on a white section is very nearly nothing. It is the right step
 * for a box that is only being separated from its background. These cards are
 * not that — they are the one place on the homepage a stranger's own words are
 * printed, they carry the section on their own, and the client has asked more
 * than once for them to look like objects.
 *
 * Step 2 is the smallest one that actually reads: 6px of blur at the contact
 * edge and 16px of cast, still navy-tinted, still a pair rather than the single
 * blurred smudge. The hover then has somewhere to go — step 2 to the tilt's own
 * shadow below is a change the eye registers; step 1 to step 3 was a jump from
 * nothing to something, which reads as the card appearing rather than lifting.
 */
.tt-review {
	position: relative;
	padding: 20px;
	box-shadow: var(--tt-elevation-2);
}

/*
 * AND THEY TURN. This is the depth the client asked for, and it is only here.
 *
 * `perspective` on the item rather than the track: each card is its own vanishing
 * point, so the one under the cursor tilts toward the cursor instead of every
 * card sharing a single camera and the outer two shearing sideways. 900px is a
 * long lens — at 3° the far edge moves about 4px, which is a card leaning, not a
 * card flipping. Text stays square enough to read at every point of the turn.
 *
 * FOUR GATES, ALL OF THEM LOAD-BEARING:
 *
 * - `hover: hover`, because `:hover` sticks after a tap and a phone would be
 *   left with one card permanently tilted behind the visitor's thumb.
 * - `prefers-reduced-motion`, because a rotating plane is exactly the motion
 *   that setting exists to stop.
 * - `min-width: 1024px`, because below it the cards are a scroll-snap carousel
 *   and the coverflow block at the end of this file is already turning the
 *   `<li>`. Two rules rotating the same box in two axes is the bug that killed
 *   the press feedback once already.
 * - The selector is `.tt-review`, the figure — never the `<li>`. Same reason.
 *
 * This REPLACES `.tt-lift:hover`'s plain translate for these cards rather than
 * adding to it: a second `transform` on the same element wins outright and would
 * silently drop the lift, so the lift is written back into the same declaration.
 */
@media (min-width: 1024px) and (hover: hover) and (prefers-reduced-motion: no-preference) {
	.tt-reviews__item {
		perspective: 900px;
	}

	.tt-reviews__item > .tt-review:hover {
		transform: translateY(-6px) rotateX(3deg) scale(1.012);
		box-shadow: var(--tt-elevation-4);
	}
}

/*
 * Kept as a stacking context for the card's own children. Nothing paints behind
 * them any more, but `position: relative` on a flex item is also what keeps a
 * focus ring on the «Διαβάστε όλη την κριτική» button above its neighbours.
 */
.tt-review__quote,
.tt-review__meta,
.tt-review__author,
.tt-review__more {
	position: relative;
}

/*
 * 15px AND A TIGHTER LEADING, WHICH IS THE OTHER HALF OF THE HEIGHT.
 *
 * A clamp caps the number of lines; this caps what a line costs. At 16px/1.65
 * one line of this quote was 26.4px, so four lines were 106px of a card meant
 * to finish near 300. At 15px/1.55 the same four lines are 93px and the text is
 * still above the 14px the reference site sets its reviews in.
 *
 * IT STAYS AT 15px THOUGH THE REFERENCE IS 14. That was the one measurement from
 * the screenshot deliberately not copied: 14px is below the 16px floor this
 * project holds for body text on a phone, and on a phone these cards are 84% of
 * the viewport rather than a quarter of a desktop row. One pixel of fidelity is
 * not worth the smallest text on the page getting smaller.
 */
.tt-review__quote p {
	color: var(--tt-text);
	font-size: 0.9375rem;
	line-height: 1.55;
}

/*
 * AND 16px ON A PHONE, WHICH IS THE HALF THE NOTE ABOVE TALKED ITSELF OUT OF.
 *
 * It argues 15 against the reference's 14 and then says, in the same paragraph,
 * that «14px is below the 16px floor this project holds for body text on a
 * phone». 15 is below it too. The trade was card height, and on a phone there
 * is no row to keep even: one card is 84% of the viewport, the clamp caps the
 * quote at lines rather than at pixels, so the whole cost is at most one line
 * box on a card the visitor is already scrolling.
 *
 * Desktop keeps 15px, where the cards ARE a row of four and the argument holds.
 */
@media (max-width: 767px) {
	.tt-review__quote p {
		font-size: var(--tt-text-body);
	}

	.tt-review__author {
		font-size: var(--tt-text-small);
	}
}

/*
 * THE QUOTATION MARK ABOVE THE HEADLINE.
 *
 * Measured on the reference: a 40 × 40 solid glyph at the padding corner, with
 * the headline starting 8px under it. A «“» is not 40px tall at 40px of
 * font-size — the glyph sits in the top third of its em box and the rest is
 * air — so the box is given an explicit height and the character is set large
 * enough to fill it, with `line-height: 1` so nothing below it moves.
 *
 * Half-opacity blue rather than a lighter blue token: this has to read as
 * punctuation set loudly, not as a second piece of text competing with the
 * headline 8px under it. It is `aria-hidden` in the markup, so contrast rules
 * do not apply to it — but sitting above a 700-weight navy line, anything
 * darker than this simply looked like the card had two headlines.
 */
.tt-review__mark {
	display: block;
	height: 30px;
	margin-bottom: 6px;
	overflow: hidden;
	color: var(--tt-blue);
	font-size: 56px;
	font-weight: 700;
	line-height: 1;
	opacity: 0.45;
}

/*
 * The attribution row: a letter, a name, and where they wrote it.
 *
 * `margin-top: auto` is what pins it to the bottom of a stretched card. It used
 * to be the quote's `flex: 1 1 auto` that did that, and a clamped quote no
 * longer grows — so without this the caption floated up under a four-line
 * review and the cards in a row stopped ending on the same line.
 */
.tt-review__author {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-top: auto;
	padding-top: var(--tt-space-2);
	border-top: 1px solid var(--tt-border);
	color: var(--tt-text-muted);
	font-size: var(--tt-text-small);
	font-weight: 400;
}

/*
 * `.tt-review__initial` was here — a 38px circle holding the first letter of the
 * author's name. It went with the redesign against the client's screenshot: the
 * reference's attribution is a bold name over a hairline rule and nothing else,
 * and the circle was 15% of a 262px card spent restating a letter printed in
 * full beside it. The rule it sat next to is `border-top` on the caption below,
 * which is what actually separates the quotation from the person.
 */

/*
 * Two rows — the name, and «πού γράφτηκε · τι ταξίδι». The date lives in the
 * metadata row under the headline, which is what let this caption shrink to two
 * tight lines.
 */
.tt-review__who {
	display: grid;
	gap: 2px;
	min-width: 0;
}

.tt-review__name {
	color: var(--tt-navy);
	font-weight: 700;
	line-height: 1.25;
}

/*
 * AMBER, AND THE SIZE OF SOMETHING MEANT TO BE SEEN. At 16px in --tt-border
 * grey the empty stars were invisible and the filled ones were blue text.
 */
.tt-rating__star {
	color: rgb(180 106 0 / 22%);
	font-size: 1.0625rem;
}

.tt-rating__star.is-filled {
	color: var(--tt-star-fill);

	/*
	 * A hairline of the darker amber around the fill. --tt-star-fill on white is
	 * 2.1:1 — fine for a decorative shape that is also written out in words for
	 * assistive technology, not fine as the only edge the shape has. The stroke
	 * is what makes it read as a drawn star rather than a pale smudge, and it
	 * carries the contrast SC 1.4.11 asks of a meaningful graphic.
	 */
	-webkit-text-stroke: 0.6px var(--tt-star);
}

/*
 * The section head is a `space-between` row, so on a phone the controls slot
 * would sit in the same line as a heading that already fills it. Below 768 the
 * buttons drop under the heading and align right, where a thumb reaches them.
 */
@media (max-width: 767px) {
	.tt-section__controls {
		width: 100%;
	}
}

/* Photographs that carry text --------------------------------------------- */

/*
 * SIX PLACES ON THIS SITE PUT WORDS ON A PHOTOGRAPH, and each of them was
 * solving the legibility problem separately or not at all. A caption over an
 * unknown image is the classic accessibility failure: it passes on the picture
 * the designer tested and fails on the one the office uploads next week.
 *
 * The scrim is the fix and it is a pseudo-element rather than a `background-image`
 * on the media box, because the box holds an `<img>` and a gradient behind it
 * would be behind the photograph.
 *
 * A THREE-STOP GRADIENT, NOT TWO. A linear ramp from transparent to black is
 * perceptually front-loaded — it looks like a hard edge two thirds of the way
 * up. The middle stop at 40% of the opacity around halfway is what makes it
 * read as light falling off.
 */
.tt-media__scrim {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background: linear-gradient(
		to top,
		rgb(9 20 36 / 82%) 0%,
		rgb(9 20 36 / 34%) 38%,
		rgb(9 20 36 / 0%) 72%
	);
}

/*
 * A label sitting ON the photograph rather than under it. `backdrop-filter` is
 * progressive: where it is unsupported the 88% white behind it is already
 * opaque enough to read against anything.
 */
.tt-media__badge {
	position: absolute;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 5px 11px;
	border-radius: var(--tt-radius-pill);
	background: rgb(255 255 255 / 88%);
	color: var(--tt-navy);
	font-size: var(--tt-text-micro);
	font-weight: 700;
	line-height: 1.4;
	backdrop-filter: blur(6px);
	box-shadow: 0 1px 3px rgb(9 20 36 / 18%);
}

.tt-media__badge--start {
	inset: 10px auto auto 10px;
}

.tt-media__badge--end {
	inset: 10px 10px auto auto;
}

.tt-media__badge--bottom {
	inset: auto auto 10px 10px;
}

/* The inverse, for a mark that should read as the site speaking rather than as a note. */
.tt-media__badge--solid {
	background: rgb(18 43 77 / 88%);
	color: var(--tt-white);
	box-shadow: 0 1px 3px rgb(9 20 36 / 30%);
}

/*
 * THE ONE BADGE THAT HAD TO STOP LOOKING PRESSABLE.
 *
 * The tour gallery marks its last tile with how many photographs are not on the
 * page, and it was drawn `--solid` with a drop shadow: a filled pill floating on
 * the corner of the last photograph, which on every travel site in the world is
 * the button that opens all of them. There is no lightbox on this site and there
 * is not going to be one — see the header of `template-parts/tours/gallery.php`
 * — so the pill was promising something nothing could deliver.
 *
 * Three declarations, each undoing one signal. The shadow is what made it float
 * ABOVE the picture rather than sit on it. 600 rather than 700 puts it at the
 * weight of a caption instead of a label the site is asserting.
 * `pointer-events: none` is the one that is not cosmetic: the press now reaches
 * the photograph underneath, so a visitor who aims at it gets the same nothing
 * they would get from aiming anywhere else in the grid, rather than a control
 * swallowing their click.
 */
.tt-media__badge--count {
	max-width: calc(100% - 20px);
	box-shadow: none;
	font-weight: 600;
	pointer-events: none;
}

/* Dark sections ----------------------------------------------------------- */

/*
 * A NAVY BAND IS PUNCTUATION, and this site had exactly one — the coach banner
 * — sitting on a homepage of eight white and pale-blue sections that ran
 * together into a single scroll.
 *
 * Everything inside inherits its colour rather than being overridden per
 * element, which is the same reasoning as the focus-ring token: a component
 * dropped in here later is correct without its author knowing this rule exists.
 */
.tt-section--dark {
	background: var(--tt-navy);
	color: var(--tt-on-navy);
}

.tt-section--dark h2,
.tt-section--dark h3,
.tt-section--dark .tt-stat__value,
.tt-section--dark .tt-stat__label,
.tt-section--dark .tt-feature__title,
.tt-section--dark .tt-flow__title {
	color: var(--tt-white);
}

.tt-section--dark .tt-section__head p:not([class]),
.tt-section--dark .tt-lede,
.tt-section--dark .tt-stat__note,
.tt-section--dark .tt-feature__text,
.tt-section--dark .tt-flow__text {
	color: var(--tt-on-navy);
}

/*
 * A card on navy is a lighter navy, not a white box. White cards on a dark band
 * invert the figure and ground — the band stops being a surface and becomes a
 * gap between cards.
 */
.tt-section--dark .tt-stat,
.tt-section--dark .tt-feature {
	background: rgb(255 255 255 / 6%);
	border-color: rgb(255 255 255 / 14%);
	box-shadow: none;
}

.tt-section--dark .tt-icon-badge {
	background: rgb(255 255 255 / 12%);
	color: var(--tt-white);
	box-shadow: inset 0 0 0 1px rgb(255 255 255 / 16%);
}

.tt-section--dark .tt-stat__unit {
	color: var(--tt-blue-soft);
}

/*
 * The flow marker on navy: brand blue on navy is 2.69:1 and the number inside
 * it would be unreadable, so it inverts to white-on-navy exactly as the focus
 * ring does on this surface.
 */
.tt-section--dark .tt-flow__marker {
	background: var(--tt-white);
	color: var(--tt-navy);
	box-shadow: 0 0 0 4px var(--tt-navy), 0 0 0 5px rgb(255 255 255 / 24%);
}

.tt-section--dark .tt-flow__item:not(:last-child)::before {
	background: linear-gradient(to bottom, rgb(255 255 255 / 28%), rgb(255 255 255 / 0%));
}

@media (min-width: 768px) {
	.tt-section--dark .tt-flow__item:not(:last-child)::before {
		background: linear-gradient(to right, rgb(255 255 255 / 28%), rgb(255 255 255 / 0%));
	}
}

/* Running text ------------------------------------------------------------ */

/*
 * `the_content()` OUTPUT HAD NO RULES AT ALL, which is why the about page reads
 * as a text file: paragraphs at the body default, no measure, headings at the
 * global sizes with the global margins, and nothing distinguishing the opening
 * from the rest.
 *
 * The measure is the whole game. The page was setting it at 1,180.
 *
 * 56ch AND NOT THE 68 THAT WAS HERE. See the long note beside `.tt-columns`
 * above: `ch` is the width of a zero and Greek is narrower than that — 1ch is
 * 9.77px here against a mean Greek lowercase glyph of 8.80px, about 11% out —
 * so 56ch reproduces roughly 62 Greek characters per line and 68ch reproduced
 * 80. This is now the only place the measure is set.
 */
.tt-prose {
	max-width: 56ch;
}

.tt-prose > p {
	margin: 0 0 var(--tt-space-2);
	line-height: 1.75;
}

/*
 * THE FIRST PARAGRAPH IS THE STANDFIRST, and it is chosen by position rather
 * than by asking the office to mark it up. Every page's opening paragraph is
 * its summary — that is what an opening paragraph is — and a page whose author
 * did not think about it still gets a sensible one.
 */
.tt-prose > p:first-of-type {
	color: var(--tt-navy);
	font-size: var(--tt-text-lead);
	line-height: 1.6;
}

/*
 * EXCEPT INSIDE AN ITINERARY DAY, WHERE «FIRST PARAGRAPH» MEANS SOMETHING ELSE.
 *
 * Each day of a tour renders its own `.tt-prose`, so the rule above promoted the
 * first paragraph of EVERY day to 21px standfirst — under a `.tt-flow__title`
 * set at 18px. The heading of each day was smaller than the sentence beneath it,
 * on both tours and at both widths, which inverts the one hierarchy an itinerary
 * has.
 *
 * A page has one summary; a day inside it does not. Scoped rather than made
 * opt-in, because the standfirst is deliberately chosen by position and asking
 * the office to mark it up is the thing that note refuses to do.
 */
.tt-flow__body .tt-prose > p:first-of-type {
	color: var(--tt-text);
	font-size: var(--tt-text-body);
	line-height: 1.75;
}

/*
 * AND EXCEPT ON A PAGE THAT ALREADY HAS A STANDFIRST WRITTEN FOR IT.
 *
 * The rule above chooses the summary by position, which is right for a page
 * whose author did not think about it. The six service pages have thought about
 * it: the header band carries `.tt-page-header__lede`, an office-written summary
 * of the page, and the body then opened with a SECOND paragraph at a larger size
 * than the first.
 *
 * On /epikoinonia/ the two say different kinds of thing, which is what makes it
 * visible rather than merely redundant: the lede is «Πες μας πού θέλεις να πας…»
 * and the paragraph promoted under it is «Αν το μήνυμα αφορά συγκεκριμένη
 * εκδρομή, γράψε το όνομά της…» — instructions, set at 21px against the summary's
 * 17px, so the smaller of the two was the one that summarised.
 *
 * `:has()` rather than a class, so nothing has to be remembered: a page that
 * loses its lede gets its standfirst back automatically, on the same markup.
 */
.tt-page-header:has( .tt-page-header__lede ) + .tt-service__body > p:first-of-type {
	color: var(--tt-text);
	font-size: var(--tt-text-body);
	line-height: 1.75;
}

/*
 * NO SIZE OF ITS OWN, SO THE LEVEL KEEPS ITS HIERARCHY ON A PHONE.
 *
 * `--tt-text-title` is a fixed 28px. The global `h1` and `h2` are fluid clamps
 * that converge as the screen narrows, and at 390px the page title lands at
 * 30.16px — so the four legal pages rendered every `h2` at 28px under a 30px
 * `h1`, a 2px difference doing the work of a heading level across 8,729
 * characters of policy nobody wants to read twice.
 *
 * The margins stay; they are what gives a long document its rhythm. Only the
 * size goes, and the global clamp takes over — 22px at 390 against the h1's 30.
 */
.tt-prose > h2 {
	margin: var(--tt-space-5) 0 var(--tt-space-2);
}

.tt-prose > h3 {
	margin: var(--tt-space-4) 0 var(--tt-space-1);
}

.tt-prose > ul,
.tt-prose > ol {
	margin: 0 0 var(--tt-space-2);
	padding-left: 1.25em;
	line-height: 1.75;
}

.tt-prose > ul li + li,
.tt-prose > ol li + li {
	margin-top: 6px;
}

/*
 * A pull quote in the middle of a long page, which is the one typographic
 * device that gives a wall of text somewhere to rest.
 */
.tt-prose > blockquote {
	margin: var(--tt-space-4) 0;
	padding: 0 0 0 var(--tt-space-3);
	border-left: 3px solid var(--tt-blue);
	color: var(--tt-navy);
	font-size: var(--tt-text-lead);
	line-height: 1.55;
}

/* The «or just ask us» panel ------------------------------------------------
 *
 * `.tt-service__ask` appears in four page templates and in no stylesheet, so
 * the phone number under every enquiry form has been an unstyled heading and a
 * loose button since the templates were written. It is the fallback for a
 * visitor who does not want to fill anything in, which makes it the most
 * important thing on the page for the people it is for.
 */
.tt-service__ask {
	display: grid;
	gap: var(--tt-space-2);
	align-items: center;

	/* 48px, with every other top-level join in this template. */
	margin-top: var(--tt-space-5);
	padding: var(--tt-space-3);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-gray-soft);
}

/*
 * ONE PANEL LESS ON A SMALL PHONE, BECAUSE THREE WERE NESTED.
 *
 * `.tt-container` pads 16, this panel pads 24, and `.tt-form-section` inside it
 * pads 32 — so at 390px the actual input column was 242px, 62% of the screen,
 * with 72px of chrome down each side. Three concentric grey boxes are not a
 * hierarchy, they are the same statement made three times.
 *
 * The inner panel already provides the figure and the ground this outer one was
 * drawn for, so below 640px the outer one gives up its own. Above that there is
 * room for both and the nesting reads.
 */
@media (max-width: 639px) {
	.tt-service__ask {
		padding: 0;
		border: 0;
		background: none;
	}
}

/*
 * DIRECT CHILDREN ONLY, BECAUSE THIS RULE WAS REACHING INTO THE FORM.
 *
 * It was `.tt-service__ask h2, .tt-service__ask h3` — a descendant selector on a
 * panel that CONTAINS the enquiry form. So `h2.tt-form-section__heading`, which
 * has its own 21px rule two thousand lines earlier, was being pulled down to
 * 18px, and /epikoinonia/ shipped three sibling H2s at 18, 18 and 21px. That is
 * the exact defect the note beside the form heading says it fixed, reintroduced
 * from below by a later selector.
 *
 * `>` keeps the panel's own heading at 18px and takes the rule off everything
 * the panel merely contains. `.tt-channels-block__heading` is a grandchild and
 * already carries its own size.
 */
.tt-service__ask > h2,
.tt-service__ask > h3 {
	margin: 0;
	font-size: var(--tt-text-strong);
}

/*
 * ONE COLUMN, AND THE SECOND ONE WAS 91% EMPTY GREY.
 *
 * `1fr auto` was drawn for a heading-and-button pair beside a form. Measured at
 * 1440 the left column is 806px of which 91% is empty: the «ή ρώτησέ μας»
 * heading and one telephone button are 70px tall against a nine-field form, so
 * the panel rendered as a tall grey rectangle with a phone number stranded at
 * the top of it. On the school page the notice under it was stranded at the
 * bottom of the same emptiness.
 *
 * Stacked, the heading and the button sit above the form — which is also the
 * order somebody reads them in: «or just call us» is an alternative to filling
 * the form in, so it belongs before it, not beside it.
 */
@media (min-width: 768px) {
	.tt-service__ask {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* Panels with an accent --------------------------------------------------- */

/*
 * For a section that is one offer rather than a list of things — the
 * membership block, the closing call to action. The rule down the leading edge
 * is the cheapest way to say «this is a single statement» without wrapping it
 * in yet another bordered card.
 */
.tt-panel--accent {
	position: relative;
	overflow: hidden;
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-2);
}

.tt-panel--accent::before {
	content: "";
	position: absolute;
	inset: 0 auto 0 0;
	width: 4px;
	background: var(--tt-blue);
}

/* The three sections the client pasted ------------------------------------- */

/*
 * «Ο στόλος», «Πώς δουλεύει», «Γιατί το γραφείο μας». These were sent back as
 * the examples of what reads poor, and the diagnosis is the same for all three:
 * each is a fact the agency owns, rendered as a heading and a paragraph of grey
 * text, three across.
 *
 * THE CLASS NAMES DO NOT CHANGE, and that is deliberate rather than lazy.
 * `page-sections.spec.js` asserts `ol.tt-steps__list`, three `.tt-steps__item`,
 * the numbers coming from the list rather than from the titles, and that a
 * half-filled row renders one item and not two. Those assertions are describing
 * real requirements — the office edits these rows in the admin and a numbering
 * scheme that comes from typed text goes wrong the first time somebody reorders
 * them. So the markup keeps its contract and this file changes what it looks
 * like, which is where the problem actually was.
 */

/* «Πώς δουλεύει» — three boxes become a route with stops on it. */
.tt-steps__item::before {
	width: 44px;
	height: 44px;
	background: var(--tt-blue);
	color: var(--tt-white);
	box-shadow: 0 0 0 4px var(--tt-white), 0 0 0 5px var(--tt-border);
	z-index: 1;
}

.tt-steps__item {
	padding-left: 60px;
}

/*
 * THE LINE BETWEEN THE STOPS IS THE INFORMATION. Three numbered boxes say
 * «three things»; they do not say «in this order», which is the single fact the
 * section exists to communicate. The last stop draws no line, so the sequence
 * ends rather than trailing off.
 *
 * It fades out rather than stopping square, so a step with a short body and one
 * with a long body do not produce two visibly different line lengths.
 */
.tt-steps__item:not(:last-child)::after {
	content: "";
	position: absolute;
	top: 44px;
	left: 22px;
	width: 2px;
	height: calc(100% - 44px + var(--tt-space-3));
	transform: translateX(-50%);
	background: linear-gradient(to bottom, var(--tt-border), rgb(220 228 236 / 0%));
}

@media (min-width: 768px) {
	.tt-steps__item:not(:last-child)::after {
		top: 22px;
		left: 44px;
		width: calc(100% - 44px + var(--tt-space-4));
		height: 2px;
		transform: translateY(-50%);
		background: linear-gradient(to right, var(--tt-border), rgb(220 228 236 / 0%));
	}
}

/*
 * THE ROUTE DRAWS ITSELF, STOP BY STOP.
 *
 * The client asked whether these numbers could have an animation — «να είναι
 * διαδραστικό στη σελίδα». The project's own rule is that an animation must have
 * a UX purpose, and this one does, which is why it is the line that moves rather
 * than the numbers.
 *
 * The section's single job is to say «in this order». Three numbered circles say
 * «three things»; the connector is the part that says «and then», and it was
 * being drawn as finished furniture before the reader had met the first stop.
 * Drawing it as they arrive is the sequence happening rather than the sequence
 * described. Nothing is revealed that a still page would not show, no content
 * waits on it, and nothing about the numbers themselves moves — a marker that
 * bounced would be the decoration this rule forbids.
 *
 * NOT ONE LINE OF NEW JAVASCRIPT. `data-tt-reveal` already puts `.is-revealed`
 * on each item as it enters the viewport and already staggers it by index in
 * `--tt-reveal-delay`, so the whole effect is this transition hanging off a
 * class the page was setting anyway. A second observer for the same elements
 * would be a second thing to keep working.
 *
 * The two surfaces get it for free and they mean different things, which is the
 * part worth writing down. On a phone the stops are a vertical rail and the
 * reveal fires as the reader actually reaches each one, so the line grows ahead
 * of them — genuinely responsive to where they are. From 768px all three are on
 * screen at once, no scroll position is being reported, and what the stagger
 * gives instead is the route being traced left to right: order, not progress.
 * One mechanism, and neither reading is a lie about the other.
 *
 * SCOPED TO `.tt-motion`, which is the same fence every reveal on this site
 * sits behind: no script, no class, no rule, and the connector is simply drawn.
 * The failure mode of the alternative is a site whose steps are permanently
 * disconnected.
 *
 * A HOVER OR FOCUS STATE ON THE STOPS WAS CONSIDERED AND REFUSED, and it is
 * written here so the idea does not come back as a fresh proposal. «Interactive»
 * has an obvious second reading — make the discs respond to a pointer — and it
 * fails twice. A `.tt-steps__item` is an `<h3>` and a `<p>`: there is nothing to
 * activate, so a hover tint would promise an action that does not exist.
 * `tabindex="0"` on the `<li>` to make it reachable would be worse — three dead
 * tab stops that announce two paragraphs as an operable control, on the one page
 * of a service where a keyboard user is trying to reach the enquiry form.
 *
 * A SCROLL TIMELINE (`animation-timeline: view()`) WAS ALSO REFUSED, for the
 * measurement two paragraphs down rather than for its browser support. A `view()`
 * timeline resolves an already-entered subject past the end of its entry range,
 * so a section that is on screen at load paints the finished state and never
 * draws — which is the same defect as the transition version and would land on
 * the same two pages.
 *
 * ⚠ THIS IS THE NINTH RULE THAT HARD-CODES THE 44px MARKER. `transform-origin`
 * anchors the line to the edge of the disc, so resizing the marker breaks where
 * the animation starts as well as the line's length and offset — the other eight
 * are the `top`, `left`, `width` and `height` declarations above and their 768px
 * twins. A marker size token is the honest fix and it is not this change.
 */
/*
 * A KEYFRAME AND NOT A TRANSITION, WHICH IS THE SAME LESSON THE HERO TAUGHT.
 *
 * A transition needs two computed values and a moment between them. Anything
 * above the fold is marked `is-revealed` and `is-settled` BEFORE `.tt-motion`
 * is added — that ordering is the reveal system's whole anti-flicker design —
 * so by the time these rules can match, the finished state is the only state
 * there has ever been and a transition has no distance to travel. Measured at
 * 1440x900: two of the three pages with steps start the section above the fold,
 * so two thirds of the desktop cases drew nothing at all.
 *
 * An animation does not care. It runs when its selector begins to match, which
 * happens for an above-fold section the instant `.tt-motion` lands and for one
 * below the fold when the observer reaches it. One rule, both cases, and the
 * same rule the homepage hero already uses for exactly this reason.
 *
 * `forwards`, so the line is left at full length under the animation's control
 * rather than snapping back to the `scale(0)` in the rule below it.
 */
.tt-motion .tt-steps__item:not(:last-child)::after {
	/*
	 * `transform` and not `scale`, and it is the opposite of the choice made for
	 * the reveals themselves — for the opposite reason. The reveal uses the
	 * independent `translate` property precisely so it cannot collide with the
	 * `transform: translateY(1px)` press feedback; nothing here is pressable,
	 * and this pseudo-element ALREADY carries a `transform` for its own
	 * centring. Keeping both facts in one property is what lets a keyframe state
	 * the whole box in one line.
	 */
	transform: translateX(-50%) scaleY(0);
	transform-origin: 50% 0;
}

.tt-motion .tt-steps__item.is-revealed:not(:last-child)::after {
	/*
	 * After its own stop has landed, not with it. The item's reveal runs for
	 * `--tt-duration-slow` from its stagger delay, so the line leaves once that
	 * is finished and the eye reads «stop, then line, then next stop» rather
	 * than four things arriving at once.
	 *
	 * `--tt-reveal-delay` is set by navigation.js for every target now, above
	 * the fold included — see the note there, which is the other half of this
	 * fix.
	 */
	animation: tt-step-line var(--tt-duration-slow) var(--tt-ease-out)
		calc(var(--tt-reveal-delay, 0ms) + var(--tt-duration-slow)) forwards;
}

@keyframes tt-step-line {
	from {
		transform: translateX(-50%) scaleY(0);
	}

	to {
		transform: translateX(-50%) scaleY(1);
	}
}

@media (min-width: 768px) {
	/*
	 * The line is horizontal here, so it grows along the other axis and from the
	 * marker rather than from the top. `transform-origin: 0 50%` is what makes it
	 * leave the circle it belongs to instead of arriving at it backwards.
	 *
	 * A second keyframe rather than a shared one with a variable in it: the
	 * translate axis changes too, and a `@keyframes` name resolves once per
	 * document rather than per matching rule, so redefining `tt-step-line`
	 * inside this query would silently replace the vertical one everywhere.
	 */
	.tt-motion .tt-steps__item:not(:last-child)::after {
		transform: translateY(-50%) scaleX(0);
		transform-origin: 0 50%;
	}

	.tt-motion .tt-steps__item.is-revealed:not(:last-child)::after {
		animation-name: tt-step-line-across;
	}
}

@keyframes tt-step-line-across {
	from {
		transform: translateY(-50%) scaleX(0);
	}

	to {
		transform: translateY(-50%) scaleX(1);
	}
}

/*
 * THE CONNECTOR HOLDS STILL FOR SOMEBODY WHO ASKED FOR LESS MOTION — AND THIS
 * BLOCK LIVES HERE, NOT UP WITH THE REVEALS, BECAUSE THERE IT DID NOTHING.
 *
 * The first version sat inside the reduced-motion block beside
 * `[data-tt-reveal]`, about 800 lines above this. Same selector, same
 * specificity (0-4-1), declared EARLIER — so the animation rule won on source
 * order and the carve-out was dead code that read as a safeguard. Measured with
 * the preference emulated mid-visit: `animationName` still `tt-step-line`.
 *
 * Nothing was visibly wrong, which is the part worth writing down. The blanket
 * rule at the top of this file forces `animation-duration: 0.01ms !important` on
 * everything, so the line snapped to its finished state instead of drawing and
 * no reader was ever going to notice. A safeguard that is doing nothing while a
 * second, unrelated one covers for it is the kind of thing that stops working
 * silently the day somebody scopes the blanket rule more narrowly.
 *
 * WHICH PATH REACHES IT: not a page loaded with the setting on — there the
 * reveal script returns before adding `.tt-motion` and none of these rules match
 * at all. This is for a visitor who turns the setting on DURING a visit, where
 * `.tt-motion` is already on the document. `motion-asked-for.spec.js` now walks
 * exactly that path, which is how the dead code was found.
 *
 * `transform` is restored rather than zeroed: `transform: none` would throw away
 * the `translateX(-50%)` that centres the line and shift every connector by a
 * pixel.
 */
@media (prefers-reduced-motion: reduce) {
	.tt-motion .tt-steps__item:not(:last-child)::after,
	.tt-motion .tt-steps__item.is-revealed:not(:last-child)::after {
		transform: translateX(-50%) scaleY(1);
		animation: none;
	}
}

@media (prefers-reduced-motion: reduce) and (min-width: 768px) {
	.tt-motion .tt-steps__item:not(:last-child)::after,
	.tt-motion .tt-steps__item.is-revealed:not(:last-child)::after {
		transform: translateY(-50%) scaleX(1);
	}
}

/*
 * From 768px the marker sits above its text rather than beside it, which is
 * what turns the row into a horizontal route. Below that it stays a left rail,
 * because three stacked stops with the number on top is just three headings.
 */
@media (min-width: 768px) {
	.tt-steps__item {
		padding-left: 0;
		padding-top: 60px;
	}
}

/* «Γιατί το γραφείο μας» — cards that are objects rather than outlines. */
.tt-points__item {
	box-shadow: var(--tt-elevation-1);
	transition:
		transform var(--tt-duration-base) var(--tt-ease-out),
		box-shadow var(--tt-duration-base) var(--tt-ease-out),
		border-color var(--tt-duration-base) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-points__item:hover {
		transform: translateY(-4px);
		box-shadow: var(--tt-elevation-3);
		border-color: rgb(8 104 215 / 28%);
	}
}

.tt-points__item:active {
	transform: translateY(1px);
	box-shadow: var(--tt-elevation-1);
	transition-duration: 0s;
}

/* Room for the badge the template puts above the title. */
.tt-points__item .tt-icon-badge {
	margin-bottom: 14px;
}

@media (prefers-reduced-motion: reduce) {
	.tt-points__item,
	.tt-points__item:hover {
		transform: none;
		transition: none;
	}
}

/*
 * «Ο στόλος» — the band stays, the contents stop being sentences.
 *
 * The one real change here is that a number inside this band is now set at
 * display size. «Στόλος περίπου 15 οχημάτων» was fifteen coaches rendered as
 * two characters inside a 16px sentence; whatever else the section does, the
 * quantity has to look like a quantity.
 */
.tt-fleet .tt-stats {
	gap: var(--tt-space-3);
}

.tt-fleet .tt-stat {
	border-color: rgb(8 104 215 / 14%);
	background: rgb(255 255 255 / 72%);
}

/*
 * THE CARD FILLS ITS CELL, AND ON THE SERVICE PAGES IT WAS NOT EVEN TRYING.
 *
 * The homepage puts `.tt-stat` straight into the grid, so it stretches to the
 * row and four cards come out 307px each. The service pages wrap each one in an
 * `<li class="tt-fleet__item">` for the list semantics — and the `li` is what
 * stretches. The card inside it is an ordinary block, as tall as its own words.
 *
 * Measured on /enoikiasi-poulman/ at 1440px: three cards in one band at 238px,
 * 259px and 280px, so each white panel stopped at a different place and the
 * band underneath showed through by up to 42px on the right-hand card. It reads
 * as three cards that failed to line up, which is exactly what it is.
 *
 * `display: grid` on the wrapper is the smallest fix that keeps the list: a
 * single-item grid stretches its child by default, which is the behaviour the
 * homepage gets for free by having no wrapper at all.
 */
.tt-fleet__item {
	display: grid;
}

/* The FAQ ------------------------------------------------------------------ */

/*
 * A disclosure whose only affordance was a small caret drawn with a
 * pseudo-element. It stays a native `<details>` — that is a documented
 * commitment and it is the right control — but the marker becomes a plate you
 * can see and aim at, and the open state gets a background so an answer is
 * visibly attached to its question rather than floating under it.
 */
.tt-faq__item {
	border-radius: var(--tt-radius-card);
	transition: background-color var(--tt-duration-base) var(--tt-ease-out);
}

.tt-faq__item[open] {
	background: var(--tt-gray-soft);
}

.tt-faq__question {
	border-radius: var(--tt-radius-card);
}

/*
 * The answer unrolls rather than appearing. `content-visibility` on a closed
 * `<details>` means the browser knows the block's size before it is shown,
 * which is what makes this animatable at all — the usual reason people reach
 * for a script here.
 */
@keyframes tt-faq-opens {
	from {
		opacity: 0;
		transform: translateY(-6px);
	}
}

.tt-faq__item[open] .tt-faq__answer {
	animation: tt-faq-opens var(--tt-duration-base) var(--tt-ease-out);
}

@media (prefers-reduced-motion: reduce) {
	.tt-faq__item[open] .tt-faq__answer {
		animation: none;
	}
}

/* Forms, finished ---------------------------------------------------------- */

/*
 * THE PANEL WAS A FLAT GREY RECTANGLE and the form inside it was a grid of
 * white boxes on grey. Grey-on-grey is the reason it read as an admin screen:
 * there is no figure and no ground, so nothing says where the form starts.
 *
 * White panel, real elevation, and the page behind it does the contrast. On a
 * page that is already soft-grey the panel keeps its border so it does not
 * dissolve.
 */
.tt-form-section {
	background: var(--tt-white);
	box-shadow: var(--tt-elevation-2);
}

/*
 * THE FIELD ITSELF NEEDS A RESTING STATE, NOT ONLY A FOCUS STATE. A white box
 * with a grey outline on a white panel is the weakest possible signal for the
 * single thing the visitor has to interact with. A hair of tint says «type
 * here» before anything is focused, and the box turns white as it takes focus,
 * which is the transition from «available» to «active».
 */
.tt-input,
.tt-select,
.tt-textarea {
	background: #fbfcfe;
	transition:
		border-color var(--tt-duration-fast) var(--tt-ease-out),
		box-shadow var(--tt-duration-fast) var(--tt-ease-out),
		background-color var(--tt-duration-fast) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-input:hover,
	.tt-select:hover,
	.tt-textarea:hover {
		border-color: var(--tt-navy);
	}
}

/*
 * A soft ring OUTSIDE the focus outline, not instead of it. The outline is the
 * accessibility requirement and is untouched — `:focus-visible` elsewhere in
 * this file sets it — and this is the halo that makes the focused field the
 * brightest thing on the panel. Drawn with `box-shadow` so it costs no layout.
 */
.tt-input:focus,
.tt-select:focus,
.tt-textarea:focus {
	background: var(--tt-white);
	box-shadow: 0 0 0 4px rgb(8 104 215 / 12%);
}

.tt-input[aria-invalid="true"]:focus,
.tt-select[aria-invalid="true"]:focus,
.tt-textarea[aria-invalid="true"]:focus {
	box-shadow: 0 0 0 4px rgb(179 38 30 / 12%);
}

/* The label was 13px, under a 16px field, doing the naming. */
.tt-field__label {
	font-size: var(--tt-text-small);
}

/*
 * THE ERROR HAS TO BE FOUND BEFORE IT IS READ. It was a red sentence in the
 * same column as five other sentences. The mark in front of it is what the eye
 * catches when it scans a failed form.
 */
.tt-field__error::before {
	content: "!";
	display: inline-grid;
	place-items: center;
	width: 16px;
	height: 16px;
	margin-right: 6px;
	border-radius: 50%;
	background: #b3261e;
	color: var(--tt-white);
	font-size: 11px;
	font-weight: 700;
	vertical-align: -0.2em;
}

/* The wizard --------------------------------------------------------------- */

/*
 * THE PROGRESS BAR WAS A 4px GREY LINE and the counter above it was 12px muted
 * text — the two things telling somebody how much of an eighteen-field form is
 * left, drawn as the two quietest elements on the panel.
 *
 * The bar is the same mechanism, measured the same way, at a size that can be
 * read from where the visitor's eye actually is: on the field they are filling.
 */
.tt-steps__progress {
	gap: 8px;
	margin-bottom: var(--tt-space-3);
	padding-bottom: var(--tt-space-3);
	border-bottom: 1px solid var(--tt-border);
}

.tt-steps__count {
	color: var(--tt-navy);
	font-size: var(--tt-text-small);
	font-weight: 700;
	font-variant-numeric: tabular-nums;
}

.tt-steps__bar {
	height: 6px;
	background: var(--tt-blue-soft);
	box-shadow: inset 0 0 0 1px rgb(8 104 215 / 10%);
}

/*
 * The fill is the brand blue with a lighter leading edge, so the bar has a
 * direction. Not a decorative gradient: it is the difference between a
 * measurement and a coloured rectangle, and it is why the eye follows the right
 * edge when it moves.
 */
.tt-steps__fill {
	background: linear-gradient(90deg, var(--tt-blue-dark), var(--tt-blue));
	transition: width 0.32s var(--tt-ease-out);
}

/*
 * «Συνέχεια» is the action; «Πίσω» is the escape. They were rendered as two
 * buttons of equal weight side by side, which makes a visitor choose between
 * them on every step. Back goes to the left of the row and quiet, forward to
 * the right and primary — the arrangement every form on the web uses, for the
 * reason that reading order is left to right.
 */
.tt-steps__nav {
	justify-content: space-between;
	padding-top: var(--tt-space-1);
}

.tt-steps__nav button:only-child {
	margin-left: auto;
}

.tt-step__legend {
	display: flex;
	align-items: center;
	gap: 10px;
}

@media (prefers-reduced-motion: reduce) {
	.tt-input,
	.tt-select,
	.tt-textarea {
		transition: none;
	}
}

/* The search bar's labels -------------------------------------------------- */

/*
 * The glyph sits with the label rather than inside the control — the reason is
 * written out in tour-search.php, and it is that the hero's three columns have
 * no horizontal room to give. Baseline alignment matters here more than
 * anywhere else on the site, because these three labels are read as a row.
 */
.tt-search__label {
	display: flex;
	align-items: center;
	gap: 6px;
}

.tt-search__label .tt-icon {
	color: var(--tt-blue);
	vertical-align: 0;
}

/* The empty result --------------------------------------------------------- */

/*
 * A DASHED BORDER MEANS «DROP A FILE HERE», WHICH IS NOT WHAT HAPPENED.
 *
 * The end of a search that found nothing was drawn as a dashed rectangle of
 * muted grey text — the exact visual convention the whole web uses for an upload
 * target or a placeholder still being edited. It is the one screen where a
 * visitor is already disappointed, and it looked like a hole in the page rather
 * than an answer to their question.
 *
 * `.tt-panel--accent` is the shape it should have been all along: one statement,
 * with a rule down its leading edge saying so. The dashed border is overridden
 * rather than removed at source, because `.tt-empty` is used in more than one
 * place and the border is inherited from the base rule.
 */
.tt-empty {
	border-style: solid;
	border-color: var(--tt-border);
	background: var(--tt-white);
	color: var(--tt-text);
	padding-inline: var(--tt-space-4);
}

.tt-empty__icon {
	display: grid;
	place-items: center;
	width: 68px;
	height: 68px;
	margin: 0 auto var(--tt-space-2);
	border-radius: 22px;
	background: var(--tt-blue-soft);
	color: var(--tt-blue-dark);
	box-shadow: inset 0 0 0 1px rgb(8 104 215 / 10%);
}

/*
 * The accent rule is drawn on the leading edge, and this box is centred text —
 * so the padding has to clear it or the first character sits on the rule.
 */
.tt-empty.tt-panel--accent {
	padding-left: calc(var(--tt-space-4) + 4px);
}

/* The header, now that it is set in the typeface it always named ----------- */

/*
 * SHIPPING INTER WRAPPED THE NAVIGATION, AND THE HEADER HAD NO SLACK TO ABSORB
 * IT.
 *
 * This is the cost of the font nobody had noticed was missing. Every width in
 * this bar was arrived at against whatever the developer's operating system
 * supplied — San Francisco on the Mac it was built on — and Inter sets Greek
 * wider. Measured at 1200px, the first width where the desktop navigation
 * appears at all:
 *
 *   brand 179 + nav 665 + cta 260 + three 32px gaps = 1200, into 1152 of
 *   content box. The bar wrapped to two rows and stood 94px tall instead of 69.
 *
 * The arrow added to the header's call to action made it worse and did not
 * cause it: with the arrow removed the row still overflowed by 42px. So the
 * fix is slack rather than removing the mark.
 *
 * Three small reductions rather than one large one, because the alternative —
 * raising the drawer breakpoint — would take the whole navigation away from
 * everybody on a 1280px laptop to solve a problem worth 48 pixels:
 *
 *   column-gap 32 -> 24   the bar's own three gaps, -24
 *   nav gap      24 -> 18   five gaps between six links, -30
 *   cta padding  24 -> 16   -16
 *
 *   179 + 635 + 244 + 72 = 1130 into 1152. 22px of slack at the tightest width.
 *
 * Scoped to the width where the row exists. Below 1200 the drawer takes over
 * and none of this applies.
 */
@media (min-width: 1200px) {
	.tt-header__inner {
		column-gap: var(--tt-space-3);
	}

	.tt-nav__list {
		gap: 18px;
	}

	/*
	 * THE HEADER'S BUTTON IS SMALLER THAN THE PAGE'S, on the client's
	 * instruction and for a reason the measurement backs up: at the body size it
	 * was 232px of a row that wraps at 1,200px, and it was the loudest thing in
	 * a bar whose job is to get out of the way. Shortening the label to
	 * «Επικοινωνία» and taking it to 40px tall leaves it at 146 x 40, measured
	 * at 1200, 1280 and 1440 — 86px of the row handed back, and the row is the
	 * thing that was running out.
	 *
	 * Still the one primary action, still the only filled button in the header,
	 * still 40px tall — above the 24px floor for a non-essential control, and
	 * beside a 44px WhatsApp target and a 44px menu button, so the row's real
	 * hit area is unchanged. Nothing here is the only way to reach anything: the
	 * same page is in the menu and in the footer.
	 */
	.tt-header__cta .tt-button {
		min-height: 40px;
		padding-block: 0;
		padding-inline: var(--tt-space-2);
		font-size: 0.875rem;
	}
}

/*
 * THE ASK PANEL WAS CENTRING A SHORT COLUMN AGAINST A TALL ONE. On /epikoinonia/
 * the left column is a heading and one telephone button and the right column is
 * a nine-field form, so `align-items: center` left the phone number floating in
 * the middle of 400px of nothing — which reads as a rendering fault rather than
 * as a layout.
 *
 * Both columns start at the top, which is where a reader's eye already is.
 */
.tt-service__ask {
	align-items: start;
}

/* Gaps the wiring pass ran into ------------------------------------------- */

/*
 * THE ITINERARY IS A FLOW THAT MUST NOT GO SIDEWAYS, and this was reported as
 * blocking for four of the twelve published tours.
 *
 * `.tt-flow` turns horizontal at 768px because the sections it was drawn for —
 * «Πώς δουλεύει», three stops of a dozen words each — read as a route when they
 * sit side by side. A tour's day-by-day programme is the same structure with
 * two hundred words per stop, and four days across a 1,180px page gives each
 * day 280px: a column of text eleven characters wide.
 *
 * A modifier rather than a second component, because everything else about it
 * is identical — the counter, the marker, the connector, the reveal. Only the
 * direction is wrong.
 */
.tt-flow--stacked {
	display: grid;
	grid-auto-flow: row;
	grid-auto-columns: auto;
}

@media (min-width: 768px) {
	.tt-flow--stacked {
		grid-auto-flow: row;
		grid-auto-columns: auto;
	}

	.tt-flow--stacked .tt-flow__item {
		grid-template-columns: 44px 1fr;
		gap: var(--tt-space-2);
	}

	.tt-flow--stacked .tt-flow__body {
		padding-top: 8px;
	}

	/*
	 * The connector goes back to running downwards between the stops, and the
	 * base rule above now does that on its own — same selector, same geometry.
	 * Only the horizontal variant needs undoing, which the block that turns it
	 * sideways already handles.
	 */
	.tt-flow--stacked .tt-flow__item:not(:last-child)::before {
		top: 49px;
		left: 21px;
		right: auto;
		bottom: calc(var(--tt-space-3) * -1);
		width: 2px;
		height: auto;
		background: linear-gradient(to bottom, var(--tt-border), rgb(220 228 236 / 0%));
	}
}

/*
 * AN APPLIED FILTER IS ALREADY BLUE, so an icon drawn in the brand blue inside
 * it is blue on blue. Measured by the wiring pass as the reason the filter chips
 * were left without icons at all: `.tt-chip .tt-icon { color: var(--tt-blue) }`
 * against `.tt-chip.is-current { background: var(--tt-blue) }` is 1:1.
 *
 * `currentcolor` rather than white, so the icon follows whatever the chip's own
 * text colour is — including the pressed state, which goes darker.
 */
.tt-chip.is-current .tt-icon {
	color: currentcolor;
}

/*
 * The pagination is `inline-flex`, which strips the collapsible whitespace
 * WordPress puts between the page links — so «1 2 Επόμενη» ran together as one
 * 44px-tall bar with no seams. The parent has a gap; each link needed its own
 * for the icon inside it.
 */
.tt-archive__results .page-numbers {
	gap: 6px;
}

/*
 * The breadcrumb separator is a «›» typed into a pseudo-element, which is a
 * character from whatever font is resolving and sits on the text baseline
 * rather than in the middle of the row. Now that the theme actually ships
 * Inter, it renders noticeably high. Nudged rather than replaced with an SVG:
 * a pseudo-element cannot hold one, and the crumb list is not worth a wrapper
 * per item.
 */
.tt-crumbs__item:not(:last-child)::after {
	display: inline-block;
	transform: translateY(-1px);
}

/*
 * THE NAVY BANNER IS A DARK SURFACE THAT IS NOT `.tt-section--dark`, so every
 * inheriting rule written for one had to be repeated for it. Kept as a
 * selector list beside the existing `.tt-footer`/`.tt-hero` group rather than
 * given `.tt-section--dark` in the markup, because the banner is a card inside
 * a light section and the class would repaint the whole section.
 */
.tt-banner .tt-kicker {
	color: var(--tt-blue-soft);
}

.tt-banner .tt-stat {
	background: rgb(255 255 255 / 6%);
	border-color: rgb(255 255 255 / 14%);
	box-shadow: none;
}

.tt-banner .tt-stat__value,
.tt-banner .tt-stat__label {
	color: var(--tt-white);
}

.tt-banner .tt-stat__note {
	color: var(--tt-on-navy);
}

.tt-banner .tt-stat__unit {
	color: var(--tt-blue-soft);
}

/*
 * A badge on the banner's photograph needs something to be positioned against,
 * and `.tt-banner__media` had no containing block of its own — unlike
 * `.tt-media`, which has had one since the tour cards were written.
 */
.tt-banner__media {
	position: relative;
	overflow: hidden;
}

/*
 * A BADGE ABOVE A HEADING HAD NOWHERE TO SIT. Every heading in this file
 * carries `margin: 0 0 var(--tt-space-2)` and no top margin, which is right
 * when the heading starts its box and wrong the moment something is stacked
 * above it — the icon and the words ended up 0px apart.
 */
.tt-icon-badge + h2,
.tt-icon-badge + h3,
.tt-feature .tt-icon-badge,
.tt-stat .tt-icon-badge {
	margin-top: 0;
}

.tt-feature > .tt-icon-badge,
.tt-stat > .tt-icon-badge {
	margin-bottom: 4px;
}

/* The footer's contact rows ------------------------------------------------ */

/*
 * These lines are `display: block`, so an icon dropped in front of the text
 * became an inline box on the text baseline with a 3.7px gap — measured by the
 * wiring pass in Chromium. A grid with a fixed first column is what keeps the
 * second and third lines of a wrapped address aligned under the first rather
 * than tucked under the icon.
 */
.tt-footer__office-line,
.tt-footer__hours span {
	display: grid;
	grid-template-columns: 18px 1fr;
	gap: 8px;
	align-items: start;
}

.tt-footer__office-line .tt-icon,
.tt-footer__hours span .tt-icon {
	margin-top: 2px;
	color: var(--tt-blue-soft);
}

/*
 * THE ICON FLOATED 12-17px ABOVE THE WORDS IT LABELS, ON EVERY PHONE.
 *
 * `align-items: start` is right for the address, whose second and third lines
 * must align under the first rather than tuck under the pin. But the tap-target
 * block gives every footer LINK `min-height: 44px` under a coarse pointer, so
 * the telephone and email rows became 44px cells with a 16px icon pinned to the
 * top of them and the text centred in the middle: the pin sat well above its
 * own number, on the one surface where somebody is reaching for it with a
 * thumb.
 *
 * Only the rows whose content is a single line get centred, and only where the
 * 44px floor applies. The address keeps `start` because it still wraps.
 */
@media (pointer: coarse) {
	.tt-footer__office-line:has( > a ),
	.tt-footer__hours span {
		align-items: center;
	}

	.tt-footer__office-line:has( > a ) .tt-icon,
	.tt-footer__hours span .tt-icon {
		margin-top: 0;
	}
}

/*
 * The registry line is the company's legal identity — ΓΕΜΗ and ΑΦΜ — and it was
 * one muted sentence at the very bottom, which is where it belongs and not how
 * it should look. Small bordered badges say «these are numbers on a register»
 * rather than «this is more small print».
 */
.tt-footer__registry {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	align-items: center;
}

.tt-footer__registry span {
	padding: 3px 10px;
	border: 1px solid rgb(255 255 255 / 18%);
	border-radius: var(--tt-radius-pill);
	font-size: var(--tt-text-micro);
	font-variant-numeric: tabular-nums;
}

/* Blocks the wiring pass introduced --------------------------------------- */

/*
 * The child-data promise on the school page. It is a trust statement, not a
 * warning, so it takes the site's own blue rather than the amber every notice
 * component reaches for — and it is enforced by the form schema, which carries
 * no field for a child's name, date of birth or address.
 */
.tt-notice--trust {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: var(--tt-space-2);
	align-items: start;
	border-color: rgb(8 104 215 / 22%);
	background: var(--tt-blue-soft);
	color: var(--tt-text);
}

.tt-office__block {
	display: grid;
	gap: var(--tt-space-2);
}

/*
 * Address, telephone, email and hours as labelled rows. The label is the quiet
 * half and the value is the half somebody came for, which is the opposite of
 * how a definition list renders by default.
 */
.tt-office__details {
	display: grid;
	gap: var(--tt-space-2);
	margin: 0;
}

.tt-office__details .tt-icon-badge {
	margin-bottom: 0;
}

.tt-faq__heading,
.tt-steps__heading,
.tt-points__heading {
	font-size: var(--tt-text-title);
	margin-bottom: var(--tt-space-3);
}

/*
 * `.tt-faq__label` IS THE QUESTION, NOT A CAPTION ABOVE IT.
 *
 * This rule was first written from the class name alone — a small muted label,
 * which is what «label» usually means in this stylesheet — and it shrank every
 * FAQ question to 12px grey text above a 16px answer. A question set smaller
 * than its own answer inverts the hierarchy of the one component whose whole
 * job is to be scanned.
 *
 * It is a `<span>` wrapping the whole sentence, put there so the summary can be
 * a flex row of «the question» and «the marker» rather than a line of text with
 * a chevron drifting at the end of it. So it inherits, and its only job is to
 * take the space the marker does not.
 */
.tt-faq__label {
	flex: 1 1 auto;
	min-width: 0;
	font-size: var(--tt-text-body);
	font-weight: 700;
	color: inherit;
}

/*
 * `.tt-home-membership` has been in the markup since Milestone 3 and in no
 * stylesheet. It is one offer in one box, which is what `.tt-panel--accent` is
 * for; all this adds is the room the box needs.
 */
.tt-home-membership {
	padding: var(--tt-space-4);
}

/*
 * THE CLIENT'S ARTWORK BESIDE THE OFFER, AND WHOLE.
 *
 * «Πρέπει να υπάρχει ολόκληρο σαν γραφικό στο section γίνε τουρίστας.» Whole is
 * load-bearing rather than decorative: the graphic ends in the wordmark and the
 * line «Γίνε τουρίστας!», so anything that crops it removes the words. There is
 * no `object-fit: cover` here and the surface it is served from is the only
 * uncropped one in media.php.
 *
 * `--art` and not a bare child selector, because the panel has to keep working
 * with no image at all. The modifier is only printed when there is one, so the
 * one-column rules below are the default and the two-column layout is the
 * exception — a panel whose grid assumed a second child would collapse to a
 * half-width column of text the day the office cleared the field.
 *
 * The artwork comes SECOND in the markup and stays second on a phone. It is the
 * heading's own words drawn, so a screen reader and a narrow screen both meet
 * the offer before the picture of it.
 */
/*
 * SOFT BLUE, AND NAVY WAS TRIED FIRST AND WAS WRONG.
 *
 * «Πρέπει να είναι πιο γαμάτο το section» got a navy panel, and the client's
 * next message was that the artwork does not read against it. They were right,
 * and the reason is in the artwork rather than in the colour: it is drawn FOR a
 * white ground. «TOURISTAS» is navy lettering with a thin light outline and the
 * «Γίνε τουρίστας!» script is brand blue — on `--tt-navy` the wordmark is navy
 * on navy and the script disappears entirely. Composited both ways and compared
 * side by side before changing it.
 *
 * `--tt-blue-soft` keeps the section distinct from the white cards above it —
 * which is what «πιο γαμάτο» was asking for — while giving the illustration the
 * light ground it was drawn on. The white ring around its circle reads as a ring
 * again instead of dissolving.
 *
 * NO GRADIENT. The brief rules them out as a visual language, and a flat tint is
 * what makes the cut-out edge look intentional.
 */
.tt-home-membership--art {
	border-color: var(--tt-blue-soft);
	background: var(--tt-blue-soft);
}

.tt-home-membership__art {
	margin-top: var(--tt-space-3);
}

/*
 * `width` and no `object-fit`: the artwork is drawn, it ends in a wordmark, and
 * every crop in this stylesheet would take the word off. See the `artwork`
 * surface in media.php, the only uncropped one.
 */
.tt-home-membership__art img {
	display: block;
	width: min(78vw, 320px);
	height: auto;
	margin-inline: auto;
}

@media (min-width: 768px) {
	.tt-home-membership {
		padding: var(--tt-space-5);
	}

	/*
	 * `auto` for the artwork rather than a fraction, so the column is exactly as
	 * wide as the image is allowed to be and the text takes everything left. A
	 * `1fr 1fr` split would give a square graphic half a 1,180px panel, which is
	 * a 550px illustration next to four lines of copy.
	 */
	.tt-home-membership--art {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: center;
		gap: var(--tt-space-5);
	}

	/*
	 * A size up from the page's other `h2`, because this section is one thing
	 * rather than a list and has a 380px illustration beside it. At the shared
	 * heading size the words looked like a caption for the picture.
	 */
	.tt-home-membership--art h2 {
		font-size: clamp(1.75rem, 2.4vw, 2.125rem);
	}

	.tt-home-membership--art .tt-lede {
		max-width: 46ch;
	}

	.tt-home-membership--art .tt-home-membership__art {
		margin-top: 0;
	}

	.tt-home-membership--art .tt-home-membership__art img {
		width: 320px;
	}
}

@media (min-width: 1024px) {
	.tt-home-membership--art .tt-home-membership__art img {
		width: 380px;
	}
}

/* The FAQ, given something to aim at -------------------------------------- */

/*
 * THE CARET WAS TEN PIXELS OF BORDER, and it was the only affordance on a row
 * whose whole job is «this opens». At the far right of a 1,180px page it is a
 * mark most people never notice, which is why the FAQ read as three sentences
 * with hairlines between them rather than as three questions.
 *
 * The plate is a real 34px target with the caret inside it, and it is the thing
 * that changes when the row opens — filled blue instead of outlined grey. The
 * question stays the accessible name and the whole summary stays the click
 * target; the plate is drawn, not clicked.
 *
 * Still a `::after` on the summary rather than an icon in the markup, because
 * the summary is generated by the plugin's FAQ rows and a pseudo-element needs
 * no template change to reach every one of them.
 */
.tt-faq__question {
	padding-inline: var(--tt-space-2);
	border-radius: var(--tt-radius-card);
	transition: background-color var(--tt-duration-fast) var(--tt-ease-out);
}

@media (hover: hover) {
	.tt-faq__question:hover {
		background: var(--tt-gray-soft);
	}
}

/*
 * TWO PSEUDO-ELEMENTS, BECAUSE ONE CANNOT BE BOTH THE PLATE AND THE CARET.
 *
 * The first attempt tried: it took the existing caret — a square with two
 * borders, rotated 45° — and gave it padding, a background and a radius to make
 * it sit in a disc. It renders as a blob. The rotation turns the radius with
 * it, the padding box fills behind the two borders, and what reaches the screen
 * is a rounded blue lozenge with no chevron in it at all. Photographed before
 * it was believed.
 *
 * So `::before` is the plate and `::after` is the caret, stacked at the same
 * spot. The summary becomes the positioning context; the caret keeps its own
 * rotation and knows nothing about the disc behind it.
 */
.tt-faq__question {
	position: relative;
	padding-right: calc(var(--tt-space-2) + 34px);
}

.tt-faq__question::before {
	content: "";
	position: absolute;
	right: var(--tt-space-2);
	top: 50%;
	width: 34px;
	height: 34px;
	margin-top: -17px;
	border-radius: 50%;
	background: var(--tt-blue-soft);
	box-shadow: inset 0 0 0 1px rgb(8 104 215 / 12%);
	transition: background-color var(--tt-duration-base) var(--tt-ease-out);
}

.tt-faq__question::after {
	position: absolute;
	right: calc(var(--tt-space-2) + 12px);
	top: 50%;
	width: 8px;
	height: 8px;
	margin-top: -7px;
	border-right-color: var(--tt-blue-dark);
	border-bottom-color: var(--tt-blue-dark);
	transition:
		transform var(--tt-duration-base) var(--tt-ease-out),
		border-color var(--tt-duration-base) var(--tt-ease-out);
}

/*
 * Open: the plate fills and the caret turns white, so a scan down a list of six
 * questions shows at a glance which one is the answer on screen.
 */
.tt-faq__item[open] .tt-faq__question::before {
	background: var(--tt-blue);
	box-shadow: none;
}

.tt-faq__item[open] .tt-faq__question::after {
	border-right-color: var(--tt-white);
	border-bottom-color: var(--tt-white);
}

.tt-faq__item[open] .tt-faq__question {
	color: var(--tt-blue-dark);
}

/* The open row is a block, so its answer needs the same inset as its question. */
.tt-faq__answer {
	padding-inline: var(--tt-space-2);
}

@media (prefers-reduced-motion: reduce) {
	.tt-faq__question,
	.tt-faq__question::before,
	.tt-faq__question::after {
		transition: none;
	}
}

/* The consent card, at the size the message deserves ---------------------- */

/*
 * 383px ON A LAPTOP AND 405 ON A PHONE — 43% AND 48% OF THE SCREEN — FOR A
 * NOTICE ABOUT ONE MAP.
 *
 * The first version of the card stacked six blocks in a single column: a 40px
 * icon, a title, a three-sentence paragraph, a disclosure, two buttons and a
 * policy link. Every one of them earns its place — the paragraph is deliberately
 * specific about what this site does and does not do, and the pair of buttons is
 * a legal requirement drawn as equals — but stacked they add up to a panel that
 * covers half the page it is asking about.
 *
 * Measured consequence rather than an aesthetic one: on the homepage at 1440 the
 * card lands exactly on the first destination tile, and `elementFromPoint` at
 * the middle of that tile returns `.tt-consent__button`. A visitor cannot press
 * the thing the notice is sitting on. The existing «a destination tile
 * acknowledges being pressed» test is what found it.
 *
 * Nothing is removed and no wording changes. The blocks are placed on a grid
 * instead of stacked, which is purely a layout change and reaches the markup
 * through `grid-area` rather than through the plugin:
 *
 *   the icon moves beside the title instead of above it   about 50px
 *   the policy link shares the disclosure's row           about 30px
 *
 * Grid areas rather than reordering the markup, because the markup is the
 * plugin's and the reading order it produces — icon, heading, what this is,
 * what exactly, decide, policy — is already the right one for a screen reader.
 */
.tt-consent__inner {
	grid-template-columns: 40px minmax(0, 1fr);
	grid-template-areas:
		"mark  title"
		"text  text"
		"panel panel"
		"acts  acts";
	align-items: center;
	column-gap: 12px;
}

.tt-consent__mark {
	grid-area: mark;
}

.tt-consent__title {
	grid-area: title;
}

.tt-consent__text {
	grid-area: text;
}

.tt-consent__detail {
	grid-area: panel;
	justify-self: start;
}

.tt-consent__actions {
	grid-area: acts;
}

/*
 * The policy link rides on the disclosure's row, at the far end. Both are
 * secondary — «read more about this» and «read the policy» — so they belong on
 * one line rather than each taking a row of their own.
 */
.tt-consent__policy {
	grid-area: panel;
	justify-self: end;
	align-self: start;
	line-height: var(--tt-tap);
}

/*
 * The disclosure expands downwards into its own row when it is open, and the
 * policy link must not be dragged down the side of it.
 */
.tt-consent__detail[open] {
	justify-self: stretch;
	padding-right: 140px;
}

/* The coach photograph on a phone ----------------------------------------- */

/*
 * IT WAS HIDDEN BELOW 768px, AND THE REASON EXPIRED.
 *
 * The rule further up this file says «on a phone it would be a tall empty block
 * carrying no information at all», and that was exactly right: the box held a
 * hatched stand-in waiting for the client's fleet photography. That photography
 * arrived — attachment 5757 is the agency's own Setra, and it is the only
 * picture on this entire site of the thing this banner is selling.
 *
 * So the box now carries the most information of anything in the banner, on the
 * device where «Χρειάζεσαι πούλμαν;» is most likely to be read, and hiding it is
 * the opposite of the decision that was made. Same shape of reasoning as the
 * map: the justification was sound, the thing it was about changed, and the rule
 * has to change with it rather than outlive it.
 *
 * A 5:2 band rather than the desktop's full column — enough to see a coach,
 * not enough to push the button below the fold. `inc/media.php` had to change
 * with it: the surface asked for a 1px candidate under 768px, which was correct
 * for something never painted and would have stretched one pixel across a phone.
 */
@media (max-width: 767px) {
	.tt-banner__media {
		display: block;
		aspect-ratio: 5 / 2;

		/*
		 * BOTTOM corners, because the photograph is the LAST child. The banner
		 * is a two-column grid above 768px and a stack below it, and in the
		 * stack the media follows the text — so rounding the top edge, which is
		 * what the first draft did, left the photograph square where it meets
		 * the banner's own rounded bottom and rounded where it meets flat navy.
		 */
		border-radius: 0 0 var(--tt-radius-card) var(--tt-radius-card);
		overflow: hidden;
	}

	.tt-banner__media img {
		width: 100%;
		height: 100%;
		object-fit: cover;
	}
}

/* The destination rail's containing block ---------------------------------- */

/*
 * THE SAME BUG AS THE REVIEW CAROUSEL, ONE COMPONENT ALONG, AND IT ARRIVED BY
 * REMOVING SOMETHING RATHER THAN ADDING IT.
 *
 * The rule is written out in full at `.tt-carousel__track`: an `overflow` box
 * only clips descendants whose containing block is inside it, and
 * `.screen-reader-text` is `position: absolute !important`. Each destination
 * tile now carries one — the tour count, repeated in words after the name,
 * because the badge that shows it sits inside the photograph and would otherwise
 * be read out before the place it belongs to.
 *
 * With nothing positioned between that span and the document, its containing
 * block was the page: a tile at x=1994 in a 375px viewport, and the homepage
 * scrolled 1,386px sideways.
 *
 * IT WAS HIDDEN BY AN ACCIDENT FOR MOST OF AN AFTERNOON. While the tiles carried
 * `data-tt-reveal` they were moved with `translate`, and a non-`none` `translate`
 * creates a containing block — so the spans were contained by luck, by a
 * property that exists for an animation. Taking the reveal off the tiles, for an
 * unrelated and correct reason, removed the accident and three tests failed at
 * once.
 *
 * On the track, matching the rule as it is stated, so it holds for anything
 * added to a tile later by somebody who has not read this.
 */
.tt-destinations {
	position: relative;
}

/* Audience chips wrap; filter chips scroll -------------------------------- */

/*
 * TWO ROWS OF CHIPS THAT LOOK THE SAME AND ARE NOT THE SAME THING.
 *
 * The catalogue's category chips are a FILTER. One of them is applied, the rest
 * are alternatives, and a horizontal rail is the right shape for that — it is
 * the pattern every catalogue on the web uses and the visitor is looking for one
 * value, not reading the set.
 *
 * The service pages' chips are a LIST OF WHAT THE OFFICE DOES. «Σχολεία»,
 * «Σύλλογοι», «Γάμοι», «Εταιρικές εκδηλώσεις», «Αεροδρόμιο και λιμάνι»,
 * «Ιδιωτικές ομάδες» — six answers to «do you do my kind of trip», and the whole
 * value of the row is seeing all six. On a phone the shared rail showed ONE, with
 * the other five behind a sideways scroll and no sign they were there.
 *
 * The reason the rail was reached for is real and measured: «Μεταφορές από και
 * προς αεροδρόμιο και λιμάνι» is a 424px pill on a 390px screen, and a chip that
 * cannot shrink below its own label pushes the document sideways. So the fix is
 * to let the label wrap rather than to let the row scroll — a two-line chip is
 * an ordinary thing, and six visible chips beat one.
 *
 * `max-width` on the chip itself, because that is the property that was missing:
 * a flex item's automatic minimum size is its content, which is what made the
 * pill wider than the screen in the first place.
 *
 * ONE PROPERTY, AND IT HAS TO STAY THAT WAY. This is the only `.tt-chip`
 * selector in the file besides the base rule in the Chips section, and it is
 * safe because nothing else sets `max-width` on a chip. The moment a second
 * declaration is added here it becomes a competing base rule — same weight,
 * later line — and it will take the fill back off `.tt-chip--remove` for the
 * second time. Anything else about a chip belongs in the base rule or on a
 * modifier that names its element.
 */
.tt-chip {
	max-width: 100%;
}

.tt-chips-row .tt-chips--wrap {
	flex-wrap: wrap;
	overflow: visible;
	scroll-snap-type: none;
}

/*
 * `white-space: nowrap` IS THE ONE THAT ACTUALLY HELD THE ROW OPEN, and
 * `max-width` alone did nothing — measured: the pill stayed 424px through
 * `min-width: 0`, `overflow-wrap: anywhere` and `flex-wrap` on the chip itself,
 * because a line that is forbidden to break has a min-content width equal to the
 * whole sentence.
 *
 * Scoped to the wrapping variant. The catalogue's filter chips keep `nowrap`,
 * which is right for them: they live in a rail, they are short, and a filter
 * that breaks across two lines reads as two filters.
 */
.tt-chips--wrap > li {
	/*
	 * THE `<li>` IS THE FLEX ITEM, AND IT WAS THE ONE HOLDING THE ROW OPEN.
	 *
	 * `max-width: 100%` on the chip resolves against its parent, and its parent
	 * is this `<li>` — which was itself 424px, so the constraint was measured
	 * against the very thing that was too wide. Capping the item is what makes
	 * the percentage mean «the row» instead of «yourself», and `min-width: 0`
	 * lets it go below its automatic content-based minimum.
	 */
	max-width: 100%;
	min-width: 0;
}

.tt-chips--wrap .tt-chip {
	white-space: normal;
	text-align: left;
	max-width: 100%;
}

/* ==========================================================================
   Depth, motion, and the seam above the footer
   ==========================================================================

   Everything below was asked for by name: reviews that move on their own and
   have some thickness to them, and a real edge where the page meets the
   footer instead of a colour change. Each block records what it refused to do
   as well as what it does, because in all three cases the tempting version is
   the one that breaks something already tested.
   ========================================================================== */

/* The autoplay toggle ------------------------------------------------------ */

/*
 * The pause control is a peer of the two arrows, not a smaller sibling of
 * them: it is the WCAG 2.2.2 mechanism, so it has to be as findable and as
 * tappable as the things it governs. Same 44px box, same border, same ring.
 *
 * What separates it is the fill. Pause and play are the only controls in this
 * group that describe a STATE rather than an action, and a filled disc reads
 * as «switched on» in a way an outlined one does not.
 */
.tt-carousel__button--toggle {
	background: var(--tt-blue-soft);
	border-color: transparent;
	color: var(--tt-blue-dark);
}

@media (hover: hover) {
	.tt-carousel__button--toggle:hover {
		background: var(--tt-blue);
		border-color: var(--tt-blue);
		color: var(--tt-white);
	}
}

/*
 * The play triangle is the one glyph in the set that is a shape rather than a
 * line. `.tt-carousel__button svg` sets `fill: none` for the chevrons, so
 * without this the triangle is drawn as three strokes around a hole.
 */
.tt-carousel__button--toggle svg path[d^="M8 5l11"] {
	fill: currentcolor;
}

/* Cards with thickness ----------------------------------------------------- */

/*
 * A ROW THAT LOOKS LIKE IT HAS A NEAR EDGE AND A FAR ONE.
 *
 * Cover flow, done with a scroll-driven animation and no script at all: the
 * card's rotation is a function of where it sits in the track's own scrollport,
 * which is exactly what `view(inline)` measures. The timer that moves the track
 * and a finger that flicks it produce identical motion, because neither of them
 * is driving this — the scroll position is.
 *
 * FOUR THINGS ARE DELIBERATE AND ONE OF THEM IS A BUG THIS FILE HAS HAD BEFORE:
 *
 * - Six degrees, not the twenty a cover flow usually takes. These cards carry
 *   body text somebody is meant to read, and text on a plane turned twenty
 *   degrees away is text nobody reads.
 *
 * - `scale(0.96)` at the extremes is not only taste. Perspective makes the near
 *   edge of a rotated card BIGGER — at 6° and 1200px that is about 1.7% — and
 *   the track clips in both axes, because a box with `overflow-x: auto` and
 *   `overflow-y: visible` computes the second one to `auto` as well. Shrinking
 *   by 4% means the rotated card is never larger than the box it was measured
 *   into, so nothing is ever clipped and nothing ever adds a scrollbar.
 *
 * - No opacity in the keyframes, though it is the usual third ingredient. The
 *   outermost visible card at rest would then sit permanently dimmed, and that
 *   card is a real customer's sentence, not chrome.
 *
 * - The transform is on the `<li>` and never on `.tt-review`. `.tt-lift` puts
 *   its hover translate on the figure inside, and two rules moving the same box
 *   is how the press feedback died the last time this section was touched.
 *
 * Where `animation-timeline` is not supported the whole block is skipped and
 * the cards are flat, which is what they were yesterday. There is no fallback
 * to write because the fallback is the design.
 */
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.tt-carousel--depth .tt-carousel__track {
			perspective: 1200px;
			perspective-origin: 50% 50%;
		}

		.tt-carousel--depth .tt-reviews__item {
			animation: tt-coverflow linear both;
			animation-timeline: view(inline);
			animation-range: cover;
			transform-style: preserve-3d;
			backface-visibility: hidden;
		}

		/*
		 * IT TILTS ON THE WAY IN AND NEVER ON THE WAY OUT, AND THE SYMMETRICAL
		 * VERSION SHIPPED FIRST AND WAS WRONG.
		 *
		 * A true cover flow angles both sides, which needs content on both
		 * sides to make sense of. This row starts at card one: at rest the first
		 * card sits against the left edge of the track with nothing beyond it,
		 * and `view(inline)` puts it at 76% of its pass — so it was drawn
		 * permanently turned about two degrees, with the first letter of every
		 * line leaning away. That does not read as depth. It reads as a
		 * rendering fault, on the one card that is a real customer's sentence.
		 *
		 * Measured rather than guessed. With three cards visible the resting
		 * progressions are 0.76, 0.50 and 0.24, so putting the flat span at 50%
		 * and beyond leaves the two cards a visitor is reading square on and
		 * angles only the sliver at the fold — which is the half of cover flow
		 * that was doing the work: the row continues, and it continues into
		 * depth. On a phone, where one card and a sliver are visible, the same
		 * numbers put the card at 0.54 and the sliver at 0.07.
		 *
		 * `rotateY` positive sends the right-hand edge away from the viewer, so
		 * a card still out on the right is turned to face the middle of the row.
		 */
		@keyframes tt-coverflow {
			0% {
				transform: rotateY(8deg) scale(0.93);
			}

			50%,
			100% {
				transform: none;
			}
		}
	}
}

/* The seam above the footer ------------------------------------------------ */

/*
 * WHAT THIS REPLACED: nothing. The last section of every page ended, there was
 * whitespace, and then the navy started. A hard colour change is a legitimate
 * way to end a page — GetYourGuide does exactly that — but it says the page
 * stopped rather than that it arrived somewhere.
 *
 * WHAT IT IS NOT. It is not a wave, a slant or a blob, all of which are the
 * generic section divider CLAUDE.md rules out, and none of which mean anything.
 * This is the route line: the same device the itinerary and «Πώς δουλεύει» are
 * already drawn with, run full width at the one point on the page where the
 * visitor is leaving. Two stops and the road between them, on a company that
 * sells the road between two stops.
 *
 * It is `aria-hidden` in the markup and it carries no words, so nothing here
 * has to be translated, proofread or agreed with the client.
 */
/*
 * THE SEA BETWEEN THE PAGE AND THE FOOTER.
 *
 * NO BACKGROUND, AND THAT IS THE ENTIRE FIX. This block used to be navy and to
 * live inside `.tt-footer`, which is also navy — so whatever the artwork did,
 * the page turned navy along the straight top edge of a rectangle and the
 * drawing was decoration underneath it. The client's note was structural:
 * «να ξεκίναγε από πάνω ώστε να μην φαίνεται ευθεία γραμμή.»
 *
 * Transparent, outside the footer, the crests are the only navy above the
 * footer's top edge and the page shows through the troughs. The boundary IS the
 * wave.
 *
 * The section gap moved here with it — see `.tt-footer`, which no longer has a
 * top margin of its own. Two of them would open white space above the water.
 */
.tt-seam {
	position: relative;
	display: block;
	margin-top: var(--tt-section);
	line-height: 0;

	/*
	 * A wave is decoration; nothing in it may swallow a click meant for the
	 * page behind it, and it is `aria-hidden` in the markup for the same reason.
	 */
	pointer-events: none;
}

/*
 * 130px, and the height is the wave's amplitude as much as its size:
 * `preserveAspectRatio="none"` squashes a 160-unit viewBox into whatever this
 * says, so a short band does not make a small wave — it makes a flat one. At
 * 110 the crests read as a gentle slope; at 130 they read as water.
 */
.tt-seam__art {
	display: block;
	width: 100%;
	height: 130px;
}

/*
 * TWO TOKENS AT FOUR STRENGTHS, and the first attempt used one.
 *
 * Navy at 28% and 55% over white is grey — measured on the page, and it read as
 * two flat bands of concrete rather than as water. Sea gets its colour from
 * being SHALLOWER at a distance, not from being paler, so the far layers are
 * `--tt-blue` and only the near one is the footer's navy. Blue at 16% over
 * white is about rgb(226 238 250) and at 42% about rgb(157 196 238) — both
 * inside the palette, neither of them grey.
 *
 * Tokens rather than hand-picked tints, so the water stays right if either
 * colour is ever retuned.
 */
.tt-seam__water--far,
.tt-seam__water--mid {
	fill: var(--tt-blue);
}

.tt-seam__water--far {
	opacity: 0.16;
}

.tt-seam__water--mid {
	opacity: 0.42;
}

/*
 * Full strength, and the occluder: opaque from its crest down, so no lighter
 * layer can reach the bottom edge where the water meets the footer. That join
 * has to be one colour meeting itself — it is the failure the client saw on the
 * first seam, and `motion-asked-for.spec.js` samples it in pixels either side.
 */
.tt-seam__water--near {
	fill: var(--tt-navy);
	opacity: 1;
}

/*
 * Shorter where the page is narrow. At 110px a phone spends a fifth of its
 * screen on water before the footer starts.
 */
@media (max-width: 767px) {
	.tt-seam__art {
		height: 80px;
	}
}

/*
 * THE ROAD IS GONE, AND WITH IT THE ONE SCROLL-DRIVEN ANIMATION THAT LIVED
 * INSIDE A `view-transition-name` SUBTREE.
 *
 * `.tt-seam__road` drew itself on `animation-timeline: view()`. It was deleted
 * once before on the strength of three single runs that appeared to show it
 * costing `motion.spec.js` twelve times its runtime, and put back when the same
 * measurement repeated six times showed 1 failure in 12 against 2 in 12 without
 * it. The lesson outlived the rule and is worth more:
 *
 *     n=1 on a known-flaky test proves nothing in either direction, and a
 *     mechanism that sounds right makes a wrong answer easier to believe
 *     rather than more likely to be true.
 *
 * It goes now for a reason that has nothing to do with any of that: the client
 * asked for water instead of a road. Nothing on this site is currently a
 * scroll-driven timeline inside a named transition subtree, so the test that
 * guarded that combination goes with it. If one is ever added back, so does the
 * test.
 */

/* Who built it ------------------------------------------------------------- */

/*
 * The credit line. It sits with the copyright rather than in a column of its
 * own, at the same size and the same muted colour as everything else on that
 * row — a signature, not an advertisement. The one thing it does not do is
 * outrank the client's own registry line above it.
 */
.tt-footer__bottom .tt-footer__credit {
	margin-top: 6px;

	/* The same muted white as the registry line above it, and for the same
	   reason: neither of them is what somebody came to the bottom of the page
	   to read. */
	color: rgb(219 230 242 / 72%);
}

.tt-footer__credit a {
	color: inherit;
	text-decoration: underline;
	text-decoration-color: rgb(255 255 255 / 32%);
	text-underline-offset: 3px;
	transition: text-decoration-color var(--tt-duration-base) var(--tt-ease-out);
}

.tt-footer__credit a:hover,
.tt-footer__credit a:focus-visible {
	color: var(--tt-white);
	text-decoration-color: var(--tt-white);
}

/* The coach that drives ---------------------------------------------------- */

/*
 * A BAND, NOT A PICTURE IN A COLUMN. It runs the full width of the container so
 * the road has somewhere to go; boxed into a card it would be a coach in a
 * garage.
 *
 * The colour is `--tt-blue-soft`, the same wash the fleet band under it uses,
 * so the two read as one statement about vehicles rather than as two unrelated
 * boxes.
 */
.tt-roadshow {
	position: relative;
	overflow: hidden;
	margin-block: var(--tt-space-4) var(--tt-space-3);
	border-radius: var(--tt-radius-lg);
	background: var(--tt-blue-soft);
}

/*
 * `overflow: visible` ON AN SVG, WHICH IS UNUSUAL AND IS THE POINT.
 *
 * `preserveAspectRatio="…meet"` fits the whole viewBox inside the band without
 * distorting the coach, which at any wide viewport leaves the drawing narrower
 * than its container — so the road stopped a couple of hundred pixels short of
 * each end and the band read as a picture of a road rather than as a road.
 *
 * The alternatives were both worse: `slice` fills the width by cropping the
 * top, which decapitates the coach, and `none` fills it by stretching, which
 * turns a coach into a limousine. Letting the paths run past the viewBox and
 * clipping them on `.tt-roadshow` instead keeps the coach at its true
 * proportions and lets the road and the hills reach the edges at any width.
 */
.tt-roadshow__art {
	display: block;
	overflow: visible;
	width: 100%;
	height: 132px;
}

@media (min-width: 768px) {
	.tt-roadshow__art {
		height: 190px;
	}
}

/* The scenery -------------------------------------------------------------- */

.tt-roadshow__hills {
	fill: rgb(8 104 215 / 9%);
}

.tt-roadshow__road,
.tt-roadshow__kerb {
	fill: none;
	stroke-linecap: round;
}

.tt-roadshow__road {
	stroke: rgb(8 104 215 / 46%);
	stroke-width: 5;
	stroke-dasharray: 34 26;
}

.tt-roadshow__kerb {
	stroke: rgb(8 104 215 / 20%);
	stroke-width: 3;
}

.tt-roadshow__kerb--low {
	stroke: rgb(8 104 215 / 10%);
}

/* The coach ---------------------------------------------------------------- */

.tt-coach__shadow {
	fill: rgb(11 31 59 / 12%);
}

.tt-coach__body {
	fill: var(--tt-blue);
}

.tt-coach__glass {
	fill: rgb(255 255 255 / 88%);
}

.tt-coach__pillars {
	fill: var(--tt-blue);
}

.tt-coach__stripe {
	fill: rgb(255 255 255 / 62%);
}

.tt-coach__lamp {
	fill: var(--tt-star-fill);
}

.tt-coach__tyre {
	fill: var(--tt-navy);
}

.tt-coach__hub {
	fill: var(--tt-white);
}

.tt-coach__spokes {
	fill: none;
	stroke: rgb(11 31 59 / 30%);
	stroke-width: 2;
	stroke-linecap: round;
}

/* The motion --------------------------------------------------------------- */

/*
 * FOUR ANIMATIONS, ALL COMPOSITOR WORK, NONE OF THEM ON THE MAIN THREAD.
 *
 * `stroke-dashoffset` is the exception and it is deliberate: it is the only
 * property that moves a dashed line without moving the line itself, and the
 * path is three straight segments, so the repaint is trivial. Everything else
 * is a transform.
 *
 * WHY THE WHEELS NEED `transform-box`. An SVG element's transform origin is the
 * SVG's own coordinate system, not the shape's box — so `transform-origin:
 * center` on a wheel means the centre of the whole drawing, and each wheel
 * swings round the middle of the scene like a hammer thrower. `fill-box` is
 * what makes `center` mean the centre of the circle.
 *
 * IT RUNS FOR AS LONG AS THE PAGE IS OPEN, AND THAT WAS WEIGHED RATHER THAN
 * OVERLOOKED. The reviews carousel stops itself when it scrolls out of view
 * because it holds a timer and moves real content; this holds neither. Four
 * transforms on one small SVG on one page is below the cost of the observer
 * that would switch them off, and browsers already stop compositor animations
 * in a background tab. `animation-timeline: view()` is no help here either — it
 * maps progress to scroll position, and this is a loop, not a progression.
 *
 * TWO CORRECTIONS TO THE PARAGRAPH ABOVE, BOTH FOUND BY COUNTING.
 *
 * There are FIVE animations on this page, not four: the aeroplane at the end of
 * this section is the fifth, added later, and it is the longest-running of them
 * at 34 seconds. Six elements move, because there are two wheels.
 *
 * And «the band already holds still off screen», written a few lines below in
 * the note on the plane and repeated in `coach-scene.php`, was never true.
 * Nothing in this stylesheet or in `navigation.js` stops this scene when it
 * scrolls out of view — there is no `content-visibility`, no observer and no
 * scroll timeline anywhere near `.tt-roadshow`. The claim is removed rather
 * than implemented: the honest statement is that these five run continuously
 * while the page is open, which is exactly why the paragraph below this one
 * exists.
 *
 * SC 2.2.2 IS THE PART `prefers-reduced-motion` DOES NOT COVER. The media query
 * below means the scene never starts for somebody whose system says «less
 * motion», and that is necessary and is not the criterion: 2.2.2 asks for a
 * mechanism, on the page, for anything that starts by itself and runs past five
 * seconds. `navigation.js` builds one — the same off-canvas-until-focused
 * button the reviews row and the hero rotation already use — and it stops the
 * scene by putting `is-still` on the band. The list below is what it stops, and
 * it is a list rather than a `*` so that adding a sixth animation to this scene
 * is a decision somebody has to make here, next to the other five.
 */
.tt-roadshow.is-still .tt-roadshow__road,
.tt-roadshow.is-still .tt-roadshow__hills,
.tt-roadshow.is-still .tt-coach__wheel,
.tt-roadshow.is-still .tt-coach__ride,
.tt-roadshow.is-still .tt-plane {
	animation-play-state: paused;
}

/*
 * THE STOP ITSELF, DRAWN LIKE THE OTHER TWO ON THIS SITE.
 *
 * `.tt-carousel__nav--quiet .tt-carousel__button` and `.tt-hero__pause` are the
 * precedent and the reasoning is recorded on both: the client asked for nothing
 * to be drawn over the pictures, WCAG 2.2 SC 2.2.2 asks for a mechanism that a
 * keyboard and a screen reader can find, and a control positioned off the canvas
 * that returns at full size on focus is the one shape that satisfies both.
 *
 * `:focus` AND NOT `:focus-visible`, for the reason recorded on those two rules:
 * the only way to reach a control positioned out of the flow is the keyboard.
 *
 * `position: static` ON FOCUS, LIKE THE CAROUSEL'S AND UNLIKE THE HERO'S. The
 * hero's button is absolutely positioned inside `.tt-hero`, which is a
 * positioned box it can hang from; this band is followed by whatever the page
 * template puts next, and an absolute control with no positioned ancestor would
 * resolve against the document and land at the bottom of the page. Coming back
 * into the flow costs 44px of layout at the moment of focus and puts the button
 * directly under the drawing it governs, which is where somebody looking for it
 * would expect it.
 */
.tt-roadshow__pause {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.tt-roadshow__pause:focus {
	position: static;
	display: grid;
	place-items: center;
	inline-size: var(--tt-tap);
	block-size: var(--tt-tap);
	margin: calc(var(--tt-space-3) * -1) 0 var(--tt-space-3) auto;
	overflow: visible;
	clip-path: none;
	border: 1px solid var(--tt-border-control);
	border-radius: 50%;
	background: var(--tt-white);
	color: var(--tt-navy);
	cursor: pointer;
}

.tt-roadshow__pause svg {
	inline-size: 18px;
	block-size: 18px;
	fill: none;
	stroke: currentcolor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

@media (prefers-reduced-motion: no-preference) {
	.tt-roadshow__road {
		animation: tt-road 1.6s linear infinite;
	}

	.tt-roadshow__hills {
		animation: tt-hills 26s linear infinite;
	}

	.tt-coach__wheel {
		transform-box: fill-box;
		transform-origin: center;
		animation: tt-wheel 1.1s linear infinite;
	}

	.tt-coach__ride {
		transform-box: fill-box;
		animation: tt-ride 1.7s ease-in-out infinite;
	}

	/* One dash plus one gap. Any other distance and the loop visibly jumps. */
	@keyframes tt-road {
		to {
			stroke-dashoffset: -60;
		}
	}

	/* One full hill. The three paths in the markup are the same hill at -880,
	   0 and 880, so sliding by exactly 880 is seamless. */
	@keyframes tt-hills {
		to {
			transform: translateX(-880px);
		}
	}

	@keyframes tt-wheel {
		to {
			transform: rotate(360deg);
		}
	}

	/*
	 * Suspension. One and a half pixels, which sounds like nothing and is the
	 * difference between a coach that is driving and a sticker of a coach.
	 */
	@keyframes tt-ride {
		0%,
		100% {
			transform: translateY(0);
		}

		50% {
			transform: translateY(-1.5px);
		}
	}
}

/* The company page --------------------------------------------------------- */

/*
 * ONE GRID FOR THE STORY AND THE FACTS.
 *
 * /to-grafeio/ is the only page on the site that is pure prose, and it was the
 * only one that looked broken because of it: `.tt-prose` caps the measure at 68
 * characters, so on a 1,440px laptop the text ran down the left third of a
 * 1,180px container with roughly 700px of nothing beside it — and then the three
 * points underneath spanned the full width. Two alignments, the wider starting
 * where the narrower stopped.
 *
 * The measure is not the problem and must not be widened to fix this; it is the
 * reason the page is readable. What was wrong is that the facts were stacked
 * BELOW a column that had already given up two thirds of the page. They now sit
 * in that space.
 *
 * `align-items: start` so the rail keeps its own height instead of stretching
 * to the length of the story, and `minmax(0, 1fr)` on the text column because a
 * grid track's default `min-width: auto` refuses to shrink below its content —
 * which is how a long unbroken word in the prose would push the rail off screen.
 */
.tt-about {
	display: grid;
	gap: var(--tt-space-5);
}

@media (min-width: 1024px) {
	.tt-about {
		grid-template-columns: minmax(0, 1fr) 340px;
		gap: var(--tt-space-6);
		align-items: start;
	}
}

/*
 * The rail. One column inside it — the shared rule turns the list into three
 * across, which inside a 340px track would give three 100px cards.
 *
 * 1024 AND NOT 768, WHICH IS WHERE THE RAIL ACTUALLY BEGINS.
 *
 * `.tt-about` becomes two columns at 1024px. This override fired at 768, so
 * across the whole 768-1023 band — every tablet in portrait, and a laptop
 * window dragged narrow — there was no rail to be narrow inside: the page was
 * still one column, and the three fact cards that read as a set of three at 767
 * suddenly became three full-width banners stacked down 1,180px of page,
 * turning back into a set the moment the window reached 1024.
 *
 * Tying it to `.tt-about`'s own breakpoint means the rule turns on with the
 * thing it exists to fit inside.
 */
@media (min-width: 1024px) {
	.tt-about__facts .tt-points__list {
		grid-template-columns: minmax(0, 1fr);
		gap: var(--tt-space-3);
	}
}

/*
 * No top margin on the rail's own section: the grid gap already separates the
 * two columns, and the section's spacing was written for a block that follows
 * prose rather than one that sits beside it.
 */
.tt-about__facts .tt-points {
	margin-top: 0;
}

@media (min-width: 1024px) {
	.tt-about__facts .tt-points__heading {
		font-size: var(--tt-text-lead);
	}
}

/*
 * THE DISTANCE, ON THE COACH-HIRE PAGE.
 *
 * THE ORDER THAT MATTERS IS SUBJECT AGAINST BACKGROUND, and it is worth being
 * exact rather than reciting a rule this drawing does not actually obey. The
 * tempting claim is strict atmospheric perspective — everything further away is
 * paler than everything nearer — and the cliff below breaks it: at 11% it is
 * marginally stronger than the 9% haze hills that stand in front of it. That is
 * deliberate. The hills are texture and repeat five times across the band; the
 * cliff appears once and has to be legible as a place.
 *
 * What must hold is the gap to the coach, which is solid --tt-blue. Every value
 * here is a wash under 12%, or a white held below full strength, so nothing in
 * the distance can compete with the thing the page is about. Raise any of them
 * past the coach's contrast and the band stops being a scene with depth and
 * becomes a sheet of stickers.
 */
.tt-scenery__rock {
	fill: rgb(8 104 215 / 11%);
}

/*
 * The village is NOT pure white. White at this distance jumps in front of the
 * coach — it is the brightest value in the band and the eye goes to it first,
 * which is the opposite of what a background is for. Held down to a tint of
 * the page's own soft blue.
 */
.tt-scenery__village {
	fill: rgb(255 255 255 / 78%);
}

.tt-scenery__domes {
	fill: rgb(8 104 215 / 38%);
}

.tt-plane {
	fill: rgb(11 31 59 / 26%);

	/*
	 * Parked at the left of the sky when nothing animates it, which is what a
	 * visitor who asked for less motion sees: an aeroplane in the sky, not an
	 * empty band and not a shape stuck to the edge.
	 */
	transform: translate(120px, 46px);
}

/*
 * IT CROSSES, ONCE EVERY 34 SECONDS.
 *
 * The second half of that heading used to read «and only while the section is on
 * screen», on the strength of a brake that does not exist anywhere in this
 * stylesheet — see the correction in the motion note above, which is where the
 * five animations are now listed and where the one real stop for them lives.
 *
 * 34s is chosen against the two animations it shares the scene with: the road
 * repeats every 1.6s and the hills every 26s,
 * and a third period that is a near-multiple of either produces a visible beat
 * where all three line up. It is also slow enough to read as distance: the
 * plane crosses the band in the time the coach's road repeats twenty times,
 * which is the only cue that says one of them is far away.
 */
@media (prefers-reduced-motion: no-preference) {
	.tt-plane {
		animation: tt-fly 34s linear infinite;
	}

	@keyframes tt-fly {
		from {
			transform: translate(-260px, 62px);
		}

		to {
			transform: translate(1140px, 30px);
		}
	}
}

/*
 * A ROW THAT MOVES ON ITS OWN WITH NOTHING BESIDE IT.
 *
 * The client asked for the reviews to advance by themselves with no play
 * button and no arrows. The arrows are genuinely gone — the template names no
 * labels, so `navigation.js` never builds them, and nothing sits in the
 * accessibility tree pretending to be operable.
 *
 * THE PAUSE CONTROL CANNOT GO THE SAME WAY, and this is the compromise rather
 * than a refusal. WCAG 2.2.2 asks for a mechanism to pause anything that starts
 * by itself, runs past five seconds and sits beside other content — and «it
 * stops if your mouse happens to rest on it» is not a mechanism a keyboard or a
 * screen reader can find. So the control survives, off the screen, and returns
 * the instant a keyboard reaches it.
 *
 * `:focus` AND NOT `:focus-visible`. The only way to reach a button positioned
 * off the canvas is the keyboard, so the two would behave identically here —
 * except on the engines where `:focus-visible` support is patchy, and there the
 * difference is a focused control nobody can see, which fails 2.4.7 on top of
 * everything this rule exists to satisfy.
 *
 * The row still has five other brakes: hover, focus inside it, a hidden tab,
 * scrolling off screen, and any deliberate swipe or key, which switches it off
 * for good. This is the one that has a name and can be found on purpose.
 */
.tt-carousel__nav--quiet {
	gap: 0;
	margin-top: 0;
}

.tt-carousel__nav--quiet .tt-carousel__button {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.tt-carousel__nav--quiet .tt-carousel__button:focus {
	position: static;
	width: 44px;
	height: 44px;
	margin: 0;
	overflow: visible;
	clip-path: none;
	border: 1px solid var(--tt-border-control);
}

/* Blog ----------------------------------------------------------------------
 *
 * A READING LIST, DELIBERATELY NOT THE CATALOGUE GRID.
 *
 * The obvious move is to reuse `.tt-tours` and give articles the same three-across
 * cards as the trips. It would look consistent and it would be wrong: a grid of
 * equal tiles is a set of things to CHOOSE BETWEEN, which is exactly what a
 * catalogue is and exactly what a blog is not. Nobody comparison-shops articles.
 * A single column, newest first, image beside the words, is the shape of
 * something to read down.
 *
 * It also keeps the two apart at a glance, which matters on a site where the
 * trips are the business: a visitor who lands on /blog/ should never be a
 * second away from thinking they are looking at products.
 */

.tt-posts {
	display: grid;
	gap: var(--tt-space-3);
	list-style: none;
	margin: 0;
	padding: 0;
}

.tt-post-card {
	/*
	 * `relative` is not decoration: the title's link stretches an `::after` over
	 * the whole card so a thumb gets the full box, and that pseudo-element needs
	 * this to anchor to. Declared here rather than in a second block further
	 * down, where it was, so the two facts about this box are in one place.
	 */
	position: relative;
	display: grid;
	gap: var(--tt-space-2);
	padding: var(--tt-space-2);
	border: 1px solid var(--tt-border);
	border-radius: var(--tt-radius-card);
	background: var(--tt-white);
}

/*
 * From 640px the photograph moves beside the words. A fixed 34% rather than a
 * fraction of the row: the picture is a 3:2 crop and the text block is as tall
 * as the excerpt makes it, so an `1fr 1fr` split leaves a short card with a
 * letterboxed image and a long one with a stretched column of white.
 */
@media (min-width: 640px) {
	.tt-post-card {
		grid-template-columns: minmax(0, 34%) minmax(0, 1fr);
		gap: var(--tt-space-3);
		align-items: center;
	}
}

.tt-post-card__media {
	display: block;
	overflow: hidden;
	border-radius: var(--tt-radius-media);
}

.tt-post-card__media img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 3 / 2;
	object-fit: cover;
}

/*
 * A CARD WITH NO PHOTOGRAPH IS NOT A CARD WITH A HOLE IN IT. The office may
 * publish an article without one, and `:only-child` puts the words across the
 * whole card rather than leaving a 34% column of nothing beside them.
 */
@media (min-width: 640px) {
	.tt-post-card__body:only-child {
		grid-column: 1 / -1;
	}
}

.tt-post-card__date,
.tt-article__date {
	display: block;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
	font-weight: 600;

	/*
	 * NOT `text-transform: uppercase`, which is the reflex for a date label and
	 * is forbidden on this site: Greek capitals drop their accents by
	 * typographic rule and CSS does not know that, so «Αυγούστου» would render
	 * as «ΑΥΓΟΎΣΤΟΥ» with an accent no Greek reader would write.
	 */
	letter-spacing: 0.01em;
}

.tt-post-card__date {
	margin-bottom: 4px;
}

.tt-post-card__title {
	margin: 0 0 6px;
	font-size: var(--tt-text-lead);
	line-height: 1.3;
	text-wrap: balance;
}

.tt-post-card__title a {
	color: var(--tt-navy);
	text-decoration: none;
}

/*
 * The whole card is the target, and the underline still belongs to the words.
 * `::after` on the link stretched over the card gives a thumb the full box
 * without wrapping the date and the excerpt in the anchor — which would have a
 * screen reader read the entire card as one link name.
 *
 * The photograph beside it is a link too, and is deliberately left underneath
 * this one: it carries `tabindex="-1" aria-hidden="true"` precisely so it is not
 * a second tab stop and a second announcement of the same headline.
 */
.tt-post-card__title a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.tt-post-card__title a:hover,
.tt-post-card__title a:focus-visible {
	color: var(--tt-blue);
	text-decoration: underline;
	text-decoration-thickness: 2px;
	text-underline-offset: 4px;
}

.tt-post-card__excerpt {
	margin: 0;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-body);
	line-height: 1.6;
}

/* The article itself -------------------------------------------------------- */

/*
 * CENTRED ON THE PAGE AND NOT ON THE CONTAINER'S FULL WIDTH. `.tt-container` is
 * 1,180px, which is right for a catalogue and far too wide for one column of
 * prose. The article sets its own ceiling at the width of its lead photograph
 * and lets `.tt-prose` cap the words inside it at the 56ch measure this project
 * measured for Greek.
 */
.tt-article,
.tt-article__back,
/*
 * THE BREADCRUMB BELONGS TO THE ARTICLE, NOT TO THE PAGE.
 *
 * `.tt-crumbs` is a child of `.tt-container`, so it sat at x=152 on a 1440px
 * window while the article's own column started at 340 — a 188px step between
 * the trail and the title it leads to, which reads as two unrelated blocks
 * rather than as one page. Measured on the rendered article.
 *
 * Scoped with `:has()` so the trail on a tour page, which genuinely does span
 * the full catalogue width, is untouched.
 */
.tt-container:has( > .tt-article ) > .tt-crumbs {
	max-width: 760px;
	margin-inline: auto;
}

.tt-article__head {
	margin-bottom: var(--tt-space-3);
}

.tt-article__title {
	margin: 6px 0 0;

	/*
	 * A headline wraps where the sense breaks, not where the box ends. `balance`
	 * evens the lines of a short block, which is what a title is, and it is the
	 * one place on this site where the property earns its cost.
	 */
	text-wrap: balance;
}

.tt-article__lede {
	margin: var(--tt-space-2) 0 0;
	max-width: 56ch;
	color: var(--tt-navy);
	font-size: var(--tt-text-lead);
	line-height: 1.6;
}

.tt-article__cover {
	margin: 0 0 var(--tt-space-3);
}

.tt-article__cover img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: var(--tt-radius-media);
}

.tt-article__cover figcaption {
	margin-top: 8px;
	color: var(--tt-text-muted);
	font-size: var(--tt-text-micro);
}

/*
 * THE STANDFIRST IS WRITTEN, SO THE BODY MUST STOP INVENTING ONE.
 *
 * `.tt-prose > p:first-of-type` promotes the opening paragraph to 21px navy,
 * chosen by position, which is the right default for a page whose author did
 * not think about the summary. An article HAS a field for it and this template
 * prints it above the photograph — so without this the reader gets two
 * standfirsts, the office's and an automatic one, at the same size and one
 * after the other. The itinerary day has the same rule for the same reason.
 */
.tt-article__body > p:first-of-type {
	color: var(--tt-text);
	font-size: var(--tt-text-body);
	line-height: 1.75;
}

.tt-article__back {
	margin-block: var(--tt-space-5) 0;
}
