/* One365 FM — small-screen and tablet layout.
 *
 * WHY THIS FILE EXISTS, AND WHY IT LOADS LAST
 *
 * The design system was authored at 1200px and re-checked at 924px. Below that
 * it was never measured, and the pages carry their layout in two places a
 * normal stylesheet cannot reach:
 *
 *   - each page's own <style> block, which the build keeps verbatim and which
 *     therefore beats anything of equal specificity loaded before it;
 *   - inline `style="grid-template-columns: …"` attributes, which beat every
 *     stylesheet rule regardless of order.
 *
 * So this file is linked after the page's <style> (see build/lib/head.mjs) and
 * uses !important only where an inline attribute has to be overridden. The
 * build tags those elements with data-o365-grid / data-o365-span
 * (build/lib/render.mjs) so the targets are stable across a design re-import.
 *
 * RULES OF ENGAGEMENT
 *   - Nothing here applies at >=1200px. The reviewed desktop rendering is
 *     untouched, byte for byte.
 *   - No wording changes, ever. Only where lines break and how boxes stack.
 *   - Every rule below is here because build/audit-responsive.mjs measured a
 *     concrete failure: clipped text, a squeezed column, sideways scroll or an
 *     untappable target. The comment on each block names it.
 *
 * BREAKPOINTS
 *   1199  tablet landscape — the band where 4-up grids start clipping words
 *    999  tablet portrait  — the design's own nav breakpoint
 *    899  large phone      — where the fixed rem type scale stops fitting
 *    619  phone            — everything is one column
 */

/* ==========================================================================
   1. Header and navigation
   ========================================================================== */

/* The design's own rule for <=1000px is
 *     header nav { flex-wrap:nowrap; overflow-x:auto; … }
 * but the element carries an inline `flex-wrap: wrap`, so the rule never won
 * and phones got the opposite of the reviewed decision: the nav wrapped onto
 * three rows (four at 320px) inside a sticky header, costing 169-200px of a
 * 844px screen before any content. Measured on every page.
 *
 * This restores the reviewed single-row strip and makes the scroll visible,
 * because a scrollable row nobody notices is a hidden menu. */
@media (max-width: 999px) {
  [data-o365-role='nav'] {
    flex-wrap: nowrap !important;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    box-sizing: border-box;
    /* No edge bleed. The header's horizontal padding is 4vw, not the 6vw the
       sections use, and a negative margin guessing at it pushed the document
       sideways by exactly the difference on every page. The strip stays inside
       the header's own padding, which is what the design's rule did too.
       Fades out at the right edge while anything is still off-view. */
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
  }

  [data-o365-role='nav']::-webkit-scrollbar {
    display: none;
  }

  /* 23px links are below the WCAG 2.2 minimum and far below a comfortable
     thumb. Padding, not font-size — the type stays as designed. */
  [data-o365-nav-link] {
    display: flex;
    align-items: center;
    min-height: 44px;
    scroll-snap-align: start;
    white-space: nowrap;
  }

  /* The row now owns its own height, so the header can give it back. */
  [data-o365='header'] {
    padding-block: 10px !important;
    row-gap: 2px;
  }
}

/* One row of links instead of three moves the sticky header up by ~64px;
   anchor targets have to follow it or headings land underneath. */
@media (max-width: 999px) {
  section[id] {
    scroll-margin-top: 118px;
  }
}
@media (max-width: 619px) {
  section[id] {
    scroll-margin-top: 112px;
  }
}

/* ==========================================================================
   2. Typography
   ========================================================================== */

/* The type scale is fixed rem. At 390px a 52px h1 cannot hold a German
 * compound — "Liegenschaft", "Visitenkarte", "Spezialreinigungen" — and the
 * hero's overflow:hidden simply cut it off on 8 of 10 pages.
 *
 * The first fix for that used stepped rem plus `hyphens: auto`, and the client
 * rightly rejected the result: hero lines broke as "Eine gepflegte Lie-
 * genschaft" and "rundum ge-pflegt". A hyphen in a headline reads as a
 * mistake even when the break is orthographically correct.
 *
 * So: no automatic hyphenation anywhere. Instead the display sizes go fluid,
 * scaling with the viewport so the longest word on the page fits without help.
 *
 * Each clamp maximum is the design's own token value, and each vw coefficient
 * is set so the maximum is reached at ~810px. That matters: the scale is then
 * continuous through the 900px breakpoint instead of stepping, so nothing
 * changes at or above the widths the design was reviewed at. */
@media (max-width: 899px) {
  :root {
    --text-display-2xl: 700 clamp(1.95rem, 8.4vw, 4.25rem) / 1.06 var(--font-display);
    --text-display-xl: 700 clamp(1.75rem, 6.4vw, 3.25rem) / 1.08 var(--font-display);
    --text-display-lg: 700 clamp(1.5rem, 4.9vw, 2.5rem) / 1.12 var(--font-display);
    --text-display-md: 600 clamp(1.2rem, 3.95vw, 2rem) / 1.18 var(--font-display);
    --text-display-sm: 600 clamp(1.15rem, 2.96vw, 1.5rem) / 1.24 var(--font-display);
  }

  /* Backstop. The clamps above size the type so German compounds fit, but the
   * floors are floors — at 320px inside a padded card, a word like
   * "Besichtigungstermin" can still be wider than its column. Inheriting
   * break-word means such a word wraps instead of being cut off by the
   * nearest overflow:hidden. It has no effect on any word that already fits,
   * so it costs nothing anywhere else. */
  body {
    overflow-wrap: break-word;
  }

  /* Release the desktop-only &nbsp; bindings so bound phrases can wrap.
     (The runs are wrapped in .o365-nb at build time — see render.mjs.) */
  .o365-nb {
    white-space: normal;
  }

  /* Last resort only, and never with a hyphen: an unbreakable string that is
     genuinely wider than the screen is broken rather than clipped away. */
  h1,
  h2,
  h3,
  h4,
  .card-title,
  .grp-h {
    overflow-wrap: break-word;
    hyphens: manual;
    -webkit-hyphens: manual;
  }
}

@media (min-width: 900px) {
  .o365-nb {
    white-space: nowrap;
  }
}

/* ==========================================================================
   3. Layouts the design never collapsed
   ========================================================================== */

/* --- 3a. Homepage service cards -----------------------------------------
 * An inline `repeat(6, minmax(0,1fr))` holding five cards that span 2, 2, 2,
 * 3, 3 — so 3-up then 2-up on desktop. It had no media query, so a phone got
 * three 101px columns with every card title clipped mid-word ("Hauswar",
 * "Spezialre"). This is the layout in the client's screenshots.
 *
 * Both the track list and the spans are inline, so both need !important. */
@media (max-width: 999px) {
  [data-o365-grid='6'] {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
  [data-o365-grid='6'] > [data-o365-span] {
    grid-column: auto !important;
  }
}

@media (max-width: 619px) {
  [data-o365-grid='6'] {
    grid-template-columns: minmax(0, 1fr) !important;
  }
}

/* --- 3b. Raum & Vision teaser card --------------------------------------
 * Inline `minmax(0,1fr) minmax(0,300px)`: the image column holds its 300px on
 * a phone and leaves 43px for the text, which is how "Ladenausbauten und
 * Räume mit Charakter" became one clipped letter per line. */
@media (max-width: 819px) {
  .rv-card {
    grid-template-columns: minmax(0, 1fr) !important;
  }

  /* The photo cell contains nothing but an absolutely-positioned <img>, so it
   * has no height of its own. Side by side that never showed, because
   * align-items:stretch handed it the text column's height — stacked, it
   * collapses to zero and the photo disappears entirely. Give it a shape. */
  .rv-card > :last-child {
    aspect-ratio: 16 / 9;
  }
}

/* --- 3c. Spezialreinigungen groups --------------------------------------
 * `.grp` is `minmax(0,300px) minmax(0,1fr)` with no media query. At 390px the
 * heading column keeps its 300px and the bullet list gets 19px — every item
 * clipped to a single character. Six groups, the whole page body.
 * It also pushed the document 154px sideways. */
@media (max-width: 899px) {
  .grp {
    grid-template-columns: minmax(0, 1fr);
    gap: 20px;
    padding: 28px 0;
  }
}

/* --- 3d. Image strip -----------------------------------------------------
 * Collapses to one column at 660px but stays 3-up above it, which is three
 * ~200px photos at 700px. Two reads better through the tablet band. */
@media (max-width: 899px) and (min-width: 661px) {
  .img-strip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ==========================================================================
   4. The tablet band (1000-1199px)
   ========================================================================== */

/* Below 1000px the design already drops these to 2 columns; above 1200px they
 * were reviewed as-is. In between they stay 4-up at ~221px per card, which is
 * narrower than the German compounds inside them — measured clipping on
 * hauswartung at 1024px. Continuing the 2-up layout up to 1199 keeps the
 * reviewed 1200px rendering untouched. */
@media (max-width: 1199px) and (min-width: 1000px) {
  .svc-grid-4 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Same shape, same reason: 4-up until 1040/900 leaves a band of very narrow
 * columns on the Raum & Vision page. */
@media (max-width: 1199px) and (min-width: 1041px) {
  .rv-tri {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 1199px) and (min-width: 901px) {
  .rv-steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ==========================================================================
   5. Components
   ========================================================================== */

/* --- 5a. Carousel arrows -------------------------------------------------
 * The prev/next buttons sit outside the track (`left:-16px` + a -100%
 * translate). On desktop the track is 640px inside a 1160px column, so there
 * is room. On a phone the track fills the width and both buttons hang off the
 * edges — 37px of sideways document scroll on Raum & Vision, the only page
 * where the whole document could be dragged.
 *
 * Below the tablet band they move inside and overlay the media instead. */
@media (max-width: 899px) {
  [data-o365='carousel'] > [data-o365-role='prev'],
  [data-o365='carousel'] > [data-o365-role='next'],
  [data-o365='ba-carousel'] > [data-o365-role='prev'],
  [data-o365='ba-carousel'] > [data-o365-role='next'] {
    transform: translateY(-50%) !important;
    z-index: 2;
    background: rgba(23, 26, 36, 0.62);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    color: #fff;
  }
  [data-o365='carousel'] > [data-o365-role='prev'],
  [data-o365='ba-carousel'] > [data-o365-role='prev'] {
    left: 8px !important;
    right: auto !important;
  }
  [data-o365='carousel'] > [data-o365-role='next'],
  [data-o365='ba-carousel'] > [data-o365-role='next'] {
    right: 8px !important;
    left: auto !important;
  }
}

/* --- 5b. Form fields -----------------------------------------------------
 * Inputs are content-box and fall back to their intrinsic ~215px + padding,
 * so in the 201px column the two-up row produces at 1024px they overflow
 * their own label. Making them fill the column fixes the overflow and stops
 * them looking short of the field next to them at every other width. */
[data-o365='offer-form'] input,
[data-o365='offer-form'] select,
[data-o365='offer-form'] textarea {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* iOS zooms the page in on focus for any field under 16px and never zooms
   back out. The design's 15px inputs are close enough that rounding up costs
   nothing visually and removes the zoom entirely. */
@media (max-width: 899px) {
  [data-o365='offer-form'] input,
  [data-o365='offer-form'] select,
  [data-o365='offer-form'] textarea {
    font-size: max(16px, 1em);
  }
}

/* --- 5c. Time-slot buttons ----------------------------------------------
 * Two columns puts "Vormittag (8-12 Uhr)" on two lines at 131px. Below 380px
 * one column keeps each slot on one line. */
@media (max-width: 379px) {
  [data-o365-grid='2']:has(.slot-btn) {
    grid-template-columns: minmax(0, 1fr) !important;
  }
}

/* --- 5c-2. Call-to-action rows ------------------------------------------
 * Most hero button rows already carry `flex-wrap: wrap` and stack themselves
 * on a phone. The homepage hero's does not, so "Offerte anfragen" and
 * "Leistungen entdecken" stayed side by side in ~160px each and broke across
 * two lines. Letting the row wrap is the design's own behaviour on every other
 * page, and it changes nothing above ~420px where both labels still fit. */
[data-o365-ctarow] {
  flex-wrap: wrap;
}

/* --- 5d. Before/after sliders -------------------------------------------
 * Dragging is the only way to use these, so the handle has to be thumb-sized
 * and the page must not take the gesture as a scroll. */
@media (max-width: 899px) {
  [data-o365='ba-pair'] {
    touch-action: pan-y;
  }
  [data-o365-ba='handle'] {
    width: 48px !important;
    height: 48px !important;
  }
}

/* --- 5e. Horizontal galleries -------------------------------------------
 * `.gal` is a deliberate scroll strip. It only needs the same affordance the
 * nav gets, and momentum scrolling on iOS. */
@media (max-width: 899px) {
  .gal {
    -webkit-overflow-scrolling: touch;
    scroll-padding-inline: clamp(20px, 6vw, 40px);
  }
}

/* ==========================================================================
   6. Touch targets
   ========================================================================== */

/* WCAG 2.2 AA wants 24px; Apple and Google both say 44. */
@media (pointer: coarse) {
  .slot-btn,
  .calc-toggle,
  .calc-check,
  [data-o365-role='prev'],
  [data-o365-role='next'] {
    min-height: 44px;
  }

  /* Text-only links — footer items, legal links, the sister-site link — render
   * 16-21px tall: below the minimum and, stacked 10px apart, easy to mis-tap.
   * Growing them with padding would push the footer around, so the hit area
   * grows with an overlay instead: nothing moves, the target gets bigger.
   * The build tags exactly the links that need it (annotateLayout in
   * render.mjs), so a link wrapping a button or a whole card — which already
   * has a large target — never gets a transparent box laid over it.
   *
   * Vertical only. These links are 50px+ wide already, so height is the whole
   * problem; and a horizontal inset on whichever link sat nearest a container's
   * right edge widened the document by exactly its own 6px — invisibly, because
   * a pseudo-element contributes scroll width without being an element anyone
   * can find in the DOM. 4px stays inside the 10px stacking gap, so neighbours
   * cannot steal each other's taps. */
  [data-o365-textlink] {
    position: relative;
  }
  [data-o365-textlink]::after {
    content: '';
    position: absolute;
    inset: -4px 0;
  }

  /* The carousel dots are 7px by design. Grow the hit area, not the dot. */
  [data-o365-role='dots'] > [data-o365-dot] {
    position: relative;
  }
  [data-o365-role='dots'] > [data-o365-dot]::after {
    content: '';
    position: absolute;
    inset: -16px -10px;
  }
}

/* The print rule in site.css appends the href to external links via ::after.
   The hit-area overlay above would fight it — print has no pointer anyway. */
@media print {
  [data-o365-textlink]::after {
    position: static;
    inset: auto;
  }
}

/* ==========================================================================
   7. Landscape phones
   ========================================================================== */

/* A sticky header costs a third of a 390px-tall viewport. Below that height it
 * scrolls away with the page instead. */
@media (max-height: 460px) and (orientation: landscape) {
  [data-o365='header'] {
    position: static !important;
  }
}
