/* box-sizing lives in framework_v3.css, which uses an inherit-based reset
   so components can opt out. The universal border-box rule that used to be
   here loaded later and silently defeated it.

   body no longer sets overflow-x: hidden either. website_structure.css
   already clips on html, and `hidden` on one axis forces the other to
   `auto` — which turns body into a scroll container and breaks
   position: sticky for everything inside it. `clip` has no such effect. */

#header .background,
#header .background_image {
    overflow-x: hidden;
}

html {scroll-behavior: smooth;}

/* Moved here from framework_v3.css, which must own no CMS-specific
   selector. .content-image is Contao markup. */
.content-image.center_element img {
    margin-inline: auto;
}

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

body,
form,
fieldset,
h1,
h2,
h3,
h4,
h5,
h6,
figure,
p.active,
p.confirm {

    margin: 0;
    padding: 0;
}

a img { border-bottom: 0; }

header,
footer,
nav,
section,
aside,
article,
figure,
figcaption,
.custom,
.tableless label,
.checkbox_container span,
.radio_container span{
    display: block;
}

/* Controls do not inherit type by default. The old `font-size: 99%` was a
   legacy hack, and it put controls under 16px on mobile — which makes iOS
   Safari zoom the page whenever a field is focused. max() keeps the page's
   own size on desktop and never drops below 16px. */
select,
input,
textarea {
    font-family: inherit;
    font-size: max(16px, 1em);
}

/* .block is a float-clearing relic. Contao puts it on EVERY article, and in
   the layout framework an article is a .row — so an unscoped `overflow:
   hidden` here silently clips any .bleed-x inside a row (and creates a new
   block formatting context on every row). Excluding .row keeps the legacy
   clearing behaviour everywhere it is still wanted. */
.block:not(.row) {
    overflow: hidden;
}

/* Visually hidden but still announced. The previous version used zero
   dimensions and left:-1000px; zero-size elements are skipped by some
   screen readers, and off-screen positioning can cause horizontal scroll
   in RTL. Contao uses this for real assistive text, e.g.
   <span class="invisible">Mandatory field</span>. */
.invisible {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* A visible focus indicator is required (WCAG 2.4.7). This replaces an
   `outline: none` that removed it with no substitute, which left keyboard
   users unable to tell which control they were in — the site search box
   included. :focus-visible keeps it off for mouse users. */
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--prime);
    outline-offset: 2px;
}

textarea { min-height: 150px; }

fieldset {
    border: 0;
    margin-inline: 0;
    padding-inline: 0;
    padding-block: 0;
}

fieldset table,
input.text,
input.email,
textarea {
    width: 100%;
}

input.captcha {
    width: 50px;
}

input.checkbox,
.tableless .checkbox_container label,
.checkbox_container span.mandatory,
input.radio,
.tableless .radio_container label,
.radio_container span.mandatory,
.tableless span label,
address,
.pdf_link img,
.content-download img,
.content-downloads img,
.enclosure img {
    display: inline;
}

/* Strip the platform's native control chrome. The body.ios variants that
   used to be listed here were already covered by the unprefixed selectors
   below them, and the companion `-moz-border-radious: 0` was a misspelling
   of a property that has been unnecessary since Firefox 4. */
input.text,
input.submit,
.textarea,
textarea,
input.captcha,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
    appearance: none;
}

/* --second matches forms.css, so the required-field asterisk is one colour
   whether the form is a content element or a module. */
.error,
span.mandatory {
    color: var(--second);
}

.confirm {
    color: var(--prime);
}

p.error {
    margin: 0;
    padding-top: 10px;
}

.content-download img,
.content-downloads img,
.enclosure img {
    vertical-align: top;
}

.content-downloads p,
.enclosure p,
p.error {
    padding-bottom: 0;
}

.enclosure { padding-top: 20px; }
/* Table styling lives in tables.css. This stays as a bare reset for
   any table outside a .content-table element. */
table { border-spacing: 0; }
address { font-style: normal; }

/* IMAGE POSITION IN A TEXT ELEMENT
   --------------------------------
   These are FLOATS, deliberately, not flex.

   The editor's choice is literally "floating: left/right", and what that
   means is that the text WRAPS around the image and then continues full
   width beneath it. Flex cannot do that — it produces two rigid columns,
   so a short paragraph beside a tall image leaves a large empty gap and
   the text never flows underneath.

   `display: flow-root` on the parent contains the float, so the image can
   never escape the bottom of its content element. It is the modern
   replacement for a clearfix pseudo-element or `overflow: hidden`, and
   unlike overflow it creates no scroll container and no clipping. */

.content-text.media { display: flow-root; }

/* SPACING AROUND A FLOATED IMAGE
   The gap between the image and the text beside it is a GUTTER — the same
   idea as the space between two columns — so it uses the gutter scale
   rather than a hardcoded value.

   --row-gap-x already steps down at the framework's breakpoints
   (40 -> 24 -> 16), so the float gap tightens on smaller screens in step
   with every column gutter on the site. Retuning --gutter-40 moves both.

   The space BELOW the image is block rhythm, not a gutter, so it comes
   from the spacing scale instead. */

/* A plain `max-width: 50%` keeps scaling with the container, so on a wide
   site the image reaches ~900px and dominates the composition. The pixel
   cap stops it growing once there is plenty of room, which keeps the text
   column reading like a column rather than a narrow strip beside a poster.

   min() takes whichever is smaller, so narrow screens still get the 50%
   behaviour and wide screens settle at a sensible fixed size. */
.content-text.media--left figure,
.content-text.media--right figure {
    max-width: min(50%, 480px);
}

.content-text.media--left figure {
    float: left;
    margin: 0 var(--row-gap-x) var(--space-24) 0;
}

.content-text.media--right figure {
    float: right;
    margin: 0 0 var(--space-24) var(--row-gap-x);
}

/* above / below simply stack. The figure is first in the DOM, so `below`
   needs the order reversed. */
.content-text.media.media--above figure { margin-bottom: var(--space-24); }

.content-text.media.media--below {
    display: flex;
    flex-direction: column-reverse;
}

.content-text.media.media--below figure { margin-top: var(--space-24); }

/* Too narrow to wrap around: let the image take its own line.

   TUNING NOTE — the awkward zone is roughly 601-768px.
   In that range the image is still taking ~50% while the gutter is still
   at its desktop 40px, so the text column can drop to around 350px and
   the wrap goes ragged. Measured at an 820px viewport: 754px container,
   361px image, 353px of text.

   If a particular site's images or copy make that look bad, raise this
   breakpoint to 768px so the image unfloats before the column gets tight:

       @media (max-width: 768px) { ... }

   Left at 600px by default because it keeps the wrap on small tablets,
   which suits shorter captions and portrait images. Change per site
   rather than globally — it depends on the imagery. */
@media (max-width: 600px) {
    .content-text.media--left figure,
    .content-text.media--right figure {
        float: none;
        max-width: none;
        margin: 0 0 var(--space-24);
    }
}

/* Legacy float utilities, still available for hand-written content. */
.float_left { float: left; }
.float_right figure { float: right; }

.float_clear,
.center,
.clear,
#clear {
    clear: both;
}

/* --------------------------------------------------------------------------
   VIDEO EMBEDS — YouTube and Vimeo
   --------------------------------------------------------------------------
   Contao renders the player as <iframe width="640" height="360">. Those are
   HTML attributes rather than CSS, but they still size the element — so
   overriding only the width makes the embed scale sideways while its height
   stays locked at 360px. It also explains why aspect-ratio alone did nothing
   here: with BOTH dimensions definite, aspect-ratio is ignored outright.

   `height: auto` is what releases it. Once the height is auto the ratio has
   something to drive, and the embed scales on both axes.

   The framework gives .row > * img/svg/video a height: auto but deliberately
   skips iframe, because a bare iframe — a map, an external form — has to
   keep its attribute height; with no ratio to work from it would collapse to
   the 150px default size. So the release happens HERE instead, scoped to the
   players, where a ratio is guaranteed by the rule below.
   -------------------------------------------------------------------------- */

.content-youtube,
.content-vimeo,
.content-youtube iframe,
.content-vimeo iframe {
    width: 100%;
}

.content-youtube iframe,
.content-vimeo iframe {
    height: auto;

    /* The ratio dropdown is optional — Contao ships a blank option — so a
       default is required or an embed with nothing chosen would collapse.
       16/9 is exactly what Contao's own 640x360 attributes describe, so the
       unset case keeps the shape it was already rendering, just fluid.

       Using playerSize for a non-16:9 embed without picking a ratio? Set
       --video-ratio on that element rather than hardcoding a pixel size. */
    aspect-ratio: var(--video-ratio, 16 / 9);

    /* block removes the inline descender gap beneath the player; the auto
       margins centre the tall ratios once max-width starts constraining. */
    display: block;
    margin-inline: auto;
}

/* Every ratio Contao offers. The class is literally `aspect--16:9`, so the
   colon has to be escaped. */
.content-youtube .aspect--16\:9 iframe,
.content-vimeo .aspect--16\:9 iframe {
    aspect-ratio: 16 / 9;
}

.content-youtube .aspect--16\:10 iframe,
.content-vimeo .aspect--16\:10 iframe {
    aspect-ratio: 16 / 10;
}

.content-youtube .aspect--21\:9 iframe,
.content-vimeo .aspect--21\:9 iframe {
    aspect-ratio: 21 / 9;
}

.content-youtube .aspect--4\:3 iframe,
.content-vimeo .aspect--4\:3 iframe {
    aspect-ratio: 4 / 3;
}

.content-youtube .aspect--3\:2 iframe,
.content-vimeo .aspect--3\:2 iframe {
    aspect-ratio: 3 / 2;
}

/* Portrait and square are the two ratios that can run away: in a full-width
   1800px container a 9:16 player works out over 3000px tall. Both are capped
   by WIDTH rather than height — with the width pinned at 100%, a max-height
   would letterbox inside the player instead of resizing it. */
.content-youtube .aspect--9\:16 iframe,
.content-vimeo .aspect--9\:16 iframe {
    aspect-ratio: 9 / 16;
    max-width: calc(var(--video-tall-max, 80vh) * 9 / 16);
}

.content-youtube .aspect--1\:1 iframe,
.content-vimeo .aspect--1\:1 iframe {
    aspect-ratio: 1 / 1;
    max-width: var(--video-tall-max, 80vh);
}


/* --------------------------------------------------------------------------
   SELF-HOSTED PLAYER — the "player" content element
   --------------------------------------------------------------------------
   Same element for both: Contao renders <video> or <audio> depending on the
   file's mime type, wrapped in the figure component so a caption becomes a
   <figcaption>.

   The width/height fields in the back end become HTML ATTRIBUTES on the
   media element, exactly as they do on the YouTube iframe above. The
   difference is that <video> has an intrinsic size, so no ratio needs to be
   invented here: browsers already derive aspect-ratio from those attributes
   the way they do for <img>. Setting an explicit ratio would be actively
   wrong — it would force a 4:3 clip into 16:9.

   So the whole job is releasing the pinned width. Left alone, a video with
   width="640" sits at 640px inside a wider column, and one with no size set
   at all falls back to the 300x150 default for a replaced element.
   -------------------------------------------------------------------------- */

/* Bounded on BOTH axes rather than stretched to the column width. A <video>
   carries an intrinsic ratio, so a max-width and a max-height together are
   enough: the browser shrinks it to satisfy whichever bites first and keeps
   the ratio itself. No ratio has to be declared, and none should be.

   The height ceiling is what makes portrait usable. Measured with a real
   2160x3840 clip: at width 100% it rendered 639x1135 — 1.17x the viewport
   height, and about 3200px tall in a full-width container. With the ceiling
   it settles to roughly 435x774 and stays on screen.

   Trade-off worth knowing: width is `auto`, not 100%, so a video whose
   intrinsic width is SMALLER than the column renders at its own size instead
   of being upscaled. That is deliberate — upscaled video looks soft — but if
   a small clip must fill the column, set the width in the element's player
   size field. */
.content-player video {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: var(--video-tall-max, 80vh);
    margin-inline: auto;
}

/* Audio has no picture, so it has a fixed control height and only the width
   is worth touching. Full width because a centred 300px control bar in a
   wide column reads as broken rather than deliberate. */
.content-player audio {
    display: block;
    width: 100%;
}

.content-player figure {
    margin: 0;
}

.content-player figcaption {
    margin-top: var(--space-8);
    font-size: 0.875em;
    color: var(--grey-dark);
}

/* --------------------------------------------------------------------------
   PASTED EMBEDS — raw <iframe> in a text or HTML element
   --------------------------------------------------------------------------
   Clients often skip the video content element and paste YouTube's embed
   code straight into the rich text editor. That markup carries no `aspect`
   class and no .content-youtube wrapper, so none of the rules above reach
   it — it arrives as a bare <iframe width="560" height="315">.

   The ratio is genuinely unknown to CSS: it exists only in those two HTML
   attributes, and until recently no CSS could read them. So this works in
   three layers, cheapest first.

   Note a pasted embed is NOT distorted the way a stretched image would be —
   an iframe re-lays out its contents rather than scaling them. A too-narrow
   player letterboxes internally. So the failure mode for anything missed
   here is mild, which is why the rules stay conservative.

   Two places a pasted embed can land, and they are structurally different:

     .rte              inside a text element — a normal descendant
     .mod_article >    the HTML and unfiltered HTML elements emit their
                       markup with NO wrapper div at all, so the embed
                       becomes a DIRECT CHILD of the article

   The second case is the one that surprises. There is no .content-* box to
   hook onto, and a column class set in the back end is silently dropped
   because there is nothing to put it on — which also means the embed becomes
   a flex item of the row and needs to be told to take its own line.
   -------------------------------------------------------------------------- */

/* 1. Nothing pasted may break out of its column, whatever it is. */
.rte :is(iframe, embed, object, video),
.mod_article > :is(iframe, embed, object, video) {
    max-width: 100%;
}

/* An unwrapped embed is a flex item of the row. Give it a full line so it
   does not sit alongside the real columns as if it were one. */
.mod_article > :is(iframe, embed, object, video, .embed-fluid) {
    flex: 0 0 100%;
}

/* 2. Known video hosts get the fluid treatment, with 16:9 assumed — right
      for essentially every player embed code in circulation.

      Deliberately scoped by src rather than applied to every iframe: a map,
      a Spotify player (152px tall), a booking widget or a form embed would
      be wrecked by being forced into 16:9. Matching on host means those keep
      their own height and only video is treated as video.

      Add a host by extending this list. */
:is(.rte iframe, .mod_article > iframe):is(
    [src*="youtube.com"], [src*="youtube-nocookie.com"], [src*="youtu.be"],
    [src*="player.vimeo.com"], [src*="loom.com"], [src*="wistia.net"],
    [src*="dailymotion.com"], [src*="videopress.com"]),
.embed-fluid iframe {
    width: 100%;
    height: auto;                             /* release the attribute height */
    aspect-ratio: var(--video-ratio, 16 / 9);
    display: block;
}

/* 3. Where the browser can read typed attr(), take the REAL ratio from the
      embed's own width/height instead of assuming. This is what makes a
      pasted 4:3 or portrait embed correct rather than merely contained.

      Progressive enhancement only — browsers without it keep the 16:9 above,
      which is why layer 2 is not written as a fallback inside this block.
      [width][height] is required in the selector so the function always has
      both attributes to read. */
@supports (aspect-ratio: attr(width type(<number>)) / attr(height type(<number>))) {
    :is(.rte iframe, .mod_article > iframe)[width][height]:is(
        [src*="youtube.com"], [src*="youtube-nocookie.com"], [src*="youtu.be"],
        [src*="player.vimeo.com"], [src*="loom.com"], [src*="wistia.net"],
        [src*="dailymotion.com"], [src*="videopress.com"]),
    .embed-fluid iframe[width][height] {
        aspect-ratio: attr(width type(<number>), 16) / attr(height type(<number>), 9);

        /* Same 80vh ceiling the 9:16 and 1:1 elements get, for the same
           reason: a pasted portrait embed measured 777x1381 without it.
           Capping WIDTH keeps the ratio true — the width that produces a
           full-height box is height-limit x (w/h). Landscape embeds are far
           from the ceiling at any normal width, so this only ever bites on
           tall ones. */
        max-width: calc(var(--video-tall-max, 80vh)
                        * attr(width type(<number>), 16)
                        / attr(height type(<number>), 9));
        margin-inline: auto;
    }
}

/* TUNING
     --video-ratio      fallback ratio when the editor picks none, and the
                        assumed ratio for a pasted embed. 16/9.
     --video-tall-max   height ceiling for 9:16 and 1:1. 80vh leaves the
                        page furniture visible around a portrait embed.

   An embed from a host not listed above keeps its own size. To make one
   fluid, wrap it in <div class="embed-fluid"> in the editor's source view;
   add style="--video-ratio: 4/3" to that div if it is not 16:9. */

iframe { border: 0; }

/* ==========================================================================
   IMAGE RATIOS — crop a photo to a frame with a Theme Toolbox class
   ==========================================================================
   A class on an image element (ratio-3x2 …) crops the photo to that frame
   with object-fit, so an editor can drop in any photo. Faces: set
   --ratio-focus (e.g. 50% 20%) on the element. Crops belong here, in CSS —
   every image uses the "Webp Convert" size (original dimensions, as WebP). */
:is(.ratio-1x1, .ratio-4x3, .ratio-3x2, .ratio-16x9, .ratio-21x9) img {
    width: 100%;
    height: auto;
    object-fit: cover;
    object-position: var(--ratio-focus, 50% 50%);
}
.ratio-1x1 img  { aspect-ratio: 1 / 1; }
.ratio-4x3 img  { aspect-ratio: 4 / 3; }
.ratio-3x2 img  { aspect-ratio: 3 / 2; }
.ratio-16x9 img { aspect-ratio: 16 / 9; }
.ratio-21x9 img { aspect-ratio: 21 / 9; }
